ignotum 0.0.13 → 0.0.15

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 (131) hide show
  1. package/README.md +6 -6
  2. package/dist/cli/bin.mjs +6111 -4448
  3. package/dist/cli/bin.mjs.map +1 -1
  4. package/dist/cli/emscripten-module-D5GzfUNv.mjs +533 -0
  5. package/dist/cli/emscripten-module-D5GzfUNv.mjs.map +1 -0
  6. package/dist/cli/ffi-7IElU4Co.mjs +148 -0
  7. package/dist/cli/ffi-7IElU4Co.mjs.map +1 -0
  8. package/dist/cli/managed-release-WHYLZwaS.mjs +10902 -0
  9. package/dist/cli/managed-release-WHYLZwaS.mjs.map +1 -0
  10. package/dist/cli/managed-release.d.mts +1 -0
  11. package/dist/cli/managed-release.mjs +14 -0
  12. package/dist/cli/managed-release.mjs.map +1 -0
  13. package/dist/cli/module-ES6BEMUI-D36I4NVn.mjs +1289 -0
  14. package/dist/cli/module-ES6BEMUI-D36I4NVn.mjs.map +1 -0
  15. package/dist/runtime/{api-Cv3hMbzo.d.ts → api-B4fwp5a2.d.ts} +4 -4
  16. package/dist/runtime/bootstrap-BISQSn_O.js +916 -0
  17. package/dist/runtime/bootstrap-BISQSn_O.js.map +1 -0
  18. package/dist/runtime/bootstrap-BTFScKAc.d.ts +545 -0
  19. package/dist/runtime/client.d.ts +50 -15
  20. package/dist/runtime/client.js +7 -1138
  21. package/dist/runtime/client.js.map +1 -1
  22. package/dist/runtime/descriptor-CTY4mtwS-cS43EbwS.js +269 -0
  23. package/dist/runtime/descriptor-CTY4mtwS-cS43EbwS.js.map +1 -0
  24. package/dist/runtime/{id-Bt9XWRGL.js → id--1m0NuUL.js} +4 -4
  25. package/dist/runtime/id--1m0NuUL.js.map +1 -0
  26. package/dist/runtime/{id-BzFHf3Wo-DGPCjgrf.d.ts → id-7an3nxTu-DZJSM5xh.d.ts} +2 -2
  27. package/dist/runtime/id-CBOt2kDo.d.ts +1 -0
  28. package/dist/runtime/identity-QjtJRxBD.d.ts +2 -0
  29. package/dist/runtime/image-Djtjy4Z8.js +19 -0
  30. package/dist/runtime/image-Djtjy4Z8.js.map +1 -0
  31. package/dist/runtime/index-D4seNHlX.d.ts +184 -0
  32. package/dist/runtime/internal/api.d.ts +1 -1
  33. package/dist/runtime/internal/api.js +30 -1
  34. package/dist/runtime/internal/api.js.map +1 -0
  35. package/dist/runtime/internal/bootstrap.d.ts +2 -0
  36. package/dist/runtime/internal/bootstrap.js +2 -0
  37. package/dist/runtime/internal/client.d.ts +2 -0
  38. package/dist/runtime/internal/client.js +75 -0
  39. package/dist/runtime/internal/client.js.map +1 -0
  40. package/dist/runtime/internal/host.d.ts +25 -8
  41. package/dist/runtime/internal/host.js +27 -6
  42. package/dist/runtime/internal/host.js.map +1 -1
  43. package/dist/runtime/internal/routes.d.ts +2 -0
  44. package/dist/runtime/internal/routes.js +2 -0
  45. package/dist/runtime/internal/server.d.ts +1 -1
  46. package/dist/runtime/internal/server.js +1 -1
  47. package/dist/runtime/internal/types.d.ts +2 -1
  48. package/dist/runtime/internal/types.js +1 -1
  49. package/dist/runtime/pagination-CBOt2kDo.d.ts +1 -0
  50. package/dist/runtime/pagination-D3qd6s9N.js +33 -0
  51. package/dist/runtime/pagination-D3qd6s9N.js.map +1 -0
  52. package/dist/runtime/result-BkziOG1L.d.ts +1 -0
  53. package/dist/runtime/router-BSBI1oN1.js +2377 -0
  54. package/dist/runtime/router-BSBI1oN1.js.map +1 -0
  55. package/dist/runtime/routes-Ce8TVP-T.js +690 -0
  56. package/dist/runtime/routes-Ce8TVP-T.js.map +1 -0
  57. package/dist/runtime/{schema-1Zs03-iS.js → schema-D1wOqiNH.js} +40 -12
  58. package/dist/runtime/schema-D1wOqiNH.js.map +1 -0
  59. package/dist/runtime/server.d.ts +5 -5
  60. package/dist/runtime/server.js +2 -2
  61. package/dist/runtime/{sync-Bs8J3fIr.d.ts → sync-D-GK2sv9.d.ts} +3 -2
  62. package/dist/runtime/{api-CAgKDij7.js → sync-DzUwA8W9.js} +22 -41
  63. package/dist/runtime/sync-DzUwA8W9.js.map +1 -0
  64. package/dist/runtime/types-DeCCyqLU-BIm7rU-0.d.ts +341 -0
  65. package/package.json +29 -4
  66. package/src/cli/agent-files.ts +14 -8
  67. package/src/cli/build/managed-client.ts +411 -0
  68. package/src/cli/build/managed-server.ts +294 -0
  69. package/src/cli/build/managed.ts +67 -0
  70. package/src/cli/build/output.ts +47 -0
  71. package/src/cli/build/server.ts +51 -220
  72. package/src/cli/client-config.ts +1 -140
  73. package/src/cli/client-styles.ts +1 -0
  74. package/src/cli/codegen.ts +9 -4
  75. package/src/cli/command.ts +28 -5
  76. package/src/cli/control-client.ts +84 -101
  77. package/src/cli/deploy.ts +133 -177
  78. package/src/cli/dev.ts +506 -107
  79. package/src/cli/image-assets.ts +499 -0
  80. package/src/cli/managed-client-boundaries.ts +103 -0
  81. package/src/cli/managed-dev-platform.ts +118 -0
  82. package/src/cli/managed-exports.ts +219 -0
  83. package/src/cli/managed-release-bin.ts +13 -0
  84. package/src/cli/managed-release.ts +260 -0
  85. package/src/cli/module-boundaries.ts +11 -1
  86. package/src/cli/new-app.ts +111 -87
  87. package/src/cli/route-codegen.ts +311 -0
  88. package/src/cli/route-static.ts +710 -0
  89. package/src/client/bootstrap.ts +110 -0
  90. package/src/client/errors.ts +13 -7
  91. package/src/client/files.ts +64 -0
  92. package/src/client/id.ts +9 -5
  93. package/src/client/image.ts +28 -0
  94. package/src/client/index.ts +25 -2
  95. package/src/client/managed-client.ts +729 -0
  96. package/src/client/managed-upgrade.ts +283 -0
  97. package/src/client/recovery-journal.ts +195 -0
  98. package/src/client/route-boundaries.ts +184 -0
  99. package/src/client/router-history.ts +152 -0
  100. package/src/client/router-store.ts +644 -0
  101. package/src/client/router.ts +324 -0
  102. package/src/client/routes.ts +28 -0
  103. package/src/client/sync.ts +66 -592
  104. package/src/dev-runtime/database.ts +77 -2
  105. package/src/dev-runtime/functions.ts +3 -3
  106. package/src/dev-runtime/id.ts +19 -4
  107. package/src/dev-runtime/managed-functions.ts +255 -0
  108. package/src/dev-runtime/managed-sync.ts +263 -0
  109. package/src/dev-runtime/managed-websocket.ts +71 -0
  110. package/src/dev-runtime/migrations.ts +20 -0
  111. package/src/dev-runtime/sync.ts +236 -236
  112. package/src/image-assets.d.ts +36 -0
  113. package/src/internal/client.ts +186 -0
  114. package/src/internal/host.ts +1 -1
  115. package/src/internal/http-paths.ts +0 -3
  116. package/src/internal/routes.ts +45 -0
  117. package/dist/runtime/api-CAgKDij7.js.map +0 -1
  118. package/dist/runtime/descriptor-Cyo9FP9n-C0SRVXNW.js +0 -154
  119. package/dist/runtime/descriptor-Cyo9FP9n-C0SRVXNW.js.map +0 -1
  120. package/dist/runtime/id-Bt9XWRGL.js.map +0 -1
  121. package/dist/runtime/id-Dz0apuB3.d.ts +0 -1
  122. package/dist/runtime/index-D54flWtH.d.ts +0 -402
  123. package/dist/runtime/pagination-DnKg3dkI-r5ZUxBBx.d.ts +0 -112
  124. package/dist/runtime/pagination-Dz0apuB3.d.ts +0 -1
  125. package/dist/runtime/result-DKAA4gpS.d.ts +0 -1
  126. package/dist/runtime/schema-1Zs03-iS.js.map +0 -1
  127. package/src/cli/build/client.ts +0 -119
  128. package/src/cli/build/public.ts +0 -186
  129. package/src/cli/client-entry.ts +0 -152
  130. package/src/cli/client-plugin.ts +0 -136
  131. package/src/client/app.ts +0 -15
