Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ Historical entries retain their original delivery coordinates.

## Unreleased

- The packaged type sources compile again under a consumer's strict
compiler flags. Consumers that set `erasableSyntaxOnly` and narrow
`ProcessEnv.NODE_ENV` to its literal union, as Next.js does, can now
typecheck the shipped sources cleanly: the last constructor parameter
property is an explicit field, the persistent helper's environment keeps
`NODE_ENV` literal, and portable request bodies omit `body` instead of
passing an explicit `undefined`. The clean-consumer package smoke now
typechecks the public entrypoints under those same flags, and
`@hraness/local-custody` moves to the immutable 0.9.1 release, whose
shipped sources made the same erasable-syntax cleanup.
- Move the shared clip, capture, and URL-intelligence runtime from
`@hraness/kb` 0.19.6 to the immutable `@hraness/wordcell` 0.24.0 release.
The reviewed dynamic-resolution boundary now pins the two bundled modules
Expand Down
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@
"dependencies": {
"@hraness/accounts-cli": "github:hraness/accounts-cli#v0.1.3",
"@hraness/wordcell": "https://github.com/hraness/wordcell/releases/download/v0.24.0/hraness-wordcell-0.24.0.tgz",
"@hraness/local-custody": "https://github.com/hraness/local-custody/releases/download/v0.9.0/hraness-local-custody-0.9.0.tgz",
"@hraness/local-custody": "https://github.com/hraness/local-custody/releases/download/v0.9.1/hraness-local-custody-0.9.1.tgz",
"@hraness/message-like-me": "github:hraness/textbutler#83453cc7c17b49bb53fdfd89ccb69b8b44b30af1",
"@hraness/support-foundation": "github:hraness/support-foundation#8bb514d24b79dc3f305390700ae312cab88e7ad2",
"buffer-from": "1.1.2",
Expand Down
19 changes: 10 additions & 9 deletions scripts/npm-release-workflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,7 @@ describe("npm publication contract", () => {
(MAX_UNPACKED_BYTES + MAX_PACKED_ENTRIES * 1_023 + 1_024) / 512,
) * 512,
);
expect(MAX_PACKAGE_TAR_BYTES).toBe(24_573_440);
expect(MAX_PACKAGE_TAR_BYTES).toBe(24_574_464);
expect(MAX_PACKAGE_TAR_BYTES % 512).toBe(0);
expect(artifact).toContain("maxOutputLength: MAX_PACKAGE_TAR_BYTES");
expect(artifact).not.toContain("const maximumTarBytes");
Expand Down Expand Up @@ -1424,10 +1424,11 @@ describe("npm publication contract", () => {
expect(budget).toContain("90ef33da70559db4674510c401466fe74a240967f8d719a4cfb3f9e012af4227");
expect(budget).toContain("00ead58f3e0268855e0face59da2460faa723c68c18e19542c37fd891cd4431f");
expect(budget).toContain("81b82626d55fcc0ef960ac59c3dfc0e90ed6417756d614b00796d5c4122b5072");
expect(budget).toContain("d5681ab13f0bc005bcd4bbf4b18152de887c062e7e8be3d0ecc28f14e11177d5");
expect(Object.isFrozen(repairPackageMeasurement)).toBeTrue();
expect(repairPackageMeasurement).toMatchObject({
archiveSha256: "81b82626d55fcc0ef960ac59c3dfc0e90ed6417756d614b00796d5c4122b5072",
packedBytes: 12_125_239, unpackedBytes: 23_939_528, entryCount: 618,
archiveSha256: "d5681ab13f0bc005bcd4bbf4b18152de887c062e7e8be3d0ecc28f14e11177d5",
packedBytes: 12_125_748, unpackedBytes: 23_940_758, entryCount: 618,
packedPlatformProjection: 12_387, packedPortabilityAllowance: 4_096,
payloadPlatformProjection: 353, payloadAllowance: 65,
});
Expand All @@ -1437,8 +1438,8 @@ describe("npm publication contract", () => {
expect(budget).toContain("12,141,169 packed; 23,937,025 + 353 + 65 = 23,937,443 unpacked");
expect(budget).toContain("23,930,250 + 353 + 65 = 23,930,668 unpacked");
expect(budget).toContain("12,141,373 packed; 23,937,545 + 353 + 65 = 23,937,963 unpacked");
expect(MAX_PACKED_BYTES).toBe(12_141_722);
expect(MAX_PACKED_BYTES).toBe(12_125_239 + 12_387 + 4_096);
expect(MAX_PACKED_BYTES).toBe(12_142_231);
expect(MAX_PACKED_BYTES).toBe(12_125_748 + 12_387 + 4_096);
expect(budget).toContain("aa127b3193c9bb3b0cb5deece5927be60ccb7111a50169320d322ffdeaa13f39");
expect(budget).toContain("0c331bab3ab3df69a108e18f5f29845b0db90c281cbd6455c0d90fa0b24081e2");
expect(budget).toContain("873cad8139fda303e2d19c6afd61cf549cf9b4d1d76b2a1d6d632a6afe6bd0d1");
Expand Down Expand Up @@ -1559,7 +1560,7 @@ describe("npm publication contract", () => {
expect(budget).toContain("47684b3e2eb5cf3ed07fbb520aade8c7251d993f75262fbf1af627d9081a1a5f");
expect(budget).toContain("23,688,277 + 353 + 65 = 23,688,695");
expect(budget).toContain("23,759,283 + 353 + 65 = 23,759,701");
expect(MAX_UNPACKED_BYTES).toBe(23_939_946);
expect(MAX_UNPACKED_BYTES).toBe(23_941_176);
expect(budget).toContain("23,037,873 + 65 = 23,037,938");
expect(budget).toContain("f9f3ab38a682690ceaa2699a7309997512030f0fa500a9dc29dcd108123dc41f");
expect(budget).toContain("23,038,557 + 65 = 23,038,622");
Expand Down Expand Up @@ -1592,7 +1593,7 @@ describe("npm publication contract", () => {
expect(budget).toContain("01875f12ab73a49d6c7d6bf520dc3d318db816addee2fa7981889f35c958cf7c");
expect(budget).toContain("b12909f08f7c19460ced56e30619f4860a1183f4b0106170c07837dae577a937");
expect(budget).toContain("0b212ac291218528dcf979370110a36f10850e046ca90a536057d9a44e807d1d");
expect(MAX_UNPACKED_BYTES).toBe(23_939_528 + 353 + 65);
expect(MAX_UNPACKED_BYTES).toBe(23_940_758 + 353 + 65);
expect(budget).toContain("22,794,052 + 65 = 22,794,117");
expect(budget).toContain("c482efe748f880e3717727d6d39fd92a68953e6eea766642b329ba47ae772d80");
expect(budget).toContain("22,759,423 + 65 = 22,759,488");
Expand Down Expand Up @@ -1628,8 +1629,8 @@ describe("npm publication contract", () => {
expect(packageArtifactBudget).toEqual({
entryCount: { min: 618, max: 618 },
fileCount: { min: 618, max: 618 },
packedBytes: { min: 1_600_000, max: 12_141_722 },
unpackedBytes: { min: 9_000_000, max: 23_939_946 },
packedBytes: { min: 1_600_000, max: 12_142_231 },
unpackedBytes: { min: 9_000_000, max: 23_941_176 },
});
});

Expand Down
26 changes: 22 additions & 4 deletions scripts/package-budget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2065,14 +2065,32 @@
// Retain the same platform projections and allowances:
// 12,125,239 + 12,387 + 4,096 = 12,141,722 packed;
// 23,939,528 + 353 + 65 = 23,939,946 unpacked.
//
// Strict packaged-source compatibility for `erasableSyntaxOnly`
// consumers removes the remaining constructor parameter property from
// src/cookie-safe-storage.ts, keeps the persistent-helper environment
// typed with literal NODE_ENV values so narrowed consumer ProcessEnv
// unions still admit it, builds RequestInit bodies without explicit
// undefined under exactOptionalPropertyTypes, teaches the package smoke
// to compile the packed public entrypoints with the strict consumer
// flags, and repins @hraness/local-custody to the immutable 0.9.1
// release archive: three changed packed sources plus their rebuilt
// bundles over the unchanged 618-file inventory. After `bun run build`,
// a clean npm 11.19.0 pack --ignore-scripts with Node 24.18.1 on darwin
// arm64 measured 618 entries, 12,125,748 packed bytes, and 23,940,758
// unpacked bytes; archive SHA-256
// d5681ab13f0bc005bcd4bbf4b18152de887c062e7e8be3d0ecc28f14e11177d5.
// Retain the same platform projections and allowances:
// 12,125,748 + 12,387 + 4,096 = 12,142,231 packed;
// 23,940,758 + 353 + 65 = 23,941,176 unpacked.
export const repairPackageMeasurement = Object.freeze({
scope: "Wordcell 0.24.0 runtime adoption over @hraness/kb 0.19.6 with split dynamic-resolution review pins",
scope: "erasableSyntaxOnly packaged-source compatibility and @hraness/local-custody 0.9.1 pin",
command: "npm pack --ignore-scripts",
npmVersion: "11.19.0",
platform: "darwin-arm64",
archiveSha256: "81b82626d55fcc0ef960ac59c3dfc0e90ed6417756d614b00796d5c4122b5072",
packedBytes: 12_125_239,
unpackedBytes: 23_939_528,
archiveSha256: "d5681ab13f0bc005bcd4bbf4b18152de887c062e7e8be3d0ecc28f14e11177d5",
packedBytes: 12_125_748,
unpackedBytes: 23_940_758,
entryCount: 618,
packedPlatformProjection: 12_387,
packedPortabilityAllowance: 4_096,
Expand Down
6 changes: 5 additions & 1 deletion scripts/package-smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,11 @@ void [
parseMessagingRoutesV2,
];
`);
await writeFile(join(consumer, "tsconfig.bundler.json"), "{\n \"compilerOptions\": {\n \"target\": \"ES2023\",\n \"lib\": [\n \"ES2023\",\n \"DOM\",\n \"DOM.Iterable\"\n ],\n \"types\": [\n \"bun\",\n \"node\"\n ],\n \"strict\": true,\n \"noEmit\": true,\n \"skipLibCheck\": false,\n \"module\": \"Preserve\",\n \"moduleResolution\": \"Bundler\"\n },\n \"include\": [\n \"index.ts\"\n ]\n}");
// Strict consumers narrow NodeJS.ProcessEnv.NODE_ENV to the literal union
// (for example a Next.js ambient declaration), so packaged sources must keep
// NODE_ENV assignments literal instead of widening to string.
await writeFile(join(consumer, "process-env.d.ts"), "declare global {\n namespace NodeJS {\n interface ProcessEnv {\n readonly NODE_ENV: \"development\" | \"test\" | \"production\";\n }\n }\n}\n\nexport {};\n");
await writeFile(join(consumer, "tsconfig.bundler.json"), "{\n \"compilerOptions\": {\n \"target\": \"ES2023\",\n \"lib\": [\n \"ES2023\",\n \"DOM\",\n \"DOM.Iterable\"\n ],\n \"types\": [\n \"bun\",\n \"node\"\n ],\n \"strict\": true,\n \"noUncheckedIndexedAccess\": true,\n \"exactOptionalPropertyTypes\": true,\n \"noImplicitOverride\": true,\n \"noImplicitReturns\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"useUnknownInCatchVariables\": true,\n \"erasableSyntaxOnly\": true,\n \"verbatimModuleSyntax\": true,\n \"isolatedModules\": true,\n \"moduleDetection\": \"force\",\n \"allowImportingTsExtensions\": true,\n \"noEmit\": true,\n \"skipLibCheck\": false,\n \"module\": \"Preserve\",\n \"moduleResolution\": \"Bundler\"\n },\n \"include\": [\n \"index.ts\",\n \"process-env.d.ts\"\n ]\n}");
await logConsumerToolchain(consumer);
await run([process.execPath, "x", "tsc", "-p", "./tsconfig.bundler.json"], consumer);

Expand Down
24 changes: 12 additions & 12 deletions scripts/standalone-smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const expectedClosureRuntimeDependencies = Object.freeze({
"@hraness/accounts-cli": "github:hraness/accounts-cli#v0.1.3",
"@hraness/desktop-foundation": "https://github.com/hraness/desktop-foundation/releases/download/v0.8.0/hraness-desktop-foundation-0.8.0.tgz",
"@hraness/wordcell": "https://github.com/hraness/wordcell/releases/download/v0.24.0/hraness-wordcell-0.24.0.tgz",
"@hraness/local-custody": "https://github.com/hraness/local-custody/releases/download/v0.9.0/hraness-local-custody-0.9.0.tgz",
"@hraness/local-custody": "https://github.com/hraness/local-custody/releases/download/v0.9.1/hraness-local-custody-0.9.1.tgz",
"@hraness/message-like-me": "github:hraness/textbutler#83453cc7c17b49bb53fdfd89ccb69b8b44b30af1",
"@hraness/support-foundation": "github:hraness/support-foundation#8bb514d24b79dc3f305390700ae312cab88e7ad2",
// The browser cookie reader, also pinned by @hraness/wordcell. Ghostget wraps it
Expand Down Expand Up @@ -886,44 +886,44 @@ try {
root: installedLocalCustodyRoot,
sha256:
"8266fec81ffa715f1edc845c022a35939547d093b6670b6b36a137b90cf6e88b",
version: "0.9.0",
version: "0.9.1",
}),
assertInstalledClosurePackage({
keyFile: "dist/atomic-publish.js",
name: "@hraness/local-custody",
root: installedLocalCustodyRoot,
sha256:
"f74453ce9bb37e71996c8763a05b3d299f0e60a25a0a7710f6c30c24fd832177",
version: "0.9.0",
version: "0.9.1",
}),
// local-custody v0.9.0 stages the Rust sidecar and its per-target
// local-custody v0.9.1 stages the Rust sidecar and its per-target
// manifest inside the published tarball; admission pins each staged
// byte-exact file the same way as the JS entry points.
assertInstalledClosurePackage({
keyFile: "dist/rust-artifacts/manifest.json",
name: "@hraness/local-custody",
root: installedLocalCustodyRoot,
sha256:
"81eec495cae2314a66acb023c4742b34bd8d2f522a7b3567c1500f02ecab717c",
version: "0.9.0",
"00f47282e5e9f51343ec5e0c63cb0faa3019f8352e6d0709b3b062ae3a217076",
version: "0.9.1",
}),
assertInstalledClosurePackage({
keyFile:
"dist/rust-artifacts/local-custody/darwin-arm64/local-custody",
name: "@hraness/local-custody",
root: installedLocalCustodyRoot,
sha256:
"0eda257b175120daf5eb85766bd59e347c0a2d94f1ccfa07d2056ad0f0f35f7e",
version: "0.9.0",
"8a26839249060eb7822d79be39a3399b16e74250f4f289b73bfee122f5301b6a",
version: "0.9.1",
}),
assertInstalledClosurePackage({
keyFile:
"dist/rust-artifacts/local-custody/darwin-x64/local-custody",
name: "@hraness/local-custody",
root: installedLocalCustodyRoot,
sha256:
"0a3ccd963067837df7c433246f20417cdb2e4b31e8bc161f6d7aa799943c7693",
version: "0.9.0",
"f0f2cfa818073ebdb03eafa3acad48bac2bbbe1a0d14a6d6814c6c4bd6f55d57",
version: "0.9.1",
}),
assertInstalledClosurePackage({
keyFile:
Expand All @@ -932,7 +932,7 @@ try {
root: installedLocalCustodyRoot,
sha256:
"eb445c00af1ec82ed220e01a7c3161f33e4ab8e08e2276a6fcc61f38382608ca",
version: "0.9.0",
version: "0.9.1",
}),
assertInstalledClosurePackage({
keyFile:
Expand All @@ -941,7 +941,7 @@ try {
root: installedLocalCustodyRoot,
sha256:
"35de4d107e30da2440fe1ac926581fd18fdf130d80c02e95694e23993e5d982d",
version: "0.9.0",
version: "0.9.1",
}),
assertInstalledClosurePackage({
keyFile: "dist/message-bundle-v1.js",
Expand Down
4 changes: 3 additions & 1 deletion src/cookie-safe-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,14 @@ const APP_RESULT_FRAME_BYTES = 64 * 1024;

export class LocalAppError extends Error {
override readonly name = "LocalAppError";
readonly code: "identity-unavailable" | "assemble-failed" | "helper-mismatch" | "runner-failed";
constructor(
readonly code: "identity-unavailable" | "assemble-failed" | "helper-mismatch" | "runner-failed",
code: "identity-unavailable" | "assemble-failed" | "helper-mismatch" | "runner-failed",
message: string,
options?: { readonly cause?: unknown },
) {
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
this.code = code;
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/provider-plugin-portable-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1045,9 +1045,12 @@ function capabilityHost(options: {
{
method: request.method,
headers,
// A defined body stays literal for exactOptionalPropertyTypes
// consumers: RequestInit["body"] takes BodyInit | null, never
// an explicit undefined.
...(request.body.kind === "none"
? {}
: { body: bodyBytes(request.body) }),
: { body: bodyBytes(request.body) ?? null }),
redirect: "error",
signal: requestDeadline.signal,
},
Expand Down
7 changes: 5 additions & 2 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,12 @@ const pathHelperPath = join(dirname(fileURLToPath(import.meta.url)), "path-helpe
const stateCrashPlanForTest = process.env.NODE_ENV === "test"
? process.env.GHOSTGET_TEST_STATE_CRASH_PLAN
: undefined;
// NODE_ENV must stay a literal: consumers whose ambient types narrow
// NodeJS.ProcessEnv.NODE_ENV to "development" | "test" | "production" reject a
// widened string on the spawn options env field.
const helperEnvironment = stateCrashPlanForTest === undefined
? { NODE_ENV: "production" }
: { NODE_ENV: "test", GHOSTGET_TEST_STATE_CRASH_PLAN: stateCrashPlanForTest };
? { NODE_ENV: "production" as const }
: { NODE_ENV: "test" as const, GHOSTGET_TEST_STATE_CRASH_PLAN: stateCrashPlanForTest };
const helperPreloadForTest: readonly string[] = stateCrashPlanForTest === undefined
? []
: ["--preload", join(dirname(fileURLToPath(import.meta.url)), "state-crash-preload.test-support.ts")];
Expand Down
Loading