@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,1256 @@
1
+ #!/usr/bin/env node
2
+ import { C as toContentDigest, S as encodeCanonicalJson, _ as compileServerApplication, a as acquireOutputOwnership, b as getClientViewEntries, c as writeAndCheckApplicationGeneration, d as withTypescriptProjects, f as inspectProjects, g as createClientPublicUrlPlugin, h as createCssPublicAssets, i as publishApplicationArtifacts, l as collectPublicAssets, m as resolveVormaConfig, n as loadApplicationSourceLayout, o as checkOutputPath, p as getGeneratedSourceDirectories, r as preparePublicOutput, s as assembleApplicationGeneration, t as buildApplication, u as readTypescriptProjectRoots, v as containsRelativePath, x as projectBrowserContracts, y as CLIENT_SHARED_PACKAGES } from "../build-JgARonaY.js";
3
+ import { g as DEVELOPMENT_PROTOCOL, y as FRAMEWORK_IDENTITY_PROTOCOL } from "../value-transport-Bst4Hk-n.js";
4
+ import { t as VORMA_BUILD_PLUGIN_NAME } from "../build-invocation-D7Fu8gBK.js";
5
+ import { VormaConfigSchema } from "../server/config/index.js";
6
+ import { NodeChildProcessSpawner, NodeCrypto, NodeFileSystem, NodePath, NodeRuntime, NodeSocketServer, NodeStdio, NodeTerminal } from "@effect/platform-node-shared";
7
+ import { Cause, Console, Deferred, Effect, Exit, Fiber, FileSystem, Layer, Path, Queue, Result, Schedule, Schema, ScopedRef, Stream } from "effect";
8
+ import { Command, Flag } from "effect/unstable/cli";
9
+ import { createFilter, createServer, loadConfigFromFile, normalizePath } from "vite";
10
+ import { dirname, isAbsolute, posix, relative, sep } from "node:path";
11
+ import { RolldownMagicString } from "rolldown";
12
+ import { createServer as createServer$1 } from "node:http";
13
+ import { FetchHttpClient, HttpClient } from "effect/unstable/http";
14
+ import { ChildProcess } from "effect/unstable/process";
15
+ import { watch } from "chokidar";
16
+ //#region package.json
17
+ var version = "0.87.0-pre.0";
18
+ //#endregion
19
+ //#region src/build/development-artifacts.ts
20
+ function toDevelopmentModuleUrl(input) {
21
+ const pathname = input.id.startsWith("\0") ? `/@id/__x00__${input.id.slice(1)}` : `/@fs/${normalizePath(input.id).replace(/^\//, "")}`;
22
+ return input.origin + pathname.split("/").map(encodeURIComponent).join("/").replaceAll("%40", "@").replaceAll("%3A", ":");
23
+ }
24
+ /** Keeps source URLs and browser contracts on the server; responses project only requested views. */
25
+ function assembleDevelopmentArtifacts(input) {
26
+ const generation = input.build.generation.application;
27
+ function toModule(id) {
28
+ return {
29
+ importUrl: toDevelopmentModuleUrl({
30
+ origin: input.client.origin,
31
+ id
32
+ }),
33
+ depUrls: [],
34
+ cssBundleUrls: [],
35
+ staticAssetUrls: []
36
+ };
37
+ }
38
+ const clientViews = Object.fromEntries(getClientViewEntries(generation).map((view) => {
39
+ return [view.pattern, {
40
+ ...toModule(view.id),
41
+ exportNames: view.exportNames
42
+ }];
43
+ }));
44
+ return { metadata: {
45
+ runtimeMode: "development",
46
+ clientBuildId: toContentDigest(encodeCanonicalJson({
47
+ contracts: projectBrowserContracts(generation.inspection),
48
+ clientTransport: generation.clientTransport.source,
49
+ views: clientViews,
50
+ publicFilemap: generation.publicAssets.publicFilemap
51
+ })).slice(0, 24),
52
+ clientEntry: toModule(generation.config.clientEntryFilePath),
53
+ clientViews,
54
+ criticalCss: input.build.stylesheet.css,
55
+ developmentHeadHtml: input.developmentHeadHtml,
56
+ publicFilemap: generation.publicAssets.publicFilemap,
57
+ publicFiles: generation.publicAssets.assets.map((asset) => {
58
+ return {
59
+ url: asset.url,
60
+ fileName: asset.fileName,
61
+ immutable: true
62
+ };
63
+ })
64
+ } };
65
+ }
66
+ //#endregion
67
+ //#region src/build/development-protocol.ts
68
+ function toDevelopmentClientInputs(generation, sources) {
69
+ return {
70
+ viteConfigFilePath: generation.config.viteConfigFilePath,
71
+ clientEntryFilePath: generation.config.clientEntryFilePath,
72
+ clientViewModules: generation.model.clientViewModules,
73
+ coordinatedResolutionFiles: sources.resolutionFiles,
74
+ generatedDirectories: [generation.config.serverGeneratedDirectory, generation.config.clientGeneratedDirectory],
75
+ clientTransport: generation.clientTransport,
76
+ views: getClientViewEntries(generation),
77
+ publicUrlSites: generation.inspection.publicUrlSites,
78
+ clientSourceTexts: Object.fromEntries(generation.inspection.clientSourceTexts),
79
+ publicAssets: generation.publicAssets.assets,
80
+ publicFilemap: generation.publicAssets.publicFilemap,
81
+ publicDirectory: generation.config.publicDirectory
82
+ };
83
+ }
84
+ //#endregion
85
+ //#region src/build/development-stylesheet.ts
86
+ /** Reads CSS and its dependency files from Vite's native direct-CSS pipeline. */
87
+ function readDevelopmentStylesheet(input) {
88
+ return Effect.gen(function* () {
89
+ const environment = input.server.environments.client;
90
+ if (environment === void 0) return yield* Effect.fail({
91
+ _tag: "DevelopmentStylesheetIssue",
92
+ filePath: input.filePath,
93
+ cause: "Vite has no client environment"
94
+ });
95
+ const url = `/@fs/${normalizePath(input.filePath)}?direct`;
96
+ const transformed = yield* Effect.tryPromise({
97
+ try: async () => {
98
+ const previous = await environment.moduleGraph.getModuleByUrl(url);
99
+ if (previous !== void 0) environment.moduleGraph.invalidateModule(previous);
100
+ return {
101
+ result: await environment.transformRequest(url),
102
+ module: await environment.moduleGraph.getModuleByUrl(url)
103
+ };
104
+ },
105
+ catch: (cause) => {
106
+ return {
107
+ _tag: "DevelopmentStylesheetIssue",
108
+ filePath: input.filePath,
109
+ cause
110
+ };
111
+ }
112
+ });
113
+ if (transformed.result === null || transformed.module === void 0) return yield* Effect.fail({
114
+ _tag: "DevelopmentStylesheetIssue",
115
+ filePath: input.filePath,
116
+ cause: "Vite did not produce the requested stylesheet"
117
+ });
118
+ const visited = /* @__PURE__ */ new Set();
119
+ const pending = [transformed.module];
120
+ const files = /* @__PURE__ */ new Set();
121
+ while (pending.length > 0) {
122
+ const module = pending.pop();
123
+ if (module === void 0 || visited.has(module)) continue;
124
+ visited.add(module);
125
+ if (module.file !== null) files.add(module.file);
126
+ pending.push(...module.importedModules);
127
+ }
128
+ return {
129
+ css: transformed.result.code,
130
+ inputFilePaths: [...files]
131
+ };
132
+ });
133
+ }
134
+ //#endregion
135
+ //#region src/build/development-build.ts
136
+ /** Prepares server bytes and native CSS concurrently after their generated inputs exist. */
137
+ function prepareDevelopmentBuild(input) {
138
+ return Effect.gen(function* () {
139
+ const { generation, client, previous } = input;
140
+ const { application } = generation;
141
+ const inputsChanged = previous === void 0 || previous.work.inspectSources || previous.work.capturePublicAssets;
142
+ return {
143
+ generation,
144
+ ...yield* Effect.all({
145
+ server: previous === void 0 || previous.work.compileServer ? compileServerApplication({
146
+ generation: application,
147
+ sourcemap: "inline"
148
+ }) : Effect.succeed(previous.build.server),
149
+ stylesheet: Effect.gen(function* () {
150
+ if (previous !== void 0 && inputsChanged) client.updateInputsFn(toDevelopmentClientInputs(application, generation.sources));
151
+ if (previous !== void 0 && !previous.work.prepareCriticalCss) return previous.build.stylesheet;
152
+ const filePath = application.config.criticalCssEntryFilePath;
153
+ return filePath === void 0 ? {
154
+ css: "",
155
+ inputFilePaths: []
156
+ } : yield* readDevelopmentStylesheet({
157
+ server: client.server,
158
+ filePath
159
+ });
160
+ })
161
+ }, { concurrency: 2 })
162
+ };
163
+ });
164
+ }
165
+ /** Browser-only development controls use Vite's authenticated HMR connection. */
166
+ function renderDevelopmentBrowserSource() {
167
+ return String.raw`
168
+ let rebuilding;
169
+ let buildState = "ready";
170
+ let revalidations = 0;
171
+ function showRebuilding(failed) {
172
+ if (!rebuilding) {
173
+ rebuilding = document.createElement("div");
174
+ rebuilding.id = "vorma-rebuilding-overlay";
175
+ rebuilding.style.cssText = "position:fixed;bottom:16px;right:16px;z-index:2147483647;color:white;padding:10px 16px;border-radius:4px;font:14px system-ui";
176
+ document.body.append(rebuilding);
177
+ }
178
+ rebuilding.textContent = failed ? "Build error. See the console for details." : "Rebuilding…";
179
+ rebuilding.setAttribute("role", failed ? "alert" : "status");
180
+ rebuilding.style.backgroundColor = failed ? "#700" : "#222";
181
+ }
182
+ function hideRebuilding() {
183
+ rebuilding?.remove();
184
+ rebuilding = undefined;
185
+ }
186
+ function updateRebuilding() {
187
+ if (buildState === "error") showRebuilding(true);
188
+ else if (buildState === "rebuilding" || revalidations > 0) showRebuilding(false);
189
+ else hideRebuilding();
190
+ }
191
+ if (import.meta.hot) {
192
+ import.meta.hot.on("vorma:development", (message) => {
193
+ if (message.kind === "rebuilding") {
194
+ buildState = "rebuilding";
195
+ updateRebuilding();
196
+ } else if (message.kind === "error") {
197
+ console.error("[vorma] Build error", message.message);
198
+ buildState = "error";
199
+ updateRebuilding();
200
+ } else if (message.kind === "ready") {
201
+ buildState = "ready";
202
+ updateRebuilding();
203
+ } else if (message.kind === "css") {
204
+ let style = document.getElementById("vorma-critical-css");
205
+ if (!style) {
206
+ style = document.createElement("style");
207
+ style.id = "vorma-critical-css";
208
+ document.head.append(style);
209
+ }
210
+ style.textContent = message.css;
211
+ } else if (message.kind === "revalidate") {
212
+ buildState = "ready";
213
+ revalidations++;
214
+ updateRebuilding();
215
+ // Vite awaits event handlers; a data request must not block later CSS or build messages.
216
+ Promise.resolve().then(() => window.__vorma_development?.revalidateFn()).then(
217
+ () => {},
218
+ (cause) => console.error("[vorma] Data revalidation failed", cause),
219
+ ).then(() => {
220
+ revalidations--;
221
+ updateRebuilding();
222
+ });
223
+ }
224
+ });
225
+ import.meta.hot.dispose(hideRebuilding);
226
+ }
227
+ `;
228
+ }
229
+ //#endregion
230
+ //#region src/build/development-listener.ts
231
+ /** Hosts only Vite middleware and its WebSocket; the application has its own server. */
232
+ function createDevelopmentListener(port) {
233
+ return Effect.gen(function* () {
234
+ function refuseRequestFn(...[, response]) {
235
+ response.writeHead(503).end();
236
+ }
237
+ let handleRequestFn = refuseRequestFn;
238
+ const server = yield* Effect.acquireRelease(Effect.sync(() => {
239
+ return createServer$1((request, response) => handleRequestFn(request, response));
240
+ }), (server) => {
241
+ return Effect.callback((resume) => {
242
+ server.close(() => resume(Effect.void));
243
+ server.closeAllConnections();
244
+ });
245
+ });
246
+ return {
247
+ server,
248
+ listen: Effect.callback((resume) => {
249
+ function failFn(cause) {
250
+ resume(Effect.fail({
251
+ _tag: "DevelopmentListenerIssue",
252
+ cause
253
+ }));
254
+ }
255
+ server.once("error", failFn);
256
+ server.listen(port, "127.0.0.1", () => {
257
+ server.off("error", failFn);
258
+ resume(Effect.void);
259
+ });
260
+ }),
261
+ port,
262
+ origin: `http://127.0.0.1:${port}`,
263
+ setHandlerFn(next) {
264
+ handleRequestFn = next;
265
+ }
266
+ };
267
+ });
268
+ }
269
+ //#endregion
270
+ //#region src/build/development-client.ts
271
+ /** Starts Vite for one inspected application; the surrounding Effect scope closes it. */
272
+ function startDevelopmentClient(inputs, port, recordChangeFn) {
273
+ return Effect.gen(function* () {
274
+ const listener = yield* createDevelopmentListener(port);
275
+ let activeServer;
276
+ let nativeRestart;
277
+ let configurationRevision = 0;
278
+ let coordinatedRestart = false;
279
+ let views = inputs.views;
280
+ const virtualSources = new Map(views.flatMap((view) => {
281
+ return view.source === void 0 ? [] : [[view.id, view.source]];
282
+ }));
283
+ let declared = false;
284
+ let inspection;
285
+ let finishInspectionFn;
286
+ function pauseInspectionFn(filePath) {
287
+ if (activeServer?.config.configFileDependencies.includes(normalizePath(filePath))) return;
288
+ if (inputs.clientSourceTexts[normalizePath(filePath)] !== void 0 && inspection === void 0) inspection = new Promise((resolve) => {
289
+ finishInspectionFn = resolve;
290
+ });
291
+ }
292
+ function resumeInspectionFn(reload) {
293
+ finishInspectionFn?.(reload);
294
+ inspection = void 0;
295
+ finishInspectionFn = void 0;
296
+ }
297
+ const integration = {
298
+ name: VORMA_BUILD_PLUGIN_NAME,
299
+ enforce: "pre",
300
+ api: { registerDeclarationFn: () => {
301
+ declared = true;
302
+ } },
303
+ config() {
304
+ declared = false;
305
+ return {
306
+ base: "/",
307
+ publicDir: false,
308
+ appType: "custom",
309
+ resolve: { dedupe: CLIENT_SHARED_PACKAGES },
310
+ oxc: {
311
+ jsxRefreshInclude: /\.[cm]?[jt]sx?(?:$|\?)/,
312
+ jsxRefreshExclude: [/\/node_modules\//, ...inputs.clientViewModules.map((file) => {
313
+ return new RegExp(`^${normalizePath(file).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\?.*)?$`);
314
+ })]
315
+ },
316
+ optimizeDeps: { entries: [inputs.clientEntryFilePath, ...inputs.clientViewModules] },
317
+ server: {
318
+ middlewareMode: true,
319
+ origin: listener.origin,
320
+ hmr: {
321
+ server: listener.server,
322
+ clientPort: listener.port
323
+ },
324
+ headers: { "cache-control": "no-store" },
325
+ watch: { ignored: (filePath) => {
326
+ const file = normalizePath(filePath);
327
+ return inputs.coordinatedResolutionFiles.has(file) || inputs.generatedDirectories.some((directory) => {
328
+ return file === normalizePath(directory) || file.startsWith(`${normalizePath(directory)}/`);
329
+ });
330
+ } }
331
+ }
332
+ };
333
+ },
334
+ configureServer(server) {
335
+ server.watcher.on("change", pauseInspectionFn);
336
+ server.watcher.on("unlink", pauseInspectionFn);
337
+ if (activeServer !== void 0 && !coordinatedRestart) {
338
+ nativeRestart = activeServer.restart();
339
+ nativeRestart.then(() => {
340
+ configurationRevision++;
341
+ recordChangeFn(inputs.viteConfigFilePath);
342
+ }, (cause) => {
343
+ server.config.logger.error(String(cause));
344
+ });
345
+ }
346
+ },
347
+ configResolved: {
348
+ order: "post",
349
+ handler() {
350
+ if (!declared) this.error("Vorma development requires vorma() from vorma/vite in the configured Vite plugins list.");
351
+ }
352
+ },
353
+ resolveId(source) {
354
+ if (source === "\0vorma:development-browser") return source;
355
+ if (virtualSources.has(source)) return source;
356
+ return source === FRAMEWORK_IDENTITY_PROTOCOL.moduleSpecifiers.clientTransportTypes ? inputs.clientTransport.filePath : null;
357
+ },
358
+ async load(id) {
359
+ if (id === "\0vorma:development-browser") return renderDevelopmentBrowserSource();
360
+ if (inputs.clientSourceTexts[id.split("?", 1)[0] ?? id] !== void 0) await inspection;
361
+ return virtualSources.get(id) ?? null;
362
+ },
363
+ async hotUpdate() {
364
+ if (await inspection) return [];
365
+ },
366
+ transform(code, id) {
367
+ if (!views.some((view) => view.id === id)) return null;
368
+ const source = new RolldownMagicString(code);
369
+ source.append("\nif (import.meta.hot) { import.meta.hot.accept((module) => { if (module) window.__vorma_development?.updateModuleFn(import.meta.url, module); }); }");
370
+ return {
371
+ code: source.toString(),
372
+ map: source.generateMap({ hires: true }).toString()
373
+ };
374
+ }
375
+ };
376
+ const publicCss = createCssPublicAssets(inputs);
377
+ function getPublicUrlInput(application) {
378
+ return {
379
+ sites: application.publicUrlSites,
380
+ sourceTexts: new Map(Object.entries(application.clientSourceTexts)),
381
+ publicAssets: application.publicAssets,
382
+ publicDirectory: application.publicDirectory,
383
+ referencedAssets: /* @__PURE__ */ new Map()
384
+ };
385
+ }
386
+ const publicUrls = createClientPublicUrlPlugin(getPublicUrlInput(inputs));
387
+ const server = yield* Effect.acquireRelease(Effect.tryPromise({
388
+ try: () => {
389
+ return createServer({
390
+ configFile: inputs.viteConfigFilePath,
391
+ plugins: [
392
+ integration,
393
+ publicCss.plugin,
394
+ publicUrls
395
+ ]
396
+ });
397
+ },
398
+ catch: (cause) => {
399
+ return {
400
+ _tag: "DevelopmentClientIssue",
401
+ operation: "create",
402
+ cause
403
+ };
404
+ }
405
+ }), (server) => {
406
+ return Effect.tryPromise({
407
+ try: async () => {
408
+ await nativeRestart;
409
+ return Promise.all(Object.values(server.environments).map((environment) => {
410
+ return environment.waitForRequestsIdle();
411
+ }));
412
+ },
413
+ catch: (cause) => {
414
+ return {
415
+ _tag: "DevelopmentClientIssue",
416
+ operation: "close",
417
+ cause
418
+ };
419
+ }
420
+ }).pipe(Effect.ensuring(Effect.tryPromise(() => server.close()).pipe(Effect.catch((issue) => {
421
+ return Effect.logError("Could not close Vite", issue);
422
+ }))), Effect.catch((issue) => {
423
+ return Effect.logError("Could not close Vite development resources", issue);
424
+ }));
425
+ });
426
+ activeServer = server;
427
+ yield* Effect.addFinalizer(() => Effect.sync(() => resumeInspectionFn(true)));
428
+ listener.setHandlerFn((request, response) => {
429
+ server.middlewares(request, response, () => response.writeHead(404).end());
430
+ });
431
+ return {
432
+ server,
433
+ get configurationRevision() {
434
+ return configurationRevision;
435
+ },
436
+ restart: Effect.suspend(() => {
437
+ coordinatedRestart = true;
438
+ return Effect.tryPromise(() => server.restart()).pipe(Effect.ensuring(Effect.sync(() => {
439
+ coordinatedRestart = false;
440
+ })));
441
+ }).pipe(Effect.uninterruptible),
442
+ origin: listener.origin,
443
+ listen: listener.listen,
444
+ get views() {
445
+ return views;
446
+ },
447
+ resumeInspectionFn,
448
+ readHeadHtml: Effect.tryPromise({
449
+ try: async () => {
450
+ const html = await server.transformIndexHtml("/", "<head></head>");
451
+ return html.slice(html.indexOf("<head>") + 6, html.lastIndexOf("</head>")).replaceAll("\"/@vite/client\"", JSON.stringify(`${listener.origin}/@vite/client`)).replaceAll("\"/@react-refresh\"", JSON.stringify(`${listener.origin}/@react-refresh`)) + `<script type="module" src="${listener.origin}/@id/__x00__vorma:development-browser"><\/script>`;
452
+ },
453
+ catch: (cause) => {
454
+ return {
455
+ _tag: "DevelopmentClientIssue",
456
+ operation: "create",
457
+ cause
458
+ };
459
+ }
460
+ }),
461
+ /** Replaces inspected sources and asset references within the existing Vite configuration. */
462
+ updateInputsFn(next) {
463
+ const replacedFiles = /* @__PURE__ */ new Set([...Object.keys(inputs.clientSourceTexts), ...Object.keys(next.clientSourceTexts)]);
464
+ if (inputs.clientTransport.source !== next.clientTransport.source) replacedFiles.add(next.clientTransport.filePath);
465
+ const publicAssetsChanged = JSON.stringify(inputs.publicFilemap) !== JSON.stringify(next.publicFilemap);
466
+ inputs = next;
467
+ views = next.views;
468
+ const previousVirtualIds = new Set(virtualSources.keys());
469
+ virtualSources.clear();
470
+ for (const view of views) if (view.source !== void 0) virtualSources.set(view.id, view.source);
471
+ publicUrls.api.updateInputFn(getPublicUrlInput(next));
472
+ publicCss.updatePublicAssetsFn(next);
473
+ for (const environment of Object.values(server.environments)) {
474
+ const graph = environment.moduleGraph;
475
+ for (const filePath of replacedFiles) for (const module of graph.getModulesByFile(filePath) ?? []) graph.invalidateModule(module);
476
+ for (const id of previousVirtualIds) {
477
+ const module = graph.getModuleById(id);
478
+ if (module !== void 0) graph.invalidateModule(module);
479
+ }
480
+ if (publicAssetsChanged) {
481
+ for (const module of graph.idToModuleMap.values()) if (module.type === "css") graph.invalidateModule(module);
482
+ }
483
+ }
484
+ }
485
+ };
486
+ });
487
+ }
488
+ //#endregion
489
+ //#region src/build/development-sources.ts
490
+ /** Inspects declarations and records native compiler inputs without replacing filesystem behavior. */
491
+ function inspectDevelopmentSources(layout) {
492
+ return Effect.suspend(() => {
493
+ const files = /* @__PURE__ */ new Set();
494
+ const directories = /* @__PURE__ */ new Set();
495
+ return withTypescriptProjects({
496
+ configDirectory: layout.configDirectory,
497
+ serverTsconfigFilePath: layout.serverTsconfigFilePath,
498
+ clientTsconfigFilePath: layout.clientTsconfigFilePath,
499
+ fileSystem: {
500
+ readFile(filePath) {
501
+ files.add(normalizePath(filePath));
502
+ },
503
+ getAccessibleEntries(directory) {
504
+ directories.add(normalizePath(directory));
505
+ }
506
+ },
507
+ useFn(projects) {
508
+ return Effect.gen(function* () {
509
+ const inspection = yield* inspectProjects({
510
+ projects,
511
+ layout
512
+ });
513
+ const sources = yield* Effect.try({
514
+ try: () => {
515
+ return {
516
+ serverRootFiles: new Set(projects.server.rootFiles.map(normalizePath)),
517
+ clientRootFiles: new Set(projects.client.rootFiles.map(normalizePath)),
518
+ serverFiles: new Set(projects.server.program.getSourceFileNames().map(normalizePath)),
519
+ clientFiles: new Set(projects.client.program.getSourceFileNames().map(normalizePath))
520
+ };
521
+ },
522
+ catch: (cause) => {
523
+ return {
524
+ _tag: "TypescriptProjectError",
525
+ message: "Could not read TypeScript development inputs",
526
+ cause
527
+ };
528
+ }
529
+ });
530
+ return {
531
+ inspection,
532
+ inputs: {
533
+ ...sources,
534
+ resolutionFiles: new Set([...files].filter((file) => {
535
+ return !sources.serverFiles.has(file) && !sources.clientFiles.has(file);
536
+ })),
537
+ directories
538
+ }
539
+ };
540
+ });
541
+ }
542
+ });
543
+ });
544
+ }
545
+ /** Watches native compiler inputs while keeping generated writes out of the event stream. */
546
+ function getDevelopmentSourceWatchPaths(input) {
547
+ return Effect.gen(function* () {
548
+ const path = yield* Path.Path;
549
+ const generatedDirectories = getGeneratedSourceDirectories(input.layout);
550
+ return [.../* @__PURE__ */ new Set([
551
+ ...input.sources.serverFiles,
552
+ ...input.sources.clientFiles,
553
+ ...input.sources.serverRootFiles,
554
+ ...input.sources.clientRootFiles,
555
+ ...input.sources.resolutionFiles,
556
+ ...input.sources.directories
557
+ ])].filter((filePath) => {
558
+ return !generatedDirectories.some((directory) => {
559
+ const relativePath = path.relative(directory, filePath);
560
+ return containsRelativePath({
561
+ relativePath,
562
+ separator: path.sep,
563
+ absolute: path.isAbsolute(relativePath)
564
+ });
565
+ });
566
+ });
567
+ });
568
+ }
569
+ //#endregion
570
+ //#region src/build/development-generation.ts
571
+ /** Captures independent source and public inputs concurrently, without activating a runtime. */
572
+ function prepareDevelopmentGeneration(input) {
573
+ return Effect.gen(function* () {
574
+ const previous = input.previous;
575
+ const prepared = yield* Effect.all({
576
+ sources: previous === void 0 || previous.work.inspectSources ? inspectDevelopmentSources(input.layout) : Effect.succeed({
577
+ inspection: previous.generation.application.inspection,
578
+ inputs: previous.generation.sources
579
+ }),
580
+ publicAssets: previous === void 0 || previous.work.capturePublicAssets ? collectPublicAssets(input.layout) : Effect.succeed(previous.generation.application.publicAssets)
581
+ }, { concurrency: "unbounded" });
582
+ const application = yield* assembleApplicationGeneration({
583
+ sourceLayout: input.layout,
584
+ inspection: prepared.sources.inspection,
585
+ publicAssets: prepared.publicAssets
586
+ });
587
+ if (previous === void 0 || previous.work.inspectSources || previous.work.capturePublicAssets) yield* writeAndCheckApplicationGeneration(application);
588
+ return {
589
+ application,
590
+ sources: prepared.sources.inputs
591
+ };
592
+ });
593
+ }
594
+ //#endregion
595
+ //#region src/build/development-patterns.ts
596
+ /** Ordered inclusion and exclusion rules use native Vite glob matching. */
597
+ function compileDevelopmentPatterns(patterns) {
598
+ const rules = patterns.flatMap((raw) => {
599
+ const trimmed = raw.trim();
600
+ const excluded = trimmed.startsWith("!");
601
+ const source = excluded ? trimmed.slice(1).trim() : trimmed;
602
+ const anchored = source.startsWith("/") || source.startsWith("./");
603
+ let pattern = normalizePath(source);
604
+ if (pattern === "") return [];
605
+ const directory = pattern.endsWith("/");
606
+ pattern = pattern.replace(/\/$/, "");
607
+ pattern = pattern.replace(/^\.\//, "").replace(/^\//, "");
608
+ if (!anchored && !pattern.includes("/") && pattern !== ".") pattern = `**/${pattern}`;
609
+ const matchFn = createFilter(pattern, void 0, { resolve: false });
610
+ const prefix = pattern.split("/").filter((_segment, index, segments) => {
611
+ return segments.slice(0, index + 1).every((segment) => !/[*?{[]/.test(segment));
612
+ });
613
+ return [{
614
+ pattern,
615
+ directory,
616
+ excluded,
617
+ matchFn,
618
+ root: prefix.slice(0, -1).join("/") || "."
619
+ }];
620
+ });
621
+ return {
622
+ roots: rules.filter((rule) => !rule.excluded).map((rule) => rule.root),
623
+ matchesFn(relativePath) {
624
+ const segments = posix.normalize(normalizePath(relativePath)).split("/");
625
+ let matched = false;
626
+ for (const rule of rules) if (rule.pattern === "." || segments.some((_segment, index) => {
627
+ return (!rule.directory || index < segments.length - 1) && rule.matchFn(segments.slice(0, index + 1).join("/"));
628
+ })) matched = !rule.excluded;
629
+ return matched;
630
+ }
631
+ };
632
+ }
633
+ //#endregion
634
+ //#region src/build/development-server-process.ts
635
+ /** Reserves a port until the enclosing allocation scope closes. */
636
+ function getDevelopmentServerPort() {
637
+ return NodeSocketServer.make({
638
+ host: "127.0.0.1",
639
+ port: 0
640
+ }).pipe(Effect.map(({ address }) => {
641
+ if (address._tag === "UnixPathAddress") throw new Error("A TCP port probe returned a Unix socket address");
642
+ return address.port;
643
+ }));
644
+ }
645
+ /** Runs the authored server entry; its Effect scope owns process termination. */
646
+ function startDevelopmentServerProcess(input) {
647
+ return Effect.gen(function* () {
648
+ const process = yield* ChildProcess.make(globalThis.process.execPath, ["--enable-source-maps", input.layout.compiledServerEntryFilePath], {
649
+ env: { [DEVELOPMENT_PROTOCOL.serverPortEnvironmentVariable]: String(input.port) },
650
+ extendEnv: true,
651
+ stdin: "ignore",
652
+ stdout: "inherit",
653
+ stderr: "inherit",
654
+ forceKillAfter: "2 seconds"
655
+ });
656
+ const unexpectedExit = process.exitCode.pipe(Effect.flatMap((exitCode) => {
657
+ return Effect.fail({
658
+ _tag: "DevelopmentServerExit",
659
+ exitCode
660
+ });
661
+ }));
662
+ const origin = `http://127.0.0.1:${input.port}`;
663
+ const url = `${origin}${DEVELOPMENT_PROTOCOL.readinessPath}`;
664
+ yield* Effect.gen(function* () {
665
+ yield* (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatus((status) => status === 200)).get(url).pipe(Effect.flatMap((response) => response.text), Effect.timeout("1 second"), Effect.retry(Schedule.forever.pipe(Schedule.modifyDelay(({ output }) => {
666
+ return Effect.succeed(Math.min((output + 1) * 20, 200));
667
+ }))));
668
+ }).pipe(Effect.provide(FetchHttpClient.layer), Effect.timeoutOrElse({
669
+ duration: "10 seconds",
670
+ orElse: () => {
671
+ return Effect.fail({
672
+ _tag: "DevelopmentServerReadinessIssue",
673
+ url
674
+ });
675
+ }
676
+ }), Effect.raceFirst(unexpectedExit));
677
+ return {
678
+ process,
679
+ origin,
680
+ unexpectedExit
681
+ };
682
+ });
683
+ }
684
+ //#endregion
685
+ //#region src/build/development-changes.ts
686
+ /**
687
+ * Batches native watcher events after 30 ms without another event. Completed
688
+ * batches accumulate while the consumer rebuilds; no changed path is discarded.
689
+ */
690
+ function createDevelopmentChanges() {
691
+ return Effect.gen(function* () {
692
+ const notifications = yield* Queue.make();
693
+ const ready = yield* Queue.make({
694
+ capacity: 1,
695
+ strategy: "sliding"
696
+ });
697
+ let incoming = /* @__PURE__ */ new Set();
698
+ let pending = /* @__PURE__ */ new Set();
699
+ function recordChangeFn(filePath) {
700
+ Queue.offerUnsafe(notifications, normalizePath(filePath));
701
+ }
702
+ function recordErrorFn(cause) {
703
+ Queue.failCauseUnsafe(notifications, Cause.fail({
704
+ _tag: "DevelopmentWatchIssue",
705
+ cause
706
+ }));
707
+ }
708
+ yield* Stream.fromQueue(notifications).pipe(Stream.tap((filePath) => Effect.sync(() => incoming.add(filePath))), Stream.debounce("30 millis"), Stream.runForEach(() => {
709
+ return Effect.sync(() => {
710
+ for (const filePath of incoming) pending.add(filePath);
711
+ incoming = /* @__PURE__ */ new Set();
712
+ Queue.offerUnsafe(ready, void 0);
713
+ });
714
+ }), Effect.catch((issue) => Queue.fail(ready, issue)), Effect.forkScoped);
715
+ return {
716
+ take: Effect.gen(function* () {
717
+ while (true) {
718
+ yield* Queue.take(ready);
719
+ if (pending.size > 0) {
720
+ const batch = pending;
721
+ pending = /* @__PURE__ */ new Set();
722
+ return batch;
723
+ }
724
+ }
725
+ }),
726
+ recordChangeFn,
727
+ recordErrorFn
728
+ };
729
+ });
730
+ }
731
+ //#endregion
732
+ //#region src/build/development-watcher.ts
733
+ function containsPath(input) {
734
+ const relativePath = relative(input.directory, input.filePath);
735
+ return containsRelativePath({
736
+ relativePath,
737
+ separator: sep,
738
+ absolute: isAbsolute(relativePath)
739
+ });
740
+ }
741
+ /** Application watching survives Vite restarts and failed replacement builds. */
742
+ function createDevelopmentWatcher(input) {
743
+ return Effect.gen(function* () {
744
+ const changes = yield* createDevelopmentChanges();
745
+ function matchesIgnoredPath(filePath) {
746
+ return /(?:^|[/\\])(?:node_modules|\.git)(?:[/\\]|$)/.test(filePath);
747
+ }
748
+ function acquireWatcher(plan) {
749
+ return Effect.gen(function* () {
750
+ const watcher = yield* Effect.acquireRelease(Effect.sync(() => {
751
+ return watch([...plan.paths], {
752
+ ignoreInitial: true,
753
+ ignored: (filePath) => {
754
+ return matchesIgnoredPath(filePath) || plan.generatedDirectories.some((directory) => {
755
+ return containsPath({
756
+ directory,
757
+ filePath
758
+ });
759
+ });
760
+ }
761
+ });
762
+ }), (watcher) => Effect.promise(() => watcher.close()));
763
+ watcher.on("all", (_event, filePath) => changes.recordChangeFn(filePath));
764
+ watcher.on("error", changes.recordErrorFn);
765
+ yield* Effect.callback((resume) => {
766
+ function onReady() {
767
+ resume(Effect.void);
768
+ }
769
+ function onError(cause) {
770
+ resume(Effect.fail({
771
+ _tag: "DevelopmentWatchIssue",
772
+ cause
773
+ }));
774
+ }
775
+ watcher.once("ready", onReady);
776
+ watcher.once("error", onError);
777
+ return Effect.sync(() => {
778
+ watcher.off("ready", onReady);
779
+ watcher.off("error", onError);
780
+ });
781
+ });
782
+ return plan;
783
+ });
784
+ }
785
+ const reference = yield* ScopedRef.fromAcquire(acquireWatcher({
786
+ paths: [dirname(input.configFilePath)],
787
+ generatedDirectories: input.generatedDirectories
788
+ }));
789
+ return {
790
+ ...changes,
791
+ watchPathsFn(next) {
792
+ return Effect.gen(function* () {
793
+ const paths = [...new Set([dirname(input.configFilePath), ...next.paths].filter((filePath) => !matchesIgnoredPath(filePath)).map(normalizePath))].sort();
794
+ const plan = {
795
+ paths: paths.filter((filePath) => {
796
+ return !paths.some((directory) => {
797
+ return directory !== filePath && containsPath({
798
+ directory,
799
+ filePath
800
+ });
801
+ });
802
+ }),
803
+ generatedDirectories: [...next.generatedDirectories].sort()
804
+ };
805
+ if (JSON.stringify(yield* ScopedRef.get(reference)) !== JSON.stringify(plan)) yield* ScopedRef.set(reference, acquireWatcher(plan));
806
+ });
807
+ }
808
+ };
809
+ });
810
+ }
811
+ //#endregion
812
+ //#region src/build/development-work.ts
813
+ /** Classifies application work; ordinary browser modules remain Vite's HMR inputs. */
814
+ function classifyDevelopmentChanges(input) {
815
+ return Effect.gen(function* () {
816
+ const path = yield* Path.Path;
817
+ const { layout, inputs } = input;
818
+ const generated = getGeneratedSourceDirectories(layout);
819
+ function matchesGeneratedPath(filePath) {
820
+ return generated.some((directory) => {
821
+ return containsFilePath({
822
+ path,
823
+ directory,
824
+ filePath
825
+ });
826
+ });
827
+ }
828
+ const changedPaths = [...input.changedPaths].map(normalizePath).filter((filePath) => !matchesGeneratedPath(filePath));
829
+ const revalidate = compileDevelopmentPatterns(layout.dev?.revalidateClientOnChange ?? []);
830
+ let restartConfiguration = false;
831
+ let inspectSources = false;
832
+ let compileServer = false;
833
+ let capturePublicAssets = false;
834
+ let prepareCriticalCss = false;
835
+ let revalidateClient = false;
836
+ let checkProjectMembership = false;
837
+ for (const filePath of changedPaths) {
838
+ restartConfiguration ||= inputs.configurationFiles.has(filePath) || filePath === normalizePath(layout.configFilePath);
839
+ if (!inputs.configurationFiles.has(filePath) && inputs.viteConfigurationFiles.has(filePath)) continue;
840
+ const serverSource = inputs.sources.serverFiles.has(filePath) || inputs.sources.serverRootFiles.has(filePath);
841
+ const clientSource = inputs.sources.clientFiles.has(filePath) || inputs.sources.clientRootFiles.has(filePath);
842
+ const resolutionSource = inputs.sources.resolutionFiles.has(filePath);
843
+ inspectSources ||= serverSource || clientSource || resolutionSource;
844
+ compileServer ||= serverSource || resolutionSource || inputs.serverFiles.has(filePath);
845
+ capturePublicAssets ||= containsFilePath({
846
+ path,
847
+ directory: layout.publicAssetsSourceDirectory,
848
+ filePath
849
+ });
850
+ prepareCriticalCss ||= inputs.criticalCssFiles.has(filePath) || filePath === layout.criticalCssEntryFilePath;
851
+ revalidateClient ||= (layout.dev?.revalidateClientOnChange?.length ?? 0) > 0 && revalidate.matchesFn(path.relative(layout.configDirectory, filePath));
852
+ checkProjectMembership ||= !serverSource && !clientSource && !resolutionSource;
853
+ }
854
+ if (checkProjectMembership && !restartConfiguration) {
855
+ const roots = yield* readTypescriptProjectRoots(layout);
856
+ function differsFromPreviousFiles(previous, files) {
857
+ const next = new Set(files.map(normalizePath).filter((file) => !matchesGeneratedPath(file)));
858
+ const old = [...previous].filter((file) => !matchesGeneratedPath(file));
859
+ return old.length !== next.size || old.some((file) => !next.has(file));
860
+ }
861
+ const serverMembershipChanged = differsFromPreviousFiles(inputs.sources.serverRootFiles, roots.server);
862
+ const clientMembershipChanged = differsFromPreviousFiles(inputs.sources.clientRootFiles, roots.client);
863
+ inspectSources ||= serverMembershipChanged || clientMembershipChanged;
864
+ compileServer ||= serverMembershipChanged;
865
+ }
866
+ return {
867
+ restartConfiguration,
868
+ inspectSources,
869
+ compileServer,
870
+ capturePublicAssets,
871
+ prepareCriticalCss: layout.criticalCssEntryFilePath !== void 0 && (prepareCriticalCss || capturePublicAssets),
872
+ revalidateClient
873
+ };
874
+ });
875
+ }
876
+ function containsFilePath(input) {
877
+ const relativePath = input.path.relative(input.directory, input.filePath);
878
+ return containsRelativePath({
879
+ relativePath,
880
+ separator: input.path.sep,
881
+ absolute: input.path.isAbsolute(relativePath)
882
+ });
883
+ }
884
+ //#endregion
885
+ //#region src/build/load-config.ts
886
+ /** Vite's configuration loader recompiles configuration dependencies on each development restart. */
887
+ function loadDevelopmentConfig(configPath) {
888
+ return Effect.gen(function* () {
889
+ const configFilePath = (yield* Path.Path).resolve(configPath);
890
+ const loaded = yield* Effect.tryPromise({
891
+ try: () => {
892
+ return loadConfigFromFile({
893
+ command: "serve",
894
+ mode: "development"
895
+ }, configFilePath);
896
+ },
897
+ catch: (cause) => {
898
+ return {
899
+ _tag: "ConfigLoadError",
900
+ message: `Could not load Vorma config: ${configFilePath}`,
901
+ cause
902
+ };
903
+ }
904
+ });
905
+ return {
906
+ config: yield* Schema.decodeUnknownEffect(VormaConfigSchema)(loaded?.config),
907
+ configFilePath,
908
+ dependencies: loaded?.dependencies ?? []
909
+ };
910
+ });
911
+ }
912
+ /**
913
+ * Imports the explicitly selected configuration using the CLI host's module
914
+ * loader and reports the absolute path of the file it was loaded from,
915
+ * because every configured path is relative to that file's directory.
916
+ */
917
+ function loadVormaConfig(configPath) {
918
+ return Effect.gen(function* () {
919
+ const path = yield* Path.Path;
920
+ const absolutePath = path.resolve(configPath);
921
+ const url = yield* path.toFileUrl(absolutePath);
922
+ const module = yield* Effect.tryPromise({
923
+ try: () => import(url.href),
924
+ catch: (cause) => {
925
+ return {
926
+ _tag: "ConfigLoadError",
927
+ message: `Could not import Vorma config: ${absolutePath}`,
928
+ cause
929
+ };
930
+ }
931
+ });
932
+ return {
933
+ config: yield* Schema.decodeUnknownEffect(VormaConfigSchema)(module.default).pipe(Effect.mapError((cause) => {
934
+ return {
935
+ _tag: "ConfigLoadError",
936
+ message: `Expected a default-exported Vorma config in ${absolutePath}: ${cause.message}`,
937
+ cause
938
+ };
939
+ })),
940
+ configFilePath: absolutePath
941
+ };
942
+ });
943
+ }
944
+ //#endregion
945
+ //#region src/build/development.ts
946
+ /** Runs until interrupted; configuration replacement releases the complete previous session. */
947
+ function developApplication(configFilePath) {
948
+ return Effect.gen(function* () {
949
+ const { port, vitePort } = yield* Effect.scoped(Effect.all({
950
+ port: getDevelopmentServerPort(),
951
+ vitePort: getDevelopmentServerPort()
952
+ }));
953
+ let loaded = yield* loadDevelopmentConfig(configFilePath);
954
+ const resolved = yield* resolveVormaConfig(loaded);
955
+ const watcher = yield* createDevelopmentWatcher({
956
+ configFilePath: loaded.configFilePath,
957
+ generatedDirectories: getGeneratedSourceDirectories(resolved)
958
+ });
959
+ let initialized = false;
960
+ while (true) {
961
+ let ready = false;
962
+ const next = yield* Effect.scoped(runDevelopmentSession({
963
+ loaded,
964
+ port,
965
+ vitePort,
966
+ watcher,
967
+ onReadyFn: () => {
968
+ ready = true;
969
+ initialized = true;
970
+ }
971
+ })).pipe(Effect.result);
972
+ if (Result.isSuccess(next)) {
973
+ loaded = next.success;
974
+ continue;
975
+ }
976
+ if (!initialized || ready) return yield* Effect.fail(next.failure);
977
+ yield* Effect.logError(next.failure);
978
+ while (true) {
979
+ yield* watcher.take;
980
+ const corrected = yield* loadDevelopmentConfig(configFilePath).pipe(Effect.result);
981
+ if (Result.isSuccess(corrected)) {
982
+ loaded = corrected.success;
983
+ break;
984
+ }
985
+ yield* Effect.logError(corrected.failure);
986
+ }
987
+ }
988
+ }).pipe(Effect.scoped);
989
+ }
990
+ function runDevelopmentSession(input) {
991
+ return Effect.gen(function* () {
992
+ const scope = yield* Effect.scope;
993
+ const path = yield* Path.Path;
994
+ const fileSystem = yield* FileSystem.FileSystem;
995
+ const resolved = yield* resolveVormaConfig(input.loaded);
996
+ yield* input.watcher.watchPathsFn({
997
+ paths: [
998
+ ...input.loaded.dependencies,
999
+ path.dirname(resolved.serverEntryFilePath),
1000
+ path.dirname(resolved.clientEntryFilePath),
1001
+ path.dirname(resolved.serverTsconfigFilePath),
1002
+ path.dirname(resolved.clientTsconfigFilePath),
1003
+ resolved.publicAssetsSourceDirectory,
1004
+ ...resolved.criticalCssEntryFilePath === void 0 ? [] : [path.dirname(resolved.criticalCssEntryFilePath)]
1005
+ ],
1006
+ generatedDirectories: getGeneratedSourceDirectories(resolved)
1007
+ });
1008
+ const layout = yield* loadApplicationSourceLayout(resolved);
1009
+ yield* acquireOutputOwnership({
1010
+ lockDirectory: layout.clientGeneratedDirectory,
1011
+ boundaryDirectories: [layout.serverGeneratedDirectory],
1012
+ kind: "dev"
1013
+ });
1014
+ let retiredDirectories = [];
1015
+ yield* Effect.addFinalizer(() => {
1016
+ return Effect.forEach(retiredDirectories, (directory) => {
1017
+ return Effect.gen(function* () {
1018
+ const names = yield* fileSystem.readDirectory(directory);
1019
+ yield* Effect.forEach(names.filter((name) => {
1020
+ return name !== "build.lock" && name !== ".gitignore";
1021
+ }), (name) => {
1022
+ const filePath = path.join(directory, name);
1023
+ return checkOutputPath({
1024
+ filePath,
1025
+ rootDirectory: directory
1026
+ }).pipe(Effect.andThen(fileSystem.remove(filePath, { recursive: true })));
1027
+ }, {
1028
+ concurrency: 16,
1029
+ discard: true
1030
+ });
1031
+ });
1032
+ }, { discard: true }).pipe(Effect.orDie);
1033
+ });
1034
+ yield* preparePublicOutput(layout);
1035
+ const generation = yield* prepareDevelopmentGeneration({ layout });
1036
+ const client = yield* startDevelopmentClient(toDevelopmentClientInputs(generation.application, generation.sources), input.vitePort, input.watcher.recordChangeFn);
1037
+ let build = yield* prepareDevelopmentBuild({
1038
+ generation,
1039
+ client
1040
+ });
1041
+ let head = yield* client.readHeadHtml;
1042
+ let clientConfigurationRevision = client.configurationRevision;
1043
+ let artifacts = assembleDevelopmentArtifacts({
1044
+ build,
1045
+ client,
1046
+ developmentHeadHtml: head
1047
+ });
1048
+ let inputs;
1049
+ const pendingPaths = /* @__PURE__ */ new Set();
1050
+ let revision = 0;
1051
+ const restart = yield* Deferred.make();
1052
+ const fatal = yield* Deferred.make();
1053
+ let application;
1054
+ const stopApplication = Effect.suspend(() => {
1055
+ const previous = application;
1056
+ application = void 0;
1057
+ if (previous === void 0) return Effect.void;
1058
+ previous.stopping = true;
1059
+ return Fiber.interrupt(previous.fiber);
1060
+ });
1061
+ yield* Effect.addFinalizer(() => stopApplication);
1062
+ const startApplication = Effect.gen(function* () {
1063
+ const ready = yield* Deferred.make();
1064
+ let announced = false;
1065
+ const running = {
1066
+ stopping: false,
1067
+ fiber: void 0
1068
+ };
1069
+ const fiber = yield* Effect.scoped(Effect.gen(function* () {
1070
+ const server = yield* startDevelopmentServerProcess({
1071
+ layout,
1072
+ port: input.port
1073
+ });
1074
+ announced = true;
1075
+ yield* Deferred.succeed(ready, void 0);
1076
+ return yield* server.unexpectedExit;
1077
+ })).pipe(Effect.tapCause((cause) => {
1078
+ return Deferred.failCause(ready, cause).pipe(Effect.andThen(() => {
1079
+ return announced && !running.stopping ? Deferred.failCause(fatal, cause) : Effect.void;
1080
+ }));
1081
+ }), Effect.forkIn(scope));
1082
+ running.fiber = fiber;
1083
+ application = {
1084
+ fiber,
1085
+ get stopping() {
1086
+ return running.stopping;
1087
+ },
1088
+ set stopping(value) {
1089
+ running.stopping = value;
1090
+ }
1091
+ };
1092
+ yield* Deferred.await(ready).pipe(Effect.onExit((exit) => Exit.isFailure(exit) ? stopApplication : Effect.void));
1093
+ });
1094
+ function notifyFn(message) {
1095
+ client.server.ws.send({
1096
+ type: "custom",
1097
+ event: "vorma:development",
1098
+ data: message
1099
+ });
1100
+ }
1101
+ function watchInputs(next) {
1102
+ return Effect.gen(function* () {
1103
+ inputs = {
1104
+ sources: next.generation.sources,
1105
+ serverFiles: new Set(next.server.inputFilePaths.map(normalizePath)),
1106
+ criticalCssFiles: new Set(next.stylesheet.inputFilePaths.map(normalizePath)),
1107
+ configurationFiles: new Set(input.loaded.dependencies.map(normalizePath)),
1108
+ viteConfigurationFiles: new Set(client.server.config.configFileDependencies)
1109
+ };
1110
+ const sourcePaths = yield* getDevelopmentSourceWatchPaths({
1111
+ layout,
1112
+ sources: inputs.sources
1113
+ });
1114
+ const dataRoots = compileDevelopmentPatterns(layout.dev?.revalidateClientOnChange ?? []).roots.map((root) => path.resolve(layout.configDirectory, root));
1115
+ yield* input.watcher.watchPathsFn({
1116
+ paths: [
1117
+ ...sourcePaths,
1118
+ ...inputs.serverFiles,
1119
+ ...inputs.criticalCssFiles,
1120
+ ...inputs.configurationFiles,
1121
+ layout.configFilePath,
1122
+ layout.viteConfigFilePath,
1123
+ layout.publicAssetsSourceDirectory,
1124
+ ...dataRoots
1125
+ ],
1126
+ generatedDirectories: getGeneratedSourceDirectories(layout)
1127
+ });
1128
+ });
1129
+ }
1130
+ yield* publishApplicationArtifacts({
1131
+ generation: generation.application,
1132
+ server: build.server,
1133
+ artifacts
1134
+ });
1135
+ yield* startApplication;
1136
+ yield* client.listen;
1137
+ yield* watchInputs(build);
1138
+ input.onReadyFn();
1139
+ yield* Console.log(`Vorma development: http://127.0.0.1:${input.port}`);
1140
+ const rebuild = Effect.suspend(() => {
1141
+ const preparingRevision = revision;
1142
+ return Effect.gen(function* () {
1143
+ const incoming = yield* classifyDevelopmentChanges({
1144
+ layout,
1145
+ inputs: {
1146
+ ...inputs,
1147
+ viteConfigurationFiles: new Set(client.server.config.configFileDependencies)
1148
+ },
1149
+ changedPaths: new Set(pendingPaths)
1150
+ });
1151
+ const work = {
1152
+ ...incoming,
1153
+ prepareCriticalCss: incoming.prepareCriticalCss || client.configurationRevision !== clientConfigurationRevision
1154
+ };
1155
+ if (!Object.values(work).some(Boolean)) {
1156
+ if (preparingRevision === revision) pendingPaths.clear();
1157
+ return;
1158
+ }
1159
+ if (work.compileServer || work.restartConfiguration) notifyFn({ kind: "rebuilding" });
1160
+ if (work.restartConfiguration) {
1161
+ const next = yield* loadDevelopmentConfig(layout.configFilePath);
1162
+ const nextLayout = yield* resolveVormaConfig(next).pipe(Effect.flatMap(loadApplicationSourceLayout));
1163
+ const nextDirectories = new Set(getGeneratedSourceDirectories(nextLayout));
1164
+ retiredDirectories = [...new Set(getGeneratedSourceDirectories(layout))].filter((directory) => !nextDirectories.has(directory));
1165
+ yield* Deferred.succeed(restart, next);
1166
+ return;
1167
+ }
1168
+ const [next] = yield* Effect.all([prepareDevelopmentGeneration({
1169
+ layout,
1170
+ previous: {
1171
+ generation: build.generation,
1172
+ work
1173
+ }
1174
+ }).pipe(Effect.flatMap((generation) => {
1175
+ return prepareDevelopmentBuild({
1176
+ generation,
1177
+ client,
1178
+ previous: {
1179
+ build,
1180
+ work
1181
+ }
1182
+ });
1183
+ })), work.compileServer ? stopApplication : Effect.void], { concurrency: 2 });
1184
+ const previousArtifacts = artifacts;
1185
+ head = yield* client.readHeadHtml;
1186
+ const nextArtifacts = assembleDevelopmentArtifacts({
1187
+ build: next,
1188
+ client,
1189
+ developmentHeadHtml: head
1190
+ });
1191
+ const restartVite = work.compileServer || JSON.stringify(previousArtifacts.metadata.clientViews) !== JSON.stringify(nextArtifacts.metadata.clientViews);
1192
+ const reload = restartVite || previousArtifacts.metadata.clientBuildId !== nextArtifacts.metadata.clientBuildId;
1193
+ yield* publishApplicationArtifacts({
1194
+ generation: next.generation.application,
1195
+ artifacts: nextArtifacts,
1196
+ ...work.compileServer ? { server: next.server } : {}
1197
+ });
1198
+ if (work.compileServer) yield* startApplication;
1199
+ client.resumeInspectionFn(reload);
1200
+ if (restartVite) {
1201
+ yield* client.restart;
1202
+ head = yield* client.readHeadHtml;
1203
+ }
1204
+ yield* watchInputs(next);
1205
+ if (preparingRevision !== revision) return;
1206
+ build = next;
1207
+ clientConfigurationRevision = client.configurationRevision;
1208
+ artifacts = nextArtifacts;
1209
+ pendingPaths.clear();
1210
+ const retainedFiles = new Set(artifacts.metadata.publicFiles.map((file) => file.fileName));
1211
+ yield* Effect.forEach(yield* fileSystem.readDirectory(layout.publicDirectory), (fileName) => {
1212
+ return retainedFiles.has(fileName) ? Effect.void : fileSystem.remove(path.join(layout.publicDirectory, fileName));
1213
+ }, {
1214
+ concurrency: 16,
1215
+ discard: true
1216
+ });
1217
+ if (!restartVite && reload) client.server.ws.send({ type: "full-reload" });
1218
+ else {
1219
+ if (previousArtifacts.metadata.criticalCss !== artifacts.metadata.criticalCss) notifyFn({
1220
+ kind: "css",
1221
+ css: artifacts.metadata.criticalCss
1222
+ });
1223
+ notifyFn({ kind: work.revalidateClient ? "revalidate" : "ready" });
1224
+ }
1225
+ }).pipe(Effect.catchCause((cause) => {
1226
+ if (Cause.hasInterruptsOnly(cause)) return Effect.failCause(cause);
1227
+ const message = Cause.pretty(cause);
1228
+ notifyFn({
1229
+ kind: "error",
1230
+ message
1231
+ });
1232
+ return Effect.logError(message);
1233
+ }));
1234
+ });
1235
+ const changes = Stream.fromEffectRepeat(input.watcher.take).pipe(Stream.tap((incoming) => {
1236
+ return Effect.sync(() => {
1237
+ for (const filePath of incoming) pendingPaths.add(filePath);
1238
+ revision++;
1239
+ });
1240
+ }), Stream.switchMap(() => Stream.fromEffect(rebuild)), Stream.runDrain);
1241
+ return yield* Deferred.await(restart).pipe(Effect.raceFirst(changes.pipe(Effect.andThen(Effect.never))), Effect.raceFirst(Deferred.await(fatal)));
1242
+ });
1243
+ }
1244
+ //#endregion
1245
+ //#region src/build/cli.ts
1246
+ const buildCommand = Command.make("build", { config: Flag.File("config", { mustExist: true }).pipe(Flag.withDescription("Path to the application's default-exported Vorma configuration.")) }, ({ config }) => {
1247
+ return Effect.gen(function* () {
1248
+ const loaded = yield* loadVormaConfig(config);
1249
+ const result = yield* buildApplication(loaded);
1250
+ yield* Console.log(`Published Vorma build ${result.clientBuildId}`);
1251
+ });
1252
+ }).pipe(Command.withDescription("Build and publish production application artifacts."));
1253
+ const nodeServices = Layer.provideMerge(NodeChildProcessSpawner.layer, Layer.mergeAll(NodeFileSystem.layer, NodeCrypto.layer, NodePath.layer, NodeStdio.layer, NodeTerminal.layer));
1254
+ const developmentCommand = Command.make("dev", { config: Flag.File("config", { mustExist: true }).pipe(Flag.withDescription("Path to the application's Vorma configuration.")) }, ({ config }) => developApplication(config)).pipe(Command.withDescription("Run application development with rebuilds and browser hot updates."));
1255
+ Command.make("vorma").pipe(Command.withSubcommands([buildCommand, developmentCommand]), Command.run({ version })).pipe(Effect.provide(nodeServices), NodeRuntime.runMain);
1256
+ //#endregion