@@ -0,0 +1,411 @@
1
+ import { createHash } from "node:crypto";
2
+ import { fileURLToPath } from "node:url";
3
+ import { Effect, FileSystem, Option, Path, Predicate, Schema } from "effect";
4
+ import tailwindcss from "@tailwindcss/vite";
5
+ import { defaultClientConditions, normalizePath, type Plugin } from "vite";
6
+ import {
7
+ AppArtifact,
8
+ AppArtifactPath,
9
+ AppDefinition,
10
+ AppModuleId,
11
+ ModuleImport,
12
+ type AppModule,
13
+ } from "@ignotum/contracts/app-definition";
14
+ import { artifactReference, sha256, utf8Bytes, validateAppModuleBytes } from "@ignotum/versions";
15
+ import { InvalidClientFile } from "../client-config.js";
16
+ import { clientStylesSource } from "../client-styles.js";
17
+ import { createImageAssetsPlugin } from "../image-assets.js";
18
+ import { managedClientBoundariesPlugin } from "../managed-client-boundaries.js";
19
+ import { managedPreactPeerPlugin } from "../managed-exports.js";
20
+ import { moduleBoundariesPlugin } from "../module-boundaries.js";
21
+ import { generateClientRoutes } from "../route-codegen.js";
22
+ import { InvalidBuildOutput, runViteBuild, sourceMapPathTransform } from "./artifact.js";
23
+
24
+ export interface ManagedClientBuildResult {
25
+ readonly appName: string;
26
+ readonly client: AppDefinition["client"];
27
+ readonly artifacts: ReadonlyArray<AppArtifact>;
28
+ readonly bytes: ReadonlyMap<string, Uint8Array>;
29
+ }
30
+
31
+ interface ClientChunk {
32
+ readonly fileName: string;
33
+ readonly imports: ReadonlyArray<string>;
34
+ readonly dynamicImports: ReadonlyArray<string>;
35
+ readonly styles: ReadonlyArray<string>;
36
+ readonly isEntry: boolean;
37
+ }
38
+
39
+ const literal = Schema.encodeSync(Schema.fromJsonString(Schema.String));
40
+ const globalStylesId = "virtual:ignotum/managed-global.css";
41
+
42
+ const stylesheetFileName = (source: string | Uint8Array): string => {
43
+ // Vite removes its internal hash-update marker before writing CSS assets.
44
+ const finalSource = Predicate.isString(source)
45
+ ? source.replace(/\/\*\$vite\$:\d+\*\//, "")
46
+ : source;
47
+ return `assets/${createHash("sha256").update(finalSource).digest("hex")}/style.css`;
48
+ };
49
+
50
+ /** Compile route definitions and app dependencies without embedding browser runtime code. */
51
+ export const buildManagedClient = Effect.fn("Deploy.buildManagedClient")(function* (
52
+ appDirectory: string,
53
+ outputDirectory: string,
54
+ options: { readonly applicationName: string },
55
+ ) {
56
+ const fs = yield* FileSystem.FileSystem;
57
+ const path = yield* Path.Path;
58
+ const appName = yield* Schema.decodeUnknownEffect(AppDefinition.fields.appName)(
59
+ options.applicationName,
60
+ );
61
+ for (const publicPath of [
62
+ path.join(appDirectory, "public"),
63
+ path.join(appDirectory, "client/public"),
64
+ ]) {
65
+ if (
66
+ (yield* fs.exists(publicPath)) ||
67
+ Option.isSome(yield* Effect.option(fs.readLink(publicPath)))
68
+ )
69
+ return yield* InvalidClientFile.make({
70
+ path: publicPath,
71
+ message:
72
+ "App public directories are not supported. Import supported images from client/_assets instead.",
73
+ });
74
+ }
75
+ const discovery = yield* generateClientRoutes(appDirectory);
76
+ const stage = yield* fs.makeTempDirectoryScoped({ prefix: "ignotum-managed-client-" });
77
+ const images = yield* createImageAssetsPlugin(appDirectory);
78
+ const boundaries = yield* moduleBoundariesPlugin("Client", appDirectory);
79
+ const platform = yield* managedClientBoundariesPlugin(appDirectory);
80
+ const peers = yield* managedPreactPeerPlugin({ resolvePeer: (specifier) => specifier });
81
+ const entrySources = new Map<string, string>();
82
+ const input: Record<string, string> = {};
83
+ input.global = globalStylesId;
84
+ const sourceEntries = new Map<AppModuleId, string>();
85
+ for (const [index, module] of discovery.modules.entries()) {
86
+ const name = `${module.kind}-${index}`;
87
+ const id = `virtual:ignotum/managed-client/${name}`;
88
+ input[name] = id;
89
+ sourceEntries.set(module.id, `${name}.mjs`);
90
+ entrySources.set(
91
+ `\0${id}`,
92
+ `export { default } from ${literal(normalizePath(module.path))};\n`,
93
+ );
94
+ }
95
+ const chunkMetadata = new Map<string, ClientChunk>();
96
+ const maps = new Map<string, Uint8Array>();
97
+ const privateMaps: Plugin = {
98
+ name: "ignotum:private-client-source-maps",
99
+ enforce: "pre",
100
+ generateBundle: {
101
+ order: "pre",
102
+ handler(_options, bundle) {
103
+ for (const item of Object.values(bundle)) {
104
+ if (item.type !== "asset" || !item.fileName.endsWith(".mjs.map")) continue;
105
+ const owner = bundle[item.fileName.slice(0, -4)];
106
+ if (owner?.type !== "chunk" || owner.map === null) continue;
107
+ // Remove only compiler-produced chunk maps before the image plugin checks browser assets.
108
+ // The final writer records them privately with their app module owner.
109
+ delete bundle[item.fileName];
110
+ }
111
+ },
112
+ },
113
+ };
114
+ const collect: Plugin = {
115
+ name: "ignotum:managed-client-entries",
116
+ enforce: "post",
117
+ resolveId(id) {
118
+ if (id === globalStylesId || entrySources.has(`\0${id}`)) return `\0${id}`;
119
+ },
120
+ load(id) {
121
+ if (id === `\0${globalStylesId}`) return clientStylesSource;
122
+ return entrySources.get(id);
123
+ },
124
+ generateBundle(_options, bundle) {
125
+ for (const item of Object.values(bundle)) {
126
+ if (item.type !== "chunk") continue;
127
+ const metadata = Schema.decodeUnknownSync(
128
+ Schema.Struct({
129
+ viteMetadata: Schema.optional(
130
+ Schema.Struct({ importedCss: Schema.ReadonlySet(Schema.String) }),
131
+ ),
132
+ }),
133
+ )(item);
134
+ chunkMetadata.set(item.fileName, {
135
+ fileName: item.fileName,
136
+ imports: item.imports,
137
+ dynamicImports: item.dynamicImports,
138
+ styles: [...(metadata.viteMetadata?.importedCss ?? [])].sort(),
139
+ isEntry: item.isEntry,
140
+ });
141
+ if (item.map === null) {
142
+ if (Object.values(item.modules).some((module) => module.renderedLength > 0))
143
+ this.error(`Missing source map for app implementation '${item.fileName}'.`);
144
+ continue;
145
+ }
146
+ const encodedDirectory = JSON.stringify(normalizePath(appDirectory)).slice(1, -1);
147
+ maps.set(
148
+ `${item.fileName}.map`,
149
+ utf8Bytes(item.map.toString().replaceAll(encodedDirectory, "ignotum://app")),
150
+ );
151
+ }
152
+ },
153
+ };
154
+ const output = yield* runViteBuild("managed client graph", {
155
+ root: path.join(appDirectory, "client"),
156
+ configFile: false,
157
+ publicDir: false,
158
+ envDir: false,
159
+ logLevel: "warn",
160
+ mode: "production",
161
+ define: {
162
+ "process.env.NODE_ENV": JSON.stringify("production"),
163
+ "import.meta.env.DEV": "false",
164
+ "import.meta.env.PROD": "true",
165
+ },
166
+ base: "/_ignotum/",
167
+ oxc: { jsx: { importSource: "ignotum/client", runtime: "automatic", development: false } },
168
+ resolve: {
169
+ alias: [
170
+ {
171
+ find: /^tailwindcss$/,
172
+ replacement: normalizePath(fileURLToPath(import.meta.resolve("tailwindcss/index.css"))),
173
+ },
174
+ ],
175
+ conditions: defaultClientConditions.map((condition) =>
176
+ condition === "development|production" ? "production" : condition,
177
+ ),
178
+ tsconfigPaths: true,
179
+ },
180
+ plugins: [privateMaps, images.plugin, boundaries, platform, peers, collect, tailwindcss()],
181
+ build: {
182
+ emptyOutDir: false,
183
+ outDir: stage,
184
+ modulePreload: false,
185
+ sourcemap: "hidden",
186
+ target: "es2022",
187
+ rolldownOptions: {
188
+ input,
189
+ preserveEntrySignatures: "strict",
190
+ output: {
191
+ entryFileNames: "[name].mjs",
192
+ chunkFileNames: "shared-[hash].mjs",
193
+ assetFileNames: (asset) =>
194
+ asset.names.some((name) => name.endsWith(".css"))
195
+ ? stylesheetFileName(asset.source)
196
+ : "assets/[name]-[hash][extname]",
197
+ sourcemapPathTransform: yield* sourceMapPathTransform(appDirectory),
198
+ },
199
+ },
200
+ },
201
+ });
202
+ const chunks = output.flatMap((item) =>
203
+ item.type === "chunk"
204
+ ? [
205
+ {
206
+ fileName: item.fileName,
207
+ imports: item.imports,
208
+ dynamicImports: item.dynamicImports,
209
+ isEntry: item.isEntry,
210
+ styles: chunkMetadata.get(item.fileName)?.styles ?? [],
211
+ },
212
+ ]
213
+ : [],
214
+ );
215
+ const files = new Map<string, Uint8Array>();
216
+ for (const item of output) {
217
+ if (
218
+ item.type === "asset" &&
219
+ !item.fileName.endsWith(".css") &&
220
+ !images.assets.has(item.fileName)
221
+ )
222
+ return yield* InvalidBuildOutput.make({
223
+ message: `Unsupported client artifact '${item.fileName}'.`,
224
+ });
225
+ files.set(item.fileName, yield* fs.readFile(path.join(stage, item.fileName)));
226
+ }
227
+ for (const [name, bytes] of maps) files.set(name, bytes);
228
+ const knownChunks = new Map(chunks.map((chunk) => [chunk.fileName, chunk]));
229
+ const stylesFor = (fileName: string, visited = new Set<string>()): ReadonlyArray<string> => {
230
+ if (visited.has(fileName)) return [];
231
+ visited.add(fileName);
232
+ const chunk = knownChunks.get(fileName);
233
+ if (chunk === undefined) return [];
234
+ return [
235
+ ...new Set([
236
+ ...chunk.styles,
237
+ ...chunk.imports.flatMap((dependency) => stylesFor(dependency, visited)),
238
+ ]),
239
+ ].sort();
240
+ };
241
+ const globalCss = new Set(chunkMetadata.get("global.mjs")?.styles ?? []);
242
+ const routeStyles = new Set(
243
+ chunks.filter((chunk) => chunk.fileName !== "global.mjs").flatMap((chunk) => chunk.styles),
244
+ );
245
+ const styles = [...files.keys()]
246
+ .filter((name) => name.endsWith(".css") && (globalCss.has(name) || !routeStyles.has(name)))
247
+ .sort();
248
+ const registration = `import { mountClient } from "ignotum/internal/bootstrap";
249
+ const registration = {
250
+ applicationName: ${literal(appName)},
251
+ routes: ${JSON.stringify(discovery.routes)},
252
+ layouts: ${JSON.stringify(discovery.layouts)},
253
+ styles: ${JSON.stringify(styles.map((name) => `/_ignotum/${name}`))},
254
+ modules: new Map([
255
+ ${discovery.modules
256
+ .map((module) => {
257
+ const name = sourceEntries.get(module.id)!;
258
+ return ` [${literal(module.id)}, { load: () => import(${literal(`./${name}`)}), styles: ${JSON.stringify(stylesFor(name).map((style) => `/_ignotum/${style}`))} }]`;
259
+ })
260
+ .join(",\n")}
261
+ ])
262
+ };
263
+ export default function mount(selection) { return mountClient(registration, selection); }
264
+ `;
265
+ files.set("registration.mjs", utf8Bytes(registration));
266
+ chunks.push({
267
+ fileName: "registration.mjs",
268
+ imports: ["ignotum/internal/bootstrap"],
269
+ dynamicImports: [...sourceEntries.values()],
270
+ styles,
271
+ isEntry: true,
272
+ });
273
+ chunks.sort((a, b) => a.fileName.localeCompare(b.fileName));
274
+ let icon: AppArtifactPath | undefined;
275
+ const iconPath = path.join(appDirectory, "client/icon.svg");
276
+ if (yield* fs.exists(iconPath)) {
277
+ if ((yield* fs.stat(iconPath)).type !== "File")
278
+ return yield* InvalidClientFile.make({
279
+ path: iconPath,
280
+ message: "The client icon must be a regular file.",
281
+ });
282
+ const bytes = yield* fs.readFile(iconPath);
283
+ icon = AppArtifactPath.make(`assets/${yield* sha256(bytes)}/icon.svg`);
284
+ files.set(icon, bytes);
285
+ }
286
+ // App module identities include the completed inventory digest. Its source retains relative
287
+ // imports and content-named asset URLs, so cycles and registration never hash their own identity.
288
+ const inventory = yield* Effect.forEach(
289
+ [...files].sort(([a], [b]) => a.localeCompare(b)),
290
+ ([name, bytes]) => sha256(bytes).pipe(Effect.map((digest) => ({ name, digest }))),
291
+ );
292
+ const graphDigest = yield* sha256(utf8Bytes(JSON.stringify(inventory)));
293
+ const moduleId = (name: string) => AppModuleId.make(`app/client/${graphDigest}/${name}`);
294
+ const modulePath = (name: string) => AppArtifactPath.make(`client/${graphDigest}/${name}`);
295
+ const artifacts: AppArtifact[] = [];
296
+ const bytes = new Map<string, Uint8Array>();
297
+ const modules: AppModule[] = [];
298
+ const imagePaths = [...images.assets.keys()];
299
+ for (const chunk of chunks) {
300
+ const source = files.get(chunk.fileName)!;
301
+ const id = moduleId(chunk.fileName);
302
+ const artifact = {
303
+ ...(yield* artifactReference(modulePath(chunk.fileName), source)),
304
+ kind: "ClientModule" as const,
305
+ visibility: "public" as const,
306
+ contentType: "text/javascript",
307
+ };
308
+ const imports: ModuleImport[] = [];
309
+ for (const imported of new Set([...chunk.imports, ...chunk.dynamicImports])) {
310
+ if (knownChunks.has(imported)) {
311
+ const relative = normalizePath(path.relative(path.dirname(chunk.fileName), imported));
312
+ imports.push({
313
+ kind: "App",
314
+ specifier: relative.startsWith(".") ? relative : `./${relative}`,
315
+ module: moduleId(imported),
316
+ });
317
+ } else {
318
+ const kind = imported.startsWith("ignotum/internal/")
319
+ ? "Compiler"
320
+ : imported.startsWith("ignotum/client")
321
+ ? "Runtime"
322
+ : "RenderingPeer";
323
+ imports.push(
324
+ yield* Schema.decodeUnknownEffect(ModuleImport)({ kind, specifier: imported }),
325
+ );
326
+ }
327
+ }
328
+ const map = maps.get(`${chunk.fileName}.map`);
329
+ const mapPath = modulePath(`${chunk.fileName}.map`);
330
+ const moduleStyles = chunk.fileName === "registration.mjs" ? styles : stylesFor(chunk.fileName);
331
+ const assetSources = [
332
+ new TextDecoder().decode(source),
333
+ ...moduleStyles.map((style) => new TextDecoder().decode(files.get(style))),
334
+ ];
335
+ const baseModule: AppModule = {
336
+ id,
337
+ artifact: artifact.path,
338
+ imports,
339
+ styles: moduleStyles.map((name) => AppArtifactPath.make(name)),
340
+ assets: imagePaths
341
+ .filter((name) => assetSources.some((contents) => contents.includes(`/_ignotum/${name}`)))
342
+ .map((name) => AppArtifactPath.make(name)),
343
+ };
344
+ const module = map === undefined ? baseModule : { ...baseModule, sourceMap: mapPath };
345
+ yield* validateAppModuleBytes(module, artifact, source, chunk.isEntry ? ["default"] : []);
346
+ modules.push(module);
347
+ artifacts.push(artifact);
348
+ bytes.set(artifact.path, source);
349
+ if (map !== undefined) {
350
+ artifacts.push({
351
+ ...(yield* artifactReference(mapPath, map)),
352
+ kind: "SourceMap",
353
+ visibility: "private",
354
+ contentType: "application/json",
355
+ owner: id,
356
+ });
357
+ bytes.set(mapPath, map);
358
+ }
359
+ }
360
+ for (const [name, contents] of [...files].sort(([a], [b]) => a.localeCompare(b))) {
361
+ if (knownChunks.has(name) || name === "registration.mjs" || name.endsWith(".map")) continue;
362
+ const kind = name === icon ? "Icon" : name.endsWith(".css") ? "Style" : "Image";
363
+ const contentType =
364
+ kind === "Icon"
365
+ ? "image/svg+xml"
366
+ : kind === "Style"
367
+ ? "text/css"
368
+ : images.assets.get(name)?.contentType;
369
+ if (contentType === undefined)
370
+ return yield* InvalidBuildOutput.make({ message: `Unknown client asset '${name}'.` });
371
+ const reference = yield* artifactReference(name, contents);
372
+ if (kind === "Style" && name !== `assets/${reference.sha256}/style.css`)
373
+ return yield* InvalidBuildOutput.make({
374
+ message: "The bundler changed stylesheet bytes after their content identity was assigned.",
375
+ });
376
+ const artifact = yield* Schema.decodeUnknownEffect(AppArtifact)({
377
+ ...reference,
378
+ kind,
379
+ visibility: "public",
380
+ contentType,
381
+ });
382
+ artifacts.push(artifact);
383
+ bytes.set(artifact.path, contents);
384
+ }
385
+ const reference = (id: AppModuleId) => ({
386
+ module: moduleId(sourceEntries.get(id)!),
387
+ exportName: "default",
388
+ });
389
+ const baseClient = {
390
+ registration: { module: moduleId("registration.mjs"), exportName: "default" },
391
+ graph: {
392
+ entries: chunks.filter((chunk) => chunk.isEntry).map((chunk) => moduleId(chunk.fileName)),
393
+ modules,
394
+ },
395
+ routes: discovery.routes.map((route) => ({ ...route, page: reference(route.page.module) })),
396
+ layouts: discovery.layouts.map((layout) => ({
397
+ ...layout,
398
+ module: reference(layout.module.module),
399
+ })),
400
+ styles: styles.map((name) => AppArtifactPath.make(name)),
401
+ };
402
+ const client = yield* Schema.decodeUnknownEffect(AppDefinition.fields.client)(
403
+ icon === undefined ? baseClient : { ...baseClient, icon },
404
+ );
405
+ for (const [name, content] of bytes) {
406
+ const target = path.join(outputDirectory, name);
407
+ yield* fs.makeDirectory(path.dirname(target), { recursive: true });
408
+ yield* fs.writeFile(target, content);
409
+ }
410
+ return { appName, client, artifacts, bytes } satisfies ManagedClientBuildResult;
411
+ });