@swifttui/build 0.0.14 → 0.0.16

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.
Files changed (44) hide show
  1. package/README.md +23 -4
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.js +77 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/index.d.ts +5 -0
  6. package/dist/index.js +5 -0
  7. package/dist/src/build/buildAppWasm.d.ts +21 -0
  8. package/dist/src/build/buildAppWasm.js +61 -0
  9. package/dist/src/build/buildAppWasm.js.map +1 -0
  10. package/dist/src/build/buildSwiftTUIWebApp.d.ts +10 -0
  11. package/dist/src/build/buildSwiftTUIWebApp.js +23 -0
  12. package/dist/src/build/buildSwiftTUIWebApp.js.map +1 -0
  13. package/dist/src/build/generateSceneManifest.d.ts +13 -0
  14. package/dist/src/build/generateSceneManifest.js +28 -0
  15. package/dist/src/build/generateSceneManifest.js.map +1 -0
  16. package/dist/src/build/optimizePackagedWasm.js +17 -0
  17. package/dist/src/build/optimizePackagedWasm.js.map +1 -0
  18. package/dist/src/build/resolveSwiftArtifacts.d.ts +39 -0
  19. package/dist/src/build/resolveSwiftArtifacts.js +161 -0
  20. package/dist/src/build/resolveSwiftArtifacts.js.map +1 -0
  21. package/dist/src/build/runCommand.js +55 -0
  22. package/dist/src/build/runCommand.js.map +1 -0
  23. package/dist/src/build/stripPackagedWasm.js +16 -0
  24. package/dist/src/build/stripPackagedWasm.js.map +1 -0
  25. package/dist/src/build/swiftCommandPrefix.js +14 -0
  26. package/dist/src/build/swiftCommandPrefix.js.map +1 -0
  27. package/dist/src/build/wasmTypeDiagnostics.js +204 -0
  28. package/dist/src/build/wasmTypeDiagnostics.js.map +1 -0
  29. package/package.json +25 -11
  30. package/AGENTS.md +0 -44
  31. package/cli.ts +0 -98
  32. package/index.ts +0 -4
  33. package/src/build/buildAppWasm.test.ts +0 -178
  34. package/src/build/buildAppWasm.ts +0 -107
  35. package/src/build/buildSwiftTUIWebApp.ts +0 -22
  36. package/src/build/generateSceneManifest.ts +0 -46
  37. package/src/build/optimizePackagedWasm.ts +0 -20
  38. package/src/build/resolveSwiftArtifacts.test.ts +0 -90
  39. package/src/build/resolveSwiftArtifacts.ts +0 -253
  40. package/src/build/runCommand.ts +0 -81
  41. package/src/build/stripPackagedWasm.ts +0 -19
  42. package/src/build/swiftCommandPrefix.ts +0 -9
  43. package/src/build/wasmTypeDiagnostics.ts +0 -313
  44. package/tsconfig.json +0 -21
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runCommand.js","names":[],"sources":["../../../src/build/runCommand.ts"],"sourcesContent":["import { accessSync } from \"node:fs\";\nimport { delimiter, join } from \"node:path\";\nimport { spawn } from \"node:child_process\";\n\nexport interface RunCommandOptions {\n cwd?: string;\n env?: Record<string, string | undefined>;\n}\n\nexport async function runCommand(\n cmd: string[],\n options: RunCommandOptions = {}\n): Promise<string> {\n const executable = cmd[0];\n if (!executable) {\n throw new Error(\"cannot run an empty command\");\n }\n\n const proc = spawn(executable, cmd.slice(1), {\n cwd: options.cwd,\n env: normalizeEnvironment(options.env),\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n proc.stdout.on(\"data\", (chunk: Buffer) => {\n stdoutChunks.push(chunk);\n });\n proc.stderr.on(\"data\", (chunk: Buffer) => {\n stderrChunks.push(chunk);\n });\n\n const exitCode = await new Promise<number | null>((resolve, reject) => {\n proc.on(\"error\", reject);\n proc.on(\"close\", resolve);\n });\n const stdout = Buffer.concat(stdoutChunks).toString();\n const stderr = Buffer.concat(stderrChunks).toString();\n\n if (exitCode !== 0) {\n throw new Error([stdout, stderr].filter(Boolean).join(\"\\n\").trim() || `command failed: ${cmd.join(\" \")}`);\n }\n\n return stdout;\n}\n\nexport function findExecutable(\n name: string,\n pathValue: string | undefined = process.env.PATH\n): string | undefined {\n for (const directory of pathValue?.split(delimiter) ?? []) {\n if (!directory) {\n continue;\n }\n const candidate = join(directory, name);\n try {\n accessSync(candidate);\n return candidate;\n } catch {\n continue;\n }\n }\n return undefined;\n}\n\nfunction normalizeEnvironment(\n env: Record<string, string | undefined> | undefined\n): NodeJS.ProcessEnv | undefined {\n if (!env) {\n return undefined;\n }\n\n const normalized: NodeJS.ProcessEnv = {};\n for (const [key, value] of Object.entries(env)) {\n if (value !== undefined) {\n normalized[key] = value;\n }\n }\n return normalized;\n}\n"],"mappings":";;;;AASA,eAAsB,WACpB,KACA,UAA6B,CAAC,GACb;CACjB,MAAM,aAAa,IAAI;CACvB,IAAI,CAAC,YACH,MAAM,IAAI,MAAM,6BAA6B;CAG/C,MAAM,OAAO,MAAM,YAAY,IAAI,MAAM,CAAC,GAAG;EAC3C,KAAK,QAAQ;EACb,KAAK,qBAAqB,QAAQ,GAAG;EACrC,OAAO;GAAC;GAAU;GAAQ;EAAM;CAClC,CAAC;CAED,MAAM,eAAyB,CAAC;CAChC,MAAM,eAAyB,CAAC;CAChC,KAAK,OAAO,GAAG,SAAS,UAAkB;EACxC,aAAa,KAAK,KAAK;CACzB,CAAC;CACD,KAAK,OAAO,GAAG,SAAS,UAAkB;EACxC,aAAa,KAAK,KAAK;CACzB,CAAC;CAED,MAAM,WAAW,MAAM,IAAI,SAAwB,SAAS,WAAW;EACrE,KAAK,GAAG,SAAS,MAAM;EACvB,KAAK,GAAG,SAAS,OAAO;CAC1B,CAAC;CACD,MAAM,SAAS,OAAO,OAAO,YAAY,CAAC,CAAC,SAAS;CACpD,MAAM,SAAS,OAAO,OAAO,YAAY,CAAC,CAAC,SAAS;CAEpD,IAAI,aAAa,GACf,MAAM,IAAI,MAAM,CAAC,QAAQ,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,mBAAmB,IAAI,KAAK,GAAG,GAAG;CAG1G,OAAO;AACT;AAEA,SAAgB,eACd,MACA,YAAgC,QAAQ,IAAI,MACxB;CACpB,KAAK,MAAM,aAAa,WAAW,MAAM,SAAS,KAAK,CAAC,GAAG;EACzD,IAAI,CAAC,WACH;EAEF,MAAM,YAAY,KAAK,WAAW,IAAI;EACtC,IAAI;GACF,WAAW,SAAS;GACpB,OAAO;EACT,QAAQ;GACN;EACF;CACF;AAEF;AAEA,SAAS,qBACP,KAC+B;CAC/B,IAAI,CAAC,KACH;CAGF,MAAM,aAAgC,CAAC;CACvC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,IAAI,UAAU,KAAA,GACZ,WAAW,OAAO;CAGtB,OAAO;AACT"}
@@ -0,0 +1,16 @@
1
+ import { findExecutable, runCommand } from "./runCommand.js";
2
+ //#region src/build/stripPackagedWasm.ts
3
+ async function stripPackagedWasm(wasmPath) {
4
+ const objcopyPath = findExecutable("llvm-objcopy");
5
+ if (!objcopyPath) throw new Error("missing llvm-objcopy in PATH; install the swiftly-managed toolchain before packaging wasm");
6
+ await runCommand([
7
+ objcopyPath,
8
+ "--strip-debug",
9
+ "--remove-section=name",
10
+ wasmPath
11
+ ]);
12
+ }
13
+ //#endregion
14
+ export { stripPackagedWasm };
15
+
16
+ //# sourceMappingURL=stripPackagedWasm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripPackagedWasm.js","names":[],"sources":["../../../src/build/stripPackagedWasm.ts"],"sourcesContent":["import { findExecutable, runCommand } from \"./runCommand.ts\";\n\nexport async function stripPackagedWasm(\n wasmPath: string\n): Promise<void> {\n const objcopyPath = findExecutable(\"llvm-objcopy\");\n if (!objcopyPath) {\n throw new Error(\n \"missing llvm-objcopy in PATH; install the swiftly-managed toolchain before packaging wasm\"\n );\n }\n\n await runCommand([\n objcopyPath,\n \"--strip-debug\",\n \"--remove-section=name\",\n wasmPath,\n ]);\n}\n"],"mappings":";;AAEA,eAAsB,kBACpB,UACe;CACf,MAAM,cAAc,eAAe,cAAc;CACjD,IAAI,CAAC,aACH,MAAM,IAAI,MACR,2FACF;CAGF,MAAM,WAAW;EACf;EACA;EACA;EACA;CACF,CAAC;AACH"}
@@ -0,0 +1,14 @@
1
+ import { findExecutable } from "./runCommand.js";
2
+ //#region src/build/swiftCommandPrefix.ts
3
+ function swiftCommandPrefix() {
4
+ if (findExecutable("swiftly")) return [
5
+ "swiftly",
6
+ "run",
7
+ "swift"
8
+ ];
9
+ return ["swift"];
10
+ }
11
+ //#endregion
12
+ export { swiftCommandPrefix };
13
+
14
+ //# sourceMappingURL=swiftCommandPrefix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swiftCommandPrefix.js","names":[],"sources":["../../../src/build/swiftCommandPrefix.ts"],"sourcesContent":["import { findExecutable } from \"./runCommand.ts\";\n\nexport function swiftCommandPrefix(): string[] {\n if (findExecutable(\"swiftly\")) {\n return [\"swiftly\", \"run\", \"swift\"];\n }\n\n return [\"swift\"];\n}\n"],"mappings":";;AAEA,SAAgB,qBAA+B;CAC7C,IAAI,eAAe,SAAS,GAC1B,OAAO;EAAC;EAAW;EAAO;CAAO;CAGnC,OAAO,CAAC,OAAO;AACjB"}
@@ -0,0 +1,204 @@
1
+ import { createHash } from "node:crypto";
2
+ //#region src/build/wasmTypeDiagnostics.ts
3
+ const wasmMagic = [
4
+ 0,
5
+ 97,
6
+ 115,
7
+ 109
8
+ ];
9
+ const wasmVersion = [
10
+ 1,
11
+ 0,
12
+ 0,
13
+ 0
14
+ ];
15
+ const typeSectionID = 1;
16
+ const functionTypeTag = 96;
17
+ const browserMaxTypeParameterCount = 1e3;
18
+ function formatWasmTypeDiagnostics(bytes) {
19
+ const summary = summarizeWasmTypes(bytes);
20
+ const components = [`size=${bytes.byteLength} bytes`, `sha256=${createHash("sha256").update(bytes).digest("hex")}`];
21
+ if (summary.typeCount !== void 0) components.push(`typeCount=${summary.typeCount}`);
22
+ if (summary.maxTypeParameterCount !== void 0) components.push(`maxTypeParameterCount=${summary.maxTypeParameterCount}`);
23
+ if (summary.maxTypeParameterTypeIndex !== void 0) components.push(`maxTypeParameterTypeIndex=${summary.maxTypeParameterTypeIndex}`);
24
+ if (summary.overBrowserLimitTypes.length > 0) components.push(`overBrowserLimitTypes=${summary.overBrowserLimitTypes.join(",")}`);
25
+ if (summary.topParameterCounts.length > 0) components.push(`largestTypes=${summary.topParameterCounts.map(formatLargestTypeSummary).join(";")}`);
26
+ if (summary.note) components.push(`note=${summary.note}`);
27
+ return `wasm diagnostics: ${components.join(", ")}`;
28
+ }
29
+ function formatLargestTypeSummary(summary) {
30
+ return `${summary.typeIndex}:${summary.parameterCount}`;
31
+ }
32
+ function summarizeWasmTypes(bytes) {
33
+ if (!hasExpectedPrefix(bytes, 0, wasmMagic)) return {
34
+ note: "missing wasm magic header",
35
+ overBrowserLimitTypes: [],
36
+ topParameterCounts: []
37
+ };
38
+ if (!hasExpectedPrefix(bytes, wasmMagic.length, wasmVersion)) return {
39
+ note: "missing wasm version header",
40
+ overBrowserLimitTypes: [],
41
+ topParameterCounts: []
42
+ };
43
+ let offset = 8;
44
+ while (offset < bytes.length) {
45
+ let sectionHeader;
46
+ try {
47
+ sectionHeader = readSectionHeader(bytes, offset);
48
+ } catch (error) {
49
+ return {
50
+ note: `failed to read section header at byte ${offset}: ${describeError(error)}`,
51
+ overBrowserLimitTypes: [],
52
+ topParameterCounts: []
53
+ };
54
+ }
55
+ if (sectionHeader.id === typeSectionID) return summarizeTypeSection(bytes, sectionHeader.startOffset, sectionHeader.nextOffset);
56
+ offset = sectionHeader.nextOffset;
57
+ }
58
+ return {
59
+ note: "module has no type section",
60
+ overBrowserLimitTypes: [],
61
+ topParameterCounts: []
62
+ };
63
+ }
64
+ function summarizeTypeSection(bytes, startOffset, endOffset) {
65
+ let offset = startOffset;
66
+ let typeCount;
67
+ try {
68
+ const result = readUnsignedLEB128(bytes, offset);
69
+ typeCount = result.value;
70
+ offset = result.nextOffset;
71
+ } catch (error) {
72
+ return {
73
+ note: `failed to read type vector length: ${describeError(error)}`,
74
+ overBrowserLimitTypes: [],
75
+ topParameterCounts: []
76
+ };
77
+ }
78
+ let maxTypeParameterCount = -1;
79
+ let maxTypeParameterTypeIndex = -1;
80
+ const overBrowserLimitTypes = [];
81
+ const topParameterCounts = [];
82
+ for (let typeIndex = 0; typeIndex < typeCount; typeIndex += 1) {
83
+ if (offset >= endOffset) return {
84
+ note: `type section ended early while reading type ${typeIndex}`,
85
+ overBrowserLimitTypes,
86
+ topParameterCounts,
87
+ typeCount
88
+ };
89
+ const typeTag = bytes[offset];
90
+ offset += 1;
91
+ if (typeTag !== functionTypeTag) return {
92
+ note: `unexpected type tag 0x${typeTag.toString(16)} at type ${typeIndex}`,
93
+ overBrowserLimitTypes,
94
+ topParameterCounts,
95
+ typeCount
96
+ };
97
+ let parameterCount;
98
+ try {
99
+ const result = readUnsignedLEB128(bytes, offset);
100
+ parameterCount = result.value;
101
+ offset = result.nextOffset;
102
+ } catch (error) {
103
+ return {
104
+ note: `failed to read parameter count for type ${typeIndex}: ${describeError(error)}`,
105
+ overBrowserLimitTypes,
106
+ topParameterCounts,
107
+ typeCount
108
+ };
109
+ }
110
+ if (parameterCount > maxTypeParameterCount) {
111
+ maxTypeParameterCount = parameterCount;
112
+ maxTypeParameterTypeIndex = typeIndex;
113
+ }
114
+ if (parameterCount > browserMaxTypeParameterCount) overBrowserLimitTypes.push(typeIndex);
115
+ updateLargestTypeSummaries(topParameterCounts, typeIndex, parameterCount);
116
+ offset += parameterCount;
117
+ if (offset > endOffset) return {
118
+ note: `parameter vector for type ${typeIndex} overruns type section`,
119
+ overBrowserLimitTypes,
120
+ topParameterCounts,
121
+ typeCount
122
+ };
123
+ let resultCount;
124
+ try {
125
+ const result = readUnsignedLEB128(bytes, offset);
126
+ resultCount = result.value;
127
+ offset = result.nextOffset;
128
+ } catch (error) {
129
+ return {
130
+ note: `failed to read result count for type ${typeIndex}: ${describeError(error)}`,
131
+ overBrowserLimitTypes,
132
+ topParameterCounts,
133
+ typeCount
134
+ };
135
+ }
136
+ offset += resultCount;
137
+ if (offset > endOffset) return {
138
+ note: `result vector for type ${typeIndex} overruns type section`,
139
+ overBrowserLimitTypes,
140
+ topParameterCounts,
141
+ typeCount
142
+ };
143
+ }
144
+ return {
145
+ maxTypeParameterCount: maxTypeParameterCount >= 0 ? maxTypeParameterCount : void 0,
146
+ maxTypeParameterTypeIndex: maxTypeParameterTypeIndex >= 0 ? maxTypeParameterTypeIndex : void 0,
147
+ overBrowserLimitTypes,
148
+ topParameterCounts,
149
+ typeCount
150
+ };
151
+ }
152
+ function updateLargestTypeSummaries(summaries, typeIndex, parameterCount) {
153
+ summaries.push({
154
+ parameterCount,
155
+ typeIndex
156
+ });
157
+ summaries.sort((left, right) => {
158
+ if (right.parameterCount === left.parameterCount) return left.typeIndex - right.typeIndex;
159
+ return right.parameterCount - left.parameterCount;
160
+ });
161
+ summaries.splice(5);
162
+ }
163
+ function readSectionHeader(bytes, offset) {
164
+ if (offset >= bytes.length) throw new Error("unexpected end of file");
165
+ const id = bytes[offset];
166
+ const size = readUnsignedLEB128(bytes, offset + 1);
167
+ const startOffset = size.nextOffset;
168
+ const nextOffset = startOffset + size.value;
169
+ if (nextOffset > bytes.length) throw new Error(`section ${id} overruns file bounds`);
170
+ return {
171
+ id,
172
+ size: size.value,
173
+ startOffset,
174
+ nextOffset
175
+ };
176
+ }
177
+ function readUnsignedLEB128(bytes, offset) {
178
+ let shift = 0;
179
+ let value = 0;
180
+ let nextOffset = offset;
181
+ while (nextOffset < bytes.length) {
182
+ const byte = bytes[nextOffset];
183
+ nextOffset += 1;
184
+ value |= (byte & 127) << shift;
185
+ if ((byte & 128) === 0) return {
186
+ nextOffset,
187
+ value
188
+ };
189
+ shift += 7;
190
+ }
191
+ throw new Error("unterminated LEB128 value");
192
+ }
193
+ function hasExpectedPrefix(bytes, startOffset, expected) {
194
+ if (startOffset + expected.length > bytes.length) return false;
195
+ for (let index = 0; index < expected.length; index += 1) if (bytes[startOffset + index] !== expected[index]) return false;
196
+ return true;
197
+ }
198
+ function describeError(error) {
199
+ return error instanceof Error ? error.message : String(error);
200
+ }
201
+ //#endregion
202
+ export { formatWasmTypeDiagnostics };
203
+
204
+ //# sourceMappingURL=wasmTypeDiagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wasmTypeDiagnostics.js","names":[],"sources":["../../../src/build/wasmTypeDiagnostics.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\nconst wasmMagic = [0x00, 0x61, 0x73, 0x6d] as const;\nconst wasmVersion = [0x01, 0x00, 0x00, 0x00] as const;\nconst typeSectionID = 1;\nconst functionTypeTag = 0x60;\nconst browserMaxTypeParameterCount = 1000;\n\ninterface SectionHeader {\n id: number;\n size: number;\n startOffset: number;\n nextOffset: number;\n}\n\ninterface ReadUnsignedLEB128Result {\n nextOffset: number;\n value: number;\n}\n\ninterface WasmTypeSummary {\n maxTypeParameterCount?: number;\n maxTypeParameterTypeIndex?: number;\n note?: string;\n overBrowserLimitTypes: number[];\n topParameterCounts: Array<{\n parameterCount: number;\n typeIndex: number;\n }>;\n typeCount?: number;\n}\n\nexport function formatWasmTypeDiagnostics(\n bytes: Uint8Array\n): string {\n const summary = summarizeWasmTypes(bytes);\n const components = [\n `size=${bytes.byteLength} bytes`,\n `sha256=${createHash(\"sha256\").update(bytes).digest(\"hex\")}`,\n ];\n\n if (summary.typeCount !== undefined) {\n components.push(`typeCount=${summary.typeCount}`);\n }\n if (summary.maxTypeParameterCount !== undefined) {\n components.push(`maxTypeParameterCount=${summary.maxTypeParameterCount}`);\n }\n if (summary.maxTypeParameterTypeIndex !== undefined) {\n components.push(`maxTypeParameterTypeIndex=${summary.maxTypeParameterTypeIndex}`);\n }\n if (summary.overBrowserLimitTypes.length > 0) {\n components.push(`overBrowserLimitTypes=${summary.overBrowserLimitTypes.join(\",\")}`);\n }\n if (summary.topParameterCounts.length > 0) {\n components.push(\n `largestTypes=${summary.topParameterCounts.map(formatLargestTypeSummary).join(\";\")}`\n );\n }\n if (summary.note) {\n components.push(`note=${summary.note}`);\n }\n\n return `wasm diagnostics: ${components.join(\", \")}`;\n}\n\nfunction formatLargestTypeSummary(\n summary: { parameterCount: number; typeIndex: number }\n): string {\n return `${summary.typeIndex}:${summary.parameterCount}`;\n}\n\nfunction summarizeWasmTypes(\n bytes: Uint8Array\n): WasmTypeSummary {\n if (!hasExpectedPrefix(bytes, 0, wasmMagic)) {\n return {\n note: \"missing wasm magic header\",\n overBrowserLimitTypes: [],\n topParameterCounts: [],\n };\n }\n if (!hasExpectedPrefix(bytes, wasmMagic.length, wasmVersion)) {\n return {\n note: \"missing wasm version header\",\n overBrowserLimitTypes: [],\n topParameterCounts: [],\n };\n }\n\n let offset = 8;\n while (offset < bytes.length) {\n let sectionHeader: SectionHeader;\n try {\n sectionHeader = readSectionHeader(bytes, offset);\n } catch (error) {\n return {\n note: `failed to read section header at byte ${offset}: ${describeError(error)}`,\n overBrowserLimitTypes: [],\n topParameterCounts: [],\n };\n }\n\n if (sectionHeader.id === typeSectionID) {\n return summarizeTypeSection(bytes, sectionHeader.startOffset, sectionHeader.nextOffset);\n }\n\n offset = sectionHeader.nextOffset;\n }\n\n return {\n note: \"module has no type section\",\n overBrowserLimitTypes: [],\n topParameterCounts: [],\n };\n}\n\nfunction summarizeTypeSection(\n bytes: Uint8Array,\n startOffset: number,\n endOffset: number\n): WasmTypeSummary {\n let offset = startOffset;\n let typeCount: number;\n\n try {\n const result = readUnsignedLEB128(bytes, offset);\n typeCount = result.value;\n offset = result.nextOffset;\n } catch (error) {\n return {\n note: `failed to read type vector length: ${describeError(error)}`,\n overBrowserLimitTypes: [],\n topParameterCounts: [],\n };\n }\n\n let maxTypeParameterCount = -1;\n let maxTypeParameterTypeIndex = -1;\n const overBrowserLimitTypes: number[] = [];\n const topParameterCounts: Array<{ parameterCount: number; typeIndex: number }> = [];\n\n for (let typeIndex = 0; typeIndex < typeCount; typeIndex += 1) {\n if (offset >= endOffset) {\n return {\n note: `type section ended early while reading type ${typeIndex}`,\n overBrowserLimitTypes,\n topParameterCounts,\n typeCount,\n };\n }\n\n const typeTag = bytes[offset];\n offset += 1;\n if (typeTag !== functionTypeTag) {\n return {\n note: `unexpected type tag 0x${typeTag.toString(16)} at type ${typeIndex}`,\n overBrowserLimitTypes,\n topParameterCounts,\n typeCount,\n };\n }\n\n let parameterCount: number;\n try {\n const result = readUnsignedLEB128(bytes, offset);\n parameterCount = result.value;\n offset = result.nextOffset;\n } catch (error) {\n return {\n note: `failed to read parameter count for type ${typeIndex}: ${describeError(error)}`,\n overBrowserLimitTypes,\n topParameterCounts,\n typeCount,\n };\n }\n\n if (parameterCount > maxTypeParameterCount) {\n maxTypeParameterCount = parameterCount;\n maxTypeParameterTypeIndex = typeIndex;\n }\n if (parameterCount > browserMaxTypeParameterCount) {\n overBrowserLimitTypes.push(typeIndex);\n }\n updateLargestTypeSummaries(topParameterCounts, typeIndex, parameterCount);\n\n offset += parameterCount;\n if (offset > endOffset) {\n return {\n note: `parameter vector for type ${typeIndex} overruns type section`,\n overBrowserLimitTypes,\n topParameterCounts,\n typeCount,\n };\n }\n\n let resultCount: number;\n try {\n const result = readUnsignedLEB128(bytes, offset);\n resultCount = result.value;\n offset = result.nextOffset;\n } catch (error) {\n return {\n note: `failed to read result count for type ${typeIndex}: ${describeError(error)}`,\n overBrowserLimitTypes,\n topParameterCounts,\n typeCount,\n };\n }\n\n offset += resultCount;\n if (offset > endOffset) {\n return {\n note: `result vector for type ${typeIndex} overruns type section`,\n overBrowserLimitTypes,\n topParameterCounts,\n typeCount,\n };\n }\n }\n\n return {\n maxTypeParameterCount: maxTypeParameterCount >= 0 ? maxTypeParameterCount : undefined,\n maxTypeParameterTypeIndex: maxTypeParameterTypeIndex >= 0 ? maxTypeParameterTypeIndex : undefined,\n overBrowserLimitTypes,\n topParameterCounts,\n typeCount,\n };\n}\n\nfunction updateLargestTypeSummaries(\n summaries: Array<{ parameterCount: number; typeIndex: number }>,\n typeIndex: number,\n parameterCount: number\n): void {\n summaries.push({ parameterCount, typeIndex });\n summaries.sort((left, right) => {\n if (right.parameterCount === left.parameterCount) {\n return left.typeIndex - right.typeIndex;\n }\n return right.parameterCount - left.parameterCount;\n });\n summaries.splice(5);\n}\n\nfunction readSectionHeader(\n bytes: Uint8Array,\n offset: number\n): SectionHeader {\n if (offset >= bytes.length) {\n throw new Error(\"unexpected end of file\");\n }\n\n const id = bytes[offset];\n const size = readUnsignedLEB128(bytes, offset + 1);\n const startOffset = size.nextOffset;\n const nextOffset = startOffset + size.value;\n if (nextOffset > bytes.length) {\n throw new Error(`section ${id} overruns file bounds`);\n }\n\n return {\n id,\n size: size.value,\n startOffset,\n nextOffset,\n };\n}\n\nfunction readUnsignedLEB128(\n bytes: Uint8Array,\n offset: number\n): ReadUnsignedLEB128Result {\n let shift = 0;\n let value = 0;\n let nextOffset = offset;\n\n while (nextOffset < bytes.length) {\n const byte = bytes[nextOffset];\n nextOffset += 1;\n value |= (byte & 0x7f) << shift;\n if ((byte & 0x80) === 0) {\n return {\n nextOffset,\n value,\n };\n }\n shift += 7;\n }\n\n throw new Error(\"unterminated LEB128 value\");\n}\n\nfunction hasExpectedPrefix(\n bytes: Uint8Array,\n startOffset: number,\n expected: readonly number[]\n): boolean {\n if (startOffset + expected.length > bytes.length) {\n return false;\n }\n\n for (let index = 0; index < expected.length; index += 1) {\n if (bytes[startOffset + index] !== expected[index]) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,28 +1,42 @@
1
1
  {
2
2
  "name": "@swifttui/build",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "license": "MIT",
5
- "module": "index.ts",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "engines": {
8
+ "node": ">=18"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "module": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
6
13
  "bin": {
7
- "swifttui-web": "./cli.ts"
14
+ "swifttui-web": "./dist/cli.js"
8
15
  },
16
+ "files": [
17
+ "dist"
18
+ ],
9
19
  "exports": {
10
- ".": "./index.ts"
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ },
24
+ "./package.json": "./package.json"
11
25
  },
12
- "type": "module",
13
26
  "scripts": {
27
+ "build": "tsdown",
14
28
  "build:manifest": "bun run cli.ts build:manifest",
15
29
  "build:wasm": "bun run cli.ts build:wasm",
16
- "build": "bun run cli.ts build",
17
- "test": "bun test"
30
+ "test": "bun test",
31
+ "prepublishOnly": "tsdown"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
18
35
  },
19
36
  "dependencies": {
20
- "@swifttui/web": "0.0.14"
37
+ "@swifttui/web": "0.0.16"
21
38
  },
22
39
  "devDependencies": {
23
40
  "@types/bun": "1.3.13"
24
- },
25
- "peerDependencies": {
26
- "typescript": "^5"
27
41
  }
28
42
  }
