stitchkit 0.104.0 → 0.104.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -15,6 +15,31 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
15
15
 
16
16
  ## [Unreleased]
17
17
 
18
+ ## [0.104.2] - 2026-10-04
19
+
20
+ ### Added
21
+
22
+ - `stitchkit/files/packaging`: one multi-target companion plugin packages both Darwin addons into a universal JS artifact; explicit per-architecture output paths preserve single-target and embedded recipes.
23
+
24
+ ### Fixed
25
+
26
+ - Native qualification waits for the helper’s actual first write instead of inferring readiness from startup timing.
27
+
28
+ ## [0.104.1] - 2026-10-04
29
+
30
+ ### Added
31
+
32
+ - `stitchkit/files/packaging` — evolving build-only `createNativePackaging` resolves the
33
+ installed target addon, original SHA256 and a Bun-compatible plugin for custom companion
34
+ placement or embedded standalone delivery. Runtime leaves keep the same native owner;
35
+ archives, signing and installation policy remain application-owned. → ADR 0233
36
+
37
+ ### Documentation
38
+
39
+ - The JS native-asset delivery migration first affected in 0.103.13 is explicit in the
40
+ [upgrade guide](docs/guide/upgrading.md#released-migration-010313), including single-file
41
+ packaging, complete output graphs and the distinct npm/JS/compiled delivery paths.
42
+
18
43
  ## [0.104.0] - 2026-10-04
19
44
 
20
45
  ### ⚠️ Breaking changes
@@ -112,6 +137,16 @@ native CLI commands and ordinary one-argument state transitions remain valid.
112
137
 
113
138
  ## [0.103.13] - 2026-10-04
114
139
 
140
+ ### ⚠️ Delivery migration
141
+
142
+ - `stitchkit/process`, `stitchkit/files` and contained-file tools in Bun JS distributions:
143
+ the static Darwin addon graph adds companion assets. This delivery change was published
144
+ as a patch, although packagers assuming one output must migrate:
145
+ `--outfile=dist/native.js` → `--outdir=dist --entry-naming=native.js` and retain all outputs.
146
+ Ordinary npm imports keep their package assets; Bun compiled executables embed them.
147
+ **Who must act:** custom JS packagers and installers, including single-archive delivery.
148
+ [Migration](docs/guide/upgrading.md#released-migration-010313).
149
+
115
150
  ### Fixed
116
151
 
117
152
  - Darwin native process and contained-file backends retain a static addon dependency through
package/darwin-native.cjs CHANGED
@@ -1,7 +1,5 @@
1
- /** A lazy, statically discoverable addon edge shared by Node and Bun consumers. */
1
+ /** Generated from the owning native asset graph. Loading remains lazy. */
2
2
  module.exports = function loadDarwinAddon() {
3
- // The catch also keeps non-Darwin builds independent of unavailable slices.
4
- // Bun compile selects its target architecture and embeds that literal addon.
5
3
  try {
6
4
  if (process.arch === 'arm64') return require('./native/darwin-arm64.node');
7
5
  if (process.arch === 'x64') return require('./native/darwin-x64.node');
@@ -4,7 +4,7 @@ import {
4
4
  paginatedSchema2,
5
5
  encodeCursor2,
6
6
  decodeCursor2
7
- } from "../index-22rhg58f.js";
7
+ } from "../index-hp9d77wk.js";
8
8
  import {
9
9
  DEFAULT_CONTRACT_STREAM_FRAME_BYTES2,
10
10
  ContractStreamFrameSchema2
@@ -0,0 +1,3 @@
1
+ export type { NativePackagingAsset, NativePackagingOptions, NativePackagingPlugin, NativePackagingResult, } from '../../files/native-packaging.js';
2
+ export { createNativePackaging } from '../../files/native-packaging.js';
3
+ //# sourceMappingURL=packaging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packaging.d.ts","sourceRoot":"","sources":["../../../src/entrypoints/files/packaging.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,184 @@
1
+ // src/files/native-packaging.ts
2
+ import { createHash } from "node:crypto";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { dirname, relative, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { z as z2 } from "zod";
7
+
8
+ // src/files/native-packaging-layout.ts
9
+ import { z } from "zod";
10
+ var PackagePathSchema = z.string().min(1).refine((value) => !value.startsWith("/") && !/^[a-z]:/i.test(value) && !value.includes("\\") && value.split("/").every((part) => part !== ".." && part !== "." && part.length > 0), "Expected a relative package path without traversal");
11
+ var NativeLayoutSchema = z.object({
12
+ formatVersion: z.literal(1),
13
+ loader: PackagePathSchema,
14
+ assets: z.object({ arm64: PackagePathSchema, x64: PackagePathSchema })
15
+ });
16
+ function nativeLoaderSource(assets) {
17
+ const quote = (value) => `'${JSON.stringify(value).slice(1, -1).replaceAll("'", "\\'")}'`;
18
+ const branches = Object.entries(assets).map(([architecture, asset]) => ` if (process.arch === ${quote(architecture)}) return require(${quote(asset)});`);
19
+ return [
20
+ "/** Generated from the owning native asset graph. Loading remains lazy. */",
21
+ "module.exports = function loadDarwinAddon() {",
22
+ " try {",
23
+ ...branches,
24
+ " throw new Error('Unsupported Darwin addon architecture');",
25
+ " } catch (cause) {",
26
+ " throw new Error('Darwin addon loading failed', { cause });",
27
+ " }",
28
+ "};",
29
+ ""
30
+ ].join(`
31
+ `);
32
+ }
33
+
34
+ // src/files/native-packaging.ts
35
+ var OutputPathSchema = z2.string().min(1).refine((value) => !value.startsWith("/") && !/^[a-z]:/i.test(value) && !value.includes("\\") && value.split("/").every((part) => part !== ".." && part !== "." && part.length > 0), "Expected a relative output path without traversal");
36
+ var ArchitectureSchema = z2.enum(["arm64", "x64"]);
37
+ var CommonOptions = {
38
+ platform: z2.string().min(1),
39
+ entryPath: OutputPathSchema.refine((value) => !value.includes("[") && !value.includes("]"), "Expected a fixed entry path without Bun naming templates")
40
+ };
41
+ var SingleOptionsSchema = z2.object({
42
+ ...CommonOptions,
43
+ architecture: z2.string().min(1),
44
+ delivery: z2.enum(["companion", "embedded"]),
45
+ assetPath: OutputPathSchema
46
+ });
47
+ var MultipleOptionsSchema = z2.object({
48
+ ...CommonOptions,
49
+ architecture: z2.array(z2.string().min(1)).min(1),
50
+ delivery: z2.literal("companion"),
51
+ assetPath: z2.record(z2.string().min(1), OutputPathSchema)
52
+ });
53
+ var OptionsSchema = z2.union([SingleOptionsSchema, MultipleOptionsSchema]).superRefine((input, ctx) => {
54
+ const targets = typeof input.architecture === "string" ? [input.architecture] : input.architecture;
55
+ if (new Set(targets).size !== targets.length)
56
+ ctx.addIssue({ code: "custom", message: "Architecture targets must be unique" });
57
+ const assetPaths = typeof input.assetPath === "string" ? [input.assetPath] : Object.values(input.assetPath);
58
+ const assetMap = input.assetPath;
59
+ if (typeof assetMap !== "string" && (Object.keys(assetMap).length !== targets.length || targets.some((target) => !Object.hasOwn(assetMap, target))))
60
+ ctx.addIssue({
61
+ code: "custom",
62
+ message: "Asset paths must name exactly the declared architectures"
63
+ });
64
+ const paths = [input.entryPath, ...assetPaths];
65
+ for (let at = 0;at < paths.length; at++) {
66
+ const left = paths[at];
67
+ if (!left)
68
+ continue;
69
+ for (const right of paths.slice(at + 1)) {
70
+ if (left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`))
71
+ ctx.addIssue({
72
+ code: "custom",
73
+ message: "Entry and addons must have non-overlapping paths"
74
+ });
75
+ }
76
+ }
77
+ });
78
+ var AssetSchema = z2.object({
79
+ sourcePath: z2.string(),
80
+ outputPath: z2.string(),
81
+ sha256: z2.string().regex(/^[a-f0-9]{64}$/)
82
+ });
83
+ var TargetAssetSchema = AssetSchema.extend({ architecture: ArchitectureSchema });
84
+ var RefusalSchema = z2.object({
85
+ state: z2.enum(["unsupported", "missing"]),
86
+ platform: z2.string(),
87
+ architecture: z2.string(),
88
+ code: z2.enum(["NATIVE_TARGET_UNSUPPORTED", "NATIVE_ASSET_MISSING"])
89
+ });
90
+ function installedPackageRoot() {
91
+ let directory = dirname(fileURLToPath(import.meta.url));
92
+ for (let depth = 0;depth < 64; depth++) {
93
+ if (existsSync(resolve(directory, "native-assets.json")))
94
+ return directory;
95
+ const parent = dirname(directory);
96
+ if (parent === directory)
97
+ break;
98
+ directory = parent;
99
+ }
100
+ throw new Error("Native packaging must run from its installed Stitchkit package");
101
+ }
102
+ function createNativePackaging(options) {
103
+ const input = OptionsSchema.parse(options);
104
+ const declared = typeof input.architecture === "string" ? [input.architecture] : input.architecture;
105
+ const architectures = [];
106
+ for (const architecture of declared) {
107
+ const target = ArchitectureSchema.safeParse(architecture);
108
+ if (input.platform !== "darwin" || !target.success)
109
+ return {
110
+ state: "unsupported",
111
+ platform: input.platform,
112
+ architecture,
113
+ code: "NATIVE_TARGET_UNSUPPORTED"
114
+ };
115
+ architectures.push(target.data);
116
+ }
117
+ const root = installedPackageRoot();
118
+ const layout = NativeLayoutSchema.parse(JSON.parse(readFileSync(resolve(root, "native-assets.json"), "utf8")));
119
+ const manifest = z2.object({ name: z2.literal("stitchkit"), version: z2.string().min(1) }).parse(JSON.parse(readFileSync(resolve(root, "package.json"), "utf8")));
120
+ const loaderPath = resolve(root, layout.loader);
121
+ const assets = [];
122
+ const specifiers = {};
123
+ for (const architecture of architectures) {
124
+ const sourcePath = resolve(root, layout.assets[architecture]);
125
+ const outputPath = typeof input.assetPath === "string" ? input.assetPath : input.assetPath[architecture];
126
+ if (!outputPath)
127
+ throw new Error("Declared architecture has no output path");
128
+ let bytes;
129
+ try {
130
+ bytes = readFileSync(sourcePath);
131
+ } catch (cause) {
132
+ if (cause instanceof Error && "code" in cause && cause.code === "ENOENT")
133
+ return {
134
+ state: "missing",
135
+ platform: input.platform,
136
+ architecture,
137
+ code: "NATIVE_ASSET_MISSING"
138
+ };
139
+ throw cause;
140
+ }
141
+ const outputSpecifier = relative(dirname(input.entryPath), outputPath).split("\\").join("/");
142
+ specifiers[architecture] = input.delivery === "embedded" ? sourcePath : outputSpecifier.startsWith(".") ? outputSpecifier : `./${outputSpecifier}`;
143
+ const asset = {
144
+ sourcePath,
145
+ outputPath,
146
+ sha256: createHash("sha256").update(bytes).digest("hex")
147
+ };
148
+ assets.push(typeof input.architecture === "string" ? asset : { ...asset, architecture });
149
+ }
150
+ const first = architectures[0];
151
+ if (!first)
152
+ throw new Error("Native packaging has no declared targets");
153
+ const contents = nativeLoaderSource(specifiers);
154
+ const external = new Set(Object.values(specifiers));
155
+ return {
156
+ state: "ready",
157
+ platform: "darwin",
158
+ architecture: typeof input.architecture === "string" ? first : architectures,
159
+ packageVersion: manifest.version,
160
+ assets,
161
+ plugin: {
162
+ name: "stitchkit-native-packaging",
163
+ setup(build) {
164
+ if (input.delivery === "companion") {
165
+ if (build.config.entrypoints.length !== 1)
166
+ throw new Error("Native companion packaging requires exactly one entrypoint");
167
+ if (build.config.splitting)
168
+ throw new Error("Native companion packaging requires splitting: false");
169
+ const naming = build.config.naming;
170
+ const entry = typeof naming === "string" ? naming : naming?.entry;
171
+ if (entry !== input.entryPath)
172
+ throw new Error("Native companion packaging naming.entry must match entryPath");
173
+ }
174
+ build.onLoad({ filter: /\.[cm]?js$/ }, (args) => args.path === loaderPath ? { contents, loader: "js" } : undefined);
175
+ if (input.delivery === "companion") {
176
+ build.onResolve({ filter: /./ }, (args) => args.importer === loaderPath && external.has(args.path) ? { path: args.path, external: true } : undefined);
177
+ }
178
+ }
179
+ }
180
+ };
181
+ }
182
+ export {
183
+ createNativePackaging
184
+ };
@@ -80,7 +80,7 @@ import {
80
80
  paginatedSchema2,
81
81
  encodeCursor2,
82
82
  decodeCursor2
83
- } from "../index-22rhg58f.js";
83
+ } from "../index-hp9d77wk.js";
84
84
  import {
85
85
  ManagedFilePathSchema2,
86
86
  ManagedFileRefSchema2
@@ -33,7 +33,7 @@ import"../index-1bx1xv1k.js";
33
33
  import"../index-43p08g7t.js";
34
34
  import"../index-4s0fep5q.js";
35
35
  import"../index-ck71tbcf.js";
36
- import"../index-22rhg58f.js";
36
+ import"../index-hp9d77wk.js";
37
37
  import"../index-xqhede6x.js";
38
38
  import {
39
39
  bindRealtimeServer2
@@ -61,7 +61,7 @@ import {
61
61
  import"../index-43p08g7t.js";
62
62
  import"../index-4s0fep5q.js";
63
63
  import"../index-ck71tbcf.js";
64
- import"../index-22rhg58f.js";
64
+ import"../index-hp9d77wk.js";
65
65
  import"../index-xqhede6x.js";
66
66
  import {
67
67
  createFileStateStore2
@@ -81,9 +81,6 @@ import {
81
81
  signJwt2,
82
82
  verifyPkce2
83
83
  } from "../index-wh0ecvyk.js";
84
- import {
85
- deriveCodeChallenge2
86
- } from "../index-ap3gqe56.js";
87
84
  import {
88
85
  defineMultipartStream2,
89
86
  createMultipartStream2,
@@ -91,6 +88,9 @@ import {
91
88
  createImplement2,
92
89
  createScopedImplement2
93
90
  } from "../index-g2j7pnak.js";
91
+ import {
92
+ deriveCodeChallenge2
93
+ } from "../index-ap3gqe56.js";
94
94
  import {
95
95
  isWithinDir2,
96
96
  realPathWithinDir
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ export declare const NativeLayoutSchema: z.ZodObject<{
3
+ formatVersion: z.ZodLiteral<1>;
4
+ loader: z.ZodString;
5
+ assets: z.ZodObject<{
6
+ arm64: z.ZodString;
7
+ x64: z.ZodString;
8
+ }, z.core.$strip>;
9
+ }, z.core.$strip>;
10
+ /** One lazy loader for package, companion and embedded delivery. */
11
+ export declare function nativeLoaderSource(assets: Readonly<Record<string, string>>): string;
12
+ //# sourceMappingURL=native-packaging-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-packaging-layout.d.ts","sourceRoot":"","sources":["../../src/files/native-packaging-layout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,eAAO,MAAM,kBAAkB;;;;;;;iBAI7B,CAAC;AAEH,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAmBnF"}
@@ -0,0 +1,94 @@
1
+ import { z } from 'zod';
2
+ declare const ArchitectureSchema: z.ZodEnum<{
3
+ arm64: "arm64";
4
+ x64: "x64";
5
+ }>;
6
+ declare const SingleOptionsSchema: z.ZodObject<{
7
+ platform: z.ZodString;
8
+ entryPath: z.ZodString;
9
+ architecture: z.ZodString;
10
+ delivery: z.ZodEnum<{
11
+ companion: "companion";
12
+ embedded: "embedded";
13
+ }>;
14
+ assetPath: z.ZodString;
15
+ }, z.core.$strip>;
16
+ declare const MultipleOptionsSchema: z.ZodObject<{
17
+ platform: z.ZodString;
18
+ entryPath: z.ZodString;
19
+ architecture: z.ZodArray<z.ZodString>;
20
+ delivery: z.ZodLiteral<"companion">;
21
+ assetPath: z.ZodRecord<z.ZodString, z.ZodString>;
22
+ }, z.core.$strip>;
23
+ declare const AssetSchema: z.ZodObject<{
24
+ sourcePath: z.ZodString;
25
+ outputPath: z.ZodString;
26
+ sha256: z.ZodString;
27
+ }, z.core.$strip>;
28
+ declare const TargetAssetSchema: z.ZodObject<{
29
+ sourcePath: z.ZodString;
30
+ outputPath: z.ZodString;
31
+ sha256: z.ZodString;
32
+ architecture: z.ZodEnum<{
33
+ arm64: "arm64";
34
+ x64: "x64";
35
+ }>;
36
+ }, z.core.$strip>;
37
+ declare const RefusalSchema: z.ZodObject<{
38
+ state: z.ZodEnum<{
39
+ missing: "missing";
40
+ unsupported: "unsupported";
41
+ }>;
42
+ platform: z.ZodString;
43
+ architecture: z.ZodString;
44
+ code: z.ZodEnum<{
45
+ NATIVE_ASSET_MISSING: "NATIVE_ASSET_MISSING";
46
+ NATIVE_TARGET_UNSUPPORTED: "NATIVE_TARGET_UNSUPPORTED";
47
+ }>;
48
+ }, z.core.$strip>;
49
+ export type NativePackagingOptions<Multiple extends boolean = false> = Multiple extends true ? z.input<typeof MultipleOptionsSchema> : z.input<typeof SingleOptionsSchema>;
50
+ export type NativePackagingAsset<Multiple extends boolean = false> = Multiple extends true ? z.infer<typeof TargetAssetSchema> : z.infer<typeof AssetSchema>;
51
+ /** Structural Bun plugin protocol: declarations need no Bun runtime or ambient types. */
52
+ export type NativePackagingPlugin = {
53
+ name: string;
54
+ setup: (build: {
55
+ config: {
56
+ splitting?: boolean;
57
+ naming?: string | {
58
+ entry?: string;
59
+ };
60
+ entrypoints: string[];
61
+ };
62
+ onLoad: (options: {
63
+ filter: RegExp;
64
+ }, callback: (args: {
65
+ path: string;
66
+ }) => {
67
+ contents: string;
68
+ loader: 'js';
69
+ } | undefined) => unknown;
70
+ onResolve: (options: {
71
+ filter: RegExp;
72
+ }, callback: (args: {
73
+ path: string;
74
+ importer: string;
75
+ }) => {
76
+ path: string;
77
+ external: boolean;
78
+ } | undefined) => unknown;
79
+ }) => void;
80
+ };
81
+ export type NativePackagingResult<Multiple extends boolean = false> = z.infer<typeof RefusalSchema> | {
82
+ state: 'ready';
83
+ platform: 'darwin';
84
+ architecture: Multiple extends true ? z.infer<typeof ArchitectureSchema>[] : z.infer<typeof ArchitectureSchema>;
85
+ packageVersion: string;
86
+ assets: NativePackagingAsset<Multiple>[];
87
+ plugin: NativePackagingPlugin;
88
+ };
89
+ /** Resolve this installed package's asset graph, without parsing downstream loader text. */
90
+ export declare function createNativePackaging(options: NativePackagingOptions): NativePackagingResult;
91
+ export declare function createNativePackaging(options: NativePackagingOptions<true>): NativePackagingResult<true>;
92
+ export declare function createNativePackaging(options: NativePackagingOptions<boolean>): NativePackagingResult<boolean>;
93
+ export {};
94
+ //# sourceMappingURL=native-packaging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-packaging.d.ts","sourceRoot":"","sources":["../../src/files/native-packaging.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,QAAA,MAAM,kBAAkB;;;EAA2B,CAAC;AAQpD,QAAA,MAAM,mBAAmB;;;;;;;;;iBAKvB,CAAC;AACH,QAAA,MAAM,qBAAqB;;;;;;iBAKzB,CAAC;AAiCH,QAAA,MAAM,WAAW;;;;iBAIf,CAAC;AACH,QAAA,MAAM,iBAAiB;;;;;;;;iBAA2D,CAAC;AACnF,QAAA,MAAM,aAAa;;;;;;;;;;;iBAKjB,CAAC;AAEH,MAAM,MAAM,sBAAsB,CAAC,QAAQ,SAAS,OAAO,GAAG,KAAK,IAAI,QAAQ,SAAS,IAAI,GACxF,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GACrC,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACxC,MAAM,MAAM,oBAAoB,CAAC,QAAQ,SAAS,OAAO,GAAG,KAAK,IAAI,QAAQ,SAAS,IAAI,GACtF,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,GACjC,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhC,yFAAyF;AACzF,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,KAAK,EAAE;QACb,MAAM,EAAE;YACN,SAAS,CAAC,EAAE,OAAO,CAAC;YACpB,MAAM,CAAC,EAAE,MAAM,GAAG;gBAAE,KAAK,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;YACrC,WAAW,EAAE,MAAM,EAAE,CAAC;SACvB,CAAC;QACF,MAAM,EAAE,CACN,OAAO,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,EAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,KAAK;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,IAAI,CAAA;SAAE,GAAG,SAAS,KACjF,OAAO,CAAC;QACb,SAAS,EAAE,CACT,OAAO,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,EAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE;YACf,IAAI,EAAE,MAAM,CAAC;YACb,QAAQ,EAAE,MAAM,CAAC;SAClB,KAAK;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAA;SAAE,GAAG,SAAS,KAClD,OAAO,CAAC;KACd,KAAK,IAAI,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,qBAAqB,CAAC,QAAQ,SAAS,OAAO,GAAG,KAAK,IAC9D,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GAC7B;IACE,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,QAAQ,SAAS,IAAI,GAC/B,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,EAAE,GACpC,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;IACzC,MAAM,EAAE,qBAAqB,CAAC;CAC/B,CAAC;AAaN,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAAC;AAC9F,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,CAAC,IAAI,CAAC,GACpC,qBAAqB,CAAC,IAAI,CAAC,CAAC;AAC/B,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,CAAC,OAAO,CAAC,GACvC,qBAAqB,CAAC,OAAO,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ # stitchkit/files/packaging — stitchkit docs slice
2
+
3
+ > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
+ > `llms.txt` in this package.
5
+
6
+ Contains: API reference: `stitchkit/files/packaging` (docs/api/reference.md)
7
+
8
+ Also read:
9
+ - see `llms/process.txt` for the Native IO guide
10
+
11
+ ==============================================================================
12
+ # API reference: `stitchkit/files/packaging` (docs/api/reference.md)
13
+ ==============================================================================
14
+
15
+ ## `stitchkit/files/packaging`
16
+
17
+ `stitchkit/files/packaging` owns `createNativePackaging` and the types
18
+ `NativePackagingOptions`, `NativePackagingAsset`, `NativePackagingPlugin`, `NativePackagingResult`.
19
+ It resolves the installed package's target/version and original addon SHA256.
20
+ `platform`, `architecture`, `delivery`, `entryPath` and `assetPath` are explicit public inputs.
21
+ The result is `ready` with assets and a structural Bun plugin, or `unsupported`/`missing` with a safe code.
22
+ Malformed options/metadata and other filesystem errors fail rather than masquerade as missing.
23
+ Companion accepts a single `architecture` string with an `assetPath` string, or an architecture
24
+ array with an exact per-architecture `assetPath` map. `NativePackagingOptions<true>` and
25
+ `NativePackagingResult<true>` describe the array form; default types preserve single-target signatures.
26
+ A ready array-form result carries the architecture array; `NativePackagingAsset<true>` identifies
27
+ each asset architecture. Default asset types and single-target runtime objects retain their three fields.
28
+ Targets must be unique; entry and all asset paths must be disjoint. One lazy loader chooses only
29
+ the actual runtime architecture, with no fallback.
30
+ Companion mode places external addons relative to the emitted entry; embedded mode lets
31
+ Bun compile retain the matching addon. Keep JS builds without splitting, preserve every output,
32
+ and copy each original asset to its `outputPath` after verifying `sha256`.
33
+ This evolving entrypoint is a build dependency; runtime imports do not reach it.
34
+ The detailed recipe is in [native IO](../guide/native-io.md#public-native-packaging).
package/llms/process.txt CHANGED
@@ -15,7 +15,7 @@ description: Native IO для Bun и Node, с явно выбранной durabi
15
15
  type: guide
16
16
  status: active
17
17
  created: 2026-10-01 20:44 +07:00
18
- updated: 2026-10-04 09:40 +07:00
18
+ updated: 2026-10-04 20:04 +07:00
19
19
  participants:
20
20
  - role: authored
21
21
  harness: Codex
@@ -293,6 +293,89 @@ no recognized native error code, the stage stays `load`; the loader never infers
293
293
  This delivery removes a runtime kernel installation; it does not remove the build-time
294
294
  Stitchkit installation or create an independent lightweight npm package.
295
295
 
296
+ ## Public native packaging
297
+
298
+ Custom archives and installers use `stitchkit/files/packaging` at **build time**.
299
+ `createNativePackaging` resolves the installed version and target addon with its original SHA256;
300
+ its Bun-compatible plugin integrates that asset with the same lazy native loader.
301
+ The leaf is evolving. It requires no optional peer or Bun ambient declarations for Node imports.
302
+ It must run unbundled from its installed package, never from an application's runtime artifact.
303
+ This contract is available from 0.104.1; versions 0.103.13–0.104.0 use the complete-output recipe
304
+ in the previous section.
305
+
306
+ ```ts
307
+ import { createNativePackaging } from 'stitchkit/files/packaging'
308
+ import { createHash } from 'node:crypto'
309
+ import { copyFileSync, mkdirSync, readFileSync } from 'node:fs'
310
+ import { dirname, join } from 'node:path'
311
+
312
+ const native = createNativePackaging({
313
+ platform: 'darwin', architecture: 'arm64', delivery: 'companion',
314
+ entryPath: 'app/native.js', assetPath: 'addons/owner.node',
315
+ })
316
+ if (native.state !== 'ready') throw new Error(native.code)
317
+ const result = await Bun.build({
318
+ entrypoints: ['src/native.ts'], target: 'node', format: 'esm',
319
+ outdir: 'dist', naming: { entry: 'app/native.js' }, splitting: false,
320
+ plugins: [native.plugin],
321
+ })
322
+ if (!result.success) throw new AggregateError(result.logs, 'Build failed')
323
+ // Preserve every result.outputs file under dist, then add the selected companion.
324
+ for (const asset of native.assets) {
325
+ const bytes = readFileSync(asset.sourcePath)
326
+ if (createHash('sha256').update(bytes).digest('hex') !== asset.sha256)
327
+ throw new Error('Native asset changed during build')
328
+ const destination = join('dist', asset.outputPath)
329
+ mkdirSync(dirname(destination), { recursive: true })
330
+ copyFileSync(asset.sourcePath, destination)
331
+ }
332
+ ```
333
+
334
+ `entryPath` and `assetPath` are relative paths inside the application's output root, without
335
+ absolute paths, traversal or overlapping file/directory paths. `entryPath` is fixed, without
336
+ Bun naming templates such as `[dir]` or `[name]`. Companion JS builds require one entry, no splitting,
337
+ and `naming.entry` exactly matching `entryPath`; the plugin rejects a mismatching layout.
338
+ Choose any application layout; the framework does not prescribe an app directory or installer.
339
+ Keep every bundler output, then verify copied addon bytes against the original SHA256.
340
+ Archive the complete output directory with an integrity manifest, unpack to a clean directory,
341
+ verify the same hashes there, and run offline without the build tree or `node_modules`.
342
+ A digest provides integrity, not authenticity: signature and trust policy remain application-owned.
343
+
344
+ From 0.104.2, one JS artifact can run on both Darwin architectures; change only the packaging inputs:
345
+
346
+ ```ts
347
+ const native = createNativePackaging({
348
+ platform: 'darwin', architecture: ['arm64', 'x64'], delivery: 'companion',
349
+ entryPath: 'app/native.js',
350
+ assetPath: { arm64: 'addons/arm.node', x64: 'addons/intel.node' },
351
+ })
352
+ ```
353
+
354
+ Use the same single plugin and copy loop above. A single-target loader deliberately refuses the
355
+ other architecture; two competing plugins cannot produce a universal loader. The array form
356
+ requires exactly one distinct output path per declared target. A ready result carries the target
357
+ array and both original asset hashes; `NativePackagingOptions<true>` /
358
+ `NativePackagingResult<true>` describe this form when explicitly annotating variables.
359
+ The loader selects only `process.arch`: a missing or wrong-architecture selected addon refuses,
360
+ even when the other valid addon is present. Linux runtime imports remain lazy and use Linux
361
+ process primitives without loading Darwin assets. Qualify the exact same JS bytes on real
362
+ Darwin arm64 and x64 machines, with both companions preserved through archive delivery.
363
+ Build-machine architecture and cross-build success cannot establish universal native support.
364
+
365
+ For a standalone executable use `delivery: 'embedded'` and the same plugin in `Bun.build`
366
+ with `compile: { outfile: ... }`. Bun embeds the selected addon; `assets` identifies its
367
+ original bytes for qualification, not a companion that must be installed beside the executable.
368
+ Cross-builds must select the requested architecture explicitly; the artifact must run on that target.
369
+ The installed package must contain that target's addon. No automatic fallback/downgrade occurs.
370
+
371
+ The API returns `unsupported` / `NATIVE_TARGET_UNSUPPORTED` for non-Darwin or unsupported
372
+ architectures, and `missing` / `NATIVE_ASSET_MISSING` for a missing addon. Portable Linux libraries
373
+ use their normal build, without a Darwin plugin; merely importing the build leaf loads no addon.
374
+ Malformed inputs, unsupported metadata versions and other IO errors throw before packaging.
375
+ Runtime missing/corrupt companions preserve the native `unavailable` result and safe stage/code
376
+ from the existing observer; they never certify a dead owner. Preserve the raw cause internally.
377
+ The plugin does not parse consumer JS or establish a second runtime native implementation.
378
+
296
379
  ## Qualification boundaries
297
380
 
298
381
  Native command cleanup owns the child before launch subscriptions are installed. Setup
@@ -70,6 +70,7 @@ own, recorded as an ADR.
70
70
  | `stitchkit/cli` | server | stable | `createCli` / `createCliInvoker` — runtime and strict NodeNext declarations without MCP SDK / `ai` |
71
71
  | `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
72
72
  | `stitchkit/process` | server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture and process-group cancellation |
73
+ | `stitchkit/files/packaging` | build time (Bun or Node) | evolving | installed native asset graph, integrity and Bun packaging plugin; no runtime install policy |
73
74
  | `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
74
75
  | `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, resumable broadcasts, the operator channel with dedupe, local Bot API files, webhook ownership and durable webhook update intake |
75
76
  | `stitchkit/telegram/html` | browser and server | evolving | Telegram's HTML as a tree — clean any markup to what Telegram accepts, cut long text into valid parts by what the reader sees, check markup before it is sent |
@@ -1,14 +1,60 @@
1
- # Upgrading 0.62.0 → 0.79.0 — stitchkit docs slice
1
+ # Upgrading 0.62.0 → 0.80.0 — stitchkit docs slice
2
2
 
3
3
  > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
4
  > `llms.txt` in this package.
5
5
 
6
- Contains: Guide: Upgrading, 0.62.0 → 0.79.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.62.0 → 0.80.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.62.0 → 0.79.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.62.0 → 0.80.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.80.0
13
+
14
+ One thing, and only if you parse an async-operation snapshot.
15
+
16
+ ```bash
17
+ rg -n "createAsyncOperationSnapshotSchema"
18
+ ```
19
+
20
+ `progress` on a parsed snapshot used to be `unknown`. The factory built its
21
+ object shapes with a conditional spread, and the narrowing inside it widened
22
+ your schema back to a bare `ZodType`, so the emitted declaration threw the type
23
+ argument away. It is now the output of the schema you configured.
24
+
25
+ ```ts
26
+ const schema = createAsyncOperationSnapshotSchema({
27
+ progress: z.object({ done: z.number(), total: z.number() }),
28
+ failure: z.object({ code: z.string() }),
29
+ })
30
+ const snap = schema.parse(body)
31
+ if (snap.phase === 'running') {
32
+ // before: `snap.progress` was `unknown` — this needed a cast or a narrow
33
+ // after: it is `{ done: number; total: number } | undefined`
34
+ console.log(snap.progress?.done)
35
+ }
36
+ ```
37
+
38
+ **If you cast it**, delete the cast — it was working around the erased type.
39
+ **If you assigned something else to it**, the compiler now refuses, and it was
40
+ already a value the schema rejected at run time.
41
+
42
+ **If you pass explicit type arguments**, the no-progress form takes one now, not
43
+ two: the factory is two overloads. Pass none.
44
+
45
+ ```ts
46
+ // before: createAsyncOperationSnapshotSchema<undefined, typeof Failure>({ failure: Failure })
47
+ // after: createAsyncOperationSnapshotSchema({ failure: Failure })
48
+ ```
49
+
50
+ Nothing about parsing moves. A snapshot configured without `progress` still
51
+ strips an unexpected `progress` rather than refusing it — that behaviour is why
52
+ this is overloads and not one normalised key.
53
+
54
+ **If you compile with `skipLibCheck: false`**, this is also the release that
55
+ makes `stitchkit/tools/contract` compile at all: it carried five `TS2344`
56
+ errors, one per phase, from the same conditional spread.
57
+
12
58
  ## Released migration: 0.79.0
13
59
 
14
60
  One thing, and only if you assert on the whole snapshot.
@@ -1,16 +1,20 @@
1
- # Upgrading 0.80.0 → 0.104.0 — stitchkit docs slice
1
+ # Upgrading 0.81.0 → 0.104.0 — stitchkit docs slice
2
2
 
3
3
  > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
4
  > `llms.txt` in this package.
5
5
 
6
- Contains: Guide: Upgrading, 0.80.0 → 0.104.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.81.0 → 0.104.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.80.0 → 0.104.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.81.0 → 0.104.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
12
  ## Released migration: 0.104.0
13
13
 
14
+ JS bundles carrying Darwin native capabilities also need the
15
+ [delivery migration first affected in 0.103.13](#released-migration-010313).
16
+ Consumers upgrading from 0.103.12 or earlier must apply it even if their TypeScript API is unchanged.
17
+
14
18
  ### CLI runtime-tool construction
15
19
 
16
20
  **Who must act:** callers writing a schema-dependent handler or MCP/AI `present` directly
@@ -126,6 +130,36 @@ signals to `createCli` / `createCliInvoker` and cooperative handlers observe `ct
126
130
  an origin ignoring cancellation or an external provider stopped; keep resource leases until
127
131
  that work actually completes.
128
132
 
133
+ ## Released migration: 0.103.13
134
+
135
+ ### Darwin native assets in JS distributions
136
+
137
+ **Who must act:** Bun JS builds using `--outfile`, copying only `outputs[0]`, custom
138
+ single-file/archive packagers and installers delivering native process/files capabilities.
139
+ First affected publication: **0.103.13**. It shipped as a patch; its changed delivery graph
140
+ requires this migration despite unchanged API signatures.
141
+
142
+ ```sh
143
+ # Before: one JS file was assumed to be the entire distribution.
144
+ bun build src/native.ts --target=bun --outfile=dist/native.js
145
+ # After: retain every output, with its relative path.
146
+ bun build src/native.ts --target=bun --outdir=dist --entry-naming=native.js
147
+ ```
148
+
149
+ For `Bun.build`, copy **all** `result.outputs`, not just the JS entry or `outputs[0]`.
150
+ A single-file JS distribution cannot embed a native `.node` addon automatically.
151
+ An archive may contain JS plus companion assets, materialized together before execution.
152
+ Custom output layout uses the evolving build-only packaging contract in the
153
+ [native IO guide](native-io.md#public-native-packaging).
154
+
155
+ Three deliveries have different rules: an ordinary npm install retains the package's
156
+ native assets; a JS distribution must ship its complete output graph; a Bun compiled
157
+ executable embeds the matching addon and may still use `--outfile`.
158
+ The [native IO guide](native-io.md#libraries-with-a-zod-only-runtime) is the canonical recipe.
159
+ Check the resulting artifact offline outside the build tree and `node_modules`, including
160
+ process identity, live-owner lock refusal, dead-owner recovery and contained IO.
161
+ A missing or corrupt addon remains `unavailable`, never evidence that an owner died.
162
+
129
163
  ## Released migration: 0.103.0
130
164
 
131
165
  ### Diagnostic journal startup preserves torn evidence and inspects archives
@@ -1018,49 +1052,3 @@ compile error instead of building a URL with an unresolved segment. Measured
1018
1052
  across three consuming applications before release (373 endpoints with path
1019
1053
  segments), no explicit schema missed a field: the migration is deleting
1020
1054
  schemas that only repeated the path, or nothing.
1021
-
1022
- ## Released migration: 0.80.0
1023
-
1024
- One thing, and only if you parse an async-operation snapshot.
1025
-
1026
- ```bash
1027
- rg -n "createAsyncOperationSnapshotSchema"
1028
- ```
1029
-
1030
- `progress` on a parsed snapshot used to be `unknown`. The factory built its
1031
- object shapes with a conditional spread, and the narrowing inside it widened
1032
- your schema back to a bare `ZodType`, so the emitted declaration threw the type
1033
- argument away. It is now the output of the schema you configured.
1034
-
1035
- ```ts
1036
- const schema = createAsyncOperationSnapshotSchema({
1037
- progress: z.object({ done: z.number(), total: z.number() }),
1038
- failure: z.object({ code: z.string() }),
1039
- })
1040
- const snap = schema.parse(body)
1041
- if (snap.phase === 'running') {
1042
- // before: `snap.progress` was `unknown` — this needed a cast or a narrow
1043
- // after: it is `{ done: number; total: number } | undefined`
1044
- console.log(snap.progress?.done)
1045
- }
1046
- ```
1047
-
1048
- **If you cast it**, delete the cast — it was working around the erased type.
1049
- **If you assigned something else to it**, the compiler now refuses, and it was
1050
- already a value the schema rejected at run time.
1051
-
1052
- **If you pass explicit type arguments**, the no-progress form takes one now, not
1053
- two: the factory is two overloads. Pass none.
1054
-
1055
- ```ts
1056
- // before: createAsyncOperationSnapshotSchema<undefined, typeof Failure>({ failure: Failure })
1057
- // after: createAsyncOperationSnapshotSchema({ failure: Failure })
1058
- ```
1059
-
1060
- Nothing about parsing moves. A snapshot configured without `progress` still
1061
- strips an unexpected `progress` rather than refusing it — that behaviour is why
1062
- this is overloads and not one normalised key.
1063
-
1064
- **If you compile with `skipLibCheck: false`**, this is also the release that
1065
- makes `stitchkit/tools/contract` compile at all: it carried five `TS2344`
1066
- errors, one per phase, from the same conditional spread.
@@ -11,8 +11,8 @@ Contains: Guide: Upgrading, general (docs/guide/upgrading.md)
11
11
 
12
12
  Load this slice, then every range slice between your installed version and the target:
13
13
 
14
- - 0.80.0 → 0.104.0: `llms/upgrading-0.80.0-0.104.0.txt`
15
- - 0.62.0 → 0.79.0: `llms/upgrading-0.62.0-0.79.0.txt`
14
+ - 0.81.0 → 0.104.0: `llms/upgrading-0.81.0-0.104.0.txt`
15
+ - 0.62.0 → 0.80.0: `llms/upgrading-0.62.0-0.80.0.txt`
16
16
  - 0.44.0 → 0.61.0: `llms/upgrading-0.44.0-0.61.0.txt`
17
17
  - 0.37.0 → 0.44.0: `llms/upgrading-0.37.0-0.44.0.txt`
18
18
 
package/llms-full.txt CHANGED
@@ -61,6 +61,7 @@ own, recorded as an ADR.
61
61
  | `stitchkit/cli` | server | stable | `createCli` / `createCliInvoker` — runtime and strict NodeNext declarations without MCP SDK / `ai` |
62
62
  | `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
63
63
  | `stitchkit/process` | server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture and process-group cancellation |
64
+ | `stitchkit/files/packaging` | build time (Bun or Node) | evolving | installed native asset graph, integrity and Bun packaging plugin; no runtime install policy |
64
65
  | `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
65
66
  | `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, resumable broadcasts, the operator channel with dedupe, local Bot API files, webhook ownership and durable webhook update intake |
66
67
  | `stitchkit/telegram/html` | browser and server | evolving | Telegram's HTML as a tree — clean any markup to what Telegram accepts, cut long text into valid parts by what the reader sees, check markup before it is sent |
@@ -8130,7 +8131,7 @@ description: Native IO для Bun и Node, с явно выбранной durabi
8130
8131
  type: guide
8131
8132
  status: active
8132
8133
  created: 2026-10-01 20:44 +07:00
8133
- updated: 2026-10-04 09:40 +07:00
8134
+ updated: 2026-10-04 20:04 +07:00
8134
8135
  participants:
8135
8136
  - role: authored
8136
8137
  harness: Codex
@@ -8408,6 +8409,89 @@ no recognized native error code, the stage stays `load`; the loader never infers
8408
8409
  This delivery removes a runtime kernel installation; it does not remove the build-time
8409
8410
  Stitchkit installation or create an independent lightweight npm package.
8410
8411
 
8412
+ ## Public native packaging
8413
+
8414
+ Custom archives and installers use `stitchkit/files/packaging` at **build time**.
8415
+ `createNativePackaging` resolves the installed version and target addon with its original SHA256;
8416
+ its Bun-compatible plugin integrates that asset with the same lazy native loader.
8417
+ The leaf is evolving. It requires no optional peer or Bun ambient declarations for Node imports.
8418
+ It must run unbundled from its installed package, never from an application's runtime artifact.
8419
+ This contract is available from 0.104.1; versions 0.103.13–0.104.0 use the complete-output recipe
8420
+ in the previous section.
8421
+
8422
+ ```ts
8423
+ import { createNativePackaging } from 'stitchkit/files/packaging'
8424
+ import { createHash } from 'node:crypto'
8425
+ import { copyFileSync, mkdirSync, readFileSync } from 'node:fs'
8426
+ import { dirname, join } from 'node:path'
8427
+
8428
+ const native = createNativePackaging({
8429
+ platform: 'darwin', architecture: 'arm64', delivery: 'companion',
8430
+ entryPath: 'app/native.js', assetPath: 'addons/owner.node',
8431
+ })
8432
+ if (native.state !== 'ready') throw new Error(native.code)
8433
+ const result = await Bun.build({
8434
+ entrypoints: ['src/native.ts'], target: 'node', format: 'esm',
8435
+ outdir: 'dist', naming: { entry: 'app/native.js' }, splitting: false,
8436
+ plugins: [native.plugin],
8437
+ })
8438
+ if (!result.success) throw new AggregateError(result.logs, 'Build failed')
8439
+ // Preserve every result.outputs file under dist, then add the selected companion.
8440
+ for (const asset of native.assets) {
8441
+ const bytes = readFileSync(asset.sourcePath)
8442
+ if (createHash('sha256').update(bytes).digest('hex') !== asset.sha256)
8443
+ throw new Error('Native asset changed during build')
8444
+ const destination = join('dist', asset.outputPath)
8445
+ mkdirSync(dirname(destination), { recursive: true })
8446
+ copyFileSync(asset.sourcePath, destination)
8447
+ }
8448
+ ```
8449
+
8450
+ `entryPath` and `assetPath` are relative paths inside the application's output root, without
8451
+ absolute paths, traversal or overlapping file/directory paths. `entryPath` is fixed, without
8452
+ Bun naming templates such as `[dir]` or `[name]`. Companion JS builds require one entry, no splitting,
8453
+ and `naming.entry` exactly matching `entryPath`; the plugin rejects a mismatching layout.
8454
+ Choose any application layout; the framework does not prescribe an app directory or installer.
8455
+ Keep every bundler output, then verify copied addon bytes against the original SHA256.
8456
+ Archive the complete output directory with an integrity manifest, unpack to a clean directory,
8457
+ verify the same hashes there, and run offline without the build tree or `node_modules`.
8458
+ A digest provides integrity, not authenticity: signature and trust policy remain application-owned.
8459
+
8460
+ From 0.104.2, one JS artifact can run on both Darwin architectures; change only the packaging inputs:
8461
+
8462
+ ```ts
8463
+ const native = createNativePackaging({
8464
+ platform: 'darwin', architecture: ['arm64', 'x64'], delivery: 'companion',
8465
+ entryPath: 'app/native.js',
8466
+ assetPath: { arm64: 'addons/arm.node', x64: 'addons/intel.node' },
8467
+ })
8468
+ ```
8469
+
8470
+ Use the same single plugin and copy loop above. A single-target loader deliberately refuses the
8471
+ other architecture; two competing plugins cannot produce a universal loader. The array form
8472
+ requires exactly one distinct output path per declared target. A ready result carries the target
8473
+ array and both original asset hashes; `NativePackagingOptions<true>` /
8474
+ `NativePackagingResult<true>` describe this form when explicitly annotating variables.
8475
+ The loader selects only `process.arch`: a missing or wrong-architecture selected addon refuses,
8476
+ even when the other valid addon is present. Linux runtime imports remain lazy and use Linux
8477
+ process primitives without loading Darwin assets. Qualify the exact same JS bytes on real
8478
+ Darwin arm64 and x64 machines, with both companions preserved through archive delivery.
8479
+ Build-machine architecture and cross-build success cannot establish universal native support.
8480
+
8481
+ For a standalone executable use `delivery: 'embedded'` and the same plugin in `Bun.build`
8482
+ with `compile: { outfile: ... }`. Bun embeds the selected addon; `assets` identifies its
8483
+ original bytes for qualification, not a companion that must be installed beside the executable.
8484
+ Cross-builds must select the requested architecture explicitly; the artifact must run on that target.
8485
+ The installed package must contain that target's addon. No automatic fallback/downgrade occurs.
8486
+
8487
+ The API returns `unsupported` / `NATIVE_TARGET_UNSUPPORTED` for non-Darwin or unsupported
8488
+ architectures, and `missing` / `NATIVE_ASSET_MISSING` for a missing addon. Portable Linux libraries
8489
+ use their normal build, without a Darwin plugin; merely importing the build leaf loads no addon.
8490
+ Malformed inputs, unsupported metadata versions and other IO errors throw before packaging.
8491
+ Runtime missing/corrupt companions preserve the native `unavailable` result and safe stage/code
8492
+ from the existing observer; they never certify a dead owner. Preserve the raw cause internally.
8493
+ The plugin does not parse consumer JS or establish a second runtime native implementation.
8494
+
8411
8495
  ## Qualification boundaries
8412
8496
 
8413
8497
  Native command cleanup owns the child before launch subscriptions are installed. Setup
@@ -15258,6 +15342,10 @@ analytics policy belong to the application.
15258
15342
 
15259
15343
  ## Released migration: 0.104.0
15260
15344
 
15345
+ JS bundles carrying Darwin native capabilities also need the
15346
+ [delivery migration first affected in 0.103.13](#released-migration-010313).
15347
+ Consumers upgrading from 0.103.12 or earlier must apply it even if their TypeScript API is unchanged.
15348
+
15261
15349
  ### CLI runtime-tool construction
15262
15350
 
15263
15351
  **Who must act:** callers writing a schema-dependent handler or MCP/AI `present` directly
@@ -15373,6 +15461,36 @@ signals to `createCli` / `createCliInvoker` and cooperative handlers observe `ct
15373
15461
  an origin ignoring cancellation or an external provider stopped; keep resource leases until
15374
15462
  that work actually completes.
15375
15463
 
15464
+ ## Released migration: 0.103.13
15465
+
15466
+ ### Darwin native assets in JS distributions
15467
+
15468
+ **Who must act:** Bun JS builds using `--outfile`, copying only `outputs[0]`, custom
15469
+ single-file/archive packagers and installers delivering native process/files capabilities.
15470
+ First affected publication: **0.103.13**. It shipped as a patch; its changed delivery graph
15471
+ requires this migration despite unchanged API signatures.
15472
+
15473
+ ```sh
15474
+ # Before: one JS file was assumed to be the entire distribution.
15475
+ bun build src/native.ts --target=bun --outfile=dist/native.js
15476
+ # After: retain every output, with its relative path.
15477
+ bun build src/native.ts --target=bun --outdir=dist --entry-naming=native.js
15478
+ ```
15479
+
15480
+ For `Bun.build`, copy **all** `result.outputs`, not just the JS entry or `outputs[0]`.
15481
+ A single-file JS distribution cannot embed a native `.node` addon automatically.
15482
+ An archive may contain JS plus companion assets, materialized together before execution.
15483
+ Custom output layout uses the evolving build-only packaging contract in the
15484
+ [native IO guide](native-io.md#public-native-packaging).
15485
+
15486
+ Three deliveries have different rules: an ordinary npm install retains the package's
15487
+ native assets; a JS distribution must ship its complete output graph; a Bun compiled
15488
+ executable embeds the matching addon and may still use `--outfile`.
15489
+ The [native IO guide](native-io.md#libraries-with-a-zod-only-runtime) is the canonical recipe.
15490
+ Check the resulting artifact offline outside the build tree and `node_modules`, including
15491
+ process identity, live-owner lock refusal, dead-owner recovery and contained IO.
15492
+ A missing or corrupt addon remains `unavailable`, never evidence that an owner died.
15493
+
15376
15494
  ## Released migration: 0.103.0
15377
15495
 
15378
15496
  ### Diagnostic journal startup preserves torn evidence and inspects archives
@@ -21887,3 +22005,24 @@ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/nativ
21887
22005
  | `observeProcessInstance` / `ProcessInstanceObservation` | function / type | Linux/Darwin lifetime observation; observed identity or unavailable with its original cause, never guessed absence |
21888
22006
  | `ProcessInstanceSchema` / `ProcessInstance` | schema / type | platform, boot ID, namespace and process start identity from the shared native reader |
21889
22007
  | `probeProcessOwner` / `ProcessOwnerEvidence` | function / type | compare lifetimes on one caller-established machine; matched, boot change, reused PID, gone, legacy or unavailable with cause |
22008
+
22009
+ ## `stitchkit/files/packaging`
22010
+
22011
+ `stitchkit/files/packaging` owns `createNativePackaging` and the types
22012
+ `NativePackagingOptions`, `NativePackagingAsset`, `NativePackagingPlugin`, `NativePackagingResult`.
22013
+ It resolves the installed package's target/version and original addon SHA256.
22014
+ `platform`, `architecture`, `delivery`, `entryPath` and `assetPath` are explicit public inputs.
22015
+ The result is `ready` with assets and a structural Bun plugin, or `unsupported`/`missing` with a safe code.
22016
+ Malformed options/metadata and other filesystem errors fail rather than masquerade as missing.
22017
+ Companion accepts a single `architecture` string with an `assetPath` string, or an architecture
22018
+ array with an exact per-architecture `assetPath` map. `NativePackagingOptions<true>` and
22019
+ `NativePackagingResult<true>` describe the array form; default types preserve single-target signatures.
22020
+ A ready array-form result carries the architecture array; `NativePackagingAsset<true>` identifies
22021
+ each asset architecture. Default asset types and single-target runtime objects retain their three fields.
22022
+ Targets must be unique; entry and all asset paths must be disjoint. One lazy loader chooses only
22023
+ the actual runtime architecture, with no fallback.
22024
+ Companion mode places external addons relative to the emitted entry; embedded mode lets
22025
+ Bun compile retain the matching addon. Keep JS builds without splitting, preserve every output,
22026
+ and copy each original asset to its `outputPath` after verifying `sha256`.
22027
+ This evolving entrypoint is a build dependency; runtime imports do not reach it.
22028
+ The detailed recipe is in [native IO](../guide/native-io.md#public-native-packaging).
package/llms.txt CHANGED
@@ -9,7 +9,7 @@ Build with stitchkit: define a contract once, then `implement` it and serve it (
9
9
  Load the slice for each entrypoint your code imports — `import … from 'stitchkit/server'` → `llms/server.txt`; the root `stitchkit` import → `llms/stitchkit.txt`. A slice holds that entrypoint's API reference section and the guide pages whose home it is; every guide lives in exactly one slice, and a slice that needs another's guide names it under "Also read". Each file is at most 50.0 KB; a longer one continues in numbered parts, each naming the next at its top. Moving across versions: load `llms/upgrading.txt` and the range slices it lists between your version and the target. `llms-full.txt` inlines everything; it is far larger than a context window, so do not read it whole.
10
10
 
11
11
  ## Entrypoint slices
12
- - **stitchkit**: [llms/stitchkit.txt](llms/stitchkit.txt) 17.4 KB → [llms/stitchkit.2.txt](llms/stitchkit.2.txt) 46.2 KB → [llms/stitchkit.3.txt](llms/stitchkit.3.txt) 17.1 KB
12
+ - **stitchkit**: [llms/stitchkit.txt](llms/stitchkit.txt) 17.6 KB → [llms/stitchkit.2.txt](llms/stitchkit.2.txt) 46.2 KB → [llms/stitchkit.3.txt](llms/stitchkit.3.txt) 17.1 KB
13
13
  - **stitchkit/live**: [llms/live.txt](llms/live.txt) 21.6 KB
14
14
  - **stitchkit/react**: [llms/react.txt](llms/react.txt) 9.0 KB
15
15
  - **stitchkit/react/keyboard**: [llms/react-keyboard.txt](llms/react-keyboard.txt) 9.4 KB
@@ -56,12 +56,13 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
56
56
  - **stitchkit/agent-runtime/react**: [llms/agent-runtime-react.txt](llms/agent-runtime-react.txt) 1.0 KB
57
57
  - **stitchkit/agent-runtime/realtime**: [llms/agent-runtime-realtime.txt](llms/agent-runtime-realtime.txt) 1.8 KB
58
58
  - **stitchkit/agent-runtime/harness-tools**: [llms/agent-runtime-harness-tools.txt](llms/agent-runtime-harness-tools.txt) 1.3 KB
59
- - **stitchkit/process**: [llms/process.txt](llms/process.txt) 21.7 KB
59
+ - **stitchkit/process**: [llms/process.txt](llms/process.txt) 26.6 KB
60
+ - **stitchkit/files/packaging**: [llms/files-packaging.txt](llms/files-packaging.txt) 2.1 KB
60
61
 
61
62
  ## Upgrading slices
62
63
  - **Upgrading — how to move across versions**: [llms/upgrading.txt](llms/upgrading.txt) 10.3 KB
63
- - **Upgrading 0.80.0 → 0.104.0**: [llms/upgrading-0.80.0-0.104.0.txt](llms/upgrading-0.80.0-0.104.0.txt) 46.7 KB
64
- - **Upgrading 0.62.0 → 0.79.0**: [llms/upgrading-0.62.0-0.79.0.txt](llms/upgrading-0.62.0-0.79.0.txt) 43.7 KB
64
+ - **Upgrading 0.81.0 → 0.104.0**: [llms/upgrading-0.81.0-0.104.0.txt](llms/upgrading-0.81.0-0.104.0.txt) 46.8 KB
65
+ - **Upgrading 0.62.0 → 0.80.0**: [llms/upgrading-0.62.0-0.80.0.txt](llms/upgrading-0.62.0-0.80.0.txt) 45.4 KB
65
66
  - **Upgrading 0.44.0 → 0.61.0**: [llms/upgrading-0.44.0-0.61.0.txt](llms/upgrading-0.44.0-0.61.0.txt) 41.4 KB
66
67
  - **Upgrading 0.37.0 → 0.44.0**: [llms/upgrading-0.37.0-0.44.0.txt](llms/upgrading-0.37.0-0.44.0.txt) 14.7 KB
67
68
 
@@ -0,0 +1,8 @@
1
+ {
2
+ "formatVersion": 1,
3
+ "loader": "darwin-native.cjs",
4
+ "assets": {
5
+ "arm64": "native/darwin-arm64.node",
6
+ "x64": "native/darwin-x64.node"
7
+ }
8
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.104.0",
3
+ "version": "0.104.2",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",
@@ -49,7 +49,8 @@
49
49
  "llms",
50
50
  "README.md",
51
51
  "CHANGELOG.md",
52
- "LICENSE"
52
+ "LICENSE",
53
+ "native-assets.json"
53
54
  ],
54
55
  "imports": {
55
56
  "#stitchkit-darwin-native": "./darwin-native.cjs"
@@ -246,6 +247,10 @@
246
247
  "./process": {
247
248
  "types": "./dist/entrypoints/process.d.ts",
248
249
  "import": "./dist/entrypoints/process.js"
250
+ },
251
+ "./files/packaging": {
252
+ "types": "./dist/entrypoints/files/packaging.d.ts",
253
+ "import": "./dist/entrypoints/files/packaging.js"
249
254
  }
250
255
  },
251
256
  "publishConfig": {
@@ -266,7 +271,7 @@
266
271
  "consumer-lane": "bun scripts/consumer-lane/run.mjs",
267
272
  "bench:mcp-preparation": "bun scripts/benchmark-mcp-preparation.ts",
268
273
  "bench:durable-store": "bun scripts/bench-durable-store.ts",
269
- "build:entrypoints": "bun scripts/build-entrypoints.mjs"
274
+ "build:entrypoints": "bun scripts/generate-native-loader.ts && bun scripts/build-entrypoints.mjs"
270
275
  },
271
276
  "peerDependencies": {
272
277
  "@modelcontextprotocol/ext-apps": "^1.7.2 || ^2.0.0",
File without changes