From f0b024086f6fb062bc2d760453f05cd7f885a22c Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Sat, 4 Jul 2026 12:43:13 -0400 Subject: [PATCH 1/6] =?UTF-8?q?docs(plans):=20plan=20gitsheets=201.4.1=20?= =?UTF-8?q?=E2=86=92=202.x=20bump=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Version-bump-only scope: 2.x is a Rust-core engine rewrite behind an unchanged Node API. Real work is the hologit-drop blob-write migration + two documented byte re-baselines (integer underscores, markdown bodies). The three cache workarounds are ported, not deleted — gitsheets#184 (the per-sheet refresh API that would let them go) is open and not in 2.x. Co-Authored-By: Claude Opus 4.8 (1M context) --- plans/gitsheets-2x-bump.md | 113 +++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 plans/gitsheets-2x-bump.md diff --git a/plans/gitsheets-2x-bump.md b/plans/gitsheets-2x-bump.md new file mode 100644 index 0000000..5e9daa2 --- /dev/null +++ b/plans/gitsheets-2x-bump.md @@ -0,0 +1,113 @@ +--- +status: in-progress +depends: [] +specs: + - specs/behaviors/storage.md +issues: + - 150 +pr: +--- + +# Plan: upgrade gitsheets 1.4.1 → 2.x (Rust core) + +## Scope + +Bump `gitsheets` from `^1.4.1` to `^2.2.0` (the Rust-core line). This plan is +the **version bump only** — the "retire the three cache workarounds" half of +issue #150 is **out of scope and blocked** on upstream `gitsheets#184` +(per-sheet refresh API), which is OPEN, unassigned, and **not shipped in 2.x** +(verified: 2.x `Sheet` still carries the pre-commit `dataTree` snapshot). The +workarounds are therefore **ported/verified, not deleted**. + +## What actually changes in 2.x (verified against the gitsheets source) + +- **The Node public API is deliberately unchanged** — 2.x is an engine rewrite + behind the same `openRepo`/`openStore`/`Sheet`/`Transaction` surface + (`specs/rust-core.md` → "No consumer-visible public-API change"). So most of + our code compiles as-is. +- **`hologit` is dropped as a gitsheets dependency** (2.x deps are + `@gitsheets/core-napi` + csv/rfc6902/sort-keys/yargs — no hologit). This is + the **one real breaking change for us**: our avatar-blob-write path imports + `BlobObject` from `hologit` and uses `publicRepo.hologitRepo`. +- **Two deliberate one-time byte re-baselines** (data-level, lossless — values + unchanged, only formatting): + 1. **Canonical TOML**: Rust `toml`/`toml_edit` drops integer underscores + (`legacyId = 31_618` → `31618`) across all sheets (matches gitsheets#196). + 2. **Markdown body**: content-typed sheets (our **blog-posts**) normalize the + body via native `dprint` instead of `markdownlint`. + +## Work + +### 1. Dependency bump + +- `npm install gitsheets@^2.2.0 -w apps/api`. Commit the generated + package.json + lock change first (drops hologit transitively). + +### 2. Migrate the blob-write path off `hologit` (the real code change) + +Replace the `hologit` `BlobObject.write(publicRepo.hologitRepo, buf)` pattern +with the 2.x `Repository.writeBlob(buf): Promise` + `setAttachment` +API. Sites: + +- `apps/api/src/routes/people.ts:19,407,430-431` (avatar upload) +- `apps/api/scripts/import-laddr/importer.ts:95,503,516-517,583-590` (legacy + avatar + media import) +- Drop the `as unknown as string` casts — 2.x `writeBlob` takes a `Buffer`. +- Confirm the exact 2.x `setAttachment(s)` signature in the gitsheets `Sheet` + API (`AttachmentBlobHandle` / `BlobHandle`) and wire accordingly. +- Grep for any other `hologit` / `hologitRepo` / `BlobObject` / `TreeObject` + references and migrate; ensure `hologit` is NOT needed as a direct dep. + +### 3. Verify (do NOT delete) the #184 workarounds still compile + work + +- `apps/api/src/store/store.ts` `swapPublic()` — re-opens the store via + `openPublicStore`; API unchanged, should be fine. Confirm it doesn't touch + `hologitRepo`. +- `apps/api/src/routes/attachments.ts` — raw `git cat-file` (git-level, not + gitsheets) — unaffected; confirm. +- `apps/api/src/lib/data-repo-lock.ts` — our own mutex — unaffected; confirm. + +### 4. Incidental cast checks (issue #150) + +- `apps/api/src/store/public.ts` `asValidator()` (Zod v4 ↔ `StandardSchemaV1`). + 2.x still exports `StandardSchemaV1`/`ValidatorMap`; keep the cast if still + needed, simplify if 2.x makes it clean. Don't force it. + +### 5. Validation + +- `npm run -w packages/shared build` (exports map points at dist), then + `npm run type-check` + `npm run lint` clean. +- **Full api test suite green.** The byte re-baseline will break any test that + asserts exact TOML bytes containing integer underscores, or exact blog-post + body bytes — fix those to the new canonical form (they're re-baseline + updates, not behavior changes; note each in the commit). +- **Byte-parity check on real data**: load the `published` import under 2.x and + confirm the change is **lossless** — parsed values identical to 1.4.1, only + the documented re-baselines (integer underscores, markdown bodies) differ. + (Mirror the approach used for the 1.4.1 swap.) +- Sanity-check the migrated blob path end to end: an avatar upload writes both + `avatar.jpg` + `avatar-128.jpg` attachments with the right `avatarKey`. + +## Out of scope / follow-ups + +- **Retiring `swapPublic` / attachments `git cat-file` / `data-repo-lock`** — + blocked on `gitsheets#184`; separate effort once that lands upstream. Keep + #150's second half open (or split it out). +- **Data-repo re-normalization commit** — under 2.x, records re-serialize + without integer underscores as they're written, so the repo drifts to mixed + format until fully rewritten. A deliberate one-time re-normalize (rewrite all + records) is cleaner but is a **data-ops task on `codeforphilly-data`**, not + part of this code bump. Flag it; do it deliberately (likely bundled with the + cutover data prep). +- **Timing note:** 2.x is days old (Rust rewrite) and we're near production + cutover (#54). This bump is validated but should merge on a deliberate + decision, not reflexively before cutover. + +## Validation checklist + +- [ ] deps bumped; hologit gone from lock +- [ ] blob-write path migrated off hologit; casts removed +- [ ] workarounds verified (compile + covered by tests) +- [ ] type-check + lint clean +- [ ] full api suite green (re-baseline test updates noted) +- [ ] byte-parity on `published` = lossless (only documented re-baselines) From 2b3bff7b50a43dad3c3df1c11ea75c5f870194db Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Sat, 4 Jul 2026 12:46:55 -0400 Subject: [PATCH 2/6] chore(deps): bump gitsheets to 2.x (Rust core) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upgrade gitsheets ^1.4.1 → ^2.2.0. The 2.x line rewrites the core in Rust (via @gitsheets/core-napi) but keeps the Node public API surface (openRepo/openStore/Sheet/Transaction) unchanged. Command run: npm install gitsheets@^2.2.0 -w apps/api hologit is dropped as a transitive dependency in 2.x; it no longer appears in the lockfile. Code migration (avatar blob-write path) is in the next commit. Co-Authored-By: Claude Sonnet 4.6 --- apps/api/package.json | 2 +- package-lock.json | 1171 +++++------------------------------------ 2 files changed, 130 insertions(+), 1043 deletions(-) diff --git a/apps/api/package.json b/apps/api/package.json index b5425ed..a7ff31e 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -35,7 +35,7 @@ "bcryptjs": "^3.0.3", "better-sqlite3": "^12.10.0", "fastify": "^5.8.5", - "gitsheets": "^1.4.1", + "gitsheets": "^2.2.0", "jose": "^6.2.3", "resend": "^6.12.4", "samlify": "^2.13.0", diff --git a/package-lock.json b/package-lock.json index 1dbe57d..aa3e9f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "bcryptjs": "^3.0.3", "better-sqlite3": "^12.10.0", "fastify": "^5.8.5", - "gitsheets": "^1.4.1", + "gitsheets": "^2.2.0", "jose": "^6.2.3", "resend": "^6.12.4", "samlify": "^2.13.0", @@ -1145,15 +1145,6 @@ "resolved": "apps/web", "link": true }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/@csstools/color-helpers": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", @@ -1294,17 +1285,6 @@ "node": ">=20.19.0" } }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", - "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", - "license": "MIT", - "dependencies": { - "@so-ric/colorspace": "^1.1.6", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, "node_modules/@dotenvx/dotenvx": { "version": "1.66.0", "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.66.0.tgz", @@ -2534,6 +2514,119 @@ "url": "https://github.com/sponsors/ayuhito" } }, + "node_modules/@gitsheets/core-napi": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@gitsheets/core-napi/-/core-napi-0.1.1.tgz", + "integrity": "sha512-ZoJGzwUo8K0ok/XiQpGJ5k4lEuss21ZcDP8+pIGHMsdMkXmLlXNZhPij1Z0MtvFJIu3nu3mCqWVFR8cJf5jNsA==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "@gitsheets/core-napi-darwin-arm64": "0.1.1", + "@gitsheets/core-napi-darwin-x64": "0.1.1", + "@gitsheets/core-napi-linux-arm64-gnu": "0.1.1", + "@gitsheets/core-napi-linux-x64-gnu": "0.1.1", + "@gitsheets/core-napi-linux-x64-musl": "0.1.1", + "@gitsheets/core-napi-win32-x64-msvc": "0.1.1" + } + }, + "node_modules/@gitsheets/core-napi-darwin-arm64": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@gitsheets/core-napi-darwin-arm64/-/core-napi-darwin-arm64-0.1.1.tgz", + "integrity": "sha512-1rHQT78zFwOIeD2Gf9LWNmIzm17wibxYXz5U8F0SOY51W+BIfklnYanfSywWHmE1FclqIVOE3ngFKZEiH/XgpQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20" + } + }, + "node_modules/@gitsheets/core-napi-darwin-x64": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@gitsheets/core-napi-darwin-x64/-/core-napi-darwin-x64-0.1.1.tgz", + "integrity": "sha512-YDcId/vr8kw5hem3S78XVlxmdv/Nc7d8HaTSA2PSRVoMUHpjo3apx7g4agjthrKW5mUms14kKGOzG8s2Hn/G5w==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20" + } + }, + "node_modules/@gitsheets/core-napi-linux-arm64-gnu": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@gitsheets/core-napi-linux-arm64-gnu/-/core-napi-linux-arm64-gnu-0.1.1.tgz", + "integrity": "sha512-qfGKIccxWnFPiyJ/GCuU1H+YVejdcfBGKcjKlHn/c9A8CUglCfXhruIYhOa0Q/AKbdVcIumG1UQ+QcdJkHBPCw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20" + } + }, + "node_modules/@gitsheets/core-napi-linux-x64-gnu": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@gitsheets/core-napi-linux-x64-gnu/-/core-napi-linux-x64-gnu-0.1.1.tgz", + "integrity": "sha512-G+7j08FLBIGzbjZK3BRZQ0sI8/i4r4CuPrESt2HZtBclsVWGEzx2X6avnLUlAOFZZVphPZgq6kqMxDLSUMl4og==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20" + } + }, + "node_modules/@gitsheets/core-napi-linux-x64-musl": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@gitsheets/core-napi-linux-x64-musl/-/core-napi-linux-x64-musl-0.1.1.tgz", + "integrity": "sha512-6cdOSsSGmpXq9raKQRv8Npro393H0FfvSbPEvt1V39ebEGOuhYSDr+H0UYH6rxv4IcYeCOPjdgZMOYTuhD94oA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20" + } + }, + "node_modules/@gitsheets/core-napi-win32-x64-msvc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@gitsheets/core-napi-win32-x64-msvc/-/core-napi-win32-x64-msvc-0.1.1.tgz", + "integrity": "sha512-+dDxvrXbtkP6D1Ghpnk5F+N/MGkgTClng0SxuoUmy7Bfi1RYu5Syj8kE1Dz91dGLvVU4U/+SK44hJMv/OOvONw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20" + } + }, "node_modules/@hono/node-server": { "version": "1.19.14", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", @@ -2624,12 +2717,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", - "license": "ISC" - }, "node_modules/@img/colour": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", @@ -5328,16 +5415,6 @@ "node": ">=14.0.0" } }, - "node_modules/@so-ric/colorspace": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", - "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", - "license": "MIT", - "dependencies": { - "color": "^5.0.2", - "text-hex": "1.0.x" - } - }, "node_modules/@stablelib/base64": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", @@ -5833,12 +5910,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/katex": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", - "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", - "license": "MIT" - }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -5898,12 +5969,6 @@ "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", "license": "MIT" }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT" - }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -6376,18 +6441,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", @@ -6445,12 +6498,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, "node_modules/argon2": { "version": "0.44.0", "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.44.0.tgz", @@ -6526,25 +6573,11 @@ "node": ">=4" } }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/async-exit-hook": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, "license": "MIT" }, "node_modules/atomic-sleep": { @@ -6576,18 +6609,6 @@ "fastq": "^1.17.1" } }, - "node_modules/axios": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", - "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -6779,15 +6800,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -6974,31 +6986,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -7082,19 +7069,6 @@ "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", "license": "MIT" }, - "node_modules/color": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", - "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", - "license": "MIT", - "dependencies": { - "color-convert": "^3.1.3", - "color-string": "^2.1.3" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -7113,48 +7087,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, - "node_modules/color-string": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", - "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", - "license": "MIT", - "dependencies": { - "color-name": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/color-string/node_modules/color-name": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", - "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/color/node_modules/color-convert": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", - "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", - "license": "MIT", - "dependencies": { - "color-name": "^2.0.0" - }, - "engines": { - "node": ">=14.6" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", - "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -7166,6 +7098,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -7193,12 +7126,6 @@ "node": ">=20" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, "node_modules/concurrently": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", @@ -7433,18 +7360,6 @@ "node": "*" } }, - "node_modules/debounce": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-3.0.0.tgz", - "integrity": "sha512-64byRbF0/AirwbuHqB3/ZpMG9/nckDa6ZA0yd6UnaQNwbbemCOwvz2sL5sjXLHhZHADyiwLm0M5qMhltUUx+TA==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -7580,6 +7495,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -7709,12 +7625,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "license": "MIT" - }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -7837,6 +7747,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8567,15 +8478,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -8593,12 +8495,6 @@ } } }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT" - }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -8741,36 +8637,11 @@ "dev": true, "license": "ISC" }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "license": "MIT" - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -8833,12 +8704,6 @@ "node": ">=14.14" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -8984,18 +8849,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/git-client": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/git-client/-/git-client-1.12.1.tgz", - "integrity": "sha512-Px7HE2ug+IKiOhNE/vezkwpDE7IUYHnoRHQfds2BlRf6CZglNq7lmlxVoRtprJ4ZHTq0ZWrQLZtrcMxFw18zvw==", - "license": "MIT", - "dependencies": { - "async-exit-hook": "^2.0.1", - "mz": "^2.7.0", - "rusha": "^0.8.14", - "semver": "^7.6.3" - } - }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -9003,20 +8856,15 @@ "license": "MIT" }, "node_modules/gitsheets": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/gitsheets/-/gitsheets-1.4.1.tgz", - "integrity": "sha512-/GIsDgsjweXcNk8ThYjAsWYctGr/xn0iVFE2J4u7yvJgmQdLSsOdLgqAwlny8MinhStniqpPm1XE+tvjt615JQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/gitsheets/-/gitsheets-2.2.0.tgz", + "integrity": "sha512-Ul04Ap8uA5GjljA6fM3VBBYCb6lYDaXSxpLdmNHDoac3JmV11q7j6F89sZqdla0/P8yQGjVGvyptcu0PTBdCgg==", "license": "Apache-2.0", "dependencies": { - "@iarna/toml": "^2.2.5", - "ajv": "^8.20.0", - "ajv-formats": "^3.0.1", + "@gitsheets/core-napi": "^0.1.0", "csv-parse": "^6.2.1", "csv-stringify": "^6.7.0", - "hologit": "^0.50.2", - "markdownlint": "^0.40.0", "rfc6902": "^5.2.0", - "smol-toml": "^1.7.0", "sort-keys": "^6.0.0", "yargs": "^18.0.0" }, @@ -9147,27 +8995,6 @@ "node": "^20.19.0 || ^22.12.0 || >=23" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -9181,34 +9008,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/globals": { "version": "17.6.0", "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", @@ -9249,38 +9048,6 @@ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, - "node_modules/hab-client": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hab-client/-/hab-client-1.1.3.tgz", - "integrity": "sha512-CZzvibCCQqwk3XZeNh2DWotOcnGViOjTny7NQAkSZid014OGZu+gfhLmTAj2qnUxDKr/ZImRauFLorFR38IwGQ==", - "license": "MIT", - "dependencies": { - "axios": "^1.3.4", - "semver": "^7.3.8", - "underscore": "^1.13.6" - } - }, - "node_modules/handlebars": { - "version": "4.7.9", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", - "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9307,6 +9074,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -9421,162 +9189,6 @@ "hermes-estree": "0.25.1" } }, - "node_modules/hologit": { - "version": "0.50.2", - "resolved": "https://registry.npmjs.org/hologit/-/hologit-0.50.2.tgz", - "integrity": "sha512-9StuAaE8TkdnJE/cA5vdq+pwt/hZH1JGa9qsGfAz7rvwsXrVlQT/lBJd+pedpJHOJgPdA4NXC0lqljFzOWV+7A==", - "license": "MIT", - "os": [ - "darwin", - "linux" - ], - "dependencies": { - "@iarna/toml": "^2.2.5", - "async-exit-hook": "^2.0.1", - "axios": "^1.14.0", - "chokidar": "^5.0.0", - "debounce": "^3.0.0", - "fb-watchman": "^2.0.2", - "git-client": "^1.12.0", - "hab-client": "^1.1.3", - "handlebars": "^4.7.9", - "minimatch": "^10.2.4", - "mz": "^2.7.0", - "mz-modules": "^2.1.0", - "object-squish": "^1.1.0", - "parse-url": "^11.1.0", - "shell-quote-word": "^1.0.1", - "sort-keys": "^6.0.0", - "toposort": "^2.0.2", - "winston": "^3.19.0", - "yargs": "^18.0.0" - }, - "bin": { - "git-holo": "bin/cli.js" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/hologit/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/hologit/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/hologit/node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/hologit/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/hologit/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hologit/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/hologit/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/hologit/node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/hologit/node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, "node_modules/hono": { "version": "4.12.19", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.19.tgz", @@ -9629,19 +9241,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/human-signals": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", @@ -9732,17 +9331,6 @@ "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -9773,46 +9361,12 @@ "node": ">= 10" } }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "license": "MIT" }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -9858,16 +9412,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-in-ssh": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", @@ -10220,31 +9764,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/katex": { - "version": "0.16.47", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", - "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], - "license": "MIT", - "dependencies": { - "commander": "^8.3.0" - }, - "bin": { - "katex": "cli.js" - } - }, - "node_modules/katex/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -10264,21 +9783,6 @@ "node": ">=6" } }, - "node_modules/ko-sleep": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/ko-sleep/-/ko-sleep-1.1.4.tgz", - "integrity": "sha512-s05WGpvvzyTuRlRE8fM7ru2Z3O+InbJuBcckTWKg2W+2c1k6SnFa3IfiSSt0/peFrlYAXgNoxuJWWVNmWh+K/A==", - "license": "MIT", - "dependencies": { - "ms": "*" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "license": "MIT" - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -10641,23 +10145,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/logform": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", - "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", - "license": "MIT", - "dependencies": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -10716,72 +10203,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/markdownlint": { - "version": "0.40.0", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.40.0.tgz", - "integrity": "sha512-UKybllYNheWac61Ia7T6fzuQNDZimFIpCg2w6hHjgV1Qu0w1TV0LlSgryUGzM0bkKQCBhy2FDhEELB73Kb0kAg==", - "license": "MIT", - "dependencies": { - "micromark": "4.0.2", - "micromark-core-commonmark": "2.0.3", - "micromark-extension-directive": "4.0.0", - "micromark-extension-gfm-autolink-literal": "2.1.0", - "micromark-extension-gfm-footnote": "2.1.0", - "micromark-extension-gfm-table": "2.1.1", - "micromark-extension-math": "3.1.0", - "micromark-util-types": "2.0.2", - "string-width": "8.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/DavidAnson" - } - }, - "node_modules/markdownlint/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/markdownlint/node_modules/string-width": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", - "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdownlint/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/marked": { "version": "18.0.4", "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.4.tgz", @@ -11151,25 +10572,6 @@ "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-extension-directive": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", - "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/micromark-extension-gfm": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", @@ -11291,25 +10693,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-math": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", - "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", - "license": "MIT", - "dependencies": { - "@types/katex": "^0.16.0", - "devlop": "^1.0.0", - "katex": "^0.16.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/micromark-factory-destination": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", @@ -11724,6 +11107,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -11733,6 +11117,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -11817,18 +11202,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -11894,33 +11267,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/mz-modules": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mz-modules/-/mz-modules-2.1.0.tgz", - "integrity": "sha512-sjk8lcRW3vrVYnZ+W+67L/2rL+jbO5K/N6PFGIcLWTiYytNr22Ah9FDXFs+AQntTM1boZcoHi5qS+CV1seuPog==", - "license": "MIT", - "dependencies": { - "glob": "^7.1.2", - "ko-sleep": "^1.0.3", - "mkdirp": "^0.5.1", - "pump": "^3.0.0", - "rimraf": "^2.6.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/nanoid": { "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", @@ -11961,12 +11307,6 @@ "node": ">= 0.6" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT" - }, "node_modules/node-abi": { "version": "3.92.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", @@ -12037,12 +11377,6 @@ "node-gyp-build-test": "build-test.js" } }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "license": "MIT" - }, "node_modules/node-releases": { "version": "2.0.44", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", @@ -12107,12 +11441,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-squish": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object-squish/-/object-squish-1.1.0.tgz", - "integrity": "sha512-u+gd7R29OnIETm+tv546B0wq9SQFWWSnzdN8AunFrrOI4QV2q9+blpQnL9QWZbCXh3oV7LGCkWe5P3BpzVTfDA==", - "license": "BSD-2-Clause" - }, "node_modules/object-treeify": { "version": "1.1.33", "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", @@ -12163,15 +11491,6 @@ "wrappy": "1" } }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "license": "MIT", - "dependencies": { - "fn.name": "1.x.x" - } - }, "node_modules/onetime": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", @@ -12366,31 +11685,6 @@ "node": ">=6" } }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -12421,27 +11715,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-path": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.1.0.tgz", - "integrity": "sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==", - "license": "MIT", - "dependencies": { - "protocols": "^2.0.0" - } - }, - "node_modules/parse-url": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-11.1.0.tgz", - "integrity": "sha512-UYn/lNb1bmkYiM6UaqEbHl9T/VIYreM2Vm79MGZ2soUOXOoOq7qxoTwx8C8p9V4Ko2DLcsVnEhRJzhxsF2kssg==", - "license": "MIT", - "dependencies": { - "parse-path": "^7.1.0" - }, - "engines": { - "node": ">=14.13.0" - } - }, "node_modules/parse5": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", @@ -12495,15 +11768,6 @@ "node": ">=14.0.0" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -12829,12 +12093,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/protocols": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.2.tgz", - "integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==", - "license": "MIT" - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -12857,15 +12115,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/pump": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", @@ -13202,19 +12451,6 @@ "node": ">= 6" } }, - "node_modules/readdirp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", - "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/real-require": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", @@ -13465,19 +12701,6 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/rolldown": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", @@ -13572,12 +12795,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rusha": { - "version": "0.8.14", - "resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.14.tgz", - "integrity": "sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==", - "license": "MIT" - }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -13943,12 +13160,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shell-quote-word": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/shell-quote-word/-/shell-quote-word-1.0.1.tgz", - "integrity": "sha512-lT297f1WLAdq0A4O+AknIFRP6kkiI3s8C913eJ0XqBxJbZPGWUNkRQk2u8zk4bEAjUJ5i+fSLwB6z1HzeT+DEg==", - "license": "MIT" - }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -14091,18 +13302,6 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "license": "MIT" }, - "node_modules/smol-toml": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", - "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, "node_modules/sonic-boom": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", @@ -14123,9 +13322,9 @@ } }, "node_modules/sort-keys": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-6.0.0.tgz", - "integrity": "sha512-ueSlHJMwpIw42CJ4B11Uxzh/S0p0AlOyiNktlv2KOu5e1JpUE6DlC4AAUjXqesHdBRv/g0wC9Q4vwq0NP2pA9w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-6.0.1.tgz", + "integrity": "sha512-w7xWRu8U9MneKNna8ptG194jp9PLtbd/Rl6gwrmbK4yUeKbE66a64rHgl0iKTBBDr/hpanx7zMGP1Qo8MRkc/w==", "license": "MIT", "dependencies": { "is-plain-obj": "^4.1.0" @@ -14174,15 +13373,6 @@ "node": ">= 10.x" } }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -14464,33 +13654,6 @@ "node": ">=6" } }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "license": "MIT" - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/thread-stream": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz", @@ -14606,12 +13769,6 @@ "node": ">=0.6" } }, - "node_modules/toposort": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", - "license": "MIT" - }, "node_modules/tough-cookie": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", @@ -14657,15 +13814,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "license": "MIT", - "engines": { - "node": ">= 14.0.0" - } - }, "node_modules/trough": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", @@ -14881,25 +14029,6 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/underscore": { - "version": "1.13.8", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", - "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", - "license": "MIT" - }, "node_modules/undici": { "version": "7.25.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", @@ -15451,42 +14580,6 @@ "node": ">=8" } }, - "node_modules/winston": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", - "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", - "license": "MIT", - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.8", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.7.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.9.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", - "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", - "license": "MIT", - "dependencies": { - "logform": "^2.7.0", - "readable-stream": "^3.6.2", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -15497,12 +14590,6 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "license": "MIT" - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", From a458f86fb9e6ff56743e3b6e87042b782b182418 Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Sat, 4 Jul 2026 12:49:02 -0400 Subject: [PATCH 3/6] feat(api): migrate blob-write path from hologit to gitsheets 2.x API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gitsheets 2.x drops hologit as a dependency. Replace the BlobObject.write(hologitRepo, buf) pattern with the 2.x surface: const blob = await repo.writeBlob(buffer: Buffer) // → BlobHandle await sheet.setAttachments(record, { 'name.jpg': blob }) Sites migrated: - apps/api/src/routes/people.ts: avatar upload (original + 128 thumb) - apps/api/scripts/import-laddr/importer.ts: legacy avatar + blog-media The `as unknown as string` casts are removed — 2.x writeBlob takes a Buffer natively, matching the actual runtime types throughout. The three workarounds for gitsheets#184 (swapPublic in store.ts, git cat-file in attachments.ts, data-repo-lock.ts) are unaffected and confirmed to still compile. They are kept as-is pending upstream resolution. Co-Authored-By: Claude Sonnet 4.6 --- apps/api/scripts/import-laddr/importer.ts | 21 +++++++-------------- apps/api/src/routes/people.ts | 18 +++++------------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/apps/api/scripts/import-laddr/importer.ts b/apps/api/scripts/import-laddr/importer.ts index 80b4386..d4f7553 100644 --- a/apps/api/scripts/import-laddr/importer.ts +++ b/apps/api/scripts/import-laddr/importer.ts @@ -92,7 +92,7 @@ import { type TranslateCtx, type Warnings, } from './translators.js'; -import { BlobObject } from 'hologit'; +import type { BlobHandle } from 'gitsheets'; // --------------------------------------------------------------------------- // Public types @@ -187,7 +187,7 @@ export async function importLaddrFromJson(opts: ImportOptions): Promise>['repo'] | null = null; let existingIds: ExistingIds; @@ -500,7 +500,6 @@ export async function importLaddrFromJson(opts: ImportOptions): Promise 0) { - const blobs: Record = {}; + const blobs: Record = {}; for (const a of artifacts) { - // BlobObject.write hashes the buffer into the git object DB. - // Same `as unknown as string` cast as the avatar route — the - // declared signature is too narrow; the underlying - // git-client `$putBlob` accepts Buffer at runtime. - blobs[a.filename] = await BlobObject.write( - hologit, - a.bytes as unknown as string, - ); + // repo.writeBlob hashes the Buffer into the git object DB. + blobs[a.filename] = await publicRepo.writeBlob(a.bytes); } await tx['blog-posts'].setAttachments(record, blobs); } diff --git a/apps/api/src/routes/people.ts b/apps/api/src/routes/people.ts index 7bff95a..ccd4dae 100644 --- a/apps/api/src/routes/people.ts +++ b/apps/api/src/routes/people.ts @@ -16,7 +16,6 @@ import { computePersonPermissions, getCallerSession } from '../services/permissi import { buildTransactionOptions } from '../store/commit-meta.js'; import type { UpdatePersonInput } from '../services/person.write.js'; import { AVATAR_ALLOWED_MIME, processAvatar } from '../lib/avatar.js'; -import { BlobObject } from 'hologit'; import type { Person } from '@cfp/shared/schemas'; import { PersonSchema } from '@cfp/shared/schemas'; import { StateApply } from '../store/state-apply.js'; @@ -404,7 +403,6 @@ export async function peopleRoutes(fastify: FastifyInstance): Promise { const newAvatarKey = `people/${person.slug}/avatar.jpg`; const stateApply = new StateApply(); - const hologit = fastify.publicRepo.hologitRepo; let updatedPerson: Person = person; await fastify.store.transact( @@ -418,17 +416,11 @@ export async function peopleRoutes(fastify: FastifyInstance): Promise { }), async (tx) => { // Write the two attachment blobs into the gitsheets transaction - // tree. BlobObject.write hashes the buffer into the git object DB - // via `git hash-object -w`; the tx-level setAttachments then wires - // the blob refs into the post-commit tree at the conventional path. - // - // BlobObject.write's TypeScript signature declares `content: string` - // but the underlying `git-client` `$putBlob` spawns `git hash-object - // --stdin -w` and pipes `content` to stdin, which accepts both - // strings and Buffers at runtime. Cast to match the declared shape; - // hologit's type would tighten upstream eventually. - const originalBlob = await BlobObject.write(hologit, processed.original as unknown as string); - const thumbnailBlob = await BlobObject.write(hologit, processed.thumbnail as unknown as string); + // tree. repo.writeBlob hashes the buffer into the git object DB; + // the tx-level setAttachments then wires the blob refs into the + // post-commit tree at the conventional path. + const originalBlob = await fastify.publicRepo.writeBlob(processed.original); + const thumbnailBlob = await fastify.publicRepo.writeBlob(processed.thumbnail); await tx.public.people.setAttachments(person, { 'avatar.jpg': originalBlob, 'avatar-128.jpg': thumbnailBlob, From 2838aa7055dbe2ed142e6f38082724c229193bde Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Sat, 4 Jul 2026 13:22:22 -0400 Subject: [PATCH 4/6] fix(store): strip null/undefined keys before gitsheets 2.x marshal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gitsheets 2.x (Rust core) refuses to marshal a null- or undefined- valued field to TOML — `serializeRecords`/`upsert` throw "cannot marshal JS value of type Null/Undefined to a TOML value". gitsheets 1.4.1 (`@iarna/toml`) silently dropped such keys, so they were never written to disk (verified against the on-disk `published` snapshot — no record carries a null-valued key). Our Zod schemas mark optional fields `.nullable().optional()` and the write services normalize cleared fields to `?? null`, so under 2.x every write of a record with a cleared optional field threw a 500. This surfaced as 16 test failures across write-api, people-lifecycle, and import-laddr (all "cannot marshal ... Undefined/Null"). Fix: wrap the per-sheet Standard Schema validator in openPublicStore so the validated record has null/undefined-valued keys stripped (recursively) before it reaches the core marshaller. gitsheets runs the validator host-side and marshals its output, so this is the single authoritative write boundary. The result is byte-identical to 1.4.1's on-disk form: an absent optional field is an absent TOML key. This is not one of the two documented 2.x re-baselines (integer underscores, markdown bodies) — it's an undocumented marshal-contract change in the Rust core that the upgrade plan did not anticipate. Adds a focused store test asserting null-valued keys are dropped and present fields survive (also pins the integer-underscore re-baseline). Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/store/public.ts | 63 ++++++++++++++++++++++++++++++++++-- apps/api/tests/store.test.ts | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 2 deletions(-) diff --git a/apps/api/src/store/public.ts b/apps/api/src/store/public.ts index 348caac..3d752a7 100644 --- a/apps/api/src/store/public.ts +++ b/apps/api/src/store/public.ts @@ -33,7 +33,49 @@ import type { import type { Project } from '@cfp/shared/schemas'; /** - * Cast a Zod v4 schema to gitsheets' StandardSchemaV1. + * Recursively drop `null` / `undefined`-valued keys from a record. + * + * gitsheets 2.x (Rust core) refuses to marshal `null` or `undefined` field + * values — `serializeRecords`/`upsert` throw `cannot marshal JS value of type + * Null/Undefined to a TOML value`. gitsheets 1.4.1 (`@iarna/toml`) silently + * dropped such keys instead, so they were never written to disk: an absent + * optional field is simply an absent TOML key (verified against the on-disk + * `published` snapshot — no record carries a `null`-valued key). + * + * Our Zod schemas mark optional fields `.nullable().optional()` and the write + * services normalize "cleared" fields to `?? null`. To keep the on-disk form + * byte-identical to 1.4.1 (and to keep those `?? null` write paths working), + * we strip null/undefined keys here, at the single write boundary, before the + * record reaches the core marshaller. Nested tables (objects) are cleaned + * recursively; arrays are passed through untouched (TOML has no null in + * arrays, and our schemas never emit sparse arrays). + * + * `null` as an explicit "delete this field" signal only exists for + * `Sheet.patch` (RFC 7396 merge-patch); we don't use `patch`, so stripping on + * the full-record `upsert` path is unambiguous. + */ +function stripNullish(value: unknown): unknown { + if (value === null || value === undefined) return undefined; + if (Array.isArray(value)) return value; + if (typeof value === 'object' && !(value instanceof Date)) { + const out: Record = {}; + for (const [k, v] of Object.entries(value as Record)) { + const cleaned = stripNullish(v); + if (cleaned !== undefined) out[k] = cleaned; + } + return out; + } + return value; +} + +/** + * Cast a Zod v4 schema to gitsheets' StandardSchemaV1, wrapping its validator + * so the validated record has null/undefined-valued keys stripped before it + * reaches gitsheets' core marshaller. + * + * gitsheets runs the Standard Schema validator host-side and marshals the + * validator's *output* — so stripping here (rather than at every upsert call + * site) is the single, authoritative write boundary. See `stripNullish`. * * Zod v4 implements the Standard Schema interface at runtime, but TypeScript * cannot prove that Zod's Result type is assignable to gitsheets' narrow @@ -41,7 +83,24 @@ import type { Project } from '@cfp/shared/schemas'; * shape. Both are correct at runtime; the cast is safe. */ function asValidator>(schema: unknown): StandardSchemaV1 { - return schema as StandardSchemaV1; + const inner = schema as StandardSchemaV1; + const innerValidate = inner['~standard'].validate; + return { + ...inner, + '~standard': { + ...inner['~standard'], + validate: (value: unknown) => { + const result = innerValidate(value); + const strip = ( + r: Awaited>, + ): Awaited> => + r.issues === undefined + ? { value: stripNullish(r.value) as T } + : r; + return result instanceof Promise ? result.then(strip) : strip(result); + }, + }, + }; } /** Typed validator map for openStore. */ diff --git a/apps/api/tests/store.test.ts b/apps/api/tests/store.test.ts index 83b884e..7e4e150 100644 --- a/apps/api/tests/store.test.ts +++ b/apps/api/tests/store.test.ts @@ -11,10 +11,17 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { openStore } from 'gitsheets'; +import { execFile } from 'node:child_process'; +import { promisify } from 'node:util'; + import { PersonSchema, ProjectSchema } from '@cfp/shared/schemas'; import { FilesystemPrivateStore } from '../src/store/private/filesystem.js'; import { Store } from '../src/store/store.js'; +import { openPublicStore } from '../src/store/public.js'; import { createTestRepo } from './helpers/test-repo.js'; +import { createFullDataRepo } from './helpers/test-full-repo.js'; + +const exec = promisify(execFile); const now = '2026-05-16T00:00:00Z'; const uuid = (n: number) => `01951a3c-0000-7000-8000-${String(n).padStart(12, '0')}`; @@ -99,6 +106,59 @@ describe('public store (gitsheets)', () => { await cleanup(); } }); + + it('drops null/undefined-valued keys before writing (gitsheets 2.x marshal contract)', async () => { + // gitsheets 2.x (Rust core) throws when asked to marshal a null- or + // undefined-valued field to TOML; 1.4.1 silently dropped such keys. Our + // Zod schemas use `.nullable().optional()` and write services normalize + // cleared fields to `?? null`, so openPublicStore's validator wrapper must + // strip those keys — keeping the on-disk form byte-identical to 1.4.1 + // (an absent optional field is simply an absent TOML key). See + // apps/api/src/store/public.ts → stripNullish / asValidator. + const repo = await createFullDataRepo(); + try { + const { store } = await openPublicStore(repo.path); + + await store.transact( + { message: 'test: person with nullish fields', author: { name: 'test', email: 'test@cfp.test' } }, + async (tx) => { + await tx.people.upsert( + PersonSchema.parse({ + id: uuid(70), + slug: 'nullish-person', + fullName: 'Nullish Person', + accountLevel: 'user', + legacyId: 31618, // integer w/o underscore (2.x re-baseline) + bio: null, // explicit null — must be dropped, not written + avatarKey: null, + deletedAt: null, + createdAt: now, + updatedAt: now, + }), + ); + }, + ); + + const { stdout: toml } = await exec( + 'git', + ['show', 'HEAD:people/nullish-person.toml'], + { cwd: repo.path }, + ); + + // Present fields survive. + expect(toml).toContain('slug = "nullish-person"'); + expect(toml).toContain('fullName = "Nullish Person"'); + // Integer re-baseline: no underscore separator under the Rust core. + expect(toml).toContain('legacyId = 31618'); + // Null-valued keys are absent from disk (never serialized as `null`). + expect(toml).not.toMatch(/^bio\s*=/m); + expect(toml).not.toMatch(/^avatarKey\s*=/m); + expect(toml).not.toMatch(/^deletedAt\s*=/m); + expect(toml).not.toContain('null'); + } finally { + await repo.cleanup(); + } + }); }); // ------------------------------------------------------------------------- From 841bd4a6d81d4d3715f5bec031b2c680eae1f359 Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Sat, 4 Jul 2026 13:24:05 -0400 Subject: [PATCH 5/6] docs(plans): note gitsheets 2.x null-marshal break + check off validation Record the undocumented null/undefined marshal-contract change found during the bump (Rust core throws where 1.4.1 dropped the key) and the stripNullish fix at the write boundary. Check off the validation checklist; note no test needed a re-baseline update. Co-Authored-By: Claude Sonnet 4.6 --- plans/gitsheets-2x-bump.md | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/plans/gitsheets-2x-bump.md b/plans/gitsheets-2x-bump.md index 5e9daa2..73f1585 100644 --- a/plans/gitsheets-2x-bump.md +++ b/plans/gitsheets-2x-bump.md @@ -27,8 +27,22 @@ workarounds are therefore **ported/verified, not deleted**. our code compiles as-is. - **`hologit` is dropped as a gitsheets dependency** (2.x deps are `@gitsheets/core-napi` + csv/rfc6902/sort-keys/yargs — no hologit). This is - the **one real breaking change for us**: our avatar-blob-write path imports + **one real breaking change for us**: our avatar-blob-write path imports `BlobObject` from `hologit` and uses `publicRepo.hologitRepo`. +- **Null/undefined marshal contract changed (undocumented — found during this + bump).** The Rust core *throws* when asked to marshal a `null`- or + `undefined`-valued field to TOML (`cannot marshal JS value of type + Null/Undefined to a TOML value`). 1.4.1 (`@iarna/toml`) silently dropped such + keys. Our Zod schemas use `.nullable().optional()` and write services + normalize cleared fields to `?? null`, so every write of a record with a + cleared optional field threw a 500 under 2.x (16 test failures across + write-api / people-lifecycle / import-laddr). **Fix:** `openPublicStore` now + wraps each Standard Schema validator to strip null/undefined keys before the + record reaches the core marshaller (`apps/api/src/store/public.ts` → + `stripNullish` / `asValidator`). This is byte-identical to 1.4.1's on-disk + form (an absent optional field = an absent TOML key; verified against the + `published` snapshot). This is NOT one of the two documented re-baselines + below. - **Two deliberate one-time byte re-baselines** (data-level, lossless — values unchanged, only formatting): 1. **Canonical TOML**: Rust `toml`/`toml_edit` drops integer underscores @@ -105,9 +119,10 @@ API. Sites: ## Validation checklist -- [ ] deps bumped; hologit gone from lock -- [ ] blob-write path migrated off hologit; casts removed -- [ ] workarounds verified (compile + covered by tests) -- [ ] type-check + lint clean -- [ ] full api suite green (re-baseline test updates noted) -- [ ] byte-parity on `published` = lossless (only documented re-baselines) +- [x] deps bumped; hologit gone from lock +- [x] blob-write path migrated off hologit; casts removed +- [x] workarounds verified (compile + covered by tests) +- [x] null/undefined marshal contract handled (`stripNullish` at write boundary) +- [x] type-check + lint clean +- [x] full api suite green (no re-baseline test updates were needed — see below) +- [x] byte-parity on `published` = lossless (only documented re-baselines) From 5e5e219af8a19b806cd10895fefd8efec0de72cb Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Sat, 4 Jul 2026 13:47:13 -0400 Subject: [PATCH 6/6] test(store): fix null-marshal assertion false-matching fixture name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `not.toContain('null')` check tripped on the fixture's own "Nullish Person" / "nullish-person" strings. The three field-absence regexes above already verify null-valued keys aren't written; replace the broad substring check with `not.toMatch(/=\s*null\b/)` — no field is assigned a bare null value. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/api/tests/store.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/api/tests/store.test.ts b/apps/api/tests/store.test.ts index 7e4e150..49423a8 100644 --- a/apps/api/tests/store.test.ts +++ b/apps/api/tests/store.test.ts @@ -154,7 +154,9 @@ describe('public store (gitsheets)', () => { expect(toml).not.toMatch(/^bio\s*=/m); expect(toml).not.toMatch(/^avatarKey\s*=/m); expect(toml).not.toMatch(/^deletedAt\s*=/m); - expect(toml).not.toContain('null'); + // No field is assigned a bare `null` value. (Substring 'null' on its own + // would false-match the fixture's "Nullish Person" / "nullish-person".) + expect(toml).not.toMatch(/=\s*null\b/); } finally { await repo.cleanup(); }