package/AGENTS.md DELETED
@@ -1,44 +0,0 @@
1
- # AGENTS.md
2
-
3
- Guidance for agentic assistants working in **`@swifttui/build`**. Keep this
4
- concise; [`README.md`](README.md) is the full reference.
5
-
6
- ## What this package is
7
-
8
- The **build/packaging tooling** for SwiftTUI browser apps — the sibling of
9
- [`@swifttui/web`](../web) (which owns the runtime). It captures the Swift app's
10
- scene manifest and packages its WASI/wasm artifact for the browser. Exposes the
11
- `swifttui-web` CLI (see `bin` in `package.json`) and a programmatic `index.ts`.
12
-
13
- Keep the split clean: **packaging/build steps live here; browser-safe runtime
14
- APIs live in `@swifttui/web`.** This package depends on `@swifttui/web`.
15
-
16
- ## Toolchains
17
-
18
- - **Bun** for the CLI, bundling, and tests.
19
- - **`swiftly`** Swift 6.3.1 for the wasm build it invokes
20
- (`swiftly run swift ...`), not bare `swift`.
21
-
22
- ## Commands
23
-
24
- ```bash
25
- bun test # package tests
26
- bun run build:manifest -- --app <Exe> # capture TUIGUI_MODE=manifest output
27
- bun run build:wasm -- --app <Exe> # copy + validate the app's wasm
28
- bun run build -- --app <Exe> # manifest + wasm
29
- ```
30
-
31
- `build:wasm`/`build` default to `--configuration release`; pass
32
- `--configuration debug` for local debug wasm.
33
-
34
- ## Gotcha
35
-
36
- WASI release builds need specific flags (`-Osize` plus
37
- `-disable-llvm-merge-functions-pass`) to stay under the browser WebAssembly
38
- API's 1000-parameter limit. The canonical command lives in this package's build
39
- code — don't hand-roll the swift invocation. See
40
- [`WebExample`](../../../swift-tui-examples/WebExample) for the full rationale.
41
-
42
- ## Conventions
43
-
44
- `AGENTS.md` is the real file; `CLAUDE.md` is a symlink to it. Edit `AGENTS.md`.
package/cli.ts DELETED
@@ -1,98 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { resolve } from "node:path";
4
- import {
5
- buildAppWasm,
6
- buildSwiftTUIWebApp,
7
- generateSceneManifest,
8
- type WasmBuildConfiguration,
9
- } from "./index.ts";
10
-
11
- void runCli(process.argv.slice(2));
12
-
13
- async function runCli(argv: string[]): Promise<void> {
14
- const command = argv[0] ?? "build";
15
- const flags = parseFlags(argv.slice(1));
16
- const packagePath = resolve(flags["package-path"] ?? "../../");
17
- const distPath = resolve(flags["dist"] ?? "./dist");
18
- const appExecutable = flags.app ?? flags.product ?? flags["app-product"] ?? "";
19
- const configuration = parseWasmBuildConfiguration(flags.configuration ?? "release");
20
-
21
- switch (command) {
22
- case "build:manifest":
23
- assertAppExecutable(appExecutable);
24
- await generateSceneManifest({
25
- packagePath,
26
- outputPath: resolve(distPath, "scene-manifest.json"),
27
- appExecutable,
28
- });
29
- return;
30
- case "build:wasm":
31
- assertAppExecutable(appExecutable);
32
- await buildAppWasm({
33
- configuration,
34
- packagePath,
35
- outputDirectory: distPath,
36
- product: appExecutable,
37
- });
38
- return;
39
- case "build":
40
- assertAppExecutable(appExecutable);
41
- await buildSwiftTUIWebApp({
42
- configuration,
43
- packagePath,
44
- outputDirectory: distPath,
45
- product: appExecutable,
46
- });
47
- return;
48
- default:
49
- throw new Error(`unknown command: ${command}`);
50
- }
51
- }
52
-
53
- function parseFlags(
54
- argv: string[]
55
- ): Record<string, string> {
56
- const flags: Record<string, string> = {};
57
- for (let index = 0; index < argv.length; index += 1) {
58
- const value = argv[index];
59
- if (!value.startsWith("--")) {
60
- continue;
61
- }
62
- const equalsIndex = value.indexOf("=");
63
- if (equalsIndex !== -1) {
64
- flags[value.slice(2, equalsIndex)] = value.slice(equalsIndex + 1);
65
- continue;
66
- }
67
- const name = value.slice(2);
68
- const next = argv[index + 1];
69
- if (next && !next.startsWith("--")) {
70
- flags[name] = next;
71
- index += 1;
72
- } else {
73
- flags[name] = "true";
74
- }
75
- }
76
- return flags;
77
- }
78
-
79
- function parseWasmBuildConfiguration(
80
- value: string
81
- ): WasmBuildConfiguration {
82
- switch (value) {
83
- case "debug":
84
- return "debug";
85
- case "release":
86
- return "release";
87
- default:
88
- throw new Error(`unsupported wasm build configuration: ${value}`);
89
- }
90
- }
91
-
92
- function assertAppExecutable(
93
- value: string
94
- ): asserts value is string {
95
- if (!value) {
96
- throw new Error("missing --app or --product flag");
97
- }
98
- }
package/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export * from "./src/build/buildAppWasm.ts";
2
- export * from "./src/build/buildSwiftTUIWebApp.ts";
3
- export * from "./src/build/generateSceneManifest.ts";
4
- export * from "./src/build/resolveSwiftArtifacts.ts";
@@ -1,178 +0,0 @@
1
- import { afterEach, expect, test } from "bun:test";
2
- import { mkdtemp, rm } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { tmpdir } from "node:os";
5
- import { packageBrowserValidatedWasm } from "./buildAppWasm.ts";
6
-
7
- const minimalWasmBytes = new Uint8Array([
8
- 0x00, 0x61, 0x73, 0x6d,
9
- 0x01, 0x00, 0x00, 0x00,
10
- ]);
11
-
12
- const temporaryDirectories: string[] = [];
13
-
14
- afterEach(async () => {
15
- for (const directory of temporaryDirectories.splice(0)) {
16
- await rm(directory, { recursive: true, force: true });
17
- }
18
- });
19
-
20
- test("falls back to the original wasm when strip tooling throws", async () => {
21
- const fixture = await createFixture();
22
- const warnings: string[] = [];
23
-
24
- await packageBrowserValidatedWasm({
25
- optimize: async () => {},
26
- sourceWasmPath: fixture.sourceWasmPath,
27
- outputWasmPath: fixture.outputWasmPath,
28
- strip: async () => {
29
- throw new Error("missing llvm-objcopy");
30
- },
31
- onWarning: (warning) => warnings.push(warning),
32
- });
33
-
34
- expect(await Bun.file(fixture.outputWasmPath).bytes())
35
- .toEqual(minimalWasmBytes);
36
- expect(warnings).toHaveLength(1);
37
- expect(warnings[0]).toContain("keeping unstripped wasm");
38
- expect(warnings[0]).toContain("missing llvm-objcopy");
39
- await WebAssembly.compile(await Bun.file(fixture.outputWasmPath).arrayBuffer());
40
- });
41
-
42
- test("falls back to the original wasm when stripping corrupts the artifact", async () => {
43
- const fixture = await createFixture();
44
- const warnings: string[] = [];
45
-
46
- await packageBrowserValidatedWasm({
47
- optimize: async () => {},
48
- sourceWasmPath: fixture.sourceWasmPath,
49
- outputWasmPath: fixture.outputWasmPath,
50
- strip: async (wasmPath) => {
51
- await Bun.write(wasmPath, new Uint8Array([0x00, 0x61, 0x73, 0x6d]));
52
- },
53
- onWarning: (warning) => warnings.push(warning),
54
- });
55
-
56
- expect(await Bun.file(fixture.outputWasmPath).bytes())
57
- .toEqual(minimalWasmBytes);
58
- expect(warnings).toHaveLength(1);
59
- expect(warnings[0])
60
- .toContain("stripped wasm does not parse in browser WebAssembly");
61
- await WebAssembly.compile(await Bun.file(fixture.outputWasmPath).arrayBuffer());
62
- });
63
-
64
- test("fails when the source wasm itself is not browser-parseable", async () => {
65
- const fixture = await createFixture(buildHugeFunctionTypeWasm(1001));
66
-
67
- await expect(
68
- packageBrowserValidatedWasm({
69
- optimize: async () => {},
70
- sourceWasmPath: fixture.sourceWasmPath,
71
- outputWasmPath: fixture.outputWasmPath,
72
- strip: async () => {},
73
- })
74
- ).rejects.toThrow("generated wasm does not parse in browser WebAssembly");
75
- await expect(
76
- packageBrowserValidatedWasm({
77
- optimize: async () => {},
78
- sourceWasmPath: fixture.sourceWasmPath,
79
- outputWasmPath: fixture.outputWasmPath,
80
- strip: async () => {},
81
- })
82
- ).rejects.toThrow("maxTypeParameterCount=1001");
83
- await expect(
84
- packageBrowserValidatedWasm({
85
- optimize: async () => {},
86
- sourceWasmPath: fixture.sourceWasmPath,
87
- outputWasmPath: fixture.outputWasmPath,
88
- strip: async () => {},
89
- })
90
- ).rejects.toThrow("overBrowserLimitTypes=0");
91
- });
92
-
93
- test("uses optimized wasm when the raw compiler output is not browser-parseable", async () => {
94
- const fixture = await createFixture(buildHugeFunctionTypeWasm(1001));
95
-
96
- await packageBrowserValidatedWasm({
97
- optimize: async (wasmPath) => {
98
- await Bun.write(wasmPath, minimalWasmBytes);
99
- },
100
- sourceWasmPath: fixture.sourceWasmPath,
101
- outputWasmPath: fixture.outputWasmPath,
102
- strip: async () => {},
103
- });
104
-
105
- expect(await Bun.file(fixture.outputWasmPath).bytes())
106
- .toEqual(minimalWasmBytes);
107
- });
108
-
109
- test("reports the optimization failure when the raw wasm is still invalid", async () => {
110
- const fixture = await createFixture(buildHugeFunctionTypeWasm(1001));
111
-
112
- await expect(
113
- packageBrowserValidatedWasm({
114
- optimize: async () => {
115
- throw new Error("missing wasm-opt");
116
- },
117
- sourceWasmPath: fixture.sourceWasmPath,
118
- outputWasmPath: fixture.outputWasmPath,
119
- strip: async () => {},
120
- })
121
- ).rejects.toThrow("wasm optimization step failed: missing wasm-opt");
122
- });
123
-
124
- async function createFixture(
125
- sourceBytes: Uint8Array = minimalWasmBytes
126
- ): Promise<{ sourceWasmPath: string; outputWasmPath: string }> {
127
- const directory = await mkdtemp(join(tmpdir(), "webhost-wasm-"));
128
- temporaryDirectories.push(directory);
129
-
130
- const sourceWasmPath = join(directory, "source.wasm");
131
- const outputWasmPath = join(directory, "output.wasm");
132
- await Bun.write(sourceWasmPath, sourceBytes);
133
-
134
- return {
135
- sourceWasmPath,
136
- outputWasmPath,
137
- };
138
- }
139
-
140
- function buildHugeFunctionTypeWasm(
141
- parameterCount: number
142
- ): Uint8Array {
143
- const payload = [
144
- ...encodeUnsignedLEB128(1),
145
- 0x60,
146
- ...encodeUnsignedLEB128(parameterCount),
147
- ...new Array<number>(parameterCount).fill(0x7f),
148
- 0x00,
149
- ];
150
-
151
- return new Uint8Array([
152
- ...minimalWasmBytes,
153
- 0x01,
154
- ...encodeUnsignedLEB128(payload.length),
155
- ...payload,
156
- ]);
157
- }
158
-
159
- function encodeUnsignedLEB128(
160
- value: number
161
- ): number[] {
162
- if (value < 0) {
163
- throw new Error("LEB128 values must be non-negative");
164
- }
165
-
166
- const bytes: number[] = [];
167
- let remaining = value;
168
- do {
169
- let byte = remaining & 0x7f;
170
- remaining >>>= 7;
171
- if (remaining !== 0) {
172
- byte |= 0x80;
173
- }
174
- bytes.push(byte);
175
- } while (remaining !== 0);
176
-
177
- return bytes;
178
- }