@sjcx/vorma 0.0.0-pre.0

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 (168) hide show
  1. package/LICENSE +20 -0
  2. package/dist/build/assemble-artifacts.d.ts +23 -0
  3. package/dist/build/browser-contracts.d.ts +8 -0
  4. package/dist/build/bundle-client.d.ts +29 -0
  5. package/dist/build/cli.d.ts +2 -0
  6. package/dist/build/cli.js +1256 -0
  7. package/dist/build/client-inputs.d.ts +11 -0
  8. package/dist/build/client-output-hashing.d.ts +3 -0
  9. package/dist/build/client-public-urls.d.ts +20 -0
  10. package/dist/build/compare-text.d.ts +5 -0
  11. package/dist/build/compile-application.d.ts +40 -0
  12. package/dist/build/compile-server.d.ts +42 -0
  13. package/dist/build/contained-path.d.ts +29 -0
  14. package/dist/build/critical-css.d.ts +7 -0
  15. package/dist/build/css-public-assets.d.ts +15 -0
  16. package/dist/build/development-artifacts.d.ts +35 -0
  17. package/dist/build/development-browser-source.d.ts +3 -0
  18. package/dist/build/development-build.d.ts +30 -0
  19. package/dist/build/development-changes.d.ts +14 -0
  20. package/dist/build/development-client.d.ts +21 -0
  21. package/dist/build/development-generation.d.ts +20 -0
  22. package/dist/build/development-listener.d.ts +14 -0
  23. package/dist/build/development-patterns.d.ts +5 -0
  24. package/dist/build/development-protocol.d.ts +20 -0
  25. package/dist/build/development-server-process.d.ts +30 -0
  26. package/dist/build/development-sources.d.ts +23 -0
  27. package/dist/build/development-stylesheet.d.ts +15 -0
  28. package/dist/build/development-watcher.d.ts +15 -0
  29. package/dist/build/development-work.d.ts +32 -0
  30. package/dist/build/development.d.ts +3 -0
  31. package/dist/build/generate-application.d.ts +62 -0
  32. package/dist/build/index.d.ts +15 -0
  33. package/dist/build/index.js +2 -0
  34. package/dist/build/inspect-css-artifacts.d.ts +11 -0
  35. package/dist/build/inspect-projects.d.ts +69 -0
  36. package/dist/build/load-config.d.ts +56 -0
  37. package/dist/build/output-files.d.ts +8 -0
  38. package/dist/build/output-ownership.d.ts +21 -0
  39. package/dist/build/public-assets.d.ts +32 -0
  40. package/dist/build/publish-artifacts.d.ts +23 -0
  41. package/dist/build/render-client-contracts.d.ts +10 -0
  42. package/dist/build/render-client-transport.d.ts +11 -0
  43. package/dist/build/render-server-generated.d.ts +33 -0
  44. package/dist/build/render-source-text.d.ts +24 -0
  45. package/dist/build/render-types.d.ts +31 -0
  46. package/dist/build/resolve-config.d.ts +50 -0
  47. package/dist/build/source-layout.d.ts +24 -0
  48. package/dist/build/typescript-projects.d.ts +36 -0
  49. package/dist/build-JgARonaY.js +3214 -0
  50. package/dist/build-invocation-D7Fu8gBK.js +4 -0
  51. package/dist/client/assembly.d.ts +40 -0
  52. package/dist/client/browser-http-client.d.ts +10 -0
  53. package/dist/client/client-loaders.d.ts +93 -0
  54. package/dist/client/commit.d.ts +41 -0
  55. package/dist/client/contracts.d.ts +192 -0
  56. package/dist/client/core.d.ts +45 -0
  57. package/dist/client/development.d.ts +16 -0
  58. package/dist/client/document-recovery.d.ts +4 -0
  59. package/dist/client/endpoints.d.ts +54 -0
  60. package/dist/client/errors.d.ts +87 -0
  61. package/dist/client/freshness.d.ts +51 -0
  62. package/dist/client/head.d.ts +14 -0
  63. package/dist/client/history.d.ts +25 -0
  64. package/dist/client/index.d.ts +80 -0
  65. package/dist/client/index.js +150 -0
  66. package/dist/client/links.d.ts +48 -0
  67. package/dist/client/materialize.d.ts +21 -0
  68. package/dist/client/modules.d.ts +2 -0
  69. package/dist/client/navigation.d.ts +47 -0
  70. package/dist/client/payload.d.ts +54 -0
  71. package/dist/client/prefetch.d.ts +28 -0
  72. package/dist/client/prepare.d.ts +47 -0
  73. package/dist/client/projections.d.ts +26 -0
  74. package/dist/client/queries.d.ts +33 -0
  75. package/dist/client/react/application.d.ts +87 -0
  76. package/dist/client/react/hooks.d.ts +36 -0
  77. package/dist/client/react/index.d.ts +5 -0
  78. package/dist/client/react/index.js +3237 -0
  79. package/dist/client/react/link.d.ts +27 -0
  80. package/dist/client/react/view.d.ts +86 -0
  81. package/dist/client/request.d.ts +16 -0
  82. package/dist/client/route-context.d.ts +31 -0
  83. package/dist/client/route-matching.d.ts +22 -0
  84. package/dist/client/route-synchronization.d.ts +30 -0
  85. package/dist/client/route-url.d.ts +5 -0
  86. package/dist/client/runtime.d.ts +82 -0
  87. package/dist/client/scroll.d.ts +16 -0
  88. package/dist/client/services.d.ts +34 -0
  89. package/dist/client/state.d.ts +102 -0
  90. package/dist/client/static-assets.d.ts +3 -0
  91. package/dist/client/stylesheets.d.ts +7 -0
  92. package/dist/client/transport-type-entries.d.ts +2 -0
  93. package/dist/client/types.d.ts +152 -0
  94. package/dist/client/view-definition.d.ts +24 -0
  95. package/dist/client/work-indicator.d.ts +18 -0
  96. package/dist/compile-Me8EOyMK.js +870 -0
  97. package/dist/development-B_rxuwY7.js +128 -0
  98. package/dist/hash-set-BPx3iXec.js +141 -0
  99. package/dist/href-Cqyax8Y0.js +357 -0
  100. package/dist/materialize-DZ7VSLFk.js +739 -0
  101. package/dist/metadata-BoPKo-G2.js +32 -0
  102. package/dist/primitives/tasks/index.d.ts +1 -0
  103. package/dist/primitives/tasks/index.js +1 -0
  104. package/dist/route-context-CtY_9U_W.js +559 -0
  105. package/dist/route-matching-D_uSG2P9.js +33 -0
  106. package/dist/search-parse-0f7oqVsG.js +375 -0
  107. package/dist/server/config/index.d.ts +36 -0
  108. package/dist/server/config/index.js +30 -0
  109. package/dist/server/development/metadata.d.ts +11 -0
  110. package/dist/server/document.d.ts +37 -0
  111. package/dist/server/form-data.d.ts +21 -0
  112. package/dist/server/graph/compile.d.ts +118 -0
  113. package/dist/server/graph/declarations.d.ts +117 -0
  114. package/dist/server/graph/literal-error-tag.d.ts +14 -0
  115. package/dist/server/graph/pattern-conflicts.d.ts +36 -0
  116. package/dist/server/graph/search-schema.d.ts +13 -0
  117. package/dist/server/graph/transport-schema.d.ts +11 -0
  118. package/dist/server/index.d.ts +310 -0
  119. package/dist/server/index.js +2311 -0
  120. package/dist/server/public-assets.d.ts +18 -0
  121. package/dist/server/runtime/application.d.ts +15 -0
  122. package/dist/server/runtime/assets.d.ts +15 -0
  123. package/dist/server/runtime/context.d.ts +96 -0
  124. package/dist/server/runtime/domain-errors.d.ts +11 -0
  125. package/dist/server/runtime/effects.d.ts +83 -0
  126. package/dist/server/runtime/execute.d.ts +13 -0
  127. package/dist/server/runtime/finalize.d.ts +44 -0
  128. package/dist/server/runtime/handler.d.ts +106 -0
  129. package/dist/server/runtime/head.d.ts +22 -0
  130. package/dist/server/runtime/payload.d.ts +50 -0
  131. package/dist/server/runtime/pipeline.d.ts +27 -0
  132. package/dist/server/runtime/plan.d.ts +98 -0
  133. package/dist/server/runtime/render-document.d.ts +16 -0
  134. package/dist/server/runtime/startup-error.d.ts +11 -0
  135. package/dist/server/runtime/transport-type-entries.d.ts +2 -0
  136. package/dist/server/runtime/vercel-deployment.d.ts +2 -0
  137. package/dist/server/url-pathname.d.ts +13 -0
  138. package/dist/static-assets-BrH3O0M8.js +199 -0
  139. package/dist/universal/content-digest.d.ts +7 -0
  140. package/dist/universal/generated-types.d.ts +9 -0
  141. package/dist/universal/href.d.ts +17 -0
  142. package/dist/universal/http-method.d.ts +4 -0
  143. package/dist/universal/json-transport.d.ts +14 -0
  144. package/dist/universal/null-prototype-record.d.ts +7 -0
  145. package/dist/universal/path-wire.d.ts +71 -0
  146. package/dist/universal/pattern-language.d.ts +4 -0
  147. package/dist/universal/protocol-schemas.d.ts +99 -0
  148. package/dist/universal/protocol.d.ts +162 -0
  149. package/dist/universal/search-grammar.d.ts +7 -0
  150. package/dist/universal/search-parse.d.ts +22 -0
  151. package/dist/universal/search-serialize.d.ts +16 -0
  152. package/dist/universal/search-shape.d.ts +32 -0
  153. package/dist/universal/static-assets.d.ts +2 -0
  154. package/dist/universal/transport-types/big-decimal.d.ts +13 -0
  155. package/dist/universal/transport-types/chunk.d.ts +8 -0
  156. package/dist/universal/transport-types/date-time-utc.d.ts +13 -0
  157. package/dist/universal/transport-types/date-time-zoned.d.ts +13 -0
  158. package/dist/universal/transport-types/duration.d.ts +13 -0
  159. package/dist/universal/transport-types/hash-map.d.ts +8 -0
  160. package/dist/universal/transport-types/hash-set.d.ts +8 -0
  161. package/dist/universal/transport-types/option.d.ts +10 -0
  162. package/dist/universal/transport-types/redacted.d.ts +9 -0
  163. package/dist/universal/value-transport.d.ts +117 -0
  164. package/dist/value-transport-Bst4Hk-n.js +864 -0
  165. package/dist/vite/build-invocation.d.ts +4 -0
  166. package/dist/vite/index.d.ts +3 -0
  167. package/dist/vite/index.js +15 -0
  168. package/package.json +84 -0
@@ -0,0 +1,3214 @@
1
+ import { c as compileUiPattern, m as ARTIFACT_PROTOCOL, s as compileEndpointPattern, t as EFFECT_TRANSPORT_TYPES, y as FRAMEWORK_IDENTITY_PROTOCOL } from "./value-transport-Bst4Hk-n.js";
2
+ import { t as VORMA_BUILD_PLUGIN_NAME } from "./build-invocation-D7Fu8gBK.js";
3
+ import { c as findConflictingDynamicParameterNames, f as matchesHttpMethod, o as createNestedRouteChains } from "./compile-Me8EOyMK.js";
4
+ import { VormaConfigSchema } from "./server/config/index.js";
5
+ import { Effect, FileSystem, Option, Record, Result, Schema } from "effect";
6
+ import { build } from "vite";
7
+ import { blake3 } from "@noble/hashes/blake3.js";
8
+ import { utf8ToBytes } from "@noble/hashes/utils.js";
9
+ import { base32nopad } from "@scure/base";
10
+ import { isAbsolute, join } from "node:path";
11
+ import { RolldownMagicString, build as build$1 } from "rolldown";
12
+ import { readFile } from "node:fs/promises";
13
+ import * as Path$1 from "effect/Path";
14
+ import { SyntaxKind, TokenFlags, isAsExpression, isCallExpression, isElementAccessExpression, isExportSpecifier, isIdentifier, isImportClause, isImportSpecifier, isNamespaceImport, isNoSubstitutionTemplateLiteral, isNonNullExpression, isParenthesizedExpression, isPropertyAccessExpression, isSatisfiesExpression, isStringLiteral, isTypeAssertion, isTypeNode } from "typescript/unstable/ast";
15
+ import { API, ElementFlags, SignatureKind, SymbolFlags } from "typescript/unstable/sync";
16
+ import { createTemplateHead, createTemplateMiddle, createTemplateTail } from "typescript/unstable/ast/factory";
17
+ import * as FileSystem$1 from "effect/FileSystem";
18
+ import { pid, platform } from "node:process";
19
+ import { transform } from "lightningcss";
20
+ //#region src/universal/content-digest.ts
21
+ /** BLAKE3 content identity, encoded as lowercase base32 without padding. */
22
+ function toContentDigest(source) {
23
+ return encodeContentDigest(blake3(typeof source === "string" ? utf8ToBytes(source) : source));
24
+ }
25
+ /** Encodes an already computed BLAKE3 digest. */
26
+ function encodeContentDigest(digest) {
27
+ return base32nopad.encode(digest).toLowerCase();
28
+ }
29
+ /** Canonical object ordering; array order carries framework semantics. */
30
+ function encodeCanonicalJson(value) {
31
+ if (Array.isArray(value)) return `[${value.map(encodeCanonicalJson).join(",")}]`;
32
+ if (value !== null && typeof value === "object") return `{${Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, item]) => `${JSON.stringify(key)}:${encodeCanonicalJson(item)}`).join(",")}}`;
33
+ return JSON.stringify(value);
34
+ }
35
+ //#endregion
36
+ //#region src/build/browser-contracts.ts
37
+ /**
38
+ * The static contracts on which browser code and learned routing depend.
39
+ * Source locations, export names, and runtime implementations are not inputs.
40
+ * This projection is hashed during the build and is never sent to the browser.
41
+ */
42
+ function projectBrowserContracts(inspection) {
43
+ const declarations = inspection.serverDeclarations.map((declaration) => {
44
+ if (declaration.kind === "UiLoader") return [`UiLoader ${declaration.pattern}`, {
45
+ input: declaration.input ?? "undefined",
46
+ output: declaration.output
47
+ }];
48
+ return [`${declaration.method} ${declaration.pattern}`, {
49
+ operation: declaration.operation,
50
+ inputTransport: declaration.inputTransport,
51
+ input: declaration.inputTransport === "formData" ? "FormData" : declaration.input ?? "undefined",
52
+ output: declaration.output ?? "undefined",
53
+ event: declaration.event ?? "undefined",
54
+ errors: Object.fromEntries(declaration.errors.map((error) => {
55
+ return [error.tag, error.fields];
56
+ }))
57
+ }];
58
+ });
59
+ return {
60
+ declarations: Object.fromEntries(declarations),
61
+ typeAliases: inspection.typeAliases
62
+ };
63
+ }
64
+ //#endregion
65
+ //#region src/build/client-inputs.ts
66
+ /** Runtime peers that must have one browser identity in development and production. */
67
+ const CLIENT_SHARED_PACKAGES = [
68
+ "effect",
69
+ "@effect/atom-react",
70
+ "react",
71
+ "react-dom"
72
+ ];
73
+ /** Keeps value identity checks inside the selected route's normal module load. */
74
+ function getClientViewEntries(generation) {
75
+ return [...generation.model.clientViewCandidates].flatMap(([pattern, candidates], index) => {
76
+ const first = candidates[0];
77
+ if (first === void 0) return [];
78
+ if (candidates.every((candidate) => candidate.filePath === first.filePath)) return [{
79
+ pattern,
80
+ id: first.filePath,
81
+ exportNames: candidates.map((candidate) => candidate.exportName),
82
+ source: void 0
83
+ }];
84
+ const exportNames = candidates.map((_candidate, candidateIndex) => `candidate${candidateIndex}`);
85
+ return [{
86
+ pattern,
87
+ id: `\0vorma:view-candidates:${index}`,
88
+ exportNames,
89
+ source: candidates.map((candidate, candidateIndex) => `export { ${JSON.stringify(candidate.exportName)} as ${exportNames[candidateIndex]} } from ${JSON.stringify(candidate.filePath)};`).join("\n")
90
+ }];
91
+ });
92
+ }
93
+ //#endregion
94
+ //#region src/build/contained-path.ts
95
+ /** True when a `path.relative` result names the root or a descendant of it. */
96
+ function containsRelativePath(input) {
97
+ return input.relativePath === "" || !input.absolute && input.relativePath !== ".." && !input.relativePath.startsWith(`..${input.separator}`);
98
+ }
99
+ /**
100
+ * True when a module specifier names a package or one of its subpaths.
101
+ * Relative, absolute, builtin, scheme, drive-letter, and virtual-module
102
+ * specifiers never match; only the caller decides which of those to exclude
103
+ * first.
104
+ */
105
+ function matchesPackageSpecifier(input) {
106
+ return input.source === input.packageName || input.source.startsWith(`${input.packageName}/`);
107
+ }
108
+ /**
109
+ * A `path.relative` result with platform separators converted to POSIX
110
+ * separators, for generated module specifiers, output keys, and build
111
+ * identities, which are POSIX even when the build runs on Windows.
112
+ */
113
+ function toPosixRelativePath(input) {
114
+ return input.path.relative(input.from, input.to).split(input.path.sep).join("/");
115
+ }
116
+ //#endregion
117
+ //#region src/build/compile-server.ts
118
+ /** Log codes reporting a specifier the compiler could not resolve. */
119
+ const UNRESOLVED_LOG_CODE = /^UNRESOLVED/;
120
+ const TERMINAL_STYLE = new RegExp(`${String.fromCharCode(27)}\\[[\\d;]*m`, "g");
121
+ /** The plain first line of a compiler message, with the module it concerns. */
122
+ function toLogSummary(log) {
123
+ const plain = log.message.replace(TERMINAL_STYLE, "");
124
+ const [firstLine] = plain.split("\n");
125
+ const summary = firstLine === void 0 ? plain : firstLine;
126
+ return log.id === void 0 ? summary : `${summary} (${log.id})`;
127
+ }
128
+ /**
129
+ * Compiles the configured server entry and internal runtime
130
+ * entry together into ECMAScript modules that run
131
+ * without a TypeScript loader, a Vorma installation, or a bundler in the
132
+ * target process. The compilation rewrites nothing in the application's
133
+ * source. Effect remains a runtime peer shared with the containing application.
134
+ *
135
+ * Rolldown uses the configured server TypeScript project. The client's Vite
136
+ * configuration does not configure server compilation.
137
+ *
138
+ * Production bytes must depend on the sources alone, so the debug comments
139
+ * that name module paths relative to the build directory are off: the
140
+ * checkout's location must not reach the artifact. Symlink resolution stays on
141
+ * because an isolated package store reaches a package's own dependencies only
142
+ * through the real path. Development source maps retain authored locations.
143
+ */
144
+ function compileServerApplication(input) {
145
+ return Effect.gen(function* () {
146
+ const { generation } = input;
147
+ const { config } = generation;
148
+ const diagnostics = [];
149
+ const unresolvedImports = [];
150
+ let inputFilePaths = [];
151
+ const output = yield* Effect.tryPromise(() => {
152
+ return build$1({
153
+ input: {
154
+ entry: config.serverEntryFilePath,
155
+ runtime: config.generatedBuildFilePath
156
+ },
157
+ preserveEntrySignatures: "strict",
158
+ cwd: config.serverProjectDirectory,
159
+ platform: "node",
160
+ tsconfig: config.serverTsconfigFilePath,
161
+ write: false,
162
+ plugins: [{
163
+ name: "vorma:server-inputs",
164
+ generateBundle() {
165
+ inputFilePaths = [...this.getModuleIds()].filter(isAbsolute);
166
+ }
167
+ }],
168
+ external: (source, _importer, isResolved) => {
169
+ return !isResolved && (matchesPackageSpecifier({
170
+ source,
171
+ packageName: "effect"
172
+ }) || config.serverExternalPackages.some((packageName) => {
173
+ return matchesPackageSpecifier({
174
+ source,
175
+ packageName
176
+ });
177
+ }));
178
+ },
179
+ experimental: { attachDebugInfo: "none" },
180
+ onLog: (level, log) => {
181
+ const code = log.code === void 0 ? "" : log.code;
182
+ if (UNRESOLVED_LOG_CODE.test(code)) {
183
+ unresolvedImports.push(toLogSummary(log));
184
+ return;
185
+ }
186
+ diagnostics.push({
187
+ level,
188
+ code,
189
+ message: toLogSummary(log)
190
+ });
191
+ },
192
+ output: {
193
+ dir: config.serverGeneratedDirectory,
194
+ format: "es",
195
+ entryFileNames: (chunk) => {
196
+ return chunk.name === "runtime" ? ARTIFACT_PROTOCOL.compiledRuntimeFileName : ARTIFACT_PROTOCOL.compiledServerEntryFileName;
197
+ },
198
+ chunkFileNames: "[name]-[hash].mjs",
199
+ sourcemap: input.sourcemap
200
+ }
201
+ });
202
+ }).pipe(Effect.uninterruptible);
203
+ if (unresolvedImports.length > 0) return yield* Effect.fail(`The compiled server graph has unresolved imports: ${unresolvedImports.join("; ")}`);
204
+ const compiled = [];
205
+ for (const emitted of output.output) {
206
+ if (emitted.type !== "chunk") return yield* Effect.fail(`The server compilation emitted a file that is not a module: ${emitted.fileName}`);
207
+ if (!/^[^./][^/]*\.mjs$/.test(emitted.fileName)) return yield* Effect.fail(`The server compilation emitted a file outside its flat module directory: ${emitted.fileName}`);
208
+ compiled.push({
209
+ fileName: emitted.fileName,
210
+ source: emitted.code
211
+ });
212
+ }
213
+ const emittedFileNames = new Set(compiled.map((file) => file.fileName));
214
+ for (const entryFileName of [ARTIFACT_PROTOCOL.compiledServerEntryFileName, ARTIFACT_PROTOCOL.compiledRuntimeFileName]) if (!emittedFileNames.has(entryFileName)) return yield* Effect.fail(`The server compilation did not emit ${entryFileName}`);
215
+ return {
216
+ files: compiled,
217
+ diagnostics,
218
+ inputFilePaths
219
+ };
220
+ }).pipe(Effect.mapError((cause) => {
221
+ return {
222
+ _tag: "ServerCompilationIssue",
223
+ cause
224
+ };
225
+ }));
226
+ }
227
+ //#endregion
228
+ //#region src/build/client-public-urls.ts
229
+ /** The asset key as the public file map spells it: without a leading slash. */
230
+ function normalizePublicAssetKey(key) {
231
+ return key.startsWith("/") ? key.slice(1) : key;
232
+ }
233
+ function createClientPublicUrlPlugin(input) {
234
+ let isDevelopment = false;
235
+ let projection = preparePublicUrlReplacements(input);
236
+ const referenceIds = /* @__PURE__ */ new Map();
237
+ const referenceUrls = /* @__PURE__ */ new Map();
238
+ return {
239
+ name: "vorma:client-public-urls",
240
+ api: { updateInputFn(next) {
241
+ projection = preparePublicUrlReplacements(next);
242
+ } },
243
+ enforce: "post",
244
+ configResolved(config) {
245
+ isDevelopment = config.command === "serve";
246
+ },
247
+ resolveFileUrl({ referenceId, chunkId, fileName }) {
248
+ const url = referenceUrls.get(referenceId);
249
+ if (url === void 0) return;
250
+ let references = input.referencedAssets.get(chunkId);
251
+ if (references === void 0) {
252
+ references = /* @__PURE__ */ new Set();
253
+ input.referencedAssets.set(chunkId, references);
254
+ }
255
+ references.add(fileName);
256
+ return JSON.stringify(url);
257
+ },
258
+ async load(id) {
259
+ const { assets, sitesByFile, sourceTexts, publicDirectory } = projection;
260
+ const queryIndex = id.indexOf("?");
261
+ const filePath = queryIndex === -1 ? id : id.slice(0, queryIndex);
262
+ const sites = sitesByFile.get(filePath);
263
+ if (sites === void 0) return null;
264
+ const inspected = sourceTexts.get(filePath);
265
+ const current = await readFile(filePath, "utf8");
266
+ if (current !== inspected) return this.error(`${filePath} changed after the build read it; run the build again`);
267
+ const code = new RolldownMagicString(current);
268
+ for (const site of sites) {
269
+ const asset = assets.get(normalizePublicAssetKey(site.key));
270
+ if (asset === void 0) return this.error(`Unknown public asset: ${site.key}`);
271
+ if (isDevelopment) {
272
+ code.overwrite(site.start, site.end, JSON.stringify(asset.url));
273
+ continue;
274
+ }
275
+ let referenceId = referenceIds.get(asset.url);
276
+ if (referenceId === void 0) {
277
+ referenceId = this.emitFile({
278
+ type: "asset",
279
+ fileName: asset.fileName,
280
+ source: await readFile(join(publicDirectory, asset.fileName))
281
+ });
282
+ referenceIds.set(asset.url, referenceId);
283
+ referenceUrls.set(referenceId, asset.url);
284
+ }
285
+ code.overwrite(site.start, site.end, `import.meta.ROLLUP_FILE_URL_${referenceId}`);
286
+ }
287
+ return {
288
+ code: code.toString(),
289
+ map: code.generateMap({
290
+ source: id,
291
+ includeContent: true,
292
+ hires: true
293
+ }).toString()
294
+ };
295
+ }
296
+ };
297
+ }
298
+ function preparePublicUrlReplacements(input) {
299
+ const assets = new Map(input.publicAssets.map((asset) => [asset.sourceKey, asset]));
300
+ const sitesByFile = /* @__PURE__ */ new Map();
301
+ for (const site of input.sites) {
302
+ const sites = sitesByFile.get(site.filePath);
303
+ if (sites === void 0) sitesByFile.set(site.filePath, [site]);
304
+ else sites.push(site);
305
+ }
306
+ return {
307
+ assets,
308
+ sitesByFile,
309
+ sourceTexts: input.sourceTexts,
310
+ publicDirectory: input.publicDirectory
311
+ };
312
+ }
313
+ //#endregion
314
+ //#region src/build/css-public-assets.ts
315
+ const CSS_URL_PATTERN = /"(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|\/\*[\s\S]*?\*\/|\burl\(\s*(?:"((?:\\[\s\S]|[^"\\])*)"|'((?:\\[\s\S]|[^'\\])*)'|((?:\\[\s\S]|[^)\\])*?))\s*\)/gi;
316
+ /** Replaces public URLs before Vite hashes CSS; public files are already published. */
317
+ function createCssPublicAssets(publicAssets) {
318
+ const cssAssets = /* @__PURE__ */ new Set();
319
+ const javascriptAssets = /* @__PURE__ */ new Map();
320
+ const externalUrls = /* @__PURE__ */ new Set();
321
+ function replaceValue(value) {
322
+ let error;
323
+ const replaced = value.replace(CSS_URL_PATTERN, (matched, doubleQuoted, singleQuoted, unquoted) => {
324
+ const token = doubleQuoted ?? singleQuoted ?? unquoted;
325
+ if (token === void 0) return matched;
326
+ const reference = decodeCssEscapes(token);
327
+ if (!reference.startsWith("@public/")) return matched;
328
+ const parsed = Result.try({
329
+ try: () => {
330
+ const url = new URL(reference.slice(8), "http://vorma.invalid/");
331
+ return {
332
+ key: decodeURIComponent(url.pathname.slice(1)),
333
+ suffix: url.search + url.hash
334
+ };
335
+ },
336
+ catch: () => "Invalid public asset URL: " + reference
337
+ });
338
+ if (Result.isFailure(parsed)) {
339
+ error = parsed.failure;
340
+ return matched;
341
+ }
342
+ const { key, suffix } = parsed.success;
343
+ const assetUrl = Object.hasOwn(publicAssets.publicFilemap, key) ? publicAssets.publicFilemap[key] : void 0;
344
+ if (assetUrl === void 0) {
345
+ error = "Unknown public asset: " + reference;
346
+ return matched;
347
+ }
348
+ const resolved = assetUrl + suffix;
349
+ externalUrls.add(resolved);
350
+ externalUrls.add(decodeURI(resolved));
351
+ externalUrls.add(assetUrl);
352
+ return "url(\"" + resolved.replace(/["\\\n\r\f]/g, (character) => "\\" + character.charCodeAt(0).toString(16) + " ") + "\")";
353
+ });
354
+ return error === void 0 ? Result.succeed(replaced) : Result.fail(error);
355
+ }
356
+ const postcssPlugin = {
357
+ postcssPlugin: "vorma:public-urls",
358
+ Once(root) {
359
+ let error;
360
+ root.walkDecls((declaration) => {
361
+ const result = replaceValue(declaration.value);
362
+ if (Result.isFailure(result)) error = result.failure;
363
+ else declaration.value = result.success;
364
+ });
365
+ return error === void 0 ? void 0 : Promise.reject(new Error(error));
366
+ }
367
+ };
368
+ return {
369
+ cssAssets,
370
+ javascriptAssets,
371
+ plugin: {
372
+ name: "vorma:css-public-assets",
373
+ enforce: "post",
374
+ config(config) {
375
+ const external = config.build?.rolldownOptions?.external;
376
+ const renderBuiltUrl = config.experimental?.renderBuiltUrl;
377
+ return {
378
+ css: { postcss: { plugins: [postcssPlugin] } },
379
+ build: { rolldownOptions: { external(source, importer, isResolved) {
380
+ if (externalUrls.has(source)) return true;
381
+ if (typeof external === "function") return external(source, importer, isResolved);
382
+ return (Array.isArray(external) ? external : [external]).some((entry) => {
383
+ return typeof entry === "string" ? entry === source : entry instanceof RegExp ? entry.test(source) : false;
384
+ });
385
+ } } },
386
+ experimental: { renderBuiltUrl(fileName, context) {
387
+ if (context.hostType === "css" && context.type === "asset") cssAssets.add(stripQueryAndFragment(fileName));
388
+ const renderedUrl = renderBuiltUrl?.(fileName, context);
389
+ if (context.hostType === "js") {
390
+ const references = javascriptAssets.get(context.hostId);
391
+ const reference = {
392
+ fileName,
393
+ renderedUrl
394
+ };
395
+ if (references === void 0) javascriptAssets.set(context.hostId, [reference]);
396
+ else references.push(reference);
397
+ }
398
+ return renderedUrl;
399
+ } }
400
+ };
401
+ }
402
+ },
403
+ updatePublicAssetsFn(next) {
404
+ publicAssets = next;
405
+ }
406
+ };
407
+ }
408
+ /** Decodes escapes inside an already identified CSS URL token. */
409
+ function decodeCssEscapes(value) {
410
+ return value.replace(/\\(?:([0-9a-f]{1,6})(?:\r\n|[ \t\n\r\f])?|(\r\n|[\n\r\f])|([\s\S]))/gi, (_match, hex, newline, escaped) => {
411
+ if (hex !== void 0) {
412
+ const codePoint = Number.parseInt(hex, 16);
413
+ return codePoint === 0 || codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343 ? "�" : String.fromCodePoint(codePoint);
414
+ }
415
+ return newline === void 0 ? escaped ?? "" : "";
416
+ });
417
+ }
418
+ function stripQueryAndFragment(fileName) {
419
+ const index = fileName.search(/[?#]/);
420
+ return index === -1 ? fileName : fileName.slice(0, index);
421
+ }
422
+ //#endregion
423
+ //#region src/build/compare-text.ts
424
+ /** Compares source-derived text by Unicode code units, independent of host locale. */
425
+ function compareSourceText(input) {
426
+ return input.left < input.right ? -1 : input.left > input.right ? 1 : 0;
427
+ }
428
+ //#endregion
429
+ //#region src/build/render-source-text.ts
430
+ /** A tab-indented prefix, matching how a person writes generated source. */
431
+ function toIndent(depth) {
432
+ return " ".repeat(depth);
433
+ }
434
+ /** Moves multi-line rendered text deeper by `depth` tabs; the first line keeps its position in the line that embeds it. */
435
+ function indentRenderedText(text, depth) {
436
+ return text.split("\n").join(`\n${toIndent(depth)}`);
437
+ }
438
+ const IDENTIFIER_PROPERTY_NAME = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
439
+ function renderPropertyName(name) {
440
+ if (name === "__proto__") return "[\"__proto__\"]";
441
+ return IDENTIFIER_PROPERTY_NAME.test(name) ? name : JSON.stringify(name);
442
+ }
443
+ /** A relative module specifier, explicitly relative even when the target's own name starts with a dot. */
444
+ function getModuleSpecifier(input) {
445
+ return toRelativeSpecifier(toPosixRelativePath({
446
+ path: input.path,
447
+ from: input.path.dirname(input.fromFilePath),
448
+ to: input.toFilePath
449
+ }));
450
+ }
451
+ /** A path is a relative specifier only when it starts with `./` or `../`. */
452
+ function toRelativeSpecifier(relativePath) {
453
+ return relativePath.startsWith("./") || relativePath.startsWith("../") ? relativePath : `./${relativePath}`;
454
+ }
455
+ //#endregion
456
+ //#region src/build/render-types.ts
457
+ /** Removes the checker's missing-value marker, which is distinct from the ordinary `undefined` type. */
458
+ function withoutAbsence(input) {
459
+ const members = input.type.isUnionType() ? input.type.getTypes() : [input.type];
460
+ const present = members.filter((member) => {
461
+ return !(member.isIntrinsicType() && member.intrinsicName === "undefined" && member.id !== input.undefinedType.id);
462
+ });
463
+ return present.length === members.length ? void 0 : present;
464
+ }
465
+ /** The Effect data types the value transport carries, by the module and symbol that declare them. */
466
+ const EFFECT_TYPES = [
467
+ {
468
+ module: "Option",
469
+ symbolName: "Option",
470
+ kind: "option",
471
+ printedName: "Option.Option"
472
+ },
473
+ {
474
+ module: "Redacted",
475
+ symbolName: "Redacted",
476
+ kind: "redacted",
477
+ printedName: "Redacted.Redacted"
478
+ },
479
+ {
480
+ module: "Chunk",
481
+ symbolName: "Chunk",
482
+ kind: "chunk",
483
+ printedName: "Chunk.Chunk"
484
+ },
485
+ {
486
+ module: "Duration",
487
+ symbolName: "Duration",
488
+ kind: "duration",
489
+ printedName: "Duration.Duration"
490
+ },
491
+ {
492
+ module: "BigDecimal",
493
+ symbolName: "BigDecimal",
494
+ kind: "big-decimal",
495
+ printedName: "BigDecimal.BigDecimal"
496
+ },
497
+ {
498
+ module: "DateTime",
499
+ symbolName: "Utc",
500
+ kind: "date-time-utc",
501
+ printedName: "DateTime.Utc"
502
+ },
503
+ {
504
+ module: "DateTime",
505
+ symbolName: "Zoned",
506
+ kind: "date-time-zoned",
507
+ printedName: "DateTime.Zoned"
508
+ },
509
+ {
510
+ module: "HashMap",
511
+ symbolName: "HashMap",
512
+ kind: "hash-map",
513
+ printedName: "HashMap.HashMap"
514
+ },
515
+ {
516
+ module: "HashSet",
517
+ symbolName: "HashSet",
518
+ kind: "hash-set",
519
+ printedName: "HashSet.HashSet"
520
+ }
521
+ ];
522
+ /** Platform types the value transport carries by name; their generic arguments are rendered. */
523
+ const PLATFORM_TYPE_NAMES = /* @__PURE__ */ new Set([
524
+ "Date",
525
+ "URL",
526
+ "URLSearchParams",
527
+ "RegExp",
528
+ "FormData",
529
+ "Map",
530
+ "ReadonlyMap",
531
+ "Set",
532
+ "ReadonlySet"
533
+ ]);
534
+ /** TypeScript's transient-symbol flag for a readonly property of a mapped or instantiated type. */
535
+ const CHECK_FLAG_READONLY = 8;
536
+ /** Type member order does not depend on source property order. */
537
+ function sortProperties(properties) {
538
+ return [...properties].sort((left, right) => {
539
+ return compareSourceText({
540
+ left: left.name,
541
+ right: right.name
542
+ });
543
+ });
544
+ }
545
+ /** True when a symbol is declared outside the application: in a default library or an installed package. */
546
+ function matchesLibrarySymbol(input) {
547
+ const declaration = input.symbol.declarations[0];
548
+ if (declaration === void 0) return false;
549
+ const declarationPath = String(declaration.path);
550
+ const metadata = input.program.getSourceFileMetadata(declarationPath);
551
+ return metadata !== void 0 && (metadata.isDefaultLibrary || metadata.isFromExternalLibrary || declarationPath.includes("/node_modules/"));
552
+ }
553
+ function createTypeRenderer(input) {
554
+ const { checker, program, emitter } = input;
555
+ const undefinedType = checker.getUndefinedType();
556
+ const aliases = /* @__PURE__ */ new Map();
557
+ const rendered = [];
558
+ const visiting = /* @__PURE__ */ new Set();
559
+ const effectModules = /* @__PURE__ */ new Set();
560
+ const transportTypeNames = /* @__PURE__ */ new Set();
561
+ function fail(type, reason) {
562
+ return Result.fail({
563
+ _tag: "UnrenderableContractType",
564
+ reason,
565
+ typeText: checker.typeToString(type)
566
+ });
567
+ }
568
+ function getDeclarationPath(symbol) {
569
+ const declaration = symbol.declarations[0];
570
+ return declaration === void 0 ? void 0 : String(declaration.path);
571
+ }
572
+ function matchesEffectType(symbol) {
573
+ const declarationPath = getDeclarationPath(symbol);
574
+ if (declarationPath === void 0) return;
575
+ return EFFECT_TYPES.find((entry) => {
576
+ return entry.symbolName === symbol.name && declarationPath.endsWith(`/effect/dist/${entry.module}.d.ts`);
577
+ });
578
+ }
579
+ function matchesReadonlyProperty(property) {
580
+ if ((property.checkFlags & CHECK_FLAG_READONLY) !== 0) return true;
581
+ const declaration = property.valueDeclaration?.resolve();
582
+ if (declaration === void 0) return false;
583
+ const modifiers = declaration.modifiers;
584
+ return modifiers !== void 0 && modifiers.some((modifier) => {
585
+ return modifier.kind === SyntaxKind.ReadonlyKeyword;
586
+ });
587
+ }
588
+ function renderTypeArguments(type, depth) {
589
+ const parts = [];
590
+ for (const argument of checker.getTypeArguments(type)) {
591
+ const text = renderNode(argument, depth);
592
+ if (Result.isFailure(text)) return text;
593
+ parts.push(text.success);
594
+ }
595
+ return Result.succeed(parts.length === 0 ? "" : `<${parts.join(", ")}>`);
596
+ }
597
+ function renderNamedType(type, depth) {
598
+ const aliasSymbol = type.getAliasSymbol();
599
+ const symbol = type.getSymbol();
600
+ for (const candidate of [aliasSymbol, symbol]) {
601
+ if (candidate === void 0) continue;
602
+ const effectType = matchesEffectType(candidate);
603
+ if (effectType !== void 0) {
604
+ effectModules.add(effectType.module);
605
+ transportTypeNames.add(effectType.kind);
606
+ const argumentTypes = candidate === aliasSymbol ? type.getAliasTypeArguments() : type.isTypeReference() ? checker.getTypeArguments(type) : [];
607
+ const parts = [];
608
+ for (const argument of argumentTypes) {
609
+ const text = renderNode(argument, depth);
610
+ if (Result.isFailure(text)) return text;
611
+ parts.push(text.success);
612
+ }
613
+ return Result.succeed(parts.length === 0 ? effectType.printedName : `${effectType.printedName}<${parts.join(", ")}>`);
614
+ }
615
+ }
616
+ if (symbol !== void 0 && PLATFORM_TYPE_NAMES.has(symbol.name) && matchesLibrarySymbol({
617
+ program,
618
+ symbol
619
+ })) {
620
+ const argumentsText = type.isTypeReference() ? renderTypeArguments(type, depth) : Result.succeed("");
621
+ return Result.map(argumentsText, (text) => `${symbol.name}${text}`);
622
+ }
623
+ return Result.succeed(null);
624
+ }
625
+ function renderProperty(input) {
626
+ if (checker.getSignaturesOfType(input.propertyType, SignatureKind.Call).length > 0) return fail(input.propertyType, `the property ${input.property.name} is a function, which cannot cross the wire`);
627
+ const optional = (input.property.flags & SymbolFlags.Optional) !== 0;
628
+ const text = optional ? renderOptionalType(input.propertyType, input.depth) : renderNode(input.propertyType, input.depth);
629
+ if (Result.isFailure(text)) return text;
630
+ const readonly = matchesReadonlyProperty(input.property) ? "readonly " : "";
631
+ return Result.succeed(`${toIndent(input.depth)}${readonly}${renderPropertyName(input.property.name)}${optional ? "?" : ""}: ${text.success};`);
632
+ }
633
+ /** A class instance crosses the wire as the record of its schema fields; the fields come from the class's static `fields`. */
634
+ function renderClassInstance(type, classSymbol, depth) {
635
+ const constructorType = checker.getTypeOfSymbol(classSymbol);
636
+ const fields = constructorType === void 0 ? void 0 : checker.getPropertyOfType(constructorType, "fields");
637
+ const fieldsType = fields === void 0 ? void 0 : checker.getTypeOfSymbol(fields);
638
+ if (fieldsType === void 0) return fail(type, `the class ${classSymbol.name} is not an Effect Schema class, so its wire form is unknown`);
639
+ const lines = [];
640
+ for (const field of sortProperties(checker.getPropertiesOfType(fieldsType))) {
641
+ const property = checker.getPropertyOfType(type, field.name);
642
+ const propertyType = property === void 0 ? void 0 : checker.getTypeOfSymbol(property);
643
+ if (property === void 0 || propertyType === void 0) return fail(type, `the class ${classSymbol.name} declares the field ${field.name} without a property of that name`);
644
+ const line = renderProperty({
645
+ property,
646
+ propertyType,
647
+ depth: depth + 1
648
+ });
649
+ if (Result.isFailure(line)) return line;
650
+ lines.push(line.success);
651
+ }
652
+ return Result.succeed(lines.length === 0 ? "{}" : [
653
+ "{",
654
+ ...lines,
655
+ `${toIndent(depth)}}`
656
+ ].join("\n"));
657
+ }
658
+ function renderObject(type, depth) {
659
+ const symbol = type.getSymbol();
660
+ if (symbol !== void 0 && (symbol.flags & SymbolFlags.Class) !== 0) return renderClassInstance(type, symbol, depth);
661
+ if (checker.getSignaturesOfType(type, SignatureKind.Call).length > 0 || checker.getSignaturesOfType(type, SignatureKind.Construct).length > 0) return fail(type, "a function cannot cross the wire");
662
+ const lines = [];
663
+ for (const property of sortProperties(checker.getPropertiesOfType(type))) {
664
+ if (String(property.escapedName).startsWith("__@")) continue;
665
+ const propertyType = checker.getTypeOfSymbol(property);
666
+ if (propertyType === void 0) return fail(type, `the property ${property.name} has no type`);
667
+ const line = renderProperty({
668
+ property,
669
+ propertyType,
670
+ depth: depth + 1
671
+ });
672
+ if (Result.isFailure(line)) return line;
673
+ lines.push(line.success);
674
+ }
675
+ for (const signature of checker.getIndexInfosOfType(type)) {
676
+ const keyText = renderNode(signature.keyType, depth + 1);
677
+ if (Result.isFailure(keyText)) return keyText;
678
+ const valueText = renderNode(signature.valueType, depth + 1);
679
+ if (Result.isFailure(valueText)) return valueText;
680
+ lines.push(`${toIndent(depth + 1)}${signature.isReadonly ? "readonly " : ""}[key: ${keyText.success}]: ${valueText.success};`);
681
+ }
682
+ return Result.succeed(lines.length === 0 ? "{}" : [
683
+ "{",
684
+ ...lines,
685
+ `${toIndent(depth)}}`
686
+ ].join("\n"));
687
+ }
688
+ function renderOptionalType(type, depth) {
689
+ const present = withoutAbsence({
690
+ type,
691
+ undefinedType
692
+ });
693
+ return present === void 0 ? renderNode(type, depth) : present.length === 1 && present[0] !== void 0 ? renderNode(present[0], depth) : renderUnion(present, depth, " | ");
694
+ }
695
+ function renderTuple(type, depth) {
696
+ const target = type.getTarget();
697
+ const members = [];
698
+ for (const [index, element] of checker.getTypeArguments(type).entries()) {
699
+ const flags = target.elementFlags[index] ?? ElementFlags.Required;
700
+ const text = (flags & ElementFlags.Optional) !== 0 ? renderOptionalType(element, depth) : renderNode(element, depth);
701
+ if (Result.isFailure(text)) return text;
702
+ if ((flags & ElementFlags.Rest) !== 0) members.push(`...Array<${text.success}>`);
703
+ else if ((flags & ElementFlags.Variadic) !== 0) members.push(`...(${text.success})`);
704
+ else if ((flags & ElementFlags.Optional) !== 0) members.push(`(${text.success})?`);
705
+ else members.push(text.success);
706
+ }
707
+ return Result.succeed(`${target.readonly ? "readonly " : ""}[${members.join(", ")}]`);
708
+ }
709
+ function renderUnion(members, depth, separator) {
710
+ const parts = [];
711
+ let sawTrue = false;
712
+ let sawFalse = false;
713
+ for (const member of members) {
714
+ if (member.isBooleanLiteralType()) {
715
+ if (member.value) sawTrue = true;
716
+ else sawFalse = true;
717
+ continue;
718
+ }
719
+ const text = renderNode(member, depth);
720
+ if (Result.isFailure(text)) return text;
721
+ parts.push(separator === " & " && member.isUnionType() ? `(${text.success})` : text.success);
722
+ }
723
+ if (sawTrue && sawFalse) parts.push("boolean");
724
+ else if (sawTrue) parts.push("true");
725
+ else if (sawFalse) parts.push("false");
726
+ parts.sort((left, right) => {
727
+ return compareSourceText({
728
+ left,
729
+ right
730
+ });
731
+ });
732
+ return Result.succeed(parts.length === 0 ? "never" : parts.join(separator));
733
+ }
734
+ /** Keeps literal contents independent of indentation in the containing declaration. */
735
+ function renderTemplateToken(node) {
736
+ return emitter.printNode(node).replaceAll("\r", "\\r").replaceAll("\n", "\\n");
737
+ }
738
+ function renderTemplateLiteral(type, depth) {
739
+ if (!type.isTemplateLiteralType()) return fail(type, "not a template literal type");
740
+ const spans = [];
741
+ for (const span of type.getTypes()) {
742
+ const text = renderNode(span, depth);
743
+ if (Result.isFailure(text)) return text;
744
+ spans.push(text.success);
745
+ }
746
+ const [head, ...tail] = type.texts;
747
+ let text = renderTemplateToken(createTemplateHead(head ?? "", "", TokenFlags.None));
748
+ for (const [index, span] of spans.entries()) {
749
+ const createLiteral = index === spans.length - 1 ? createTemplateTail : createTemplateMiddle;
750
+ text += span + renderTemplateToken(createLiteral(tail[index] ?? "", "", TokenFlags.None));
751
+ }
752
+ return Result.succeed(text);
753
+ }
754
+ function renderNode(type, depth) {
755
+ const existingAlias = aliases.get(type.id);
756
+ if (existingAlias !== void 0) return Result.succeed(existingAlias);
757
+ if (visiting.has(type.id)) {
758
+ const name = `Recursive${aliases.size}`;
759
+ aliases.set(type.id, name);
760
+ return Result.succeed(name);
761
+ }
762
+ visiting.add(type.id);
763
+ const text = renderTypeStructure(type, depth);
764
+ visiting.delete(type.id);
765
+ if (Result.isFailure(text)) return text;
766
+ const createdAlias = aliases.get(type.id);
767
+ if (createdAlias === void 0) return text;
768
+ const declared = depth === 0 ? text : renderTypeStructure(type, 0);
769
+ if (Result.isFailure(declared)) return declared;
770
+ rendered.push(`type ${createdAlias} = ${declared.success};`);
771
+ return Result.succeed(createdAlias);
772
+ }
773
+ function renderTypeStructure(type, depth) {
774
+ if (type.isErrorType()) return fail(type, "the type could not be resolved");
775
+ if (type.isStringLiteralType()) return Result.succeed(JSON.stringify(type.value));
776
+ if (type.isNumberLiteralType()) return Result.succeed(String(type.value));
777
+ if (type.isBigIntLiteralType()) return Result.succeed(`${type.value}n`);
778
+ if (type.isBooleanLiteralType()) return Result.succeed(type.value ? "true" : "false");
779
+ if (type.isIntrinsicType()) return Result.succeed(type.intrinsicName);
780
+ const named = renderNamedType(type, depth);
781
+ if (Result.isFailure(named)) return Result.fail(named.failure);
782
+ if (named.success !== null) return Result.succeed(named.success);
783
+ if (type.isTemplateLiteralType()) return renderTemplateLiteral(type, depth);
784
+ if (type.isUnionType()) return renderUnion(type.getTypes(), depth, " | ");
785
+ if (type.isIntersectionType()) return renderUnion(type.getTypes(), depth, " & ");
786
+ if (type.isTypeParameter()) return fail(type, "a contract type cannot be generic");
787
+ if (!type.isObjectType()) return fail(type, "the type has no wire form");
788
+ if (checker.isArrayType(type) && type.isTypeReference()) {
789
+ const [element] = checker.getTypeArguments(type);
790
+ if (element === void 0) return fail(type, "an array type without an element type");
791
+ const text = renderNode(element, depth);
792
+ if (Result.isFailure(text)) return text;
793
+ const wrapper = type.getSymbol()?.name === "ReadonlyArray" ? "ReadonlyArray" : "Array";
794
+ return Result.succeed(`${wrapper}<${text.success}>`);
795
+ }
796
+ if (checker.isTupleType(type) && type.isTypeReference()) return renderTuple(type, depth);
797
+ return renderObject(type, depth);
798
+ }
799
+ return {
800
+ renderType: (input) => renderNode(input.type, input.depth),
801
+ renderAliases: () => rendered,
802
+ getEffectModules: () => [...effectModules].sort(),
803
+ getTransportTypeNames: () => transportTypeNames
804
+ };
805
+ }
806
+ //#endregion
807
+ //#region src/server/url-pathname.ts
808
+ const URL_PATHNAME_PARSER_ORIGIN = "https://vorma.invalid";
809
+ function parseHref(input) {
810
+ return new URL(input.value, input.base);
811
+ }
812
+ /** Parses the exact pathname representation used for request classification. */
813
+ function parseUrlPathname(value) {
814
+ const parsed = Result.try({
815
+ try: () => parseHref({
816
+ value,
817
+ base: URL_PATHNAME_PARSER_ORIGIN
818
+ }),
819
+ catch: (cause) => makeUrlPathnameParseError({
820
+ value,
821
+ cause
822
+ })
823
+ });
824
+ if (Result.isFailure(parsed)) return Result.fail(parsed.failure);
825
+ if (!value.startsWith("/") || parsed.success.origin !== URL_PATHNAME_PARSER_ORIGIN || parsed.success.pathname !== value || parsed.success.search !== "" || parsed.success.hash !== "") return Result.fail(makeUrlPathnameParseError({ value }));
826
+ return Result.succeed(value);
827
+ }
828
+ function makeUrlPathnameParseError(input) {
829
+ return {
830
+ _tag: "UrlPathnameParseError",
831
+ value: input.value,
832
+ ...input.cause === void 0 ? {} : { cause: input.cause }
833
+ };
834
+ }
835
+ //#endregion
836
+ //#region src/build/resolve-config.ts
837
+ /** The build reads no Vorma directory, in either entry directory, as application source. */
838
+ function getGeneratedSourceDirectories(config) {
839
+ return [config.serverGeneratedDirectory, config.clientGeneratedDirectory];
840
+ }
841
+ /** Derives generated source, public assets, and build coordination from the entry directories. */
842
+ function getVormaOwnedPaths(input) {
843
+ const { path } = input;
844
+ const name = ARTIFACT_PROTOCOL.vormaDirectoryName;
845
+ const clientGeneratedDirectory = path.join(input.clientEntryDirectory, name);
846
+ const serverGeneratedDirectory = path.join(input.serverEntryDirectory, name);
847
+ return {
848
+ publicDirectory: path.join(clientGeneratedDirectory, ARTIFACT_PROTOCOL.publicDirectoryName),
849
+ ignoreFilePaths: [.../* @__PURE__ */ new Set([serverGeneratedDirectory, clientGeneratedDirectory])].map((directory) => path.join(directory, ARTIFACT_PROTOCOL.ignoreFileName)),
850
+ serverGeneratedDirectory,
851
+ clientGeneratedDirectory,
852
+ generatedBuildFilePath: path.join(serverGeneratedDirectory, ARTIFACT_PROTOCOL.generatedBuildFileName),
853
+ generatedServerContractsFilePath: path.join(serverGeneratedDirectory, ARTIFACT_PROTOCOL.generatedServerContractsFileName),
854
+ compiledServerEntryFilePath: path.join(serverGeneratedDirectory, ARTIFACT_PROTOCOL.compiledServerEntryFileName)
855
+ };
856
+ }
857
+ /** Resolves every configured path once against the configuration file's directory. */
858
+ function resolveVormaConfig(input) {
859
+ return Effect.gen(function* () {
860
+ const config = yield* Schema.decodeUnknownEffect(VormaConfigSchema)(input.config).pipe(Effect.mapError((cause) => {
861
+ return {
862
+ _tag: "ConfigResolutionError",
863
+ field: "config",
864
+ message: `Invalid Vorma configuration: ${cause.message}`,
865
+ cause
866
+ };
867
+ }));
868
+ if (config.bodySsr === true) return yield* Effect.fail({
869
+ _tag: "ConfigResolutionError",
870
+ field: "bodySsr",
871
+ message: "bodySsr: true requires server body rendering and hydration, which this runtime does not provide; omit bodySsr or set it to false"
872
+ });
873
+ const path = yield* Path$1.Path;
874
+ if (!path.isAbsolute(input.configFilePath)) return yield* Effect.fail({
875
+ _tag: "ConfigResolutionError",
876
+ field: "configFilePath",
877
+ message: `configFilePath must be absolute, got ${JSON.stringify(input.configFilePath)}`
878
+ });
879
+ const configDirectory = path.dirname(path.normalize(input.configFilePath));
880
+ const serverEntryFilePath = path.resolve(configDirectory, config.serverEntry);
881
+ const serverTsconfigFilePath = path.resolve(configDirectory, config.serverTsconfigFile);
882
+ const clientEntryFilePath = path.resolve(configDirectory, config.clientEntry);
883
+ const clientTsconfigFilePath = path.resolve(configDirectory, config.clientTsconfigFile);
884
+ return {
885
+ configFilePath: path.normalize(input.configFilePath),
886
+ configDirectory,
887
+ serverEntryFilePath,
888
+ serverTsconfigFilePath,
889
+ serverProjectDirectory: path.dirname(serverTsconfigFilePath),
890
+ clientEntryFilePath,
891
+ clientTsconfigFilePath,
892
+ publicAssetsSourceDirectory: path.resolve(configDirectory, config.publicAssetsDir),
893
+ viteConfigFilePath: path.resolve(configDirectory, config.viteConfigFile),
894
+ ...config.criticalCssEntry === void 0 ? {} : { criticalCssEntryFilePath: path.resolve(configDirectory, config.criticalCssEntry) },
895
+ serverExternalPackages: config.serverExternalPackages === void 0 ? [] : config.serverExternalPackages,
896
+ publicAssetsBasePath: yield* Effect.fromResult(parsePublicAssetsBasePath(config.publicAssetsBasePath)),
897
+ bodySsr: config.bodySsr,
898
+ dev: config.dev,
899
+ ...getVormaOwnedPaths({
900
+ path,
901
+ serverEntryDirectory: path.dirname(serverEntryFilePath),
902
+ clientEntryDirectory: path.dirname(clientEntryFilePath)
903
+ })
904
+ };
905
+ });
906
+ }
907
+ function parsePublicAssetsBasePath(value) {
908
+ const parsed = parseUrlPathname(value);
909
+ if (Result.isFailure(parsed)) return Result.fail(makePublicAssetsBasePathError(parsed.failure));
910
+ if (!matchesPublicAssetsBasePath(parsed.success)) return Result.fail(makePublicAssetsBasePathError());
911
+ return Result.succeed(parsed.success);
912
+ }
913
+ /** The root, or an absolute path with a trailing slash. */
914
+ function matchesPublicAssetsBasePath(value) {
915
+ return value === "/" || value.startsWith("/") && value.endsWith("/");
916
+ }
917
+ function makePublicAssetsBasePathError(cause) {
918
+ return {
919
+ _tag: "ConfigResolutionError",
920
+ field: "publicAssetsBasePath",
921
+ message: "publicAssetsBasePath must be a normalized absolute URL path ending in \"/\" without an authority, query, or fragment",
922
+ ...cause === void 0 ? {} : { cause }
923
+ };
924
+ }
925
+ //#endregion
926
+ //#region src/build/inspect-projects.ts
927
+ /** Reads every registerable declaration, view, the client application, and every public URL call from the two projects. */
928
+ function inspectProjects(input) {
929
+ return Effect.gen(function* () {
930
+ const path = yield* Path$1.Path;
931
+ const excludedDirectories = getGeneratedSourceDirectories(input.layout);
932
+ const serverFiles = listOwnSourceFiles({
933
+ project: input.projects.server,
934
+ excludedDirectories,
935
+ path
936
+ });
937
+ const clientFiles = listOwnSourceFiles({
938
+ project: input.projects.client,
939
+ excludedDirectories,
940
+ path
941
+ });
942
+ const serverRenderer = createTypeRenderer({
943
+ checker: input.projects.server.checker,
944
+ program: input.projects.server.program,
945
+ emitter: input.projects.server.emitter
946
+ });
947
+ const serverDeclarations = yield* Effect.fromResult(inspectServerDeclarations({
948
+ checker: input.projects.server.checker,
949
+ program: input.projects.server.program,
950
+ files: serverFiles,
951
+ renderer: serverRenderer
952
+ }));
953
+ const client = yield* Effect.fromResult(inspectClientProject({
954
+ checker: input.projects.client.checker,
955
+ files: clientFiles,
956
+ clientEntryFilePath: input.layout.clientEntryFilePath
957
+ }));
958
+ return {
959
+ serverDeclarations,
960
+ clientViews: client.views,
961
+ clientApplication: client.application,
962
+ publicUrlSites: client.publicUrlSites,
963
+ clientSourceTexts: client.sourceTexts,
964
+ typeAliases: serverRenderer.renderAliases(),
965
+ effectModules: serverRenderer.getEffectModules(),
966
+ transportTypeNames: serverRenderer.getTransportTypeNames()
967
+ };
968
+ });
969
+ }
970
+ /** The project's own modules: not a library, not a declaration file, and not under a Vorma output directory. */
971
+ function listOwnSourceFiles(input) {
972
+ const { program } = input.project;
973
+ const files = [];
974
+ for (const fileName of program.getSourceFileNames()) {
975
+ if (fileName.includes("/node_modules/") || input.excludedDirectories.some((directory) => {
976
+ const relativePath = input.path.relative(directory, fileName);
977
+ return containsRelativePath({
978
+ relativePath,
979
+ separator: input.path.sep,
980
+ absolute: input.path.isAbsolute(relativePath)
981
+ });
982
+ })) continue;
983
+ const metadata = program.getSourceFileMetadata(fileName);
984
+ if (metadata?.isDefaultLibrary || metadata?.isFromExternalLibrary) continue;
985
+ const sourceFile = program.getSourceFile(fileName);
986
+ if (sourceFile === void 0 || sourceFile.isDeclarationFile) continue;
987
+ files.push(sourceFile);
988
+ }
989
+ return files.sort((left, right) => {
990
+ return compareSourceText({
991
+ left: left.fileName,
992
+ right: right.fileName
993
+ });
994
+ });
995
+ }
996
+ /** Export bindings retain their public names while aliases resolve to one declaration identity. */
997
+ function listModuleExports(input) {
998
+ const { checker, sourceFile } = input;
999
+ const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
1000
+ if (moduleSymbol === void 0) return [];
1001
+ const exports = [];
1002
+ for (const exported of checker.getExportsOfModule(moduleSymbol)) {
1003
+ const symbol = (exported.flags & SymbolFlags.Alias) !== 0 ? checker.getAliasedSymbol(exported) : exported;
1004
+ if (checker.isUnknownSymbol(symbol)) continue;
1005
+ const type = checker.getTypeOfSymbol(symbol);
1006
+ if (type === void 0) continue;
1007
+ exports.push({
1008
+ exportName: exported.name,
1009
+ symbol,
1010
+ type
1011
+ });
1012
+ }
1013
+ return exports;
1014
+ }
1015
+ /** Prefer a declaration's own exported module so a re-export barrel does not enlarge a view's import closure. */
1016
+ function listRegistrationExports(input) {
1017
+ const exports = /* @__PURE__ */ new Map();
1018
+ for (const sourceFile of input.files) for (const moduleExport of listModuleExports({
1019
+ checker: input.checker,
1020
+ sourceFile
1021
+ })) {
1022
+ const own = moduleExport.symbol.declarations.some((declaration) => {
1023
+ return String(declaration.path) === String(sourceFile.path);
1024
+ });
1025
+ if (!exports.has(moduleExport.symbol.id) || own) exports.set(moduleExport.symbol.id, {
1026
+ sourceFile,
1027
+ moduleExport
1028
+ });
1029
+ }
1030
+ return [...exports.values()];
1031
+ }
1032
+ /** The phantom property that carries a definition's static types, by the unique symbol that keys it. */
1033
+ function findPhantomProperty(input) {
1034
+ const prefix = `__@${input.symbolName}@`;
1035
+ return input.checker.getPropertiesOfType(input.type).find((property) => String(property.escapedName).startsWith(prefix));
1036
+ }
1037
+ function readPropertyType(input) {
1038
+ const property = input.checker.getPropertyOfType(input.type, input.name);
1039
+ return property === void 0 ? void 0 : input.checker.getTypeOfSymbol(property);
1040
+ }
1041
+ function readLiteralProperty(input) {
1042
+ const propertyType = readPropertyType(input);
1043
+ if (propertyType === void 0) return Result.fail(`the declaration has no ${input.name} property`);
1044
+ if (!propertyType.isStringLiteralType()) return Result.fail(`${input.name} must have a literal type; its type is ${input.checker.typeToString(propertyType)}`);
1045
+ return Result.succeed(propertyType.value);
1046
+ }
1047
+ function matchesUndefinedType(type) {
1048
+ return type.isIntrinsicType() && type.intrinsicName === "undefined";
1049
+ }
1050
+ function inspectServerDeclarations(input) {
1051
+ const { checker, program, renderer } = input;
1052
+ const declarations = [];
1053
+ const candidates = [];
1054
+ for (const { sourceFile, moduleExport } of listRegistrationExports(input)) {
1055
+ const source = {
1056
+ filePath: sourceFile.fileName,
1057
+ exportName: moduleExport.exportName
1058
+ };
1059
+ const phantom = findPhantomProperty({
1060
+ checker,
1061
+ type: moduleExport.type,
1062
+ symbolName: "DEFINITION_TYPES"
1063
+ });
1064
+ if (phantom === void 0) continue;
1065
+ const tag = readPropertyType({
1066
+ checker,
1067
+ type: moduleExport.type,
1068
+ name: "_tag"
1069
+ });
1070
+ if (tag === void 0 || !tag.isStringLiteralType()) continue;
1071
+ if (tag.value !== "UiLoader" && tag.value !== "Endpoint") continue;
1072
+ const phantomType = checker.getTypeOfSymbol(phantom);
1073
+ const types = phantomType === void 0 ? void 0 : checker.getNonNullableType(phantomType);
1074
+ if (types === void 0) return Result.fail(makeIssue(source, "the definition carries no static types"));
1075
+ const pattern = readLiteralProperty({
1076
+ checker,
1077
+ type: moduleExport.type,
1078
+ name: "pattern"
1079
+ });
1080
+ if (Result.isFailure(pattern)) return Result.fail(makeIssue(source, pattern.failure));
1081
+ const method = readPropertyType({
1082
+ checker,
1083
+ type: moduleExport.type,
1084
+ name: "method"
1085
+ });
1086
+ candidates.push({
1087
+ source,
1088
+ kind: tag.value,
1089
+ type: moduleExport.type,
1090
+ types,
1091
+ key: JSON.stringify([
1092
+ tag.value,
1093
+ pattern.success,
1094
+ method?.isStringLiteralType() === true ? method.value : ""
1095
+ ])
1096
+ });
1097
+ }
1098
+ candidates.sort((left, right) => {
1099
+ return compareSourceText({
1100
+ left: left.key,
1101
+ right: right.key
1102
+ });
1103
+ });
1104
+ for (const { source, kind, type, types } of candidates) {
1105
+ const inspected = kind === "UiLoader" ? inspectUiLoader({
1106
+ checker,
1107
+ renderer,
1108
+ source,
1109
+ type,
1110
+ types
1111
+ }) : inspectEndpoint({
1112
+ checker,
1113
+ program,
1114
+ renderer,
1115
+ source,
1116
+ type,
1117
+ types
1118
+ });
1119
+ if (Result.isFailure(inspected)) return Result.fail(inspected.failure);
1120
+ declarations.push(inspected.success);
1121
+ }
1122
+ return Result.succeed(declarations);
1123
+ }
1124
+ /** An issue at a declaration; the message names the file and export so a report is complete on its own. */
1125
+ function makeIssue(source, message) {
1126
+ return {
1127
+ _tag: "InspectionIssue",
1128
+ ...source,
1129
+ message: `${source.filePath} (export ${source.exportName}): ${message}`
1130
+ };
1131
+ }
1132
+ function renderContract(input) {
1133
+ return Result.mapError(input.renderer.renderType({
1134
+ type: input.type,
1135
+ depth: 2
1136
+ }), (issue) => {
1137
+ return makeIssue(input.source, `the ${input.field} contract cannot cross the wire: ${issue.reason} (${issue.typeText})`);
1138
+ });
1139
+ }
1140
+ /** A contract's text, or `undefined` for the absent contract that the type `undefined` denotes. */
1141
+ function renderOptionalContract(input) {
1142
+ const type = readPropertyType({
1143
+ checker: input.checker,
1144
+ type: input.types,
1145
+ name: input.field
1146
+ });
1147
+ if (type === void 0 || matchesUndefinedType(type)) return Result.succeed(void 0);
1148
+ return renderContract({
1149
+ ...input,
1150
+ type
1151
+ });
1152
+ }
1153
+ function inspectUiLoader(input) {
1154
+ const { checker, renderer, source } = input;
1155
+ const pattern = readLiteralProperty({
1156
+ checker,
1157
+ type: input.type,
1158
+ name: "pattern"
1159
+ });
1160
+ if (Result.isFailure(pattern)) return Result.fail(makeIssue(source, pattern.failure));
1161
+ const loaderInput = renderOptionalContract({
1162
+ checker,
1163
+ renderer,
1164
+ source,
1165
+ field: "input",
1166
+ types: input.types
1167
+ });
1168
+ if (Result.isFailure(loaderInput)) return Result.fail(loaderInput.failure);
1169
+ const outputType = readPropertyType({
1170
+ checker,
1171
+ type: input.types,
1172
+ name: "output"
1173
+ });
1174
+ if (outputType === void 0) return Result.fail(makeIssue(source, "a UI loader must have an output contract"));
1175
+ const output = renderContract({
1176
+ renderer,
1177
+ source,
1178
+ field: "output",
1179
+ type: outputType
1180
+ });
1181
+ if (Result.isFailure(output)) return Result.fail(output.failure);
1182
+ return Result.succeed({
1183
+ ...source,
1184
+ kind: "UiLoader",
1185
+ pattern: pattern.success,
1186
+ input: loaderInput.success,
1187
+ output: output.success
1188
+ });
1189
+ }
1190
+ function inspectEndpoint(input) {
1191
+ const { checker, program, renderer, source } = input;
1192
+ const literals = {
1193
+ kind: "",
1194
+ method: "",
1195
+ pattern: ""
1196
+ };
1197
+ for (const name of [
1198
+ "kind",
1199
+ "method",
1200
+ "pattern"
1201
+ ]) {
1202
+ const literal = readLiteralProperty({
1203
+ checker,
1204
+ type: input.type,
1205
+ name
1206
+ });
1207
+ if (Result.isFailure(literal)) return Result.fail(makeIssue(source, literal.failure));
1208
+ literals[name] = literal.success;
1209
+ }
1210
+ const operation = literals.kind;
1211
+ if (operation === "stream") return Result.fail(makeIssue(source, "StreamEndpoint is an unimplemented declaration scaffold and cannot be built. Mount native Effect streaming handlers on the application's server."));
1212
+ if (operation !== "query" && operation !== "mutation") return Result.fail(makeIssue(source, `unknown endpoint kind ${operation}`));
1213
+ const method = literals.method;
1214
+ if (!matchesHttpMethod(method)) return Result.fail(makeIssue(source, `unknown endpoint method ${method}`));
1215
+ const inputType = readPropertyType({
1216
+ checker,
1217
+ type: input.types,
1218
+ name: "input"
1219
+ });
1220
+ const hasInput = inputType !== void 0 && !matchesUndefinedType(inputType);
1221
+ const inputTransport = !hasInput ? "none" : method === "GET" ? "search" : matchesFormInput({
1222
+ checker,
1223
+ program,
1224
+ types: input.types
1225
+ }) ? "formData" : "json";
1226
+ const endpointInput = inputType === void 0 || !hasInput || inputTransport === "formData" ? Result.succeed(void 0) : renderContract({
1227
+ renderer,
1228
+ source,
1229
+ field: "input",
1230
+ type: inputType
1231
+ });
1232
+ if (Result.isFailure(endpointInput)) return Result.fail(endpointInput.failure);
1233
+ const output = renderOptionalContract({
1234
+ checker,
1235
+ renderer,
1236
+ source,
1237
+ field: "output",
1238
+ types: input.types
1239
+ });
1240
+ if (Result.isFailure(output)) return Result.fail(output.failure);
1241
+ const event = renderOptionalContract({
1242
+ checker,
1243
+ renderer,
1244
+ source,
1245
+ field: "event",
1246
+ types: input.types
1247
+ });
1248
+ if (Result.isFailure(event)) return Result.fail(event.failure);
1249
+ const errors = inspectEndpointErrors({
1250
+ checker,
1251
+ renderer,
1252
+ source,
1253
+ types: input.types
1254
+ });
1255
+ if (Result.isFailure(errors)) return Result.fail(errors.failure);
1256
+ return Result.succeed({
1257
+ ...source,
1258
+ kind: "Endpoint",
1259
+ operation,
1260
+ method,
1261
+ pattern: literals.pattern,
1262
+ input: endpointInput.success,
1263
+ inputTransport,
1264
+ output: output.success,
1265
+ event: event.success,
1266
+ errors: errors.success
1267
+ });
1268
+ }
1269
+ /**
1270
+ * Whether an endpoint's input arrives as a form: its schema encodes to the
1271
+ * form-transport marker carried by Vorma's FormData schema and its derived
1272
+ * codecs. The runtime recognizes the same factory's schema annotation.
1273
+ */
1274
+ function matchesFormInput(input) {
1275
+ const symbol = readPropertyType({
1276
+ checker: input.checker,
1277
+ type: input.types,
1278
+ name: "inputEncoded"
1279
+ })?.getSymbol();
1280
+ return symbol !== void 0 && symbol.name === "FormDataEncoded" && symbol.declarations.some((declaration) => {
1281
+ return /\/vorma\/(?:src|dist)\/server\/form-data\.(?:d\.)?ts$/.test(String(declaration.path));
1282
+ }) && matchesLibrarySymbol({
1283
+ program: input.program,
1284
+ symbol
1285
+ });
1286
+ }
1287
+ /** Each declared error: its decoded `_tag` and transportable schema fields. */
1288
+ function inspectEndpointErrors(input) {
1289
+ const { checker, renderer, source } = input;
1290
+ const errorsType = readPropertyType({
1291
+ checker,
1292
+ type: input.types,
1293
+ name: "errors"
1294
+ });
1295
+ if (errorsType === void 0) return Result.succeed([]);
1296
+ const elementType = checker.getIndexInfosOfType(errorsType).find((index) => {
1297
+ return index.keyType.id === checker.getNumberType().id;
1298
+ })?.valueType;
1299
+ if (elementType === void 0) return Result.fail(makeIssue(source, "declared errors must have an array element type"));
1300
+ const errors = [];
1301
+ const variantsByTag = [];
1302
+ const elements = elementType.isUnionType() ? elementType.getTypes() : [elementType];
1303
+ for (const element of elements) {
1304
+ if (element.isIntrinsicType() && element.intrinsicName === "never") continue;
1305
+ const schemaType = readPropertyType({
1306
+ checker,
1307
+ type: element,
1308
+ name: "schema"
1309
+ });
1310
+ const decodedType = schemaType === void 0 ? void 0 : readPropertyType({
1311
+ checker,
1312
+ type: schemaType,
1313
+ name: "Type"
1314
+ });
1315
+ if (decodedType === void 0) return Result.fail(makeIssue(source, "each declared error's schema must expose its decoded Type"));
1316
+ const variants = decodedType.isUnionType() ? decodedType.getTypes() : [decodedType];
1317
+ for (const variant of variants) {
1318
+ const tag = readLiteralProperty({
1319
+ checker,
1320
+ type: variant,
1321
+ name: "_tag"
1322
+ });
1323
+ if (Result.isFailure(tag)) return Result.fail(makeIssue(source, `a declared error needs a literal _tag: ${tag.failure}`));
1324
+ variantsByTag.push({
1325
+ tag: tag.success,
1326
+ type: variant
1327
+ });
1328
+ }
1329
+ }
1330
+ variantsByTag.sort((left, right) => {
1331
+ return compareSourceText({
1332
+ left: left.tag,
1333
+ right: right.tag
1334
+ });
1335
+ });
1336
+ for (const variant of variantsByTag) {
1337
+ const fields = renderContract({
1338
+ renderer,
1339
+ source,
1340
+ field: `error ${variant.tag}`,
1341
+ type: variant.type
1342
+ });
1343
+ if (Result.isFailure(fields)) return Result.fail(fields.failure);
1344
+ errors.push({
1345
+ tag: variant.tag,
1346
+ fields: fields.success
1347
+ });
1348
+ }
1349
+ return Result.succeed(errors);
1350
+ }
1351
+ function inspectClientProject(input) {
1352
+ const { checker } = input;
1353
+ const views = [];
1354
+ const applications = /* @__PURE__ */ new Map();
1355
+ const publicUrlSites = [];
1356
+ const sourceTexts = /* @__PURE__ */ new Map();
1357
+ for (const { sourceFile, moduleExport } of listRegistrationExports(input)) {
1358
+ const source = {
1359
+ filePath: sourceFile.fileName,
1360
+ exportName: moduleExport.exportName
1361
+ };
1362
+ if (findPhantomProperty({
1363
+ checker,
1364
+ type: moduleExport.type,
1365
+ symbolName: "UI_VIEW_TYPES"
1366
+ }) !== void 0) {
1367
+ const pattern = readLiteralProperty({
1368
+ checker,
1369
+ type: moduleExport.type,
1370
+ name: "pattern"
1371
+ });
1372
+ if (Result.isFailure(pattern)) return Result.fail(makeIssue(source, pattern.failure));
1373
+ views.push({
1374
+ ...source,
1375
+ pattern: pattern.success
1376
+ });
1377
+ }
1378
+ }
1379
+ for (const sourceFile of input.files) {
1380
+ if (sourceFile.fileName === input.clientEntryFilePath) {
1381
+ for (const moduleExport of listModuleExports({
1382
+ checker,
1383
+ sourceFile
1384
+ })) if (findPhantomProperty({
1385
+ checker,
1386
+ type: moduleExport.type,
1387
+ symbolName: "CLIENT_APPLICATION_TYPES"
1388
+ }) !== void 0) applications.set(moduleExport.symbol.id, {
1389
+ filePath: sourceFile.fileName,
1390
+ exportName: moduleExport.exportName
1391
+ });
1392
+ }
1393
+ const sites = collectPublicUrlSites({
1394
+ checker,
1395
+ sourceFile
1396
+ });
1397
+ if (Result.isFailure(sites)) return Result.fail(sites.failure);
1398
+ if (sites.success.length > 0) {
1399
+ publicUrlSites.push(...sites.success);
1400
+ sourceTexts.set(sourceFile.fileName, sourceFile.text);
1401
+ }
1402
+ }
1403
+ const [application, ...moreApplications] = applications.values();
1404
+ if (application === void 0 || moreApplications.length > 0) return Result.fail({
1405
+ _tag: "InspectionIssue",
1406
+ filePath: input.clientEntryFilePath,
1407
+ message: `${input.clientEntryFilePath}: the client entry must export exactly one ClientApplication.make value, which the generated client declaration reads for the application's service types`
1408
+ });
1409
+ return Result.succeed({
1410
+ views,
1411
+ application,
1412
+ publicUrlSites,
1413
+ sourceTexts
1414
+ });
1415
+ }
1416
+ /** Whether a symbol is the `getPublicUrl` declared by `vorma/client`, however the module reached it. */
1417
+ function matchesPublicUrlSymbol(input) {
1418
+ const symbol = (input.symbol.flags & SymbolFlags.Alias) !== 0 ? input.checker.getAliasedSymbol(input.symbol) : input.symbol;
1419
+ if (symbol.name !== "getPublicUrl") return false;
1420
+ const declaration = symbol.declarations[0];
1421
+ return declaration !== void 0 && String(declaration.path).endsWith("/vorma/dist/client/index.d.ts");
1422
+ }
1423
+ /** Every call of `getPublicUrl` in one file. A use that is not a direct call with a string literal cannot be replaced by the build. */
1424
+ function collectPublicUrlSites(input) {
1425
+ const { checker, sourceFile } = input;
1426
+ const sites = [];
1427
+ let issue;
1428
+ function report(node, message) {
1429
+ if (issue === void 0) {
1430
+ const position = node.getStart(sourceFile);
1431
+ const { line, character } = sourceFile.getLineAndCharacterOfPosition(position);
1432
+ issue = {
1433
+ _tag: "InspectionIssue",
1434
+ filePath: sourceFile.fileName,
1435
+ position,
1436
+ message: `${sourceFile.fileName}:${line + 1}:${character + 1}: ${message}`
1437
+ };
1438
+ }
1439
+ }
1440
+ function visit(node) {
1441
+ if (issue !== void 0 || isTypeNode(node)) return;
1442
+ if (isIdentifier(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node)) {
1443
+ const parent = node.parent;
1444
+ if (isImportSpecifier(parent) || isImportClause(parent) || isNamespaceImport(parent) || isExportSpecifier(parent)) return;
1445
+ const symbol = isElementAccessExpression(node) ? getElementAccessSymbol({
1446
+ checker,
1447
+ node
1448
+ }) : checker.getSymbolAtLocation(node);
1449
+ if (symbol !== void 0 && matchesPublicUrlSymbol({
1450
+ checker,
1451
+ symbol
1452
+ })) {
1453
+ let callee = node;
1454
+ while (matchesTransparentExpression(callee.parent) && callee.parent.expression === callee) callee = callee.parent;
1455
+ const call = callee.parent;
1456
+ if (!isCallExpression(call) || call.expression !== callee) {
1457
+ report(node, "getPublicUrl must be called directly with a string literal so the build can replace the call");
1458
+ return;
1459
+ }
1460
+ const [authoredArgument, ...moreArguments] = call.arguments;
1461
+ const argument = authoredArgument === void 0 ? void 0 : unwrapExpression(authoredArgument);
1462
+ if (argument === void 0 || moreArguments.length > 0 || !(isStringLiteral(argument) || isNoSubstitutionTemplateLiteral(argument))) {
1463
+ report(call, "getPublicUrl requires exactly one string literal argument, so the build can replace the call with the hashed URL");
1464
+ return;
1465
+ }
1466
+ sites.push({
1467
+ filePath: sourceFile.fileName,
1468
+ start: call.getStart(sourceFile),
1469
+ end: call.getEnd(),
1470
+ key: argument.text
1471
+ });
1472
+ return;
1473
+ }
1474
+ }
1475
+ node.forEachChild(visit);
1476
+ }
1477
+ visit(sourceFile);
1478
+ return issue === void 0 ? Result.succeed(sites) : Result.fail(issue);
1479
+ }
1480
+ /** Parentheses and erased TypeScript wrappers preserve the underlying expression. */
1481
+ function matchesTransparentExpression(node) {
1482
+ return isParenthesizedExpression(node) || isAsExpression(node) || isTypeAssertion(node) || isSatisfiesExpression(node) || isNonNullExpression(node);
1483
+ }
1484
+ function unwrapExpression(node) {
1485
+ while (matchesTransparentExpression(node)) node = node.expression;
1486
+ return node;
1487
+ }
1488
+ function getElementAccessSymbol(input) {
1489
+ const objectType = input.checker.getTypeAtLocation(input.node.expression);
1490
+ const keyType = input.checker.getTypeAtLocation(input.node.argumentExpression);
1491
+ return objectType !== void 0 && keyType?.isStringLiteralType() ? input.checker.getPropertyOfType(objectType, keyType.value) : void 0;
1492
+ }
1493
+ //#endregion
1494
+ //#region src/build/typescript-projects.ts
1495
+ /** Reads native project membership without loading programs or declaration types. */
1496
+ function readTypescriptProjectRoots(input) {
1497
+ return withTypescriptApi({
1498
+ configDirectory: input.configDirectory,
1499
+ useFn: (api) => {
1500
+ return openCompilerResource(() => {
1501
+ return {
1502
+ server: api.parseConfigFile(input.serverTsconfigFilePath).fileNames,
1503
+ client: api.parseConfigFile(input.clientTsconfigFilePath).fileNames
1504
+ };
1505
+ });
1506
+ }
1507
+ });
1508
+ }
1509
+ /** Checks actual compiler membership after generation, including restrictive files/include/exclude settings. */
1510
+ function checkGeneratedTypeInclusion(input) {
1511
+ return withTypescriptProjects({
1512
+ ...input,
1513
+ useFn: (projects) => {
1514
+ const missing = [...input.serverFiles.filter((file) => {
1515
+ return projects.server.program.getSourceFileMetadata(file) === void 0;
1516
+ }).map((file) => {
1517
+ return {
1518
+ project: input.serverTsconfigFilePath,
1519
+ file
1520
+ };
1521
+ }), ...input.clientFiles.filter((file) => {
1522
+ return projects.client.program.getSourceFileMetadata(file) === void 0;
1523
+ }).map((file) => {
1524
+ return {
1525
+ project: input.clientTsconfigFilePath,
1526
+ file
1527
+ };
1528
+ })];
1529
+ return missing.length === 0 ? Effect.void : Effect.fail({
1530
+ _tag: "TypescriptProjectError",
1531
+ message: missing.map(({ project, file }) => `${project} does not include generated type file ${file}. Include the entry's _vorma directory in that TypeScript project.`).join("\n")
1532
+ });
1533
+ }
1534
+ });
1535
+ }
1536
+ /** Opens the two configured TypeScript projects for the duration of `useFn`, then closes the checker session. */
1537
+ function withTypescriptProjects(input) {
1538
+ return withTypescriptApi({
1539
+ ...input,
1540
+ useFn: (api) => {
1541
+ return Effect.scoped(Effect.gen(function* () {
1542
+ const projects = findProjects({
1543
+ snapshot: yield* Effect.acquireRelease(openCompilerResource(() => {
1544
+ return api.updateSnapshot({ openProjects: [input.serverTsconfigFilePath, input.clientTsconfigFilePath] });
1545
+ }), (snapshot) => Effect.sync(() => snapshot.dispose())),
1546
+ ...input
1547
+ });
1548
+ return yield* Result.isFailure(projects) ? Effect.fail(projects.failure) : input.useFn(projects.success);
1549
+ }));
1550
+ }
1551
+ });
1552
+ }
1553
+ function openCompilerResource(fn) {
1554
+ return Effect.try({
1555
+ try: fn,
1556
+ catch: (cause) => {
1557
+ return {
1558
+ _tag: "TypescriptProjectError",
1559
+ message: "Vorma could not read the application's TypeScript projects",
1560
+ cause
1561
+ };
1562
+ }
1563
+ });
1564
+ }
1565
+ function withTypescriptApi(input) {
1566
+ return Effect.scoped(Effect.gen(function* () {
1567
+ const api = yield* Effect.acquireRelease(openCompilerResource(() => {
1568
+ return new API({
1569
+ cwd: input.configDirectory,
1570
+ ...input.fileSystem === void 0 ? {} : { fs: input.fileSystem }
1571
+ });
1572
+ }), (api) => Effect.sync(() => api.close()));
1573
+ return yield* input.useFn(api);
1574
+ }));
1575
+ }
1576
+ function findProjects(input) {
1577
+ const server = input.snapshot.getProject(input.serverTsconfigFilePath);
1578
+ const client = input.snapshot.getProject(input.clientTsconfigFilePath);
1579
+ if (server === void 0 || client === void 0) return Result.fail({
1580
+ _tag: "TypescriptProjectError",
1581
+ message: `TypeScript did not load ${server === void 0 ? input.serverTsconfigFilePath : input.clientTsconfigFilePath}`
1582
+ });
1583
+ return Result.succeed({
1584
+ server,
1585
+ client
1586
+ });
1587
+ }
1588
+ //#endregion
1589
+ //#region src/build/compile-application.ts
1590
+ function compareSources(left, right) {
1591
+ const byPath = compareSourceText({
1592
+ left: left.filePath,
1593
+ right: right.filePath
1594
+ });
1595
+ return byPath === 0 ? compareSourceText({
1596
+ left: left.exportName,
1597
+ right: right.exportName
1598
+ }) : byPath;
1599
+ }
1600
+ function getSegmentSources(segment) {
1601
+ return [...segment.loader === void 0 ? [] : [segment.loader], ...segment.view === void 0 ? [] : [segment.view]];
1602
+ }
1603
+ /** Joins the inspected declarations and views into one validated model. */
1604
+ function compileApplicationModel(inspection) {
1605
+ const issues = [];
1606
+ const serverDeclarations = [...inspection.serverDeclarations].sort(compareSources);
1607
+ const clientViews = [...inspection.clientViews].sort(compareSources);
1608
+ const uiLoaders = serverDeclarations.filter((declaration) => {
1609
+ return declaration.kind === "UiLoader";
1610
+ });
1611
+ const endpoints = serverDeclarations.filter((declaration) => {
1612
+ return declaration.kind === "Endpoint";
1613
+ });
1614
+ const segments = /* @__PURE__ */ new Map();
1615
+ const loaderSources = /* @__PURE__ */ new Map();
1616
+ for (const loader of uiLoaders) {
1617
+ const registered = compileUiPattern(loader.pattern);
1618
+ if (Result.isFailure(registered)) {
1619
+ issues.push({
1620
+ _tag: "InvalidPattern",
1621
+ role: "uiLoader",
1622
+ pattern: loader.pattern,
1623
+ source: loader,
1624
+ cause: registered.failure
1625
+ });
1626
+ continue;
1627
+ }
1628
+ const key = registered.success.pattern;
1629
+ const sources = loaderSources.get(key);
1630
+ if (sources === void 0) {
1631
+ loaderSources.set(key, [loader]);
1632
+ segments.set(key, {
1633
+ pattern: loader.pattern,
1634
+ registered: registered.success,
1635
+ loader,
1636
+ view: void 0
1637
+ });
1638
+ } else sources.push(loader);
1639
+ }
1640
+ const viewSources = /* @__PURE__ */ new Map();
1641
+ for (const view of clientViews) {
1642
+ const registered = compileUiPattern(view.pattern);
1643
+ if (Result.isFailure(registered)) {
1644
+ issues.push({
1645
+ _tag: "InvalidPattern",
1646
+ role: "clientView",
1647
+ pattern: view.pattern,
1648
+ source: view,
1649
+ cause: registered.failure
1650
+ });
1651
+ continue;
1652
+ }
1653
+ const key = registered.success.pattern;
1654
+ const sources = viewSources.get(key);
1655
+ if (sources !== void 0) {
1656
+ sources.push(view);
1657
+ continue;
1658
+ }
1659
+ viewSources.set(key, [view]);
1660
+ const existing = segments.get(key);
1661
+ segments.set(key, {
1662
+ pattern: existing === void 0 ? view.pattern : existing.pattern,
1663
+ registered: registered.success,
1664
+ loader: existing === void 0 ? void 0 : existing.loader,
1665
+ view
1666
+ });
1667
+ }
1668
+ const endpointSources = /* @__PURE__ */ new Map();
1669
+ for (const endpoint of endpoints) {
1670
+ const registered = compileEndpointPattern(endpoint.pattern);
1671
+ if (Result.isFailure(registered)) {
1672
+ issues.push({
1673
+ _tag: "InvalidPattern",
1674
+ role: "endpoint",
1675
+ pattern: endpoint.pattern,
1676
+ source: endpoint,
1677
+ cause: registered.failure
1678
+ });
1679
+ continue;
1680
+ }
1681
+ const key = `${endpoint.method} ${registered.success.pattern}`;
1682
+ const sources = endpointSources.get(key);
1683
+ if (sources === void 0) endpointSources.set(key, [endpoint]);
1684
+ else sources.push(endpoint);
1685
+ }
1686
+ const orderedSegments = [...segments.values()].sort((left, right) => {
1687
+ return compareSourceText({
1688
+ left: left.pattern,
1689
+ right: right.pattern
1690
+ });
1691
+ });
1692
+ for (const conflict of findConflictingDynamicParameterNames({
1693
+ patterns: orderedSegments,
1694
+ getMatcherPattern: (segment) => segment.registered.pattern,
1695
+ getSegments: (segment) => segment.registered.segments,
1696
+ makeCause: ({ conflict, attemptedPattern }) => {
1697
+ return {
1698
+ _tag: "ConflictingDynamicParameterName",
1699
+ ...conflict,
1700
+ pattern: attemptedPattern.pattern
1701
+ };
1702
+ }
1703
+ })) issues.push({
1704
+ _tag: "ConflictingUiPatternParameters",
1705
+ patterns: conflict.patterns.map((segment) => segment.pattern),
1706
+ sources: conflict.patterns.flatMap(getSegmentSources).sort(compareSources),
1707
+ cause: conflict.cause
1708
+ });
1709
+ if (issues.length > 0) return Result.fail(issues);
1710
+ const linkedSegments = createNestedRouteChains({ nodes: orderedSegments }).map(({ target, chain }) => {
1711
+ return {
1712
+ ...target,
1713
+ searchOwnerInputs: chain.flatMap((node) => {
1714
+ return node.loader === void 0 || node.loader.input === void 0 ? [] : [node.loader.input];
1715
+ })
1716
+ };
1717
+ });
1718
+ return Result.succeed({
1719
+ serverDeclarations,
1720
+ uiLoaders: [...loaderSources.values()].flatMap((sources) => sources.slice(0, 1)),
1721
+ endpoints: [...endpointSources.values()].flatMap((sources) => sources.slice(0, 1)),
1722
+ clientViews: [...viewSources.values()].flatMap((sources) => sources.slice(0, 1)),
1723
+ clientViewCandidates: new Map([...viewSources.values()].flatMap((sources) => {
1724
+ const first = sources[0];
1725
+ return first === void 0 ? [] : [[first.pattern, sources]];
1726
+ })),
1727
+ clientViewModules: [...new Set(clientViews.map((view) => view.filePath))].sort((left, right) => compareSourceText({
1728
+ left,
1729
+ right
1730
+ })),
1731
+ clientApplication: inspection.clientApplication,
1732
+ segments: linkedSegments
1733
+ });
1734
+ }
1735
+ //#endregion
1736
+ //#region src/build/output-files.ts
1737
+ /** Atomically replaces generated source that TypeScript and build tools can read. */
1738
+ function writeOutputFile(input) {
1739
+ return Effect.gen(function* () {
1740
+ const fileSystem = yield* FileSystem$1.FileSystem;
1741
+ const path = yield* Path$1.Path;
1742
+ const directory = path.dirname(input.filePath);
1743
+ yield* fileSystem.makeDirectory(directory, { recursive: true });
1744
+ const bytes = typeof input.source === "string" ? new TextEncoder().encode(input.source) : input.source;
1745
+ yield* Effect.acquireUseRelease(fileSystem.makeTempDirectory({
1746
+ directory,
1747
+ prefix: "_vorma-write-"
1748
+ }), (tempDirectory) => {
1749
+ return Effect.gen(function* () {
1750
+ const temporary = path.join(tempDirectory, "output");
1751
+ yield* fileSystem.writeFile(temporary, bytes);
1752
+ yield* fileSystem.rename(temporary, input.filePath);
1753
+ });
1754
+ }, (tempDirectory) => {
1755
+ return fileSystem.remove(tempDirectory, {
1756
+ force: true,
1757
+ recursive: true
1758
+ }).pipe(Effect.catch((cause) => {
1759
+ return Effect.logWarning("Could not remove the temporary output directory " + tempDirectory, cause);
1760
+ }));
1761
+ });
1762
+ }).pipe(Effect.uninterruptible);
1763
+ }
1764
+ //#endregion
1765
+ //#region src/build/public-assets.ts
1766
+ /** Hashes and publishes each public file's exact bytes, retaining only metadata and erased key types. */
1767
+ function collectPublicAssets(config) {
1768
+ return Effect.gen(function* () {
1769
+ const fileSystem = yield* FileSystem$1.FileSystem;
1770
+ const path = yield* Path$1.Path;
1771
+ const root = config.publicAssetsSourceDirectory;
1772
+ const pending = [{
1773
+ directory: root,
1774
+ ancestors: /* @__PURE__ */ new Set([root])
1775
+ }];
1776
+ const files = [];
1777
+ while (pending.length > 0) {
1778
+ const directories = pending.splice(0);
1779
+ const inspections = yield* Effect.forEach(directories, ({ directory, ancestors }) => {
1780
+ return Effect.gen(function* () {
1781
+ const names = yield* atAssetPath({
1782
+ sourcePath: directory,
1783
+ message: "Cannot list public assets",
1784
+ effect: fileSystem.readDirectory(directory)
1785
+ });
1786
+ return {
1787
+ entries: yield* Effect.forEach(names, (name) => {
1788
+ const sourcePath = path.join(directory, name);
1789
+ return atAssetPath({
1790
+ sourcePath,
1791
+ message: "Cannot inspect public asset",
1792
+ effect: Effect.gen(function* () {
1793
+ const canonicalPath = yield* fileSystem.realPath(sourcePath);
1794
+ const stat = yield* fileSystem.stat(canonicalPath);
1795
+ return {
1796
+ sourcePath,
1797
+ canonicalPath,
1798
+ type: stat.type
1799
+ };
1800
+ })
1801
+ });
1802
+ }, { concurrency: "unbounded" }),
1803
+ ancestors
1804
+ };
1805
+ });
1806
+ }, { concurrency: "unbounded" });
1807
+ for (const { entries, ancestors } of inspections) for (const entry of entries) if (entry.type === "File") files.push(entry);
1808
+ else if (entry.type === "Directory") {
1809
+ if (ancestors.has(entry.canonicalPath)) return yield* Effect.fail({
1810
+ _tag: "PublicAssetIssue",
1811
+ sourcePath: entry.sourcePath,
1812
+ message: "Public asset directories must not contain symbolic-link cycles"
1813
+ });
1814
+ pending.push({
1815
+ directory: entry.sourcePath,
1816
+ ancestors: /* @__PURE__ */ new Set([...ancestors, entry.canonicalPath])
1817
+ });
1818
+ } else return yield* Effect.fail({
1819
+ _tag: "PublicAssetIssue",
1820
+ sourcePath: entry.sourcePath,
1821
+ message: `Public assets must be regular files or directories; found ${entry.type}`
1822
+ });
1823
+ }
1824
+ files.sort((left, right) => {
1825
+ return compareSourceText({
1826
+ left: left.sourcePath,
1827
+ right: right.sourcePath
1828
+ });
1829
+ });
1830
+ const assets = yield* Effect.forEach(files, ({ sourcePath, canonicalPath }) => {
1831
+ return atAssetPath({
1832
+ sourcePath,
1833
+ message: "Cannot publish public asset",
1834
+ effect: Effect.gen(function* () {
1835
+ const source = yield* fileSystem.readFile(canonicalPath);
1836
+ const digest = blake3(source);
1837
+ const sourceKey = toPosixRelativePath({
1838
+ path,
1839
+ from: root,
1840
+ to: sourcePath
1841
+ });
1842
+ const pathBytes = new TextEncoder().encode(sourceKey);
1843
+ const identity = new Uint8Array(pathBytes.length + 1 + 32);
1844
+ identity.set(pathBytes);
1845
+ identity.set(digest, pathBytes.length + 1);
1846
+ const suffix = toContentDigest(identity).slice(0, 12);
1847
+ const basename = path.basename(sourceKey);
1848
+ const dot = basename.lastIndexOf(".");
1849
+ const extension = dot < 0 ? "" : basename.slice(dot);
1850
+ const stem = sourceKey.slice(0, sourceKey.length - extension.length).replaceAll("/", "_");
1851
+ const fileName = `${ARTIFACT_PROTOCOL.publicOutputFileNamePrefix}${stem}_${suffix}${extension}`.replaceAll(/[<>:"/\\|?*\u0000-\u001f]/g, "_").replace(/[. ]+$/, (tail) => "_".repeat(tail.length));
1852
+ const url = `${config.publicAssetsBasePath}${encodeURIComponent(fileName)}`;
1853
+ yield* fileSystem.writeFile(path.join(config.publicDirectory, fileName), source);
1854
+ return {
1855
+ sourceKey,
1856
+ sourcePath,
1857
+ digest: encodeContentDigest(digest),
1858
+ url,
1859
+ fileName
1860
+ };
1861
+ })
1862
+ });
1863
+ }, { concurrency: 16 });
1864
+ return {
1865
+ assets,
1866
+ publicFilemap: Object.fromEntries(assets.map((asset) => [asset.sourceKey, asset.url])),
1867
+ publicFilepaths: [...new Set(assets.map((asset) => asset.url))],
1868
+ types: {
1869
+ filePath: path.join(config.clientGeneratedDirectory, ARTIFACT_PROTOCOL.generatedAssetKeysFileName),
1870
+ source: [
1871
+ "// Code generated by Vorma. DO NOT EDIT.",
1872
+ "",
1873
+ "import \"vorma/client\";",
1874
+ "",
1875
+ renderAssetKeyDeclaration({
1876
+ assets,
1877
+ depth: 0,
1878
+ prefix: "export type PublicAssetKey ="
1879
+ }),
1880
+ "",
1881
+ "declare module \"vorma/client\" {",
1882
+ " interface ClientApplicationContracts {",
1883
+ " readonly assets: PublicAssetKey;",
1884
+ " }",
1885
+ "}",
1886
+ ""
1887
+ ].join("\n")
1888
+ }
1889
+ };
1890
+ });
1891
+ }
1892
+ function renderAssetKeyDeclaration(input) {
1893
+ const [only, ...more] = input.assets;
1894
+ if (only === void 0) return `${input.prefix} never;`;
1895
+ if (more.length === 0) return `${input.prefix} ${JSON.stringify(only.sourceKey)};`;
1896
+ return input.prefix + "\n" + input.assets.map((asset) => `${" ".repeat(input.depth + 1)}| ${JSON.stringify(asset.sourceKey)}`).join("\n") + ";";
1897
+ }
1898
+ function atAssetPath(input) {
1899
+ return input.effect.pipe(Effect.mapError((cause) => {
1900
+ return {
1901
+ _tag: "PublicAssetIssue",
1902
+ sourcePath: input.sourcePath,
1903
+ message: input.message,
1904
+ cause
1905
+ };
1906
+ }));
1907
+ }
1908
+ //#endregion
1909
+ //#region src/build/render-server-generated.ts
1910
+ /**
1911
+ * The URL of the published public directory relative to a compiled server
1912
+ * module. Every compiled module sits directly in the server entry's Vorma
1913
+ * directory, so one relative URL locates the bytes from all of them.
1914
+ */
1915
+ function getPublicDirectoryRelativeUrl(input) {
1916
+ return `${input.path.relative(input.serverGeneratedDirectory, input.publicDirectory).split(input.path.sep).map((segment) => encodeURIComponent(segment)).join("/")}/`;
1917
+ }
1918
+ /** One namespace import per source file that exports a registered declaration, in a stable order. */
1919
+ function createModuleNamespaces(input) {
1920
+ const identifiers = /* @__PURE__ */ new Map();
1921
+ const filePaths = [...new Set(input.declarations.map((declaration) => declaration.filePath))].sort((left, right) => compareSourceText({
1922
+ left,
1923
+ right
1924
+ }));
1925
+ for (const filePath of filePaths) identifiers.set(filePath, `serverModule${identifiers.size}`);
1926
+ return {
1927
+ imports: filePaths.map((filePath) => {
1928
+ return {
1929
+ identifier: identifiers.get(filePath) ?? "",
1930
+ specifier: getModuleSpecifier({
1931
+ path: input.path,
1932
+ fromFilePath: input.filePath,
1933
+ toFilePath: filePath
1934
+ })
1935
+ };
1936
+ }),
1937
+ getIdentifier: (filePath) => identifiers.get(filePath) ?? ""
1938
+ };
1939
+ }
1940
+ /** Renders registration imports independently of final browser output. */
1941
+ function renderGeneratedBuildModule(input) {
1942
+ const modules = createModuleNamespaces({
1943
+ declarations: input.declarations,
1944
+ filePath: input.filePath,
1945
+ path: input.path
1946
+ });
1947
+ return {
1948
+ filePath: input.filePath,
1949
+ source: [
1950
+ "// Code generated by Vorma. DO NOT EDIT.",
1951
+ "",
1952
+ ...modules.imports.map((moduleImport) => {
1953
+ return `import * as ${moduleImport.identifier} from ${JSON.stringify(moduleImport.specifier)};`;
1954
+ }),
1955
+ "",
1956
+ "/** Every registerable declaration that the server project exports, in source order. */",
1957
+ ...input.declarations.length === 0 ? ["export const registeredDeclarations = [] as const;"] : [
1958
+ "export const registeredDeclarations = [",
1959
+ ...input.declarations.map((declaration) => {
1960
+ return `\t${modules.getIdentifier(declaration.filePath)}[${JSON.stringify(declaration.exportName)}],`;
1961
+ }),
1962
+ "] as const;"
1963
+ ],
1964
+ "",
1965
+ "/** Resolves public files relative to this compiled deployment. */",
1966
+ "export const publicAssetDirectoryUrl = new URL(",
1967
+ `\t${JSON.stringify(input.publicDirectoryRelativeUrl)},`,
1968
+ " import.meta.url,",
1969
+ ").href;",
1970
+ ""
1971
+ ].join("\n")
1972
+ };
1973
+ }
1974
+ /** Renders the declaration that augments `vorma/server` with this application's patterns, search types, and asset keys. */
1975
+ function renderGeneratedServerContracts(input) {
1976
+ const serverModuleSpecifier = JSON.stringify(FRAMEWORK_IDENTITY_PROTOCOL.moduleSpecifiers.server);
1977
+ const routeSearchTypes = input.model.segments.map((segment) => {
1978
+ const search = segment.searchOwnerInputs.length === 0 ? "undefined" : segment.searchOwnerInputs.map((text) => indentRenderedText(text, 1)).join(" &\n ");
1979
+ return `\t\t\treadonly ${JSON.stringify(segment.pattern)}: ${search};`;
1980
+ });
1981
+ const modules = createModuleNamespaces({
1982
+ declarations: input.model.serverDeclarations,
1983
+ filePath: input.filePath,
1984
+ path: input.path
1985
+ });
1986
+ return {
1987
+ filePath: input.filePath,
1988
+ source: [
1989
+ "// Code generated by Vorma. DO NOT EDIT.",
1990
+ "",
1991
+ `import ${serverModuleSpecifier};`,
1992
+ ...modules.imports.map((moduleImport) => {
1993
+ return `import type * as ${moduleImport.identifier} from ${JSON.stringify(moduleImport.specifier)};`;
1994
+ }),
1995
+ ...renderEffectModuleImports(input.effectModules),
1996
+ "",
1997
+ ...input.typeAliases.length === 0 ? [] : [...input.typeAliases, ""],
1998
+ `declare module ${serverModuleSpecifier} {`,
1999
+ " interface Register {",
2000
+ " readonly declarations: readonly [",
2001
+ ...input.model.serverDeclarations.map((declaration) => {
2002
+ return `\t\t\t(typeof ${modules.getIdentifier(declaration.filePath)})[${JSON.stringify(declaration.exportName)}],`;
2003
+ }),
2004
+ " ];",
2005
+ ...renderPatternRegistration(input.model.segments.map((segment) => segment.pattern)),
2006
+ " readonly routeSearch: {",
2007
+ ...routeSearchTypes,
2008
+ " };",
2009
+ renderAssetKeyDeclaration({
2010
+ assets: input.publicAssets.assets,
2011
+ depth: 2,
2012
+ prefix: " readonly assets:"
2013
+ }),
2014
+ " }",
2015
+ "}",
2016
+ ""
2017
+ ].join("\n")
2018
+ };
2019
+ }
2020
+ /** One type-only namespace import per Effect module that rendered contract types name. */
2021
+ function renderEffectModuleImports(effectModules) {
2022
+ return effectModules.map((moduleName) => {
2023
+ return `import type * as ${moduleName} from ${JSON.stringify(`effect/${moduleName}`)};`;
2024
+ });
2025
+ }
2026
+ function renderPatternRegistration(patterns) {
2027
+ if (patterns.length === 0) return [" readonly patterns: never;"];
2028
+ return [" readonly patterns:", ...patterns.map((pattern, index) => {
2029
+ const terminator = index === patterns.length - 1 ? ";" : "";
2030
+ return `\t\t\t| ${JSON.stringify(pattern)}${terminator}`;
2031
+ })];
2032
+ }
2033
+ //#endregion
2034
+ //#region src/build/render-client-contracts.ts
2035
+ function renderGeneratedClientContracts(input) {
2036
+ function toSpecifier(filePath) {
2037
+ return JSON.stringify(getModuleSpecifier({
2038
+ path: input.path,
2039
+ fromFilePath: input.filePath,
2040
+ toFilePath: filePath
2041
+ }));
2042
+ }
2043
+ const viewModuleIdentifiers = new Map(input.model.clientViewModules.map((filePath, index) => {
2044
+ return [filePath, `ClientViewModule${index}`];
2045
+ }));
2046
+ const uiTypes = input.model.segments.map((segment) => {
2047
+ const moduleIdentifier = segment.view === void 0 ? void 0 : viewModuleIdentifiers.get(segment.view.filePath);
2048
+ return renderTypeEntry(segment.pattern, {
2049
+ input: segment.loader?.input ?? "undefined",
2050
+ output: segment.loader?.output ?? "undefined",
2051
+ search: segment.searchOwnerInputs.length === 0 ? "undefined" : segment.searchOwnerInputs.join(" &\n "),
2052
+ clientLoader: segment.view === void 0 || moduleIdentifier === void 0 ? "undefined" : `GetUiViewClientLoaderContract<(typeof ${moduleIdentifier})[${JSON.stringify(segment.view.exportName)}]>`
2053
+ });
2054
+ });
2055
+ const endpointTypes = input.model.endpoints.map((endpoint) => {
2056
+ return renderTypeEntry(`${endpoint.method} ${endpoint.pattern}`, {
2057
+ kind: JSON.stringify(endpoint.operation),
2058
+ input: endpoint.inputTransport === "formData" ? "FormData" : endpoint.input ?? "undefined",
2059
+ output: endpoint.output ?? "undefined",
2060
+ event: endpoint.event ?? "undefined",
2061
+ errors: renderErrorTypes(endpoint.errors)
2062
+ });
2063
+ });
2064
+ const application = input.model.clientApplication;
2065
+ const entryImport = application.exportName === "default" ? `import type ClientEntry from ${toSpecifier(application.filePath)};` : `import type { ${application.exportName} as ClientEntry } from ${toSpecifier(application.filePath)};`;
2066
+ return {
2067
+ filePath: input.filePath,
2068
+ source: [
2069
+ "// Code generated by Vorma. DO NOT EDIT.",
2070
+ "",
2071
+ "import type {",
2072
+ " GetClientApplicationServiceContracts,",
2073
+ ...input.model.clientViewModules.length === 0 ? [] : [" GetUiViewClientLoaderContract,"],
2074
+ `} from ${JSON.stringify(FRAMEWORK_IDENTITY_PROTOCOL.moduleSpecifiers.clientReact)};`,
2075
+ entryImport,
2076
+ ...input.model.clientViewModules.map((filePath) => {
2077
+ return `import type * as ${viewModuleIdentifiers.get(filePath)} from ${toSpecifier(filePath)};`;
2078
+ }),
2079
+ ...renderEffectModuleImports(input.inspection.effectModules),
2080
+ "",
2081
+ "type ApplicationServiceContracts =",
2082
+ " GetClientApplicationServiceContracts<typeof ClientEntry>;",
2083
+ "",
2084
+ ...input.inspection.typeAliases.length === 0 ? [] : [...input.inspection.typeAliases, ""],
2085
+ "export type UiContracts = {",
2086
+ ...uiTypes,
2087
+ "};",
2088
+ "",
2089
+ "export type EndpointContracts = {",
2090
+ ...endpointTypes,
2091
+ "};",
2092
+ "",
2093
+ ...renderAugmentation(FRAMEWORK_IDENTITY_PROTOCOL.moduleSpecifiers.client),
2094
+ ""
2095
+ ].join("\n")
2096
+ };
2097
+ }
2098
+ function renderAugmentation(moduleSpecifier) {
2099
+ return [
2100
+ `declare module ${JSON.stringify(moduleSpecifier)} {`,
2101
+ " interface ClientApplicationContracts {",
2102
+ " readonly ui: UiContracts;",
2103
+ " readonly endpoints: EndpointContracts;",
2104
+ " readonly clientOnlyServices: ApplicationServiceContracts[\"clientOnlyServices\"];",
2105
+ " readonly universalServices: ApplicationServiceContracts[\"universalServices\"];",
2106
+ " }",
2107
+ "}"
2108
+ ];
2109
+ }
2110
+ /** One keyed entry of a contract map, its properties one per line. */
2111
+ function renderTypeEntry(key, properties) {
2112
+ return [
2113
+ `\treadonly ${JSON.stringify(key)}: {`,
2114
+ ...Object.entries(properties).map(([name, type]) => {
2115
+ return `\t\treadonly ${name}: ${type};`;
2116
+ }),
2117
+ " };"
2118
+ ].join("\n");
2119
+ }
2120
+ /** An operation's declared errors, keyed by their literal tags. */
2121
+ function renderErrorTypes(errors) {
2122
+ if (errors.length === 0) return "{}";
2123
+ return [
2124
+ "{",
2125
+ ...errors.map((error) => {
2126
+ return `\t\t\treadonly ${JSON.stringify(error.tag)}: ${indentRenderedText(error.fields, 1)};`;
2127
+ }),
2128
+ " }"
2129
+ ].join("\n");
2130
+ }
2131
+ //#endregion
2132
+ //#region src/build/render-client-transport.ts
2133
+ const ADAPTER_EXPORT_BY_TYPE = {
2134
+ option: "optionTransportType",
2135
+ redacted: "redactedTransportType",
2136
+ chunk: "chunkTransportType",
2137
+ duration: "durationTransportType",
2138
+ "big-decimal": "bigDecimalTransportType",
2139
+ "date-time-utc": "dateTimeUtcTransportType",
2140
+ "date-time-zoned": "dateTimeZonedTransportType",
2141
+ "hash-map": "hashMapTransportType",
2142
+ "hash-set": "hashSetTransportType"
2143
+ };
2144
+ /**
2145
+ * Selects generic value adapters without generating application schemas or route
2146
+ * metadata. The shared adapters use pure initializers so that unused Effect
2147
+ * export reads do not retain optional data modules in the browser bundle.
2148
+ */
2149
+ function renderGeneratedClientTransport(input) {
2150
+ const entries = EFFECT_TRANSPORT_TYPES.flatMap(({ name }) => {
2151
+ return input.typeNames.has(name) ? [ADAPTER_EXPORT_BY_TYPE[name]] : [];
2152
+ });
2153
+ const moduleSpecifier = JSON.stringify(FRAMEWORK_IDENTITY_PROTOCOL.moduleSpecifiers.client);
2154
+ return {
2155
+ filePath: input.filePath,
2156
+ source: [
2157
+ "// Code generated by Vorma. DO NOT EDIT.",
2158
+ "",
2159
+ ...entries.length === 0 ? [] : [`import { ${entries.join(", ")} } from ${moduleSpecifier};`],
2160
+ `import type { TransportTypeEntry } from ${moduleSpecifier};`,
2161
+ "",
2162
+ `export const transportTypeEntries = [${entries.join(", ")}] satisfies ReadonlyArray<TransportTypeEntry>;`,
2163
+ ""
2164
+ ].join("\n")
2165
+ };
2166
+ }
2167
+ //#endregion
2168
+ //#region src/build/generate-application.ts
2169
+ /** Writes correlated generated sources and verifies their native TypeScript inclusion. */
2170
+ function writeAndCheckApplicationGeneration(generation) {
2171
+ return Effect.gen(function* () {
2172
+ yield* Effect.all([writeApplicationGeneration(generation), writeGeneratedServerApplication({ generation })], {
2173
+ concurrency: 2,
2174
+ discard: true
2175
+ });
2176
+ yield* checkGeneratedTypeInclusion({
2177
+ ...generation.config,
2178
+ serverFiles: [generation.config.generatedServerContractsFilePath],
2179
+ clientFiles: [generation.clientContracts.filePath, generation.publicAssets.types.filePath]
2180
+ });
2181
+ });
2182
+ }
2183
+ /** Reads both projects through the checker, captures public assets, and prepares generated build inputs without publishing them. */
2184
+ function prepareApplicationGeneration(sourceLayout) {
2185
+ return Effect.gen(function* () {
2186
+ return yield* assembleApplicationGeneration({
2187
+ sourceLayout,
2188
+ ...yield* Effect.all({
2189
+ inspection: inspectApplicationSources(sourceLayout),
2190
+ publicAssets: collectPublicAssets(sourceLayout)
2191
+ }, { concurrency: "unbounded" })
2192
+ });
2193
+ });
2194
+ }
2195
+ /** Reads declarations and public URL call sites independently of public asset hashing. */
2196
+ function inspectApplicationSources(sourceLayout) {
2197
+ return withTypescriptProjects({
2198
+ configDirectory: sourceLayout.configDirectory,
2199
+ serverTsconfigFilePath: sourceLayout.serverTsconfigFilePath,
2200
+ clientTsconfigFilePath: sourceLayout.clientTsconfigFilePath,
2201
+ useFn: (projects) => {
2202
+ return inspectProjects({
2203
+ projects,
2204
+ layout: sourceLayout
2205
+ });
2206
+ }
2207
+ });
2208
+ }
2209
+ /** Prepares correlated build inputs from an inspection and a captured public directory. */
2210
+ function assembleApplicationGeneration(input) {
2211
+ return Effect.gen(function* () {
2212
+ const path = yield* Path$1.Path;
2213
+ const { sourceLayout, inspection, publicAssets } = input;
2214
+ const model = compileApplicationModel(inspection);
2215
+ if (Result.isFailure(model)) return yield* Effect.fail({
2216
+ _tag: "ApplicationModelIssues",
2217
+ issues: model.failure
2218
+ });
2219
+ const { publicFilemap } = publicAssets;
2220
+ for (const site of inspection.publicUrlSites) if (!Object.hasOwn(publicFilemap, normalizePublicAssetKey(site.key))) return yield* Effect.fail({
2221
+ _tag: "UnknownPublicAssetKey",
2222
+ filePath: site.filePath,
2223
+ position: site.start,
2224
+ key: site.key
2225
+ });
2226
+ return {
2227
+ config: sourceLayout,
2228
+ model: model.success,
2229
+ inspection,
2230
+ publicAssets,
2231
+ clientContracts: renderGeneratedClientContracts({
2232
+ model: model.success,
2233
+ inspection,
2234
+ filePath: path.join(sourceLayout.clientGeneratedDirectory, ARTIFACT_PROTOCOL.generatedClientContractsFileName),
2235
+ path
2236
+ }),
2237
+ clientTransport: renderGeneratedClientTransport({
2238
+ typeNames: inspection.transportTypeNames,
2239
+ filePath: path.join(sourceLayout.clientGeneratedDirectory, ARTIFACT_PROTOCOL.generatedClientTransportFileName)
2240
+ })
2241
+ };
2242
+ });
2243
+ }
2244
+ /**
2245
+ * Writes the client's generated declarations, transport adapters, and an ignore
2246
+ * file in each owned output directory. Cleanup uses the complete client/server
2247
+ * output plan, so server source can be written concurrently with this work.
2248
+ */
2249
+ function writeApplicationGeneration(generation) {
2250
+ const outputs = [
2251
+ generation.publicAssets.types,
2252
+ generation.clientContracts,
2253
+ generation.clientTransport
2254
+ ];
2255
+ const plannedSourceFiles = [
2256
+ ...outputs.map((output) => output.filePath),
2257
+ generation.config.generatedBuildFilePath,
2258
+ generation.config.generatedServerContractsFilePath
2259
+ ];
2260
+ return Effect.forEach([...generation.config.ignoreFilePaths.map((filePath) => {
2261
+ return {
2262
+ filePath,
2263
+ source: ARTIFACT_PROTOCOL.ignoreFileSource
2264
+ };
2265
+ }), ...outputs], writeGeneratedSource, {
2266
+ concurrency: 16,
2267
+ discard: true
2268
+ }).pipe(Effect.andThen(Effect.forEach([.../* @__PURE__ */ new Set([generation.config.clientGeneratedDirectory, generation.config.serverGeneratedDirectory])], (directory) => {
2269
+ return removeStaleGeneratedSource({
2270
+ directory,
2271
+ plannedSourceFiles
2272
+ });
2273
+ }, {
2274
+ concurrency: 2,
2275
+ discard: true
2276
+ })));
2277
+ }
2278
+ /** Matches the generated source files the build owns by name, so a name an earlier build used and this one does not is removed. */
2279
+ const GENERATED_SOURCE_FILE_NAME = /\.gen\.(d\.)?ts$/;
2280
+ function removeStaleGeneratedSource(input) {
2281
+ return Effect.gen(function* () {
2282
+ const fileSystem = yield* FileSystem$1.FileSystem;
2283
+ const path = yield* Path$1.Path;
2284
+ const plannedSourceFiles = new Set(input.plannedSourceFiles);
2285
+ const stale = (yield* fileSystem.readDirectory(input.directory)).map((entry) => path.join(input.directory, entry)).filter((filePath) => {
2286
+ return GENERATED_SOURCE_FILE_NAME.test(filePath) && !plannedSourceFiles.has(filePath);
2287
+ }).sort((left, right) => compareSourceText({
2288
+ left,
2289
+ right
2290
+ }));
2291
+ yield* Effect.forEach(stale, (filePath) => fileSystem.remove(filePath), {
2292
+ concurrency: 16,
2293
+ discard: true
2294
+ });
2295
+ }).pipe(Effect.mapError((cause) => {
2296
+ return {
2297
+ _tag: "GeneratedApplicationWriteError",
2298
+ filePath: input.directory,
2299
+ cause
2300
+ };
2301
+ }));
2302
+ }
2303
+ /**
2304
+ * Writes the internal registration module and the application
2305
+ * contract types before server compilation.
2306
+ */
2307
+ function writeGeneratedServerApplication(input) {
2308
+ return Effect.gen(function* () {
2309
+ const path = yield* Path$1.Path;
2310
+ const { config, model, inspection } = input.generation;
2311
+ const buildModule = renderGeneratedBuildModule({
2312
+ declarations: model.serverDeclarations,
2313
+ publicDirectoryRelativeUrl: getPublicDirectoryRelativeUrl({
2314
+ path,
2315
+ serverGeneratedDirectory: config.serverGeneratedDirectory,
2316
+ publicDirectory: config.publicDirectory
2317
+ }),
2318
+ filePath: config.generatedBuildFilePath,
2319
+ path
2320
+ });
2321
+ const contracts = renderGeneratedServerContracts({
2322
+ model,
2323
+ publicAssets: input.generation.publicAssets,
2324
+ typeAliases: inspection.typeAliases,
2325
+ effectModules: inspection.effectModules,
2326
+ filePath: config.generatedServerContractsFilePath,
2327
+ path
2328
+ });
2329
+ yield* Effect.forEach([buildModule, contracts], writeGeneratedSource, {
2330
+ concurrency: 2,
2331
+ discard: true
2332
+ });
2333
+ });
2334
+ }
2335
+ function writeGeneratedSource(output) {
2336
+ return writeOutputFile(output).pipe(Effect.mapError((cause) => {
2337
+ return {
2338
+ _tag: "GeneratedApplicationWriteError",
2339
+ filePath: output.filePath,
2340
+ cause
2341
+ };
2342
+ }));
2343
+ }
2344
+ //#endregion
2345
+ //#region src/build/output-ownership.ts
2346
+ const Owner = Schema.fromJsonString(Schema.Struct({
2347
+ pid: Schema.Int.check(Schema.isGreaterThan(0)),
2348
+ kind: Schema.Literals(["build", "dev"]),
2349
+ directories: Schema.NonEmptyArray(Schema.String)
2350
+ }));
2351
+ function getRemovalCommand(lockPath) {
2352
+ if (platform === "win32") return `PowerShell: Remove-Item -LiteralPath '${lockPath.replaceAll("'", "''")}'`;
2353
+ return `rm -- '${lockPath.replaceAll("'", "'\"'\"'")}'`;
2354
+ }
2355
+ function removeOwnedLock(input) {
2356
+ return input.fileSystem.remove(input.lockPath, { force: true }).pipe(Effect.catch((cause) => {
2357
+ return Effect.logError(`Could not remove Vorma lock ${input.lockPath}. If the owner has stopped, run ${getRemovalCommand(input.lockPath)}`, cause);
2358
+ }));
2359
+ }
2360
+ /** Resolves aliases even when the output directory has not been created. */
2361
+ function canonicalizeOutputPath(filePath) {
2362
+ return Effect.gen(function* () {
2363
+ const fileSystem = yield* FileSystem.FileSystem;
2364
+ const path = yield* Path$1.Path;
2365
+ let current = path.resolve(filePath);
2366
+ const suffix = [];
2367
+ while (true) {
2368
+ const resolved = yield* Effect.result(fileSystem.realPath(current));
2369
+ if (Result.isSuccess(resolved)) return path.join(resolved.success, ...suffix);
2370
+ if (resolved.failure.reason._tag !== "NotFound" || path.dirname(current) === current) return yield* Effect.fail({
2371
+ _tag: "OutputOwnershipIssue",
2372
+ directory: filePath,
2373
+ message: `Cannot resolve output path ${filePath}`,
2374
+ cause: resolved.failure
2375
+ });
2376
+ const parent = path.dirname(current);
2377
+ suffix.unshift(path.relative(parent, current));
2378
+ current = parent;
2379
+ }
2380
+ });
2381
+ }
2382
+ /** Checks canonical containment before removing or replacing generated output. */
2383
+ function checkOutputPath(input) {
2384
+ return Effect.gen(function* () {
2385
+ const path = yield* Path$1.Path;
2386
+ const relativePath = path.relative(input.rootDirectory, input.filePath);
2387
+ const [root, target] = yield* Effect.all([canonicalizeOutputPath(input.rootDirectory), canonicalizeOutputPath(input.filePath)], { concurrency: 2 });
2388
+ if (!containsRelativePath({
2389
+ relativePath,
2390
+ separator: path.sep,
2391
+ absolute: path.isAbsolute(relativePath)
2392
+ }) || target !== path.join(root, relativePath)) return yield* Effect.fail({
2393
+ _tag: "OutputOwnershipIssue",
2394
+ directory: input.filePath,
2395
+ message: `Generated output path leaves its directory: ${input.filePath}`
2396
+ });
2397
+ });
2398
+ }
2399
+ /** Owns the application's generated output until the build or development scope closes. */
2400
+ function acquireOutputOwnership(input) {
2401
+ return Effect.gen(function* () {
2402
+ const fileSystem = yield* FileSystem.FileSystem;
2403
+ const path = yield* Path$1.Path;
2404
+ function toOwnedRoot(directory) {
2405
+ return fileSystem.makeDirectory(directory, { recursive: true }).pipe(Effect.andThen(canonicalizeOutputPath(directory)));
2406
+ }
2407
+ const lockRoot = yield* toOwnedRoot(input.lockDirectory);
2408
+ const boundaryRoots = yield* Effect.forEach(input.boundaryDirectories, toOwnedRoot, { concurrency: "unbounded" });
2409
+ const lockPath = path.join(lockRoot, "build.lock");
2410
+ const owner = {
2411
+ pid,
2412
+ kind: input.kind,
2413
+ directories: [lockRoot, ...boundaryRoots]
2414
+ };
2415
+ yield* Effect.acquireRelease(Effect.scoped(fileSystem.open(lockPath, {
2416
+ flag: "wx",
2417
+ mode: 384
2418
+ })).pipe(Effect.catch((cause) => {
2419
+ return Effect.gen(function* () {
2420
+ if (cause.reason._tag !== "AlreadyExists") return yield* Effect.fail(cause);
2421
+ const recorded = Option.getOrUndefined(yield* fileSystem.readFileString(lockPath).pipe(Effect.flatMap(Schema.decodeUnknownEffect(Owner)), Effect.option));
2422
+ return yield* Effect.fail({
2423
+ _tag: "OutputOwnershipIssue",
2424
+ directory: lockRoot,
2425
+ message: [
2426
+ `Cannot start ${input.kind}: generated output is locked.`,
2427
+ `Lockfile: ${lockPath}`,
2428
+ `Recorded owner PID: ${recorded?.pid ?? "unavailable"}`,
2429
+ `Recorded operation: ${recorded?.kind ?? "unavailable"}`,
2430
+ `Requested outputs: ${owner.directories.join(", ")}`,
2431
+ "Wait for the owning process to finish. If it has stopped, remove the leftover lock and retry:",
2432
+ ` ${getRemovalCommand(lockPath)}`
2433
+ ].join("\n")
2434
+ });
2435
+ });
2436
+ })), () => removeOwnedLock({
2437
+ fileSystem,
2438
+ lockPath
2439
+ }));
2440
+ yield* fileSystem.writeFileString(lockPath, JSON.stringify(owner));
2441
+ }).pipe(Effect.mapError((cause) => {
2442
+ return cause._tag === "OutputOwnershipIssue" ? cause : {
2443
+ _tag: "OutputOwnershipIssue",
2444
+ directory: input.lockDirectory,
2445
+ message: "Cannot acquire Vorma output ownership",
2446
+ cause
2447
+ };
2448
+ }));
2449
+ }
2450
+ //#endregion
2451
+ //#region src/build/publish-artifacts.ts
2452
+ /** Prepares one public output before Vite adds its browser files to it. */
2453
+ function preparePublicOutput(config) {
2454
+ return Effect.gen(function* () {
2455
+ const fileSystem = yield* FileSystem$1.FileSystem;
2456
+ const directory = config.publicDirectory;
2457
+ yield* checkOutputPath({
2458
+ filePath: directory,
2459
+ rootDirectory: config.clientGeneratedDirectory
2460
+ });
2461
+ yield* fileSystem.remove(directory, {
2462
+ force: true,
2463
+ recursive: true
2464
+ });
2465
+ yield* fileSystem.makeDirectory(directory, { recursive: true });
2466
+ });
2467
+ }
2468
+ /** Publishes compiled server modules and private metadata for the completed browser build. */
2469
+ function publishApplicationArtifacts(input) {
2470
+ return Effect.gen(function* () {
2471
+ const fileSystem = yield* FileSystem$1.FileSystem;
2472
+ const path = yield* Path$1.Path;
2473
+ const config = input.generation.config;
2474
+ if (input.server !== void 0) yield* removeCompiledServerModules(config.serverGeneratedDirectory);
2475
+ const files = (input.server?.files ?? []).map((file) => {
2476
+ return {
2477
+ filePath: path.join(config.serverGeneratedDirectory, file.fileName),
2478
+ source: file.source
2479
+ };
2480
+ });
2481
+ yield* Effect.forEach(files, (file) => {
2482
+ return Effect.gen(function* () {
2483
+ yield* fileSystem.makeDirectory(path.dirname(file.filePath), { recursive: true });
2484
+ yield* typeof file.source === "string" ? fileSystem.writeFileString(file.filePath, file.source) : fileSystem.writeFile(file.filePath, file.source);
2485
+ });
2486
+ }, {
2487
+ concurrency: 16,
2488
+ discard: true
2489
+ });
2490
+ const metadataPath = path.join(config.serverGeneratedDirectory, ARTIFACT_PROTOCOL.productionMetadataFileName);
2491
+ const temporaryPath = `${metadataPath}.tmp`;
2492
+ yield* fileSystem.writeFileString(temporaryPath, JSON.stringify(input.artifacts.metadata));
2493
+ yield* fileSystem.rename(temporaryPath, metadataPath);
2494
+ return {
2495
+ clientBuildId: input.artifacts.metadata.clientBuildId,
2496
+ publicDirectory: config.publicDirectory,
2497
+ serverEntryPath: config.compiledServerEntryFilePath
2498
+ };
2499
+ }).pipe(Effect.mapError((cause) => {
2500
+ return {
2501
+ _tag: "ArtifactPublicationIssue",
2502
+ cause
2503
+ };
2504
+ }));
2505
+ }
2506
+ /** Generated TypeScript source shares this directory and is not compiled output. */
2507
+ function removeCompiledServerModules(directory) {
2508
+ return Effect.gen(function* () {
2509
+ const fileSystem = yield* FileSystem$1.FileSystem;
2510
+ const path = yield* Path$1.Path;
2511
+ if (!(yield* fileSystem.exists(directory))) return;
2512
+ const fileNames = (yield* fileSystem.readDirectory(directory)).filter((fileName) => {
2513
+ return fileName.endsWith(ARTIFACT_PROTOCOL.compiledServerModuleExtension);
2514
+ });
2515
+ yield* Effect.forEach(fileNames, (fileName) => {
2516
+ const filePath = path.join(directory, fileName);
2517
+ return checkOutputPath({
2518
+ filePath,
2519
+ rootDirectory: directory
2520
+ }).pipe(Effect.andThen(fileSystem.remove(filePath)));
2521
+ }, {
2522
+ concurrency: 16,
2523
+ discard: true
2524
+ });
2525
+ });
2526
+ }
2527
+ //#endregion
2528
+ //#region src/build/source-layout.ts
2529
+ /** Loads the configured source layout before any application module executes. */
2530
+ function loadApplicationSourceLayout(config) {
2531
+ return Effect.gen(function* () {
2532
+ const fileSystem = yield* FileSystem$1.FileSystem;
2533
+ const path = yield* Path$1.Path;
2534
+ const requiredPaths = {
2535
+ serverEntryFilePath: {
2536
+ field: "serverEntry",
2537
+ path: config.serverEntryFilePath,
2538
+ expectedType: "File"
2539
+ },
2540
+ serverTsconfigFilePath: {
2541
+ field: "serverTsconfigFile",
2542
+ path: config.serverTsconfigFilePath,
2543
+ expectedType: "File"
2544
+ },
2545
+ clientEntryFilePath: {
2546
+ field: "clientEntry",
2547
+ path: config.clientEntryFilePath,
2548
+ expectedType: "File"
2549
+ },
2550
+ clientTsconfigFilePath: {
2551
+ field: "clientTsconfigFile",
2552
+ path: config.clientTsconfigFilePath,
2553
+ expectedType: "File"
2554
+ },
2555
+ publicAssetsSourceDirectory: {
2556
+ field: "publicAssetsDir",
2557
+ path: config.publicAssetsSourceDirectory,
2558
+ expectedType: "Directory"
2559
+ },
2560
+ viteConfigFilePath: {
2561
+ field: "viteConfigFile",
2562
+ path: config.viteConfigFilePath,
2563
+ expectedType: "File"
2564
+ }
2565
+ };
2566
+ const criticalCssRequirement = config.criticalCssEntryFilePath === void 0 ? void 0 : {
2567
+ field: "criticalCssEntry",
2568
+ path: config.criticalCssEntryFilePath,
2569
+ expectedType: "File"
2570
+ };
2571
+ function inspectSourcePath(requirement) {
2572
+ return Effect.all({
2573
+ stat: Effect.result(fileSystem.stat(requirement.path)),
2574
+ canonicalPath: Effect.result(fileSystem.realPath(requirement.path))
2575
+ }).pipe(Effect.map((inspection) => {
2576
+ if (Result.isFailure(inspection.stat)) return Result.fail({
2577
+ ...requirement,
2578
+ cause: inspection.stat.failure
2579
+ });
2580
+ if (inspection.stat.success.type !== requirement.expectedType) return Result.fail({
2581
+ ...requirement,
2582
+ actualType: inspection.stat.success.type
2583
+ });
2584
+ if (Result.isFailure(inspection.canonicalPath)) return Result.fail({
2585
+ ...requirement,
2586
+ cause: inspection.canonicalPath.failure
2587
+ });
2588
+ return Result.succeed(inspection.canonicalPath.success);
2589
+ }));
2590
+ }
2591
+ const inspections = yield* Effect.all({
2592
+ ...Record.map(requiredPaths, inspectSourcePath),
2593
+ criticalCssEntryFilePath: criticalCssRequirement === void 0 ? Effect.succeed(Result.succeed(void 0)) : inspectSourcePath(criticalCssRequirement)
2594
+ }, { concurrency: "unbounded" });
2595
+ const issues = Object.values(inspections).flatMap((inspection) => {
2596
+ return Result.isFailure(inspection) ? [inspection.failure] : [];
2597
+ });
2598
+ const canonicalPaths = Result.all(inspections);
2599
+ if (Result.isFailure(canonicalPaths)) return yield* Effect.fail({
2600
+ _tag: "ApplicationSourceLayoutIssues",
2601
+ issues
2602
+ });
2603
+ const { criticalCssEntryFilePath, ...canonical } = canonicalPaths.success;
2604
+ const canonicalConfig = {
2605
+ ...config,
2606
+ ...canonical,
2607
+ serverProjectDirectory: path.dirname(canonical.serverTsconfigFilePath),
2608
+ ...criticalCssEntryFilePath === void 0 ? {} : { criticalCssEntryFilePath },
2609
+ ...getVormaOwnedPaths({
2610
+ path,
2611
+ serverEntryDirectory: path.dirname(canonical.serverEntryFilePath),
2612
+ clientEntryDirectory: path.dirname(canonical.clientEntryFilePath)
2613
+ })
2614
+ };
2615
+ const publicSource = canonicalConfig.publicAssetsSourceDirectory;
2616
+ const publicOutput = canonicalConfig.publicDirectory;
2617
+ if ([path.relative(publicSource, publicOutput), path.relative(publicOutput, publicSource)].some((relativePath) => {
2618
+ return containsRelativePath({
2619
+ relativePath,
2620
+ separator: path.sep,
2621
+ absolute: path.isAbsolute(relativePath)
2622
+ });
2623
+ })) return yield* Effect.fail({
2624
+ _tag: "ApplicationSourceLayoutIssues",
2625
+ issues: [{
2626
+ field: "publicAssetsDir",
2627
+ path: publicSource,
2628
+ expectedType: "Directory",
2629
+ message: `Public asset source ${publicSource} overlaps generated output ${publicOutput}; the build cannot replace its own input or capture its own output.`
2630
+ }]
2631
+ });
2632
+ return canonicalConfig;
2633
+ });
2634
+ }
2635
+ //#endregion
2636
+ //#region src/build/inspect-css-artifacts.ts
2637
+ /** Records dependencies from compiled stylesheets for nonblocking preload hints. */
2638
+ function inspectCssArtifacts(input) {
2639
+ const references = /* @__PURE__ */ new Map();
2640
+ for (const stylesheet of input.stylesheets) {
2641
+ const urls = [];
2642
+ references.set(stylesheet.url, urls);
2643
+ const name = stylesheet.url === null ? "critical CSS" : stylesheet.url;
2644
+ const parsed = Result.try({
2645
+ try: () => {
2646
+ return transform({
2647
+ filename: name,
2648
+ code: Buffer.from(stylesheet.source),
2649
+ analyzeDependencies: true
2650
+ });
2651
+ },
2652
+ catch: (cause) => `Cannot inspect ${name}: ${String(cause)}`
2653
+ });
2654
+ if (Result.isFailure(parsed)) return Result.fail(parsed.failure);
2655
+ const dependencies = parsed.success.dependencies;
2656
+ if (dependencies === void 0) continue;
2657
+ for (const dependency of dependencies) {
2658
+ if (dependency.type !== "url" && dependency.type !== "import") continue;
2659
+ const reference = dependency.url;
2660
+ if (reference.startsWith("#") || reference.startsWith("data:")) continue;
2661
+ const resolved = Result.try({
2662
+ try: () => {
2663
+ return new URL(reference, `https://vorma.invalid${stylesheet.url === null ? "/" : stylesheet.url}`);
2664
+ },
2665
+ catch: () => `Invalid CSS URL: ${reference}`
2666
+ });
2667
+ if (Result.isFailure(resolved)) return Result.fail(resolved.failure);
2668
+ const absolute = resolved.success;
2669
+ urls.push({
2670
+ url: absolute.origin === "https://vorma.invalid" ? stylesheet.url === null && !reference.startsWith("/") ? reference : absolute.pathname + absolute.search + absolute.hash : reference.startsWith("//") ? reference : absolute.href,
2671
+ kind: dependency.type
2672
+ });
2673
+ }
2674
+ }
2675
+ return Result.succeed(references);
2676
+ }
2677
+ //#endregion
2678
+ //#region src/build/assemble-artifacts.ts
2679
+ /** Joins published file metadata and checks paths, collisions, and module projections. */
2680
+ function assembleApplicationArtifacts(input) {
2681
+ return Effect.gen(function* () {
2682
+ const { generation, client } = input;
2683
+ const base = generation.config.publicAssetsBasePath;
2684
+ function toPublicUrl(fileName) {
2685
+ return base + fileName.split("/").map(encodeURIComponent).join("/");
2686
+ }
2687
+ const byFile = /* @__PURE__ */ new Map();
2688
+ const byUrl = /* @__PURE__ */ new Map();
2689
+ const directories = /* @__PURE__ */ new Set();
2690
+ /** Records one output, or the artifact already recorded for the same bytes at the same file. */
2691
+ function addFile(output) {
2692
+ const parsed = parseArtifactFileName(output.fileName);
2693
+ if (Result.isFailure(parsed)) return Result.fail(parsed.failure);
2694
+ if (output.immutable && !output.fileName.startsWith(ARTIFACT_PROTOCOL.publicOutputFileNamePrefix)) return Result.fail(`Hashed public asset must start with ${ARTIFACT_PROTOCOL.publicOutputFileNamePrefix}: ${output.fileName}`);
2695
+ const digest = output.digest;
2696
+ const existing = byFile.get(output.fileName);
2697
+ if (existing !== void 0) {
2698
+ if (existing.digest !== digest || existing.url !== output.url || existing.immutable !== output.immutable) return Result.fail(`Conflicting artifact outputs: ${output.fileName}`);
2699
+ return Result.succeed(existing);
2700
+ }
2701
+ if (byUrl.has(output.url)) return Result.fail(`Conflicting artifact URL: ${output.url}`);
2702
+ const parts = output.fileName.split("/");
2703
+ const parents = parts.slice(0, -1).map((_part, index) => parts.slice(0, index + 1).join("/"));
2704
+ if (directories.has(output.fileName) || parents.some((parent) => byFile.has(parent))) return Result.fail(`Artifact file and directory conflict: ${output.fileName}`);
2705
+ const artifact = output;
2706
+ byFile.set(output.fileName, artifact);
2707
+ byUrl.set(output.url, artifact);
2708
+ for (const parent of parents) directories.add(parent);
2709
+ return Result.succeed(artifact);
2710
+ }
2711
+ for (const asset of generation.publicAssets.assets) yield* Effect.fromResult(addFile({
2712
+ fileName: asset.fileName,
2713
+ url: asset.url,
2714
+ digest: asset.digest,
2715
+ immutable: true
2716
+ }));
2717
+ const bundleArtifacts = [];
2718
+ for (const output of client.outputs) {
2719
+ const source = typeof output.source === "string" ? new TextEncoder().encode(output.source) : output.source;
2720
+ const artifact = yield* Effect.fromResult(addFile({
2721
+ fileName: output.fileName,
2722
+ digest: toContentDigest(source),
2723
+ url: toPublicUrl(output.fileName),
2724
+ immutable: byFile.get(output.fileName)?.immutable ?? output.immutable
2725
+ }));
2726
+ bundleArtifacts.push({
2727
+ ...artifact,
2728
+ source
2729
+ });
2730
+ }
2731
+ const cssReferences = yield* Effect.fromResult(inspectCssArtifacts({ stylesheets: [{
2732
+ url: null,
2733
+ source: new TextEncoder().encode(client.criticalCss)
2734
+ }, ...bundleArtifacts.filter((artifact) => artifact.fileName.endsWith(".css")).map((artifact) => {
2735
+ return {
2736
+ url: artifact.url,
2737
+ source: artifact.source
2738
+ };
2739
+ })] }));
2740
+ const publicFilemap = generation.publicAssets.publicFilemap;
2741
+ function collectCssAssets(stylesheets) {
2742
+ const visited = /* @__PURE__ */ new Set();
2743
+ const assets = /* @__PURE__ */ new Set();
2744
+ const pending = [...stylesheets];
2745
+ while (pending.length > 0) {
2746
+ const stylesheet = pending.pop();
2747
+ if (stylesheet === void 0 || visited.has(stylesheet)) continue;
2748
+ visited.add(stylesheet);
2749
+ for (const reference of cssReferences.get(stylesheet) ?? []) if (reference.kind === "import") pending.push(reference.url);
2750
+ else assets.add(reference.url);
2751
+ }
2752
+ return [...assets];
2753
+ }
2754
+ function addCssAssetUrls(module) {
2755
+ return {
2756
+ ...module,
2757
+ staticAssetUrls: [.../* @__PURE__ */ new Set([...module.staticAssetUrls, ...collectCssAssets(module.cssBundleUrls)])]
2758
+ };
2759
+ }
2760
+ const entryModule = addCssAssetUrls(client.clientEntry);
2761
+ const clientEntry = {
2762
+ ...entryModule,
2763
+ staticAssetUrls: [.../* @__PURE__ */ new Set([...entryModule.staticAssetUrls, ...collectCssAssets([null])])]
2764
+ };
2765
+ const clientViews = Object.fromEntries(Object.entries(client.clientViews).map(([pattern, module]) => {
2766
+ return [pattern, {
2767
+ ...addCssAssetUrls(module),
2768
+ exportNames: module.exportNames
2769
+ }];
2770
+ }));
2771
+ const files = [...byFile.values()].sort((left, right) => compareSourceText({
2772
+ left: left.url,
2773
+ right: right.url
2774
+ }));
2775
+ const metadata = {
2776
+ runtimeMode: "production",
2777
+ publicFiles: files.map((file) => {
2778
+ return {
2779
+ url: file.url,
2780
+ fileName: file.fileName,
2781
+ immutable: file.immutable
2782
+ };
2783
+ }),
2784
+ publicFilemap,
2785
+ criticalCss: client.criticalCss,
2786
+ clientEntry,
2787
+ clientViews
2788
+ };
2789
+ const { publicFiles: _publicFiles, ...browserMetadata } = metadata;
2790
+ return { metadata: {
2791
+ ...metadata,
2792
+ clientBuildId: toContentDigest(encodeCanonicalJson({
2793
+ version: 3,
2794
+ metadata: browserMetadata,
2795
+ contracts: projectBrowserContracts(generation.inspection),
2796
+ files: files.filter((file) => file.immutable).map((file) => {
2797
+ return {
2798
+ url: file.url,
2799
+ digest: file.digest
2800
+ };
2801
+ })
2802
+ })).slice(0, 24)
2803
+ } };
2804
+ }).pipe(Effect.mapError((reason) => {
2805
+ return {
2806
+ _tag: "ArtifactAssemblyIssue",
2807
+ reason
2808
+ };
2809
+ }));
2810
+ }
2811
+ function parseArtifactFileName(fileName) {
2812
+ if (fileName.split("/").some((part) => {
2813
+ return part === "" || part === "." || part === ".." || /[\\:\0]/.test(part);
2814
+ })) return Result.fail(`Invalid artifact output path: ${fileName}`);
2815
+ return Result.succeed(fileName);
2816
+ }
2817
+ //#endregion
2818
+ //#region src/build/client-output-hashing.ts
2819
+ /** Native naming retains Vite's content hashing and dependency URL rewriting. */
2820
+ function createClientOutputHashingPlugin(immutableFiles) {
2821
+ return {
2822
+ name: "vorma:client-output-hashing",
2823
+ enforce: "post",
2824
+ outputOptions: {
2825
+ order: "post",
2826
+ handler(options) {
2827
+ return {
2828
+ ...options,
2829
+ entryFileNames: ARTIFACT_PROTOCOL.publicModuleFileNamePattern,
2830
+ chunkFileNames: ARTIFACT_PROTOCOL.publicModuleFileNamePattern,
2831
+ assetFileNames: ARTIFACT_PROTOCOL.publicAssetFileNamePattern
2832
+ };
2833
+ }
2834
+ },
2835
+ generateBundle: {
2836
+ order: "post",
2837
+ handler(_options, bundle) {
2838
+ for (const output of Object.values(bundle)) {
2839
+ if (output.type !== "chunk") continue;
2840
+ if (output.preliminaryFileName !== output.fileName) immutableFiles.add(output.fileName);
2841
+ for (const file of [...output.viteMetadata?.importedCss ?? [], ...output.viteMetadata?.importedAssets ?? []]) immutableFiles.add(file);
2842
+ }
2843
+ }
2844
+ }
2845
+ };
2846
+ }
2847
+ //#endregion
2848
+ //#region src/build/critical-css.ts
2849
+ /** Finds the configured critical stylesheet through Vite's module-to-CSS association. */
2850
+ function findCriticalCssFileName(input) {
2851
+ if (input.sourcePath === void 0) return Result.succeed(void 0);
2852
+ const sourcePath = input.sourcePath;
2853
+ const stylesheets = new Set(input.outputs.flatMap((output) => {
2854
+ return output.type === "chunk" && Object.hasOwn(output.modules, sourcePath) ? [...output.viteMetadata?.importedCss ?? []] : [];
2855
+ }));
2856
+ const stylesheet = [...stylesheets][0];
2857
+ if (stylesheets.size !== 1 || stylesheet === void 0) return Result.fail("The configured critical CSS entry must produce one stylesheet in the client build");
2858
+ return Result.succeed(stylesheet);
2859
+ }
2860
+ //#endregion
2861
+ //#region src/build/bundle-client.ts
2862
+ /** Builds browser entries directly into Vorma's prepared public directory. */
2863
+ function bundleClient(generation) {
2864
+ return Effect.gen(function* () {
2865
+ const viewEntries = getClientViewEntries(generation);
2866
+ const publicCss = createCssPublicAssets(generation.publicAssets);
2867
+ const { result, criticalCss, cssAssets, referencedAssets, javascriptAssets, manifestFiles, immutableFiles } = yield* Effect.tryPromise(() => runClientBuild(generation, publicCss, viewEntries)).pipe(Effect.uninterruptible);
2868
+ if (result === void 0) return yield* Effect.fail("The Vite application build did not produce Vorma's client environment");
2869
+ const fileSystem = yield* FileSystem$1.FileSystem;
2870
+ const path = yield* Path$1.Path;
2871
+ yield* Effect.forEach(manifestFiles, (fileName) => {
2872
+ return checkOutputPath({
2873
+ filePath: path.join(generation.config.publicDirectory, fileName),
2874
+ rootDirectory: generation.config.publicDirectory
2875
+ }).pipe(Effect.andThen(fileSystem.remove(path.join(generation.config.publicDirectory, fileName), { force: true })));
2876
+ }, {
2877
+ concurrency: 2,
2878
+ discard: true
2879
+ });
2880
+ const outputs = result.output.filter((output) => !manifestFiles.has(output.fileName));
2881
+ for (const fileName of cssAssets) immutableFiles.add(fileName);
2882
+ const chunkFiles = new Set(outputs.flatMap((output) => {
2883
+ return output.type === "chunk" ? [output.fileName] : [];
2884
+ }));
2885
+ const modules = /* @__PURE__ */ new Map();
2886
+ for (const output of outputs) {
2887
+ if (output.type !== "chunk") continue;
2888
+ const assetUrls = [];
2889
+ for (const hostId of /* @__PURE__ */ new Set([output.preliminaryFileName, output.fileName])) for (const reference of javascriptAssets.get(hostId) ?? []) {
2890
+ if (!output.viteMetadata?.importedAssets.has(reference.fileName.replace(/[?#].*$/s, ""))) continue;
2891
+ const url = toJavascriptAssetUrl({
2892
+ reference,
2893
+ base: generation.config.publicAssetsBasePath
2894
+ });
2895
+ if (url !== void 0) assetUrls.push(url);
2896
+ }
2897
+ modules.set(output.fileName, {
2898
+ file: output.fileName,
2899
+ imports: output.imports.filter((file) => chunkFiles.has(file)),
2900
+ css: [...output.viteMetadata?.importedCss ?? []],
2901
+ assets: [...output.viteMetadata?.importedAssets ?? []],
2902
+ assetUrls
2903
+ });
2904
+ }
2905
+ const referencedFiles = new Map(outputs.flatMap((output) => {
2906
+ return output.type === "chunk" ? [[output.fileName, [...referencedAssets.get(output.preliminaryFileName) ?? []]]] : [];
2907
+ }));
2908
+ const files = new Set(outputs.map((output) => output.fileName));
2909
+ for (const [key, entry] of modules) for (const file of [
2910
+ entry.file,
2911
+ ...entry.css,
2912
+ ...entry.assets
2913
+ ]) if (!files.has(file)) return yield* Effect.fail(`Client module ${key} references a missing output: ${file}`);
2914
+ const frameworkEntries = /* @__PURE__ */ new Set([generation.config.clientEntryFilePath, ...viewEntries.map((entry) => entry.id)]);
2915
+ const entries = /* @__PURE__ */ new Map();
2916
+ for (const output of outputs) {
2917
+ if (output.type !== "chunk" || !output.isEntry || output.facadeModuleId === null || !frameworkEntries.has(output.facadeModuleId)) continue;
2918
+ entries.set(output.facadeModuleId, yield* Effect.fromResult(projectClientModule({
2919
+ modules,
2920
+ referencedFiles,
2921
+ immutableFiles,
2922
+ key: output.fileName,
2923
+ base: generation.config.publicAssetsBasePath
2924
+ })));
2925
+ }
2926
+ const clientEntry = entries.get(generation.config.clientEntryFilePath);
2927
+ if (clientEntry === void 0) return yield* Effect.fail("Vite did not preserve the configured client entry");
2928
+ const clientViews = yield* Effect.forEach(viewEntries, (view) => {
2929
+ const module = entries.get(view.id);
2930
+ return module === void 0 ? Effect.fail(`Vite did not preserve the view module ${view.id}`) : Effect.succeed([view.pattern, {
2931
+ ...module,
2932
+ exportNames: view.exportNames
2933
+ }]);
2934
+ }).pipe(Effect.map(Object.fromEntries));
2935
+ return {
2936
+ criticalCss,
2937
+ outputs: outputs.map((output) => {
2938
+ return {
2939
+ fileName: output.fileName,
2940
+ immutable: immutableFiles.has(output.fileName),
2941
+ source: output.type === "chunk" ? output.code : typeof output.source === "string" ? output.source : Uint8Array.from(output.source)
2942
+ };
2943
+ }),
2944
+ chunks: outputs.flatMap((output) => {
2945
+ return output.type === "chunk" ? [{
2946
+ fileName: output.fileName,
2947
+ sourceModules: Object.keys(output.modules),
2948
+ renderedLengths: Object.fromEntries(Object.entries(output.modules).map(([id, module]) => {
2949
+ return [id, module.renderedLength];
2950
+ })),
2951
+ imports: [...output.imports],
2952
+ dynamicImports: [...output.dynamicImports]
2953
+ }] : [];
2954
+ }),
2955
+ clientEntry,
2956
+ clientViews
2957
+ };
2958
+ }).pipe(Effect.mapError((cause) => {
2959
+ return {
2960
+ _tag: "ClientBundleIssue",
2961
+ cause
2962
+ };
2963
+ }));
2964
+ }
2965
+ /** The entry's module and, dependencies first, every static import it reaches with their stylesheets. */
2966
+ function projectClientModule(input) {
2967
+ const root = input.modules.get(input.key);
2968
+ if (root === void 0) return Result.fail(`Client output has no module: ${input.key}`);
2969
+ const visited = /* @__PURE__ */ new Set();
2970
+ const dependencies = /* @__PURE__ */ new Set();
2971
+ const css = /* @__PURE__ */ new Set();
2972
+ const assets = /* @__PURE__ */ new Set();
2973
+ const pending = [{
2974
+ key: input.key,
2975
+ collect: false
2976
+ }];
2977
+ for (let next = pending.pop(); next !== void 0; next = pending.pop()) {
2978
+ const entry = input.modules.get(next.key);
2979
+ if (entry === void 0) return Result.fail(`Client output has no module: ${next.key}`);
2980
+ if (!input.immutableFiles.has(entry.file)) return Result.fail(`Browser module ${entry.file} must use Vite's content-hashed output naming`);
2981
+ if (next.collect) {
2982
+ if (next.key !== input.key) dependencies.add(entry.file);
2983
+ for (const file of entry.css) css.add(file);
2984
+ for (const url of entry.assetUrls) assets.add(url);
2985
+ for (const file of input.referencedFiles.get(entry.file) ?? []) assets.add(toUrl(file));
2986
+ continue;
2987
+ }
2988
+ if (visited.has(next.key)) continue;
2989
+ visited.add(next.key);
2990
+ pending.push({
2991
+ key: next.key,
2992
+ collect: true
2993
+ });
2994
+ const imports = entry.imports;
2995
+ for (const dependency of [...imports].reverse()) pending.push({
2996
+ key: dependency,
2997
+ collect: false
2998
+ });
2999
+ }
3000
+ function toUrl(fileName) {
3001
+ return input.base + fileName.split("/").map(encodeURIComponent).join("/");
3002
+ }
3003
+ return Result.succeed({
3004
+ importUrl: toUrl(root.file),
3005
+ depUrls: [...dependencies].map(toUrl),
3006
+ cssBundleUrls: [...css].map(toUrl),
3007
+ staticAssetUrls: [...assets]
3008
+ });
3009
+ }
3010
+ /** Vite encodes a literal built URL's path, leaving its query and fragment intact. */
3011
+ function toJavascriptAssetUrl(input) {
3012
+ const { fileName, renderedUrl } = input.reference;
3013
+ if (typeof renderedUrl === "object" && renderedUrl.runtime) return;
3014
+ const usesRenderedUrl = typeof renderedUrl === "string" && renderedUrl.length > 0;
3015
+ const url = usesRenderedUrl ? renderedUrl : fileName;
3016
+ const prefix = usesRenderedUrl ? "" : input.base;
3017
+ if (url.startsWith("data:")) return;
3018
+ const suffixIndex = url.search(/[?#]/);
3019
+ return prefix + (suffixIndex < 0 ? encodeURI(url) : encodeURI(url.slice(0, suffixIndex)) + url.slice(suffixIndex));
3020
+ }
3021
+ async function runClientBuild(generation, publicCss, viewEntries) {
3022
+ const manifestFiles = /* @__PURE__ */ new Set();
3023
+ const immutableFiles = /* @__PURE__ */ new Set();
3024
+ const virtualViewSources = new Map(viewEntries.flatMap((entry) => {
3025
+ return entry.source === void 0 ? [] : [[entry.id, entry.source]];
3026
+ }));
3027
+ let criticalCss = "";
3028
+ let criticalCssFileName;
3029
+ const captured = {};
3030
+ const entries = [
3031
+ {
3032
+ name: "entry",
3033
+ id: generation.config.clientEntryFilePath
3034
+ },
3035
+ ...[...new Set(viewEntries.map((entry) => entry.id))].map((id, index) => {
3036
+ return {
3037
+ name: `view-${index}`,
3038
+ id
3039
+ };
3040
+ }),
3041
+ ...generation.config.criticalCssEntryFilePath === void 0 ? [] : [{
3042
+ name: "critical",
3043
+ id: generation.config.criticalCssEntryFilePath
3044
+ }]
3045
+ ];
3046
+ let hasDeclaration = false;
3047
+ const plugin = {
3048
+ name: VORMA_BUILD_PLUGIN_NAME,
3049
+ api: { registerDeclarationFn: () => {
3050
+ hasDeclaration = true;
3051
+ } },
3052
+ enforce: "post",
3053
+ applyToEnvironment: (environment) => environment.name === "client",
3054
+ configResolved: {
3055
+ order: "post",
3056
+ handler() {
3057
+ if (!hasDeclaration) return this.error("Vorma's build requires vorma() from vorma/vite in the configured Vite plugins list.");
3058
+ }
3059
+ },
3060
+ buildStart() {
3061
+ for (const entry of entries) this.emitFile({
3062
+ type: "chunk",
3063
+ ...entry,
3064
+ preserveSignature: "strict"
3065
+ });
3066
+ },
3067
+ resolveId(source) {
3068
+ if (virtualViewSources.has(source)) return source;
3069
+ return source === FRAMEWORK_IDENTITY_PROTOCOL.moduleSpecifiers.clientTransportTypes ? generation.clientTransport.filePath : null;
3070
+ },
3071
+ load(id) {
3072
+ return virtualViewSources.get(id) ?? null;
3073
+ },
3074
+ config(config) {
3075
+ const workerPlugins = config.worker?.plugins;
3076
+ return {
3077
+ base: generation.config.publicAssetsBasePath,
3078
+ publicDir: false,
3079
+ resolve: { dedupe: CLIENT_SHARED_PACKAGES },
3080
+ worker: { plugins: () => {
3081
+ return [...workerPlugins?.() ?? [], createClientOutputHashingPlugin(immutableFiles)];
3082
+ } }
3083
+ };
3084
+ },
3085
+ configEnvironment(name, config) {
3086
+ if (name !== "client") return;
3087
+ const build = config.build;
3088
+ const output = build?.rolldownOptions?.output;
3089
+ if (Array.isArray(output)) return this.error("Vorma requires one output for its client environment");
3090
+ return { build: {
3091
+ outDir: generation.config.publicDirectory,
3092
+ emptyOutDir: false,
3093
+ write: true,
3094
+ cssCodeSplit: true,
3095
+ rolldownOptions: {
3096
+ input: build?.rolldownOptions?.input ?? { entry: generation.config.clientEntryFilePath },
3097
+ output: {
3098
+ ...output,
3099
+ format: "es"
3100
+ }
3101
+ }
3102
+ } };
3103
+ },
3104
+ generateBundle: {
3105
+ order: "post",
3106
+ handler(_options, bundle) {
3107
+ const config = this.environment.config;
3108
+ if (config.build.watch || config.build.lib || config.build.ssr) return this.error("Vorma requires a non-watching browser client environment");
3109
+ if (captured.bundle !== void 0) return this.error("Vorma requires one completed client output");
3110
+ captured.bundle = bundle;
3111
+ if (criticalCssFileName !== void 0) {
3112
+ const stylesheet = bundle[criticalCssFileName];
3113
+ if (stylesheet === void 0 || stylesheet.type !== "asset") return this.error("Vite did not preserve the compiled critical stylesheet");
3114
+ criticalCss = typeof stylesheet.source === "string" ? stylesheet.source : new TextDecoder().decode(stylesheet.source);
3115
+ const sourcePath = generation.config.criticalCssEntryFilePath;
3116
+ const source = sourcePath === void 0 ? null : this.getModuleInfo(sourcePath);
3117
+ if (!((source?.importers.length ?? 0) > 0 || (source?.dynamicImporters.length ?? 0) > 0 || Object.values(bundle).some((output) => {
3118
+ return output.type === "chunk" && (output.viteMetadata?.importedCss.has(stylesheet.fileName) || output.viteMetadata?.importedAssets.has(stylesheet.fileName));
3119
+ }))) delete bundle[stylesheet.fileName];
3120
+ else immutableFiles.add(stylesheet.fileName);
3121
+ }
3122
+ for (const [setting, defaultName] of [[config.build.manifest, ".vite/manifest.json"], [config.build.ssrManifest, ".vite/ssr-manifest.json"]]) if (setting) manifestFiles.add(typeof setting === "string" ? setting : defaultName);
3123
+ }
3124
+ }
3125
+ };
3126
+ const referencedAssets = /* @__PURE__ */ new Map();
3127
+ function forClient(plugin) {
3128
+ return {
3129
+ ...plugin,
3130
+ applyToEnvironment: (environment) => environment.name === "client"
3131
+ };
3132
+ }
3133
+ await build({
3134
+ configFile: generation.config.viteConfigFilePath,
3135
+ mode: "production",
3136
+ plugins: [
3137
+ plugin,
3138
+ forClient({
3139
+ name: "vorma:critical-css",
3140
+ generateBundle: {
3141
+ order: "pre",
3142
+ handler(_options, bundle) {
3143
+ const critical = findCriticalCssFileName({
3144
+ sourcePath: generation.config.criticalCssEntryFilePath,
3145
+ outputs: Object.values(bundle)
3146
+ });
3147
+ if (Result.isFailure(critical)) return this.error(critical.failure);
3148
+ criticalCssFileName = critical.success;
3149
+ }
3150
+ }
3151
+ }),
3152
+ forClient(publicCss.plugin),
3153
+ forClient(createClientPublicUrlPlugin({
3154
+ sites: generation.inspection.publicUrlSites,
3155
+ sourceTexts: generation.inspection.clientSourceTexts,
3156
+ publicAssets: generation.publicAssets.assets,
3157
+ publicDirectory: generation.config.publicDirectory,
3158
+ referencedAssets
3159
+ })),
3160
+ forClient(createClientOutputHashingPlugin(immutableFiles))
3161
+ ]
3162
+ });
3163
+ return {
3164
+ result: captured.bundle === void 0 ? void 0 : { output: Object.values(captured.bundle) },
3165
+ criticalCss,
3166
+ manifestFiles,
3167
+ immutableFiles,
3168
+ referencedAssets,
3169
+ cssAssets: publicCss.cssAssets,
3170
+ javascriptAssets: publicCss.javascriptAssets
3171
+ };
3172
+ }
3173
+ //#endregion
3174
+ //#region src/build/index.ts
3175
+ /**
3176
+ * Builds and publishes one production application while holding all generated
3177
+ * output ownership. `configFilePath` is the absolute path of the configuration
3178
+ * file; every configured path resolves against its directory.
3179
+ */
3180
+ function buildApplication(input) {
3181
+ return Effect.scoped(Effect.gen(function* () {
3182
+ const resolved = yield* resolveVormaConfig(input).pipe(Effect.flatMap(loadApplicationSourceLayout));
3183
+ yield* acquireOutputOwnership({
3184
+ lockDirectory: resolved.clientGeneratedDirectory,
3185
+ boundaryDirectories: [resolved.serverGeneratedDirectory],
3186
+ kind: "build"
3187
+ });
3188
+ yield* preparePublicOutput(resolved);
3189
+ const generation = yield* prepareApplicationGeneration(resolved);
3190
+ yield* writeAndCheckApplicationGeneration(generation);
3191
+ const { artifacts, server } = yield* Effect.all({
3192
+ artifacts: bundleClient(generation).pipe(Effect.flatMap((client) => {
3193
+ return assembleApplicationArtifacts({
3194
+ generation,
3195
+ client
3196
+ });
3197
+ })),
3198
+ server: compileServerApplication({
3199
+ generation,
3200
+ sourcemap: false
3201
+ })
3202
+ }, { concurrency: 2 });
3203
+ yield* Effect.forEach(server.diagnostics, (diagnostic) => {
3204
+ return Effect.logWarning(`Server compilation ${diagnostic.level}${diagnostic.code === "" ? "" : ` (${diagnostic.code})`}: ${diagnostic.message}`);
3205
+ }, { discard: true });
3206
+ return yield* publishApplicationArtifacts({
3207
+ generation,
3208
+ artifacts,
3209
+ server
3210
+ });
3211
+ }));
3212
+ }
3213
+ //#endregion
3214
+ export { toContentDigest as C, encodeCanonicalJson as S, compileServerApplication as _, acquireOutputOwnership as a, getClientViewEntries as b, writeAndCheckApplicationGeneration as c, withTypescriptProjects as d, inspectProjects as f, createClientPublicUrlPlugin as g, createCssPublicAssets as h, publishApplicationArtifacts as i, collectPublicAssets as l, resolveVormaConfig as m, loadApplicationSourceLayout as n, checkOutputPath as o, getGeneratedSourceDirectories as p, preparePublicOutput as r, assembleApplicationGeneration as s, buildApplication as t, readTypescriptProjectRoots as u, containsRelativePath as v, projectBrowserContracts as x, CLIENT_SHARED_PACKAGES as y };