@blinkk/root 3.0.6 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/build-worker.js +108 -0
  2. package/dist/build-worker.js.map +7 -0
  3. package/dist/chunk-3PWR4F2R.js +77 -0
  4. package/dist/chunk-3PWR4F2R.js.map +7 -0
  5. package/dist/{chunk-3EFECH2D.js → chunk-GK3TDYUY.js} +727 -611
  6. package/dist/chunk-GK3TDYUY.js.map +7 -0
  7. package/dist/{chunk-7HK6F5RQ.js → chunk-I3RCAIW7.js} +1 -75
  8. package/dist/chunk-I3RCAIW7.js.map +7 -0
  9. package/dist/chunk-KHO3HMX6.js +291 -0
  10. package/dist/chunk-KHO3HMX6.js.map +7 -0
  11. package/dist/chunk-M3E4RKS7.js +374 -0
  12. package/dist/chunk-M3E4RKS7.js.map +7 -0
  13. package/dist/{chunk-6NBKAR5Y.js → chunk-TM6QRBGS.js} +31 -1
  14. package/dist/chunk-TM6QRBGS.js.map +7 -0
  15. package/dist/chunk-WX62JK5E.js +81 -0
  16. package/dist/chunk-WX62JK5E.js.map +7 -0
  17. package/dist/{chunk-7PSEEE6C.js → chunk-YU22SAIG.js} +31 -317
  18. package/dist/chunk-YU22SAIG.js.map +7 -0
  19. package/dist/cli/build-page.d.ts +57 -0
  20. package/dist/cli/build-progress.d.ts +82 -0
  21. package/dist/cli/build-worker-pool.d.ts +46 -0
  22. package/dist/cli/build-worker.d.ts +4 -0
  23. package/dist/cli/build.d.ts +14 -0
  24. package/dist/cli.js +8 -4
  25. package/dist/core/config.d.ts +41 -3
  26. package/dist/core/security.d.ts +14 -0
  27. package/dist/core.js.map +2 -2
  28. package/dist/functions.js +8 -4
  29. package/dist/functions.js.map +1 -1
  30. package/dist/middleware/common.d.ts +18 -0
  31. package/dist/middleware/compression.d.ts +12 -0
  32. package/dist/middleware/middleware.d.ts +1 -0
  33. package/dist/middleware.js +6 -1
  34. package/dist/node.js +7 -5
  35. package/dist/render/render.d.ts +15 -9
  36. package/dist/render.js +42 -95
  37. package/dist/render.js.map +2 -2
  38. package/package.json +2 -2
  39. package/dist/chunk-3EFECH2D.js.map +0 -7
  40. package/dist/chunk-6NBKAR5Y.js.map +0 -7
  41. package/dist/chunk-7HK6F5RQ.js.map +0 -7
  42. package/dist/chunk-7PSEEE6C.js.map +0 -7
@@ -1,21 +1,23 @@
1
1
  import {
2
- RouteTrie,
3
- isValidTagName,
4
- parseTagNames,
5
- transformHtml
6
- } from "./chunk-7HK6F5RQ.js";
2
+ collectPods,
3
+ createViteServer,
4
+ preactToRootJsxPlugin,
5
+ rootPodsVitePlugin
6
+ } from "./chunk-YU22SAIG.js";
7
7
  import {
8
- headersMiddleware,
9
- rootProjectMiddleware,
10
- sessionMiddleware,
11
- trailingSlashMiddleware
12
- } from "./chunk-6NBKAR5Y.js";
8
+ RouteTrie
9
+ } from "./chunk-I3RCAIW7.js";
10
+ import {
11
+ BuildAssetMap,
12
+ ElementGraph,
13
+ buildPage,
14
+ getElements,
15
+ getElementsDirs
16
+ } from "./chunk-M3E4RKS7.js";
13
17
  import {
14
18
  bundleRootConfig,
15
- collectPods,
16
19
  copyDir,
17
20
  copyGlob,
18
- createViteServer,
19
21
  dirExists,
20
22
  directoryContains,
21
23
  fileExists,
@@ -27,144 +29,35 @@ import {
27
29
  loadRootConfig,
28
30
  loadRootConfigWithDeps,
29
31
  makeDir,
30
- preactToRootJsxPlugin,
31
32
  rmDir,
32
- rootPodsVitePlugin,
33
33
  writeFile,
34
34
  writeJson
35
- } from "./chunk-7PSEEE6C.js";
35
+ } from "./chunk-KHO3HMX6.js";
36
36
  import {
37
37
  configureServerPlugins,
38
38
  getVitePlugins
39
39
  } from "./chunk-XSNCF7WU.js";
40
+ import {
41
+ compressionMiddleware,
42
+ headersMiddleware,
43
+ rootProjectMiddleware,
44
+ securityHeadersMiddleware,
45
+ sessionMiddleware,
46
+ trailingSlashMiddleware
47
+ } from "./chunk-TM6QRBGS.js";
40
48
 
41
49
  // src/cli/cli.ts
42
- import { Command, InvalidArgumentError } from "commander";
50
+ import { Command, InvalidArgumentError, Option } from "commander";
43
51
  import { bgGreen, black } from "kleur/colors";
44
52
 
45
53
  // src/cli/build.ts
46
- import path3 from "node:path";
47
- import { fileURLToPath } from "node:url";
54
+ import os from "node:os";
55
+ import path2 from "node:path";
56
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
48
57
  import fsExtra from "fs-extra";
49
- import { dim as dim2, cyan } from "kleur/colors";
50
- import glob2 from "tiny-glob";
51
- import { build as viteBuild } from "vite";
52
-
53
- // src/node/element-graph.ts
54
- import fs from "node:fs";
55
- import path from "node:path";
58
+ import { dim as dim3, cyan as cyan2 } from "kleur/colors";
56
59
  import glob from "tiny-glob";
57
- import { searchForWorkspaceRoot } from "vite";
58
- var ElementGraph = class _ElementGraph {
59
- /**
60
- * Element tagName => sourceFile.
61
- */
62
- sourceFiles = {};
63
- /**
64
- * Element tagName => set of element tagName dependencies.
65
- */
66
- deps = {};
67
- constructor(sourceFiles) {
68
- this.sourceFiles = sourceFiles;
69
- }
70
- toJson() {
71
- for (const tagName in this.sourceFiles) {
72
- this.deps[tagName] ??= this.parseDepsFromSource(tagName);
73
- }
74
- return {
75
- sourceFiles: this.sourceFiles,
76
- deps: this.deps
77
- };
78
- }
79
- static fromJson(data) {
80
- const graph = new _ElementGraph(data.sourceFiles);
81
- graph.deps = data.deps;
82
- return graph;
83
- }
84
- getDeps(tagName, visited) {
85
- visited ??= /* @__PURE__ */ new Set();
86
- if (visited.has(tagName)) {
87
- return [];
88
- }
89
- visited.add(tagName);
90
- this.deps[tagName] ??= this.parseDepsFromSource(tagName);
91
- const deps = new Set(this.deps[tagName]);
92
- for (const depTagName of this.deps[tagName]) {
93
- for (const childDep of this.getDeps(depTagName, visited)) {
94
- deps.add(childDep);
95
- }
96
- }
97
- return Array.from(deps);
98
- }
99
- /**
100
- * Parses an element's source file for usage of other custom elements.
101
- */
102
- parseDepsFromSource(tagName) {
103
- const srcFile = this.sourceFiles[tagName];
104
- if (!srcFile) {
105
- throw new Error(`could not find file path for tagName <${tagName}>`);
106
- }
107
- const src = fs.readFileSync(srcFile.filePath, "utf-8");
108
- const tagNames = parseTagNames(src);
109
- const deps = /* @__PURE__ */ new Set();
110
- for (const depTagName of tagNames) {
111
- if (depTagName !== tagName && depTagName in this.sourceFiles) {
112
- deps.add(depTagName);
113
- }
114
- }
115
- return Array.from(deps);
116
- }
117
- };
118
- async function getElements(rootConfig, pods) {
119
- const rootDir = rootConfig.rootDir;
120
- const elementsDirs = getElementsDirs(rootConfig, pods);
121
- const excludePatterns = rootConfig.elements?.exclude || [];
122
- const excludeElement = (moduleId) => {
123
- return excludePatterns.some((pattern) => Boolean(moduleId.match(pattern)));
124
- };
125
- const elementFilePaths = {};
126
- for (const dirPath of elementsDirs) {
127
- if (await isDirectory(dirPath)) {
128
- const files = await glob("**/*", { cwd: dirPath });
129
- files.forEach((file) => {
130
- const parts = path.parse(file);
131
- if (isJsFile(parts.base) && isValidTagName(parts.name)) {
132
- const tagName = parts.name;
133
- const filePath = path.join(dirPath, file);
134
- const relPath = path.relative(rootDir, filePath);
135
- if (!excludeElement(relPath)) {
136
- elementFilePaths[tagName] = { filePath, relPath };
137
- }
138
- }
139
- });
140
- }
141
- }
142
- const graph = new ElementGraph(elementFilePaths);
143
- return graph;
144
- }
145
- function getElementsDirs(rootConfig, pods) {
146
- const rootDir = rootConfig.rootDir;
147
- const workspaceRoot = searchForWorkspaceRoot(rootDir);
148
- const elementsDirs = [path.join(rootDir, "elements")];
149
- const elementsInclude = rootConfig.elements?.include || [];
150
- for (const dirPath of elementsInclude) {
151
- const elementsDir = path.resolve(rootDir, dirPath);
152
- if (!directoryContains(rootDir, elementsDir)) {
153
- throw new Error(
154
- `the elements dir (${dirPath}) should be within the project's workspace (${workspaceRoot})`
155
- );
156
- }
157
- elementsDirs.push(elementsDir);
158
- }
159
- if (pods) {
160
- for (const pod of pods) {
161
- for (const dir of pod.elementsDirs) {
162
- elementsDirs.push(dir);
163
- }
164
- }
165
- }
166
- return elementsDirs;
167
- }
60
+ import { build as viteBuild } from "vite";
168
61
 
169
62
  // src/node/vite-plugin-prune-empty-chunks.ts
170
63
  import { dim } from "kleur/colors";
@@ -231,233 +124,371 @@ function removeSideEffectImport(code, fileName) {
231
124
  return code.replace(re, "");
232
125
  }
233
126
 
234
- // src/render/asset-map/build-asset-map.ts
235
- import fs2 from "node:fs";
236
- import path2 from "node:path";
237
- var BuildAssetMap = class _BuildAssetMap {
238
- rootConfig;
239
- srcToAsset;
240
- constructor(rootConfig) {
241
- this.rootConfig = rootConfig;
242
- this.srcToAsset = /* @__PURE__ */ new Map();
243
- }
244
- async get(src) {
245
- const asset = this.srcToAsset.get(src);
246
- if (asset) {
247
- return asset;
248
- }
249
- const realSrc = realPathRelativeTo(this.rootConfig.rootDir, src);
250
- if (realSrc !== src) {
251
- const asset2 = this.srcToAsset.get(realSrc);
252
- if (asset2) {
253
- return asset2;
254
- }
255
- }
256
- console.log(`could not find build asset: ${src}`);
257
- return null;
258
- }
259
- add(asset) {
260
- this.srcToAsset.set(asset.src, asset);
261
- }
262
- toJson() {
263
- const result = {};
264
- for (const src of this.srcToAsset.keys()) {
265
- result[src] = this.srcToAsset.get(src).toJson();
127
+ // src/utils/batch.ts
128
+ async function batchAsyncCalls(data, concurrency, asyncFunction) {
129
+ const result = new Array(data.length);
130
+ let nextIndex = 0;
131
+ async function worker() {
132
+ while (nextIndex < data.length) {
133
+ const index = nextIndex++;
134
+ result[index] = await asyncFunction(data[index]);
266
135
  }
267
- return result;
268
136
  }
269
- static fromViteManifest(rootConfig, clientManifest, elementsGraph, podRoutes = []) {
270
- const assetMap = new _BuildAssetMap(rootConfig);
271
- const elementFiles = /* @__PURE__ */ new Set();
272
- Object.values(elementsGraph.sourceFiles).forEach((elementSource) => {
273
- elementFiles.add(elementSource.relPath);
274
- const realSrc = realPathRelativeTo(
275
- rootConfig.rootDir,
276
- elementSource.relPath
277
- );
278
- if (realSrc !== elementSource.filePath) {
279
- elementFiles.add(realSrc);
280
- }
281
- });
282
- Object.keys(clientManifest).forEach((manifestKey) => {
283
- const src = manifestKey;
284
- const manifestChunk = clientManifest[manifestKey];
285
- const isElement = elementFiles.has(src);
286
- const assetUrl = src.startsWith("routes/") && isJsFile(src) ? "" : `/${manifestChunk.file}`;
287
- const assetData = {
288
- src,
289
- assetUrl,
290
- importedModules: manifestChunk.imports || [],
291
- importedCss: (manifestChunk.css || []).map((relPath) => `/${relPath}`),
292
- isElement
293
- };
294
- assetMap.add(new BuildAsset(assetMap, assetData));
295
- });
296
- podRoutes.forEach((podRoute) => {
297
- if (assetMap.srcToAsset.has(podRoute.src)) {
298
- return;
299
- }
300
- const relPath = path2.relative(rootConfig.rootDir, podRoute.filePath).replace(/\\/g, "/");
301
- const realPath = realPathRelativeTo(rootConfig.rootDir, relPath).replace(
302
- /\\/g,
303
- "/"
304
- );
305
- const candidateKeys = [relPath, realPath];
306
- for (const key of candidateKeys) {
307
- const asset = assetMap.srcToAsset.get(key);
308
- if (asset) {
309
- assetMap.add(
310
- new BuildAsset(assetMap, {
311
- ...asset.toJson(),
312
- src: podRoute.src,
313
- assetUrl: ""
314
- })
315
- );
316
- break;
317
- }
318
- }
319
- });
320
- return assetMap;
321
- }
322
- static fromRootManifest(rootConfig, rootManifest) {
323
- const assetMap = new _BuildAssetMap(rootConfig);
324
- Object.keys(rootManifest).forEach((moduleId) => {
325
- const assetData = rootManifest[moduleId];
326
- assetMap.add(new BuildAsset(assetMap, assetData));
327
- });
328
- return assetMap;
329
- }
330
- };
331
- function realPathRelativeTo(rootDir, src) {
332
- const fullPath = path2.resolve(rootDir, src);
333
- if (!fs2.existsSync(fullPath)) {
334
- return src;
335
- }
336
- const realpath = fs2.realpathSync(path2.resolve(rootDir, src));
337
- return path2.relative(rootDir, realpath);
137
+ const workers = Array.from(
138
+ { length: Math.min(concurrency, data.length) },
139
+ () => worker()
140
+ );
141
+ await Promise.all(workers);
142
+ return result;
338
143
  }
339
- var BuildAsset = class {
340
- src;
341
- assetUrl;
342
- assetMap;
343
- importedModules;
344
- importedCss;
345
- isElement;
346
- constructor(assetMap, assetData) {
347
- this.assetMap = assetMap;
348
- this.src = assetData.src;
349
- this.assetUrl = assetData.assetUrl;
350
- this.importedModules = assetData.importedModules;
351
- this.importedCss = assetData.importedCss;
352
- this.isElement = assetData.isElement;
144
+
145
+ // src/cli/build-progress.ts
146
+ import { cyan, dim as dim2, green } from "kleur/colors";
147
+ function parseBuildLogMode(value) {
148
+ if (value === void 0 || value === null || value === "") {
149
+ return "progress";
353
150
  }
354
- async getCssDeps() {
355
- const visited = /* @__PURE__ */ new Set();
356
- const deps = /* @__PURE__ */ new Set();
357
- await this.collectCss(this, deps, visited);
358
- return Array.from(deps);
151
+ if (value === "progress" || value === "verbose" || value === "quiet") {
152
+ return value;
359
153
  }
360
- async getJsDeps() {
361
- const visited = /* @__PURE__ */ new Set();
362
- const deps = /* @__PURE__ */ new Set();
363
- await this.collectJs(this, deps, visited);
364
- return Array.from(deps);
154
+ throw new Error(
155
+ `invalid --log value: "${value}" (expected "progress", "verbose", or "quiet")`
156
+ );
157
+ }
158
+ function formatBytes(bytes) {
159
+ const k = 1024;
160
+ if (bytes < k) {
161
+ return (bytes / k).toFixed(2) + " kB";
365
162
  }
366
- async collectJs(asset, urls, visited) {
367
- if (!asset) {
163
+ const units = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
164
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
165
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + units[i];
166
+ }
167
+ function formatDuration(ms) {
168
+ const secs = ms / 1e3;
169
+ if (secs < 60) {
170
+ return `${secs.toFixed(1)}s`;
171
+ }
172
+ const mins = Math.floor(secs / 60);
173
+ const rem = Math.round(secs - mins * 60);
174
+ return `${mins}m${String(rem).padStart(2, "0")}s`;
175
+ }
176
+ var BAR_WIDTH = 20;
177
+ var BuildProgress = class {
178
+ total;
179
+ mode;
180
+ itemLabel;
181
+ outputDirLabel;
182
+ stream;
183
+ now;
184
+ intervalMs;
185
+ summaryTopN;
186
+ isTTY;
187
+ startedAt;
188
+ completed = 0;
189
+ totalBytes = 0;
190
+ files = [];
191
+ lastPrintAt = 0;
192
+ lastMilestone = 0;
193
+ ttyLineActive = false;
194
+ done = false;
195
+ constructor(options) {
196
+ this.total = options.total;
197
+ this.mode = options.mode;
198
+ this.itemLabel = options.itemLabel ?? "pages";
199
+ this.outputDirLabel = options.outputDirLabel ?? "";
200
+ this.stream = options.stream ?? process.stdout;
201
+ this.now = options.now ?? Date.now;
202
+ this.isTTY = !!this.stream.isTTY;
203
+ this.intervalMs = options.intervalMs ?? (this.isTTY ? 80 : 5e3);
204
+ this.summaryTopN = options.summaryTopN ?? 5;
205
+ this.startedAt = this.now();
206
+ }
207
+ /** Records a completed output file. */
208
+ add(outputFile, sizeBytes) {
209
+ this.completed += 1;
210
+ this.totalBytes += sizeBytes;
211
+ this.files.push({ path: outputFile, size: sizeBytes });
212
+ if (this.mode === "verbose") {
213
+ const paddedSize = formatBytes(sizeBytes).padStart(9, " ");
214
+ this.println(
215
+ ` ${dim2(paddedSize)} ${dim2(this.outputDirLabel)}${cyan(outputFile)}`
216
+ );
368
217
  return;
369
218
  }
370
- if (!asset.src) {
371
- return;
219
+ if (this.mode === "progress") {
220
+ this.maybePrintProgress();
372
221
  }
373
- if (visited.has(asset.src)) {
222
+ }
223
+ /** Clears any in-place progress line (call before printing errors). */
224
+ abort() {
225
+ this.clearTtyLine();
226
+ this.done = true;
227
+ }
228
+ /** Prints the final summary. */
229
+ finish() {
230
+ if (this.done) {
374
231
  return;
375
232
  }
376
- visited.add(asset.src);
377
- if (asset.isElement) {
378
- urls.add(asset.assetUrl);
233
+ this.done = true;
234
+ this.clearTtyLine();
235
+ const elapsed = formatDuration(this.now() - this.startedAt);
236
+ const summary = `${this.completed} ${this.itemLabel} (${formatBytes(
237
+ this.totalBytes
238
+ )}) in ${elapsed}`;
239
+ this.println(` ${green("\u2713")} ${summary}`);
240
+ if (this.mode === "progress" && this.summaryTopN > 0) {
241
+ const largest = [...this.files].sort((a, b) => b.size - a.size).slice(0, this.summaryTopN);
242
+ if (largest.length > 0) {
243
+ this.println(` ${dim2(`largest ${this.itemLabel}:`)}`);
244
+ for (const file of largest) {
245
+ const paddedSize = formatBytes(file.size).padStart(9, " ");
246
+ this.println(
247
+ ` ${dim2(paddedSize)} ${dim2(this.outputDirLabel)}${cyan(
248
+ file.path
249
+ )}`
250
+ );
251
+ }
252
+ }
379
253
  }
380
- await Promise.all(
381
- asset.importedModules.map(async (src) => {
382
- const importedAsset = await this.assetMap.get(src);
383
- this.collectJs(importedAsset, urls, visited);
384
- })
385
- );
386
254
  }
387
- async collectCss(asset, urls, visited) {
388
- if (!asset) {
255
+ maybePrintProgress() {
256
+ const now = this.now();
257
+ if (this.isTTY) {
258
+ if (now - this.lastPrintAt < this.intervalMs) {
259
+ return;
260
+ }
261
+ this.lastPrintAt = now;
262
+ this.renderTtyLine(now);
389
263
  return;
390
264
  }
391
- if (!asset.src) {
265
+ const milestone = Math.floor(this.completed / this.total * 10);
266
+ if (milestone <= this.lastMilestone || this.completed === this.total) {
392
267
  return;
393
268
  }
394
- if (visited.has(asset.src)) {
269
+ if (now - this.lastPrintAt < this.intervalMs) {
395
270
  return;
396
271
  }
397
- visited.add(asset.src);
398
- if (asset.importedCss) {
399
- asset.importedCss.forEach((cssUrl) => urls.add(cssUrl));
400
- }
401
- await Promise.all(
402
- asset.importedModules.map(async (moduleId) => {
403
- const importedAsset = await this.assetMap.get(moduleId);
404
- this.collectCss(importedAsset, urls, visited);
405
- })
272
+ this.lastMilestone = milestone;
273
+ this.lastPrintAt = now;
274
+ const pct = Math.floor(this.completed / this.total * 100);
275
+ this.println(
276
+ ` ${this.completed}/${this.total} ${this.itemLabel} (${pct}%) \xB7 ${formatBytes(this.totalBytes)} \xB7 ${formatDuration(now - this.startedAt)}${this.eta(now)}`
406
277
  );
407
278
  }
408
- toJson() {
409
- return {
410
- src: this.src,
411
- assetUrl: this.assetUrl,
412
- importedModules: this.importedModules,
413
- importedCss: this.importedCss,
414
- isElement: this.isElement
415
- };
279
+ renderTtyLine(now) {
280
+ const ratio = this.total > 0 ? this.completed / this.total : 0;
281
+ const filled = Math.round(ratio * BAR_WIDTH);
282
+ const bar = "\u2595" + "\u2588".repeat(filled) + "\u2591".repeat(BAR_WIDTH - filled) + "\u258F";
283
+ const pct = String(Math.floor(ratio * 100)).padStart(3, " ");
284
+ const line = ` ${bar} ${pct}% \xB7 ${this.completed}/${this.total} ${this.itemLabel} \xB7 ${formatBytes(this.totalBytes)} \xB7 ${formatDuration(now - this.startedAt)}${this.eta(now)}`;
285
+ this.stream.write(`\r\x1B[2K${line}`);
286
+ this.ttyLineActive = true;
287
+ }
288
+ /** Returns an " · eta 12.3s" suffix once enough progress exists to estimate. */
289
+ eta(now) {
290
+ if (this.completed < 10 || this.completed / this.total < 0.05) {
291
+ return "";
292
+ }
293
+ const elapsed = now - this.startedAt;
294
+ const remaining = elapsed / this.completed * (this.total - this.completed);
295
+ return ` \xB7 eta ${formatDuration(remaining)}`;
296
+ }
297
+ clearTtyLine() {
298
+ if (this.ttyLineActive) {
299
+ this.stream.write("\r\x1B[2K");
300
+ this.ttyLineActive = false;
301
+ }
302
+ }
303
+ println(line) {
304
+ this.clearTtyLine();
305
+ this.stream.write(line + "\n");
416
306
  }
417
307
  };
418
308
 
419
- // src/utils/batch.ts
420
- async function batchAsyncCalls(data, concurrency, asyncFunction) {
421
- const result = new Array(data.length);
422
- let nextIndex = 0;
423
- async function worker() {
424
- while (nextIndex < data.length) {
425
- const index = nextIndex++;
426
- result[index] = await asyncFunction(data[index]);
309
+ // src/cli/build-worker-pool.ts
310
+ import path from "node:path";
311
+ import { fileURLToPath } from "node:url";
312
+ import v8 from "node:v8";
313
+ import { Worker } from "node:worker_threads";
314
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
315
+ var WORKER_HEAP_LIMIT_MB = Math.ceil(
316
+ v8.getHeapStatistics().heap_size_limit / (1024 * 1024)
317
+ );
318
+ var BuildPageError = class extends Error {
319
+ urlPath;
320
+ params;
321
+ routeSrc;
322
+ workerError;
323
+ constructor(urlPath, params, routeSrc, workerError) {
324
+ super(
325
+ `BuildError: ${urlPath} (${routeSrc || "unknown route"}) failed to build.`
326
+ );
327
+ this.urlPath = urlPath;
328
+ this.params = params;
329
+ this.routeSrc = routeSrc;
330
+ this.workerError = workerError;
331
+ }
332
+ };
333
+ var BuildWorker = class {
334
+ worker;
335
+ pool;
336
+ concurrency;
337
+ inFlight = /* @__PURE__ */ new Map();
338
+ isReady = false;
339
+ ready;
340
+ constructor(pool, options) {
341
+ this.pool = pool;
342
+ this.concurrency = options.workerConcurrency;
343
+ this.worker = new Worker(path.resolve(__dirname, "./build-worker.js"), {
344
+ workerData: { rootDir: options.rootDir, mode: options.mode },
345
+ resourceLimits: { maxOldGenerationSizeMb: WORKER_HEAP_LIMIT_MB }
346
+ });
347
+ let onReady;
348
+ let onFailed;
349
+ this.ready = new Promise((resolve, reject) => {
350
+ onReady = resolve;
351
+ onFailed = reject;
352
+ });
353
+ this.worker.on("message", (msg) => {
354
+ if (msg.type === "ready") {
355
+ this.isReady = true;
356
+ onReady();
357
+ this.next();
358
+ } else if (msg.type === "result") {
359
+ const pending = this.inFlight.get(msg.id);
360
+ this.inFlight.delete(msg.id);
361
+ pending?.resolve(msg.result);
362
+ this.next();
363
+ } else if (msg.type === "error") {
364
+ const pending = this.inFlight.get(msg.id);
365
+ this.inFlight.delete(msg.id);
366
+ pending?.reject(
367
+ new BuildPageError(msg.urlPath, msg.params, msg.routeSrc, msg.error)
368
+ );
369
+ this.next();
370
+ } else if (msg.type === "fatal") {
371
+ const err = new Error(`build worker failed: ${msg.error}`);
372
+ onFailed(err);
373
+ this.failAll(err);
374
+ }
375
+ });
376
+ this.worker.on("error", (err) => {
377
+ onFailed(err);
378
+ this.failAll(err);
379
+ });
380
+ this.worker.on("exit", (code) => {
381
+ if (code !== 0) {
382
+ const err = new Error(`build worker exited with code ${code}`);
383
+ onFailed(err);
384
+ this.failAll(err);
385
+ }
386
+ });
387
+ }
388
+ /**
389
+ * Rejects all in-flight tasks after a worker-level failure (fatal error,
390
+ * crash, or non-zero exit — e.g. an OOM kill).
391
+ *
392
+ * Each task is rejected with its own `BuildPageError` so the build log
393
+ * names every page that was in flight when the worker died. Without this,
394
+ * worker deaths surface as a single generic error with no route context,
395
+ * making OOM crashes impossible to trace back to pages. Note the in-flight
396
+ * pages are the OOM *suspects*, not necessarily the cause (memory pressure
397
+ * is cumulative), which is why the message says "while rendering".
398
+ */
399
+ failAll(err) {
400
+ const pendingTasks = Array.from(this.inFlight.values());
401
+ this.inFlight.clear();
402
+ pendingTasks.forEach(
403
+ (pending) => pending.reject(
404
+ new BuildPageError(
405
+ pending.task.urlPath,
406
+ pending.task.params,
407
+ pending.task.routeSrc,
408
+ `worker thread died while rendering this page (${pendingTasks.length} page(s) were in flight): ` + String(err.message || err)
409
+ )
410
+ )
411
+ );
412
+ }
413
+ /** Pulls tasks off the pool's queue until at max concurrency. */
414
+ next() {
415
+ if (!this.isReady) {
416
+ return;
417
+ }
418
+ while (this.inFlight.size < this.concurrency) {
419
+ const pending = this.pool.dequeue();
420
+ if (!pending) {
421
+ return;
422
+ }
423
+ this.inFlight.set(pending.id, pending);
424
+ const req = {
425
+ type: "render",
426
+ id: pending.id,
427
+ task: pending.task
428
+ };
429
+ this.worker.postMessage(req);
427
430
  }
428
431
  }
429
- const workers = Array.from(
430
- { length: Math.min(concurrency, data.length) },
431
- () => worker()
432
- );
433
- await Promise.all(workers);
434
- return result;
435
- }
432
+ async terminate() {
433
+ await this.worker.terminate();
434
+ }
435
+ };
436
+ var BuildWorkerPool = class {
437
+ workers = [];
438
+ queue = [];
439
+ nextTaskId = 0;
440
+ constructor(options) {
441
+ for (let i = 0; i < options.numWorkers; i++) {
442
+ this.workers.push(new BuildWorker(this, options));
443
+ }
444
+ }
445
+ /** Waits for all workers to finish initializing. */
446
+ async ready() {
447
+ await Promise.all(this.workers.map((worker) => worker.ready));
448
+ }
449
+ /** Schedules a page build and resolves when the page finishes. */
450
+ run(task) {
451
+ return new Promise((resolve, reject) => {
452
+ this.queue.push({ id: this.nextTaskId++, task, resolve, reject });
453
+ this.workers.forEach((worker) => worker.next());
454
+ });
455
+ }
456
+ dequeue() {
457
+ return this.queue.shift();
458
+ }
459
+ async terminate() {
460
+ await Promise.all(this.workers.map((worker) => worker.terminate()));
461
+ }
462
+ };
436
463
 
437
464
  // src/cli/build.ts
438
- var __dirname = path3.dirname(fileURLToPath(import.meta.url));
465
+ var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
466
+ var activeLogMode = "progress";
439
467
  async function build(rootProjectDir, options) {
440
468
  const mode = options?.mode || "production";
441
469
  process.env.NODE_ENV = mode;
442
- const rootDir = path3.resolve(rootProjectDir || process.cwd());
470
+ activeLogMode = options?.quiet ? "quiet" : parseBuildLogMode(options?.log);
471
+ const rootDir = path2.resolve(rootProjectDir || process.cwd());
443
472
  const rootConfig = await loadRootConfig(rootDir, { command: "build" });
444
- const distDir = path3.join(rootDir, "dist");
473
+ const distDir = path2.join(rootDir, "dist");
445
474
  const ssrOnly = options?.ssrOnly || false;
446
- console.log();
447
- console.log(`${dim2("\u2503")} project: ${rootDir}`);
448
- console.log(`${dim2("\u2503")} output: ${distDir}/html`);
449
- console.log(`${dim2("\u2503")} mode: ${mode}`);
450
- console.log();
475
+ if (activeLogMode !== "quiet") {
476
+ console.log();
477
+ console.log(`${dim3("\u2503")} project: ${rootDir}`);
478
+ console.log(`${dim3("\u2503")} output: ${distDir}/html`);
479
+ console.log(`${dim3("\u2503")} mode: ${mode}`);
480
+ console.log();
481
+ }
451
482
  await rmDir(distDir);
452
483
  await makeDir(distDir);
453
484
  const pods = await collectPods(rootConfig);
454
485
  const routeFiles = [];
455
- if (await isDirectory(path3.join(rootDir, "routes"))) {
456
- const pageFiles = await glob2("routes/**/*", { cwd: rootDir });
486
+ if (await isDirectory(path2.join(rootDir, "routes"))) {
487
+ const pageFiles = await glob("routes/**/*", { cwd: rootDir });
457
488
  pageFiles.forEach((file) => {
458
- const parts = path3.parse(file);
489
+ const parts = path2.parse(file);
459
490
  if (!parts.name.startsWith("_") && isJsFile(parts.base)) {
460
- routeFiles.push(path3.resolve(rootDir, file));
491
+ routeFiles.push(path2.resolve(rootDir, file));
461
492
  }
462
493
  });
463
494
  }
@@ -471,12 +502,12 @@ async function build(rootProjectDir, options) {
471
502
  return sourceFile.filePath;
472
503
  });
473
504
  const bundleScripts = [];
474
- if (await isDirectory(path3.join(rootDir, "bundles"))) {
475
- const bundleFiles = await glob2("bundles/*", { cwd: rootDir });
505
+ if (await isDirectory(path2.join(rootDir, "bundles"))) {
506
+ const bundleFiles = await glob("bundles/*", { cwd: rootDir });
476
507
  bundleFiles.forEach((file) => {
477
- const parts = path3.parse(file);
508
+ const parts = path2.parse(file);
478
509
  if (isJsFile(parts.base)) {
479
- bundleScripts.push(path3.resolve(rootDir, file));
510
+ bundleScripts.push(path2.resolve(rootDir, file));
480
511
  }
481
512
  });
482
513
  }
@@ -503,10 +534,12 @@ async function build(rootProjectDir, options) {
503
534
  ...viteConfig,
504
535
  root: rootDir,
505
536
  mode,
506
- plugins: vitePlugins
537
+ plugins: vitePlugins,
538
+ // In quiet mode, only surface vite warnings and errors.
539
+ logLevel: activeLogMode === "quiet" ? "warn" : viteConfig.logLevel
507
540
  };
508
541
  const ssrInput = {
509
- render: path3.resolve(__dirname, "./render.js")
542
+ render: path2.resolve(__dirname2, "./render.js")
510
543
  };
511
544
  rootPlugins.forEach((plugin) => {
512
545
  if (plugin.ssrInput) {
@@ -537,7 +570,7 @@ async function build(rootProjectDir, options) {
537
570
  sanitizeFileName
538
571
  }
539
572
  },
540
- outDir: path3.join(distDir, "server"),
573
+ outDir: path2.join(distDir, "server"),
541
574
  ssr: true,
542
575
  ssrManifest: false,
543
576
  cssCodeSplit: true,
@@ -570,7 +603,7 @@ async function build(rootProjectDir, options) {
570
603
  ...viteConfig?.build?.rolldownOptions?.output
571
604
  }
572
605
  },
573
- outDir: path3.join(distDir, ".build/routes"),
606
+ outDir: path2.join(distDir, ".build/routes"),
574
607
  ssr: true,
575
608
  ssrManifest: false,
576
609
  ssrEmitAssets: true,
@@ -601,7 +634,7 @@ async function build(rootProjectDir, options) {
601
634
  ...viteConfig?.build?.rolldownOptions?.output
602
635
  }
603
636
  },
604
- outDir: path3.join(distDir, ".build/client"),
637
+ outDir: path2.join(distDir, ".build/client"),
605
638
  ssr: false,
606
639
  ssrManifest: false,
607
640
  manifest: true,
@@ -614,21 +647,21 @@ async function build(rootProjectDir, options) {
614
647
  });
615
648
  } else {
616
649
  await writeFile(
617
- path3.join(distDir, ".build/client/.vite/manifest.json"),
650
+ path2.join(distDir, ".build/client/.vite/manifest.json"),
618
651
  "{}"
619
652
  );
620
653
  }
621
- await bundleRootConfig(rootDir, path3.join(distDir, "root.config.js"));
654
+ await bundleRootConfig(rootDir, path2.join(distDir, "root.config.js"));
622
655
  await copyGlob(
623
656
  "**/*.css",
624
- path3.join(distDir, ".build/routes"),
625
- path3.join(distDir, ".build/client")
657
+ path2.join(distDir, ".build/routes"),
658
+ path2.join(distDir, ".build/client")
626
659
  );
627
660
  const routesManifest = await loadJson(
628
- path3.join(distDir, ".build/routes/.vite/manifest.json")
661
+ path2.join(distDir, ".build/routes/.vite/manifest.json")
629
662
  );
630
663
  const clientManifest = await loadJson(
631
- path3.join(distDir, ".build/client/.vite/manifest.json")
664
+ path2.join(distDir, ".build/client/.vite/manifest.json")
632
665
  );
633
666
  function collectRouteCss(asset, cssDeps, visited) {
634
667
  if (!asset || !asset.file || visited.has(asset.file)) {
@@ -671,17 +704,17 @@ async function build(rootProjectDir, options) {
671
704
  );
672
705
  const rootManifest = assetMap.toJson();
673
706
  await writeFile(
674
- path3.join(distDir, ".root/manifest.json"),
707
+ path2.join(distDir, ".root/manifest.json"),
675
708
  JSON.stringify(rootManifest, null, 2)
676
709
  );
677
710
  const elementGraphJson = elementGraph.toJson();
678
711
  await writeFile(
679
- path3.join(distDir, ".root/elements.json"),
712
+ path2.join(distDir, ".root/elements.json"),
680
713
  JSON.stringify(elementGraphJson, null, 2)
681
714
  );
682
- const buildDir = path3.join(distDir, "html");
683
- const publicDir = path3.join(rootDir, "public");
684
- if (fsExtra.existsSync(path3.join(rootDir, "public"))) {
715
+ const buildDir = path2.join(distDir, "html");
716
+ const publicDir = path2.join(rootDir, "public");
717
+ if (fsExtra.existsSync(path2.join(rootDir, "public"))) {
685
718
  fsExtra.copySync(publicDir, buildDir, { dereference: true });
686
719
  } else {
687
720
  await makeDir(buildDir);
@@ -693,8 +726,8 @@ async function build(rootProjectDir, options) {
693
726
  }
694
727
  seenAssets.add(assetUrl);
695
728
  const assetRelPath = assetUrl.slice(1);
696
- const assetFrom = path3.join(distDir, ".build/client", assetRelPath);
697
- const assetTo = path3.join(buildDir, assetRelPath);
729
+ const assetFrom = path2.join(distDir, ".build/client", assetRelPath);
730
+ const assetTo = path2.join(buildDir, assetRelPath);
698
731
  if (!await fileExists(assetFrom)) {
699
732
  console.log(`${assetFrom} does not exist`);
700
733
  return;
@@ -702,7 +735,9 @@ async function build(rootProjectDir, options) {
702
735
  await fsExtra.copy(assetFrom, assetTo);
703
736
  printFileOutput(fileSize(assetTo), "dist/html/", assetRelPath);
704
737
  }
705
- console.log("\njs/css output:");
738
+ if (activeLogMode !== "quiet") {
739
+ console.log("\njs/css output:");
740
+ }
706
741
  await Promise.all(
707
742
  Object.keys(rootManifest).map(async (src) => {
708
743
  const assetData = rootManifest[src];
@@ -724,7 +759,7 @@ async function build(rootProjectDir, options) {
724
759
  })
725
760
  );
726
761
  if (!ssrOnly) {
727
- const render = await import(path3.join(distDir, "server/render.js"));
762
+ const render = await import(path2.join(distDir, "server/render.js"));
728
763
  const renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
729
764
  let sitemap = await renderer.getSitemap();
730
765
  if (options?.filter) {
@@ -744,92 +779,141 @@ async function build(rootProjectDir, options) {
744
779
  );
745
780
  }
746
781
  const domain = rootConfig.domain;
747
- console.log("\nhtml output:");
748
- const batchSize = Number(options?.concurrency || 10);
749
- await batchAsyncCalls(
750
- Object.entries(sitemap),
751
- batchSize,
752
- async ([urlPath, sitemapItem]) => {
753
- if (rootConfig.build?.excludeDefaultLocaleFromIntlPaths) {
754
- const defaultLocale = rootConfig.i18n?.defaultLocale || "en";
755
- if (sitemapItem.locale === defaultLocale) {
756
- return;
757
- }
782
+ const addSitemapXmlItem = (urlPath, sitemapItem, outFilePath) => {
783
+ if (!rootConfig.sitemap || !outFilePath.endsWith("index.html")) {
784
+ return;
785
+ }
786
+ const sitemapXmlItem = {
787
+ url: `${domain}${urlPath}`,
788
+ locale: sitemapItem.locale,
789
+ alts: []
790
+ };
791
+ sitemapXmlItems.push(sitemapXmlItem);
792
+ if (sitemapItem.alts) {
793
+ Object.entries(sitemapItem.alts).forEach(([altLocale, item]) => {
794
+ sitemapXmlItem.alts.push({
795
+ url: `${domain}${item.urlPath}`,
796
+ locale: altLocale,
797
+ hreflang: item.hrefLang
798
+ });
799
+ });
800
+ }
801
+ };
802
+ const onPageResult = (sitemapItem, result) => {
803
+ if (result.notFound || !result.outFilePath) {
804
+ return;
805
+ }
806
+ if (!result.staticContent) {
807
+ addSitemapXmlItem(result.urlPath, sitemapItem, result.outFilePath);
808
+ }
809
+ progress.add(
810
+ result.outFilePath,
811
+ fileSizeBytes(path2.join(buildDir, result.outFilePath))
812
+ );
813
+ };
814
+ const sitemapEntries = Object.entries(sitemap).filter(([, sitemapItem]) => {
815
+ if (rootConfig.build?.excludeDefaultLocaleFromIntlPaths) {
816
+ const defaultLocale = rootConfig.i18n?.defaultLocale || "en";
817
+ if (sitemapItem.locale === defaultLocale) {
818
+ return false;
758
819
  }
820
+ }
821
+ return true;
822
+ });
823
+ if (activeLogMode !== "quiet") {
824
+ console.log("\nhtml output:");
825
+ }
826
+ const concurrency = Number(options?.concurrency || 10);
827
+ const resolvedThreads = resolveNumThreads(
828
+ options?.threads,
829
+ sitemapEntries.length
830
+ );
831
+ const numThreads = resolvedThreads.count;
832
+ const workerConcurrency = numThreads > 0 ? Math.max(Math.ceil(concurrency / numThreads), 1) : 0;
833
+ if (activeLogMode !== "quiet") {
834
+ const threadsDesc = numThreads > 0 ? `${numThreads} workers x ${workerConcurrency} pages/worker` : `in-process, ${concurrency} concurrent pages`;
835
+ console.log(
836
+ ` ${dim3(`threads: ${threadsDesc} (${resolvedThreads.source})`)}`
837
+ );
838
+ }
839
+ const progress = new BuildProgress({
840
+ total: sitemapEntries.length,
841
+ mode: activeLogMode,
842
+ itemLabel: "pages",
843
+ outputDirLabel: "dist/html/"
844
+ });
845
+ try {
846
+ if (numThreads > 0) {
847
+ const pool = new BuildWorkerPool({
848
+ numWorkers: numThreads,
849
+ workerConcurrency,
850
+ rootDir,
851
+ mode
852
+ });
759
853
  try {
760
- const routeModule = sitemapItem.route.module;
761
- if (routeModule.getStaticContent) {
762
- let props;
763
- if (routeModule.getStaticProps) {
764
- props = await routeModule.getStaticProps({
765
- rootConfig,
766
- params: sitemapItem.params
767
- });
768
- if (props?.notFound) {
769
- return;
770
- }
771
- } else {
772
- props = { rootConfig, params: sitemapItem.params };
773
- }
774
- const result = await routeModule.getStaticContent(props);
775
- let body;
776
- if (typeof result === "string") {
777
- body = result;
778
- } else if (result && typeof result === "object") {
779
- body = result.body;
780
- } else {
781
- body = "";
782
- }
783
- const outFilePath2 = urlPath.slice(1);
784
- const outPath2 = path3.join(buildDir, outFilePath2);
785
- await makeDir(path3.dirname(outPath2));
786
- await writeFile(outPath2, normalizeLineEndings(body));
787
- printFileOutput(fileSize(outPath2), "dist/html/", outFilePath2);
788
- return;
789
- }
790
- const data = await renderer.renderRoute(sitemapItem.route, {
791
- routeParams: sitemapItem.params
792
- });
793
- if (data.notFound) {
794
- return;
795
- }
796
- let outFilePath = path3.join(urlPath.slice(1), "index.html");
797
- if (outFilePath.endsWith("404/index.html")) {
798
- outFilePath = outFilePath.replace("404/index.html", "404.html");
799
- }
800
- const outPath = path3.join(buildDir, outFilePath);
801
- if (rootConfig.sitemap && outFilePath.endsWith("index.html")) {
802
- const sitemapXmlItem = {
803
- url: `${domain}${urlPath}`,
804
- locale: sitemapItem.locale,
805
- alts: []
806
- };
807
- sitemapXmlItems.push(sitemapXmlItem);
808
- if (sitemapItem.alts) {
809
- Object.entries(sitemapItem.alts).forEach(([altLocale, item]) => {
810
- sitemapXmlItem.alts.push({
811
- url: `${domain}${item.urlPath}`,
812
- locale: altLocale,
813
- hreflang: item.hrefLang
854
+ await pool.ready();
855
+ await Promise.all(
856
+ sitemapEntries.map(async ([urlPath, sitemapItem]) => {
857
+ try {
858
+ const result = await pool.run({
859
+ urlPath,
860
+ params: sitemapItem.params,
861
+ routeSrc: sitemapItem.route.src,
862
+ locale: sitemapItem.locale
814
863
  });
815
- });
816
- }
817
- }
818
- const html = await transformHtml(data.html || "", rootConfig);
819
- await writeFile(outPath, normalizeLineEndings(html));
820
- printFileOutput(fileSize(outPath), "dist/html/", outFilePath);
821
- } catch (e) {
822
- logBuildError(
823
- { route: sitemapItem.route, params: sitemapItem.params, urlPath },
824
- e
825
- );
826
- throw new Error(
827
- `BuildError: ${urlPath} (${sitemapItem.route.src}) failed to build.`,
828
- { cause: e }
864
+ onPageResult(sitemapItem, result);
865
+ } catch (e) {
866
+ if (e instanceof BuildPageError) {
867
+ progress.abort();
868
+ logBuildError(
869
+ {
870
+ route: sitemapItem.route,
871
+ params: sitemapItem.params,
872
+ urlPath
873
+ },
874
+ new Error(e.workerError)
875
+ );
876
+ }
877
+ throw e;
878
+ }
879
+ })
829
880
  );
881
+ } finally {
882
+ await pool.terminate();
830
883
  }
884
+ } else {
885
+ await batchAsyncCalls(
886
+ sitemapEntries,
887
+ concurrency,
888
+ async ([urlPath, sitemapItem]) => {
889
+ try {
890
+ const result = await buildPage(
891
+ renderer,
892
+ rootConfig,
893
+ buildDir,
894
+ sitemapItem.route,
895
+ { urlPath, params: sitemapItem.params }
896
+ );
897
+ onPageResult(sitemapItem, result);
898
+ } catch (e) {
899
+ progress.abort();
900
+ logBuildError(
901
+ { route: sitemapItem.route, params: sitemapItem.params, urlPath },
902
+ e
903
+ );
904
+ throw new Error(
905
+ `BuildError: ${urlPath} (${sitemapItem.route.src}) failed to build.`,
906
+ { cause: e }
907
+ );
908
+ }
909
+ }
910
+ );
831
911
  }
832
- );
912
+ } catch (e) {
913
+ progress.abort();
914
+ throw e;
915
+ }
916
+ progress.finish();
833
917
  if (rootConfig.sitemap) {
834
918
  const sitemapXmlBuilder = [];
835
919
  sitemapXmlItems.sort((a, b) => a.url.localeCompare(b.url));
@@ -855,7 +939,7 @@ async function build(rootProjectDir, options) {
855
939
  "</urlset>"
856
940
  ];
857
941
  const sitemapXml = sitemapXmlLines.join("\n");
858
- const outPath = path3.join(buildDir, "sitemap.xml");
942
+ const outPath = path2.join(buildDir, "sitemap.xml");
859
943
  await writeFile(outPath, sitemapXml);
860
944
  printFileOutput(fileSize(outPath), "dist/html/", "sitemap.xml");
861
945
  }
@@ -869,22 +953,46 @@ async function build(rootProjectDir, options) {
869
953
  function isRouteFile(filepath) {
870
954
  return filepath.startsWith("routes") && isJsFile(filepath);
871
955
  }
872
- function fileSize(filepath) {
873
- const stats = fsExtra.statSync(filepath);
874
- const bytes = stats.size;
875
- const k = 1024;
876
- if (bytes < k) {
877
- return (bytes / k).toFixed(2) + " kB";
956
+ var MIN_PAGES_PER_THREAD = 10;
957
+ var MEMORY_PER_THREAD = 1024 * 1024 * 1024;
958
+ function resolveNumThreads(threads, numPages) {
959
+ if (threads === void 0 || threads === false) {
960
+ return { count: 0, source: "--threads not set" };
961
+ }
962
+ if (threads === true || threads === "auto") {
963
+ const maxByCpu = Math.max(os.availableParallelism() - 1, 1);
964
+ const maxByPages = Math.floor(numPages / MIN_PAGES_PER_THREAD);
965
+ const maxByMemory = Math.max(
966
+ Math.floor(os.freemem() / MEMORY_PER_THREAD) - 1,
967
+ 1
968
+ );
969
+ const numThreads = Math.min(maxByCpu, maxByPages, maxByMemory);
970
+ const source = `auto = min(cpu ${maxByCpu}, pages ${maxByPages}, memory ${maxByMemory})`;
971
+ if (numThreads <= 1) {
972
+ return { count: 0, source };
973
+ }
974
+ return { count: numThreads, source };
878
975
  }
879
- const units = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
880
- const i = Math.floor(Math.log(bytes) / Math.log(k));
881
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + units[i];
976
+ const num = parseInt(threads);
977
+ if (isNaN(num) || num < 0) {
978
+ throw new Error(`invalid --threads value: ${threads}`);
979
+ }
980
+ return { count: num, source: `--threads=${num}` };
981
+ }
982
+ function fileSizeBytes(filepath) {
983
+ return fsExtra.statSync(filepath).size;
984
+ }
985
+ function fileSize(filepath) {
986
+ return formatBytes(fileSizeBytes(filepath));
882
987
  }
883
988
  function printFileOutput(fileSize2, outputDir, outputFile) {
989
+ if (activeLogMode === "quiet") {
990
+ return;
991
+ }
884
992
  const indent = " ".repeat(2);
885
993
  const paddedSize = fileSize2.padStart(9, " ");
886
994
  console.log(
887
- `${indent}${dim2(paddedSize)} ${dim2(outputDir)}${cyan(outputFile)}`
995
+ `${indent}${dim3(paddedSize)} ${dim3(outputDir)}${cyan2(outputFile)}`
888
996
  );
889
997
  }
890
998
  function sanitizeFileName(name) {
@@ -917,18 +1025,15 @@ function formatParams(params) {
917
1025
  return ` ${key}: ${value}`;
918
1026
  }).join("\n");
919
1027
  }
920
- function normalizeLineEndings(str) {
921
- return str.replace(/\r\n?/g, "\n");
922
- }
923
1028
 
924
1029
  // src/cli/codegen.ts
925
- import { promises as fs3 } from "node:fs";
926
- import path4 from "node:path";
927
- import { fileURLToPath as fileURLToPath2 } from "node:url";
928
- import glob3 from "tiny-glob";
929
- var __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
1030
+ import { promises as fs } from "node:fs";
1031
+ import path3 from "node:path";
1032
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
1033
+ import glob2 from "tiny-glob";
1034
+ var __dirname3 = path3.dirname(fileURLToPath3(import.meta.url));
930
1035
  async function codegen(type, name, options) {
931
- const rootDir = path4.resolve(process.cwd());
1036
+ const rootDir = path3.resolve(process.cwd());
932
1037
  const project = new Project(rootDir);
933
1038
  await project.generateCode(type, name, options);
934
1039
  }
@@ -938,8 +1043,8 @@ var Project = class {
938
1043
  constructor(rootDir) {
939
1044
  this.rootDir = rootDir;
940
1045
  this.tplDirs = [
941
- path4.join(rootDir, "codegen"),
942
- path4.join(__dirname2, "../codegen")
1046
+ path3.join(rootDir, "codegen"),
1047
+ path3.join(__dirname3, "../codegen")
943
1048
  ];
944
1049
  }
945
1050
  async generateCode(type, name, options) {
@@ -949,7 +1054,7 @@ var Project = class {
949
1054
  return;
950
1055
  }
951
1056
  const outname = options?.out || `${type}s`;
952
- const outdir = path4.join(this.rootDir, outname, name);
1057
+ const outdir = path3.join(this.rootDir, outname, name);
953
1058
  if (await dirExists(outdir)) {
954
1059
  await rmDir(outdir);
955
1060
  }
@@ -958,7 +1063,7 @@ var Project = class {
958
1063
  const tpl = await files[tplName].read();
959
1064
  const content = tpl.replaceAll("[[name]]", name).replaceAll("[[name:camel_upper]]", toCamelCaseUpper(name));
960
1065
  const filename = tplName.replace("[name]", name);
961
- const filepath = path4.join(outdir, filename);
1066
+ const filepath = path3.join(outdir, filename);
962
1067
  await writeFile(filepath, content);
963
1068
  console.log(`saved ${filepath}`);
964
1069
  }
@@ -969,7 +1074,7 @@ var Project = class {
969
1074
  */
970
1075
  async loadFiles(type) {
971
1076
  for (const tplDir of this.tplDirs) {
972
- const typeDir = path4.join(tplDir, type);
1077
+ const typeDir = path3.join(tplDir, type);
973
1078
  if (await dirExists(typeDir)) {
974
1079
  return this.loadTplFiles(typeDir);
975
1080
  }
@@ -981,12 +1086,12 @@ var Project = class {
981
1086
  */
982
1087
  async loadTplFiles(dirpath) {
983
1088
  const tplFiles = {};
984
- const files = await glob3("*.tpl", { cwd: dirpath });
1089
+ const files = await glob2("*.tpl", { cwd: dirpath });
985
1090
  for (const filename of files) {
986
- const filepath = path4.join(dirpath, filename);
1091
+ const filepath = path3.join(dirpath, filename);
987
1092
  const name = filename.slice(0, -4);
988
1093
  tplFiles[name] = {
989
- read: () => fs3.readFile(filepath, "utf-8")
1094
+ read: () => fs.readFile(filepath, "utf-8")
990
1095
  };
991
1096
  }
992
1097
  return tplFiles;
@@ -1002,23 +1107,23 @@ function toTitleCase(str) {
1002
1107
  }
1003
1108
 
1004
1109
  // src/cli/create-package.ts
1005
- import { promises as fs4 } from "node:fs";
1006
- import path5 from "node:path";
1110
+ import { promises as fs2 } from "node:fs";
1111
+ import path4 from "node:path";
1007
1112
  import { build as esbuild } from "esbuild";
1008
1113
  async function createPackage(rootProjectDir, options) {
1009
1114
  const mode = options?.mode || "production";
1010
1115
  process.env.NODE_ENV = mode;
1011
- const rootDir = path5.resolve(rootProjectDir || process.cwd());
1012
- const distDir = path5.join(rootDir, "dist");
1116
+ const rootDir = path4.resolve(rootProjectDir || process.cwd());
1117
+ const distDir = path4.join(rootDir, "dist");
1013
1118
  const target = options?.target || await getDefaultTarget(rootDir);
1014
- const outDir = path5.resolve(options?.out || target || "out");
1119
+ const outDir = path4.resolve(options?.out || target || "out");
1015
1120
  await build(rootProjectDir, { ssrOnly: true, mode });
1016
1121
  await rmDir(outDir);
1017
1122
  await makeDir(outDir);
1018
- await copyDir(distDir, path5.resolve(outDir, "dist"));
1019
- const collectionsDir = path5.resolve(rootDir, "collections");
1123
+ await copyDir(distDir, path4.resolve(outDir, "dist"));
1124
+ const collectionsDir = path4.resolve(rootDir, "collections");
1020
1125
  if (await dirExists(collectionsDir)) {
1021
- await copyDir(collectionsDir, path5.join(outDir, "collections"));
1126
+ await copyDir(collectionsDir, path4.join(outDir, "collections"));
1022
1127
  }
1023
1128
  const packageJson = await generatePackageJson(rootDir, options);
1024
1129
  if (options?.version && packageJson.dependencies) {
@@ -1033,21 +1138,21 @@ async function createPackage(rootProjectDir, options) {
1033
1138
  }
1034
1139
  }
1035
1140
  if (target === "appengine") {
1036
- const appYamlPath = options?.appYaml || path5.join(rootDir, "app.yaml");
1141
+ const appYamlPath = options?.appYaml || path4.join(rootDir, "app.yaml");
1037
1142
  await onAppEngine({ packageJson, outDir, appYamlPath });
1038
1143
  } else if (target === "firebase") {
1039
1144
  await onFirebase({ rootDir, packageJson, outDir });
1040
1145
  }
1041
- await writeJson(path5.resolve(outDir, "package.json"), packageJson);
1146
+ await writeJson(path4.resolve(outDir, "package.json"), packageJson);
1042
1147
  console.log("done!");
1043
1148
  console.log(`saved package to ${outDir}`);
1044
1149
  }
1045
1150
  async function getDefaultTarget(rootDir) {
1046
- const firebaseConfigPath = path5.resolve(rootDir, "firebase.json");
1151
+ const firebaseConfigPath = path4.resolve(rootDir, "firebase.json");
1047
1152
  if (await fileExists(firebaseConfigPath)) {
1048
1153
  return "firebase";
1049
1154
  }
1050
- const appEngineConfigPath = path5.resolve(rootDir, "app.yaml");
1155
+ const appEngineConfigPath = path4.resolve(rootDir, "app.yaml");
1051
1156
  if (await fileExists(appEngineConfigPath)) {
1052
1157
  return "appengine";
1053
1158
  }
@@ -1055,7 +1160,7 @@ async function getDefaultTarget(rootDir) {
1055
1160
  }
1056
1161
  async function generatePackageJson(rootDir, options) {
1057
1162
  const packageJson = await loadJson(
1058
- path5.resolve(rootDir, "package.json")
1163
+ path4.resolve(rootDir, "package.json")
1059
1164
  );
1060
1165
  const allDeps = flattenPackageDepsFromMonorepo(rootDir);
1061
1166
  packageJson.dependencies ??= {};
@@ -1082,7 +1187,7 @@ async function generatePackageJson(rootDir, options) {
1082
1187
  async function onAppEngine(options) {
1083
1188
  const { outDir, packageJson, appYamlPath } = options;
1084
1189
  if (await fileExists(appYamlPath)) {
1085
- await fs4.copyFile(appYamlPath, path5.resolve(outDir, "app.yaml"));
1190
+ await fs2.copyFile(appYamlPath, path4.resolve(outDir, "app.yaml"));
1086
1191
  }
1087
1192
  if (packageJson.scripts?.start) {
1088
1193
  packageJson.scripts = { start: packageJson.scripts.start };
@@ -1092,11 +1197,11 @@ async function onAppEngine(options) {
1092
1197
  }
1093
1198
  async function onFirebase(options) {
1094
1199
  const { rootDir, outDir, packageJson } = options;
1095
- const outBasename = path5.basename(outDir);
1200
+ const outBasename = path4.basename(outDir);
1096
1201
  if (outBasename === "functions") {
1097
- const indexTsFile = path5.resolve(rootDir, "index.ts");
1202
+ const indexTsFile = path4.resolve(rootDir, "index.ts");
1098
1203
  if (await fileExists(indexTsFile)) {
1099
- await bundleTsFile(indexTsFile, path5.resolve(outDir, "index.js"));
1204
+ await bundleTsFile(indexTsFile, path4.resolve(outDir, "index.js"));
1100
1205
  }
1101
1206
  }
1102
1207
  if (packageJson.scripts?.start) {
@@ -1121,7 +1226,7 @@ async function bundleTsFile(srcPath, outPath) {
1121
1226
  setup(build2) {
1122
1227
  build2.onResolve({ filter: /.*/ }, (args) => {
1123
1228
  const id = args.path;
1124
- if (id[0] !== "." && !path5.isAbsolute(id)) {
1229
+ if (id[0] !== "." && !path4.isAbsolute(id)) {
1125
1230
  return {
1126
1231
  external: true
1127
1232
  };
@@ -1135,13 +1240,13 @@ async function bundleTsFile(srcPath, outPath) {
1135
1240
  }
1136
1241
 
1137
1242
  // src/cli/dev.ts
1138
- import path12 from "node:path";
1139
- import { fileURLToPath as fileURLToPath3 } from "node:url";
1243
+ import path11 from "node:path";
1244
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
1140
1245
  import cookieParser from "cookie-parser";
1141
1246
  import { default as express } from "express";
1142
- import { dim as dim5 } from "kleur/colors";
1247
+ import { dim as dim6 } from "kleur/colors";
1143
1248
  import sirv from "sirv";
1144
- import glob4 from "tiny-glob";
1249
+ import glob3 from "tiny-glob";
1145
1250
 
1146
1251
  // src/middleware/hooks.ts
1147
1252
  function hooksMiddleware() {
@@ -1229,8 +1334,8 @@ function testIsRedirectValid(url) {
1229
1334
  }
1230
1335
 
1231
1336
  // src/render/asset-map/dev-asset-map.ts
1232
- import path6 from "node:path";
1233
- import { searchForWorkspaceRoot as searchForWorkspaceRoot2 } from "vite";
1337
+ import path5 from "node:path";
1338
+ import { searchForWorkspaceRoot } from "vite";
1234
1339
  var DevServerAssetMap = class {
1235
1340
  rootConfig;
1236
1341
  moduleGraph;
@@ -1239,7 +1344,7 @@ var DevServerAssetMap = class {
1239
1344
  this.moduleGraph = moduleGraph;
1240
1345
  }
1241
1346
  async get(src) {
1242
- const file = path6.resolve(this.rootConfig.rootDir, src);
1347
+ const file = path5.resolve(this.rootConfig.rootDir, src);
1243
1348
  const viteModules = this.moduleGraph.getModulesByFile(file);
1244
1349
  if (viteModules && viteModules.size > 0) {
1245
1350
  const [viteModule] = viteModules;
@@ -1257,7 +1362,7 @@ var DevServerAssetMap = class {
1257
1362
  getJsDeps: async () => [assetUrl]
1258
1363
  };
1259
1364
  }
1260
- const workspaceRoot = searchForWorkspaceRoot2(this.rootConfig.rootDir);
1365
+ const workspaceRoot = searchForWorkspaceRoot(this.rootConfig.rootDir);
1261
1366
  if (await directoryContains(workspaceRoot, file)) {
1262
1367
  const assetUrl = `/@fs/${file}`;
1263
1368
  return {
@@ -1271,7 +1376,7 @@ var DevServerAssetMap = class {
1271
1376
  return null;
1272
1377
  }
1273
1378
  filePathToSrc(file) {
1274
- return path6.relative(this.rootConfig.rootDir, file);
1379
+ return path5.relative(this.rootConfig.rootDir, file);
1275
1380
  }
1276
1381
  };
1277
1382
  var DevServerAsset = class _DevServerAsset {
@@ -1313,7 +1418,7 @@ var DevServerAsset = class _DevServerAsset {
1313
1418
  return;
1314
1419
  }
1315
1420
  visited.add(asset.moduleId);
1316
- const parts = path6.parse(asset.assetUrl);
1421
+ const parts = path5.parse(asset.assetUrl);
1317
1422
  if ([".js", ".jsx", ".ts", ".tsx"].includes(parts.ext) && asset.moduleId.includes("/elements/")) {
1318
1423
  urls.add(asset.assetUrl);
1319
1424
  }
@@ -1340,7 +1445,7 @@ var DevServerAsset = class _DevServerAsset {
1340
1445
  }
1341
1446
  visited.add(asset.assetUrl);
1342
1447
  if (asset.src.endsWith(".scss")) {
1343
- const parts = path6.parse(asset.src);
1448
+ const parts = path5.parse(asset.src);
1344
1449
  if (!parts.name.startsWith("_")) {
1345
1450
  urls.add(asset.assetUrl);
1346
1451
  }
@@ -1421,19 +1526,19 @@ function getSessionCookieSecret(rootConfig, rootDir) {
1421
1526
 
1422
1527
  // src/cli/secrets.ts
1423
1528
  import { spawn as spawn3 } from "node:child_process";
1424
- import fs8 from "node:fs";
1425
- import path10 from "node:path";
1529
+ import fs6 from "node:fs";
1530
+ import path9 from "node:path";
1426
1531
  import * as readline from "node:readline";
1427
- import { dim as dim3, green, red, yellow } from "kleur/colors";
1532
+ import { dim as dim4, green as green2, red, yellow } from "kleur/colors";
1428
1533
 
1429
1534
  // src/secrets/manifest.ts
1430
- import fs5 from "node:fs";
1431
- import path7 from "node:path";
1535
+ import fs3 from "node:fs";
1536
+ import path6 from "node:path";
1432
1537
  var MANIFEST_FILENAME = ".root.secrets.json";
1433
1538
  var GSM_KEY_RE = /^[A-Za-z0-9_-]{1,255}$/;
1434
1539
  var ENV_NAME_RE = /^[A-Za-z0-9_]+$/;
1435
1540
  function manifestPath(dir) {
1436
- return path7.join(dir, MANIFEST_FILENAME);
1541
+ return path6.join(dir, MANIFEST_FILENAME);
1437
1542
  }
1438
1543
  function isValidEnvName(name) {
1439
1544
  return ENV_NAME_RE.test(name);
@@ -1441,7 +1546,7 @@ function isValidEnvName(name) {
1441
1546
  async function readManifest(filePath) {
1442
1547
  let raw;
1443
1548
  try {
1444
- raw = await fs5.promises.readFile(filePath, "utf8");
1549
+ raw = await fs3.promises.readFile(filePath, "utf8");
1445
1550
  } catch (err) {
1446
1551
  if (err.code === "ENOENT") {
1447
1552
  return null;
@@ -1468,8 +1573,8 @@ async function writeManifest(filePath, manifest) {
1468
1573
  ...manifest.import ? { import: manifest.import } : {},
1469
1574
  secrets: sortedSecrets
1470
1575
  };
1471
- await fs5.promises.mkdir(path7.dirname(filePath), { recursive: true });
1472
- await fs5.promises.writeFile(
1576
+ await fs3.promises.mkdir(path6.dirname(filePath), { recursive: true });
1577
+ await fs3.promises.writeFile(
1473
1578
  filePath,
1474
1579
  JSON.stringify(ordered, null, 2) + "\n",
1475
1580
  "utf8"
@@ -1497,7 +1602,7 @@ async function resolveManagedKeys(rootDir) {
1497
1602
  }
1498
1603
  let shared;
1499
1604
  if (site.import) {
1500
- const sharedPath = path7.resolve(rootDir, site.import.manifest);
1605
+ const sharedPath = path6.resolve(rootDir, site.import.manifest);
1501
1606
  const loaded = await readManifest(sharedPath);
1502
1607
  if (!loaded) {
1503
1608
  throw new Error(`imported manifest not found: ${sharedPath}`);
@@ -1624,16 +1729,16 @@ function validateManifest(value, filePath) {
1624
1729
 
1625
1730
  // src/secrets/secrets.ts
1626
1731
  import { spawn as spawn2 } from "node:child_process";
1627
- import fs7 from "node:fs";
1628
- import path9 from "node:path";
1732
+ import fs5 from "node:fs";
1733
+ import path8 from "node:path";
1629
1734
 
1630
1735
  // src/secrets/env-file.ts
1631
- import fs6 from "node:fs";
1632
- import path8 from "node:path";
1736
+ import fs4 from "node:fs";
1737
+ import path7 from "node:path";
1633
1738
  import dotenv from "dotenv";
1634
1739
  async function readEnvFile(envPath2) {
1635
1740
  try {
1636
- return await fs6.promises.readFile(envPath2, "utf8");
1741
+ return await fs4.promises.readFile(envPath2, "utf8");
1637
1742
  } catch (err) {
1638
1743
  if (err.code === "ENOENT") {
1639
1744
  return "";
@@ -1642,11 +1747,11 @@ async function readEnvFile(envPath2) {
1642
1747
  }
1643
1748
  }
1644
1749
  async function writeEnvFile(envPath2, content) {
1645
- const dir = path8.dirname(envPath2);
1646
- await fs6.promises.mkdir(dir, { recursive: true });
1647
- const tmp = path8.join(dir, `.env.tmp-${process.pid}-${Date.now()}`);
1648
- await fs6.promises.writeFile(tmp, content, "utf8");
1649
- await fs6.promises.rename(tmp, envPath2);
1750
+ const dir = path7.dirname(envPath2);
1751
+ await fs4.promises.mkdir(dir, { recursive: true });
1752
+ const tmp = path7.join(dir, `.env.tmp-${process.pid}-${Date.now()}`);
1753
+ await fs4.promises.writeFile(tmp, content, "utf8");
1754
+ await fs4.promises.rename(tmp, envPath2);
1650
1755
  }
1651
1756
  function parseEnv(content) {
1652
1757
  return dotenv.parse(content);
@@ -1864,10 +1969,10 @@ function hashValue(salt, value) {
1864
1969
  // src/secrets/secrets.ts
1865
1970
  var STATE_FILENAME = "secrets-sync.json";
1866
1971
  function localStatePath(rootDir) {
1867
- return path9.join(rootDir, ".root", STATE_FILENAME);
1972
+ return path8.join(rootDir, ".root", STATE_FILENAME);
1868
1973
  }
1869
1974
  function envPath(rootDir) {
1870
- return path9.join(rootDir, ".env");
1975
+ return path8.join(rootDir, ".env");
1871
1976
  }
1872
1977
  var inFlight = /* @__PURE__ */ new Map();
1873
1978
  function syncSecrets(options) {
@@ -2093,7 +2198,7 @@ async function pushEnvToSecrets(options) {
2093
2198
  };
2094
2199
  }
2095
2200
  async function sharedImportedNames(rootDir, manifestFilePath) {
2096
- if (path9.resolve(manifestFilePath) !== path9.resolve(manifestPath(rootDir))) {
2201
+ if (path8.resolve(manifestFilePath) !== path8.resolve(manifestPath(rootDir))) {
2097
2202
  return /* @__PURE__ */ new Set();
2098
2203
  }
2099
2204
  try {
@@ -2156,7 +2261,7 @@ function isSyncDisabledByEnv() {
2156
2261
  }
2157
2262
  async function readLocalState(rootDir) {
2158
2263
  try {
2159
- const raw = await fs7.promises.readFile(localStatePath(rootDir), "utf8");
2264
+ const raw = await fs5.promises.readFile(localStatePath(rootDir), "utf8");
2160
2265
  const parsed = JSON.parse(raw);
2161
2266
  if (parsed && typeof parsed === "object" && typeof parsed.salt === "string" && parsed.secrets && typeof parsed.secrets === "object") {
2162
2267
  return {
@@ -2170,8 +2275,8 @@ async function readLocalState(rootDir) {
2170
2275
  }
2171
2276
  async function writeLocalState(rootDir, state) {
2172
2277
  const file = localStatePath(rootDir);
2173
- await fs7.promises.mkdir(path9.dirname(file), { recursive: true });
2174
- await fs7.promises.writeFile(
2278
+ await fs5.promises.mkdir(path8.dirname(file), { recursive: true });
2279
+ await fs5.promises.writeFile(
2175
2280
  file,
2176
2281
  JSON.stringify(state, null, 2) + "\n",
2177
2282
  "utf8"
@@ -2197,7 +2302,7 @@ function getGitEmail() {
2197
2302
  }
2198
2303
 
2199
2304
  // src/cli/secrets.ts
2200
- var BAR = dim3("\u2503");
2305
+ var BAR = dim4("\u2503");
2201
2306
  var DEV_SYNC_TIMEOUT_MS = 1e4;
2202
2307
  function registerSecretsCommands(program) {
2203
2308
  const secrets = program.command("secrets").description(
@@ -2237,7 +2342,7 @@ function registerSecretsCommands(program) {
2237
2342
  }
2238
2343
  async function secretsInit(opts) {
2239
2344
  const rootDir = process.cwd();
2240
- const target = opts.manifest ? path10.resolve(rootDir, opts.manifest) : manifestPath(rootDir);
2345
+ const target = opts.manifest ? path9.resolve(rootDir, opts.manifest) : manifestPath(rootDir);
2241
2346
  if (await readManifest(target)) {
2242
2347
  console.log(
2243
2348
  `${BAR} ${yellow("secrets:")} manifest already exists at ${rel(rootDir, target)}`
@@ -2258,41 +2363,41 @@ async function secretsInit(opts) {
2258
2363
  await writeManifest(target, manifest);
2259
2364
  const addedGitignore = await ensureRootGitignored(rootDir);
2260
2365
  console.log();
2261
- console.log(`${BAR} ${green("secrets:")} created ${rel(rootDir, target)}`);
2366
+ console.log(`${BAR} ${green2("secrets:")} created ${rel(rootDir, target)}`);
2262
2367
  if (addedGitignore) {
2263
- console.log(`${BAR} added ${dim3(".root/")} to .gitignore`);
2368
+ console.log(`${BAR} added ${dim4(".root/")} to .gitignore`);
2264
2369
  }
2265
2370
  await warnIfEnvNotIgnored(rootDir);
2266
2371
  console.log(
2267
- `${BAR} ${dim3("next: `root secrets set <NAME>` to store a value")}`
2372
+ `${BAR} ${dim4("next: `root secrets set <NAME>` to store a value")}`
2268
2373
  );
2269
2374
  console.log();
2270
2375
  }
2271
2376
  async function secretsSet(name, opts) {
2272
2377
  const rootDir = process.cwd();
2273
- const manifestFilePath = opts.manifest ? path10.resolve(rootDir, opts.manifest) : manifestPath(rootDir);
2378
+ const manifestFilePath = opts.manifest ? path9.resolve(rootDir, opts.manifest) : manifestPath(rootDir);
2274
2379
  const secretValue = await readSecretValue(name);
2275
2380
  if (!secretValue) {
2276
2381
  throw new Error("no value provided on stdin");
2277
2382
  }
2278
2383
  await setSecret({ rootDir, manifestFilePath, name, value: secretValue });
2279
- console.log(`${BAR} ${green("secrets:")} set ${name}`);
2384
+ console.log(`${BAR} ${green2("secrets:")} set ${name}`);
2280
2385
  console.log(
2281
- `${BAR} ${dim3(`commit ${rel(rootDir, manifestFilePath)} to share this change`)}`
2386
+ `${BAR} ${dim4(`commit ${rel(rootDir, manifestFilePath)} to share this change`)}`
2282
2387
  );
2283
2388
  }
2284
2389
  async function secretsRm(name, opts) {
2285
2390
  const rootDir = process.cwd();
2286
- const manifestFilePath = opts.manifest ? path10.resolve(rootDir, opts.manifest) : manifestPath(rootDir);
2391
+ const manifestFilePath = opts.manifest ? path9.resolve(rootDir, opts.manifest) : manifestPath(rootDir);
2287
2392
  await removeSecret({ rootDir, manifestFilePath, name });
2288
- console.log(`${BAR} ${green("secrets:")} removed ${name}`);
2393
+ console.log(`${BAR} ${green2("secrets:")} removed ${name}`);
2289
2394
  console.log(
2290
- `${BAR} ${dim3(`commit ${rel(rootDir, manifestFilePath)} to share this change`)}`
2395
+ `${BAR} ${dim4(`commit ${rel(rootDir, manifestFilePath)} to share this change`)}`
2291
2396
  );
2292
2397
  }
2293
2398
  async function secretsPush(opts) {
2294
2399
  const rootDir = process.cwd();
2295
- const manifestFilePath = opts.manifest ? path10.resolve(rootDir, opts.manifest) : manifestPath(rootDir);
2400
+ const manifestFilePath = opts.manifest ? path9.resolve(rootDir, opts.manifest) : manifestPath(rootDir);
2296
2401
  const only = opts.keys ? opts.keys.split(",").map((k) => k.trim()).filter(Boolean) : void 0;
2297
2402
  const confirm = opts.yes ? void 0 : async (names, gsmKey) => {
2298
2403
  if (!process.stdin.isTTY) {
@@ -2302,7 +2407,7 @@ async function secretsPush(opts) {
2302
2407
  }
2303
2408
  console.log();
2304
2409
  console.log(
2305
- `${BAR} ${green("secrets:")} push ${names.length} value(s) from .env to "${gsmKey}":`
2410
+ `${BAR} ${green2("secrets:")} push ${names.length} value(s) from .env to "${gsmKey}":`
2306
2411
  );
2307
2412
  for (const name of names) {
2308
2413
  console.log(`${BAR} ${name}`);
@@ -2345,13 +2450,13 @@ async function secretsStatus() {
2345
2450
  }
2346
2451
  console.log();
2347
2452
  console.log(
2348
- `${BAR} ${green("secrets:")} ${resolved.site.gsmKey} ${dim3(`(${resolved.site.gcpProjectId})`)}`
2453
+ `${BAR} ${green2("secrets:")} ${resolved.site.gsmKey} ${dim4(`(${resolved.site.gcpProjectId})`)}`
2349
2454
  );
2350
2455
  if (resolved.shared) {
2351
- console.log(`${BAR} imports ${dim3(resolved.shared.gsmKey)}`);
2456
+ console.log(`${BAR} imports ${dim4(resolved.shared.gsmKey)}`);
2352
2457
  }
2353
2458
  if (keys.length === 0) {
2354
- console.log(`${BAR} ${dim3("no managed keys yet")}`);
2459
+ console.log(`${BAR} ${dim4("no managed keys yet")}`);
2355
2460
  }
2356
2461
  for (const key of keys) {
2357
2462
  console.log(`${BAR} ${formatStatus(key)}`);
@@ -2379,15 +2484,15 @@ async function syncSecretsOnDev(rootDir) {
2379
2484
  function formatStatus(key) {
2380
2485
  switch (key.kind) {
2381
2486
  case "in-sync":
2382
- return `${green("\u2713")} ${key.name}`;
2487
+ return `${green2("\u2713")} ${key.name}`;
2383
2488
  case "remote-newer":
2384
- return `${yellow("\u2193")} ${key.name} ${dim3("(update available \u2014 run `root secrets sync`)")}`;
2489
+ return `${yellow("\u2193")} ${key.name} ${dim4("(update available \u2014 run `root secrets sync`)")}`;
2385
2490
  case "locally-edited":
2386
- return `${dim3("\u2022")} ${key.name} ${dim3("(locally edited)")}`;
2491
+ return `${dim4("\u2022")} ${key.name} ${dim4("(locally edited)")}`;
2387
2492
  case "conflict":
2388
- return `${yellow("!")} ${key.name} ${dim3("(changed locally & remotely)")}`;
2493
+ return `${yellow("!")} ${key.name} ${dim4("(changed locally & remotely)")}`;
2389
2494
  case "not-pulled":
2390
- return `${yellow("\u2193")} ${key.name} ${dim3("(not pulled yet)")}`;
2495
+ return `${yellow("\u2193")} ${key.name} ${dim4("(not pulled yet)")}`;
2391
2496
  default:
2392
2497
  return key.name;
2393
2498
  }
@@ -2399,13 +2504,13 @@ function printSyncSummary(result, opts = {}) {
2399
2504
  }
2400
2505
  const parts = [];
2401
2506
  if (result.changed.length) {
2402
- parts.push(green(`${result.changed.length} updated`));
2507
+ parts.push(green2(`${result.changed.length} updated`));
2403
2508
  }
2404
2509
  if (result.removed.length) {
2405
2510
  parts.push(`${result.removed.length} removed`);
2406
2511
  }
2407
2512
  if (result.kept.length) {
2408
- parts.push(dim3(`${result.kept.length} local`));
2513
+ parts.push(dim4(`${result.kept.length} local`));
2409
2514
  }
2410
2515
  if (result.conflicts.length) {
2411
2516
  parts.push(yellow(`${result.conflicts.length} conflict`));
@@ -2415,22 +2520,22 @@ function printSyncSummary(result, opts = {}) {
2415
2520
  }
2416
2521
  console.log();
2417
2522
  console.log(
2418
- `${BAR} ${green("secrets:")} ${parts.length ? parts.join(", ") : "up to date"}`
2523
+ `${BAR} ${green2("secrets:")} ${parts.length ? parts.join(", ") : "up to date"}`
2419
2524
  );
2420
2525
  for (const name of result.changed) {
2421
- console.log(`${BAR} ${green("+")} ${name}`);
2526
+ console.log(`${BAR} ${green2("+")} ${name}`);
2422
2527
  }
2423
2528
  for (const name of result.removed) {
2424
- console.log(`${BAR} ${dim3("-")} ${name} ${dim3("(removed)")}`);
2529
+ console.log(`${BAR} ${dim4("-")} ${name} ${dim4("(removed)")}`);
2425
2530
  }
2426
2531
  for (const name of result.overwritten) {
2427
2532
  console.log(
2428
- `${BAR} ${yellow("!")} ${name} ${dim3("(local value replaced)")}`
2533
+ `${BAR} ${yellow("!")} ${name} ${dim4("(local value replaced)")}`
2429
2534
  );
2430
2535
  }
2431
2536
  for (const name of result.conflicts) {
2432
2537
  console.log(
2433
- `${BAR} ${yellow("!")} ${name} ${dim3(
2538
+ `${BAR} ${yellow("!")} ${name} ${dim4(
2434
2539
  "(changed locally & remotely; keeping yours \u2014 `root secrets pull` to take remote)"
2435
2540
  )}`
2436
2541
  );
@@ -2452,28 +2557,28 @@ function printPushResult(result, rootDir, manifestFilePath) {
2452
2557
  console.log(`${BAR} ${yellow("secrets:")} nothing to push`);
2453
2558
  } else {
2454
2559
  console.log(
2455
- `${BAR} ${green("secrets:")} pushed ${result.pushed.length} value(s) to "${result.gsmKey}"`
2560
+ `${BAR} ${green2("secrets:")} pushed ${result.pushed.length} value(s) to "${result.gsmKey}"`
2456
2561
  );
2457
2562
  for (const name of result.pushed) {
2458
- console.log(`${BAR} ${green("+")} ${name}`);
2563
+ console.log(`${BAR} ${green2("+")} ${name}`);
2459
2564
  }
2460
2565
  }
2461
2566
  for (const skip of result.skipped) {
2462
- console.log(`${BAR} ${dim3("-")} ${skip.name} ${dim3(`(${skip.reason})`)}`);
2567
+ console.log(`${BAR} ${dim4("-")} ${skip.name} ${dim4(`(${skip.reason})`)}`);
2463
2568
  }
2464
2569
  if (result.pushed.length > 0) {
2465
2570
  console.log(
2466
- `${BAR} ${dim3(`commit ${rel(rootDir, manifestFilePath)} to share`)}`
2571
+ `${BAR} ${dim4(`commit ${rel(rootDir, manifestFilePath)} to share`)}`
2467
2572
  );
2468
2573
  }
2469
2574
  console.log();
2470
2575
  }
2471
2576
  async function ensureRootGitignored(rootDir) {
2472
2577
  const gitRoot = await gitToplevel(rootDir) ?? rootDir;
2473
- const gitignore = path10.join(gitRoot, ".gitignore");
2578
+ const gitignore = path9.join(gitRoot, ".gitignore");
2474
2579
  let content = "";
2475
2580
  try {
2476
- content = await fs8.promises.readFile(gitignore, "utf8");
2581
+ content = await fs6.promises.readFile(gitignore, "utf8");
2477
2582
  } catch {
2478
2583
  }
2479
2584
  const lines = content.split(/\r?\n/).map((line) => line.trim());
@@ -2481,7 +2586,7 @@ async function ensureRootGitignored(rootDir) {
2481
2586
  return false;
2482
2587
  }
2483
2588
  const prefix = content && !content.endsWith("\n") ? "\n" : "";
2484
- await fs8.promises.writeFile(gitignore, `${content}${prefix}.root/
2589
+ await fs6.promises.writeFile(gitignore, `${content}${prefix}.root/
2485
2590
  `, "utf8");
2486
2591
  return true;
2487
2592
  }
@@ -2489,7 +2594,7 @@ async function warnIfEnvNotIgnored(rootDir) {
2489
2594
  const ignored = await isPathGitIgnored(rootDir, ".env");
2490
2595
  if (ignored === false) {
2491
2596
  console.log(
2492
- `${BAR} ${yellow("warning:")} ${dim3(".env is not git-ignored \u2014 add it to .gitignore")}`
2597
+ `${BAR} ${yellow("warning:")} ${dim4(".env is not git-ignored \u2014 add it to .gitignore")}`
2493
2598
  );
2494
2599
  }
2495
2600
  }
@@ -2609,7 +2714,7 @@ function withTimeout(promise, ms) {
2609
2714
  });
2610
2715
  }
2611
2716
  function rel(rootDir, filePath) {
2612
- return path10.relative(rootDir, filePath) || path10.basename(filePath);
2717
+ return path9.relative(rootDir, filePath) || path9.basename(filePath);
2613
2718
  }
2614
2719
  function firstLine(text) {
2615
2720
  return String(text).split("\n")[0];
@@ -2626,11 +2731,11 @@ function action(fn) {
2626
2731
  }
2627
2732
 
2628
2733
  // src/cli/startup/startup-tasks.ts
2629
- import os from "node:os";
2630
- import path11 from "node:path";
2734
+ import os2 from "node:os";
2735
+ import path10 from "node:path";
2631
2736
 
2632
2737
  // src/cli/startup/check-version.ts
2633
- import { dim as dim4, green as green2, yellow as yellow2 } from "kleur/colors";
2738
+ import { dim as dim5, green as green3, yellow as yellow2 } from "kleur/colors";
2634
2739
  var PACKAGE_NAME = "@blinkk/root";
2635
2740
  var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
2636
2741
  var REGISTRY_TIMEOUT_MS = 3e3;
@@ -2701,16 +2806,16 @@ function parseVersion(version) {
2701
2806
  return [toInt(parts[0]), toInt(parts[1]), toInt(parts[2])];
2702
2807
  }
2703
2808
  function printUpdateNotice(current, latest) {
2704
- const bar = dim4("\u2503");
2809
+ const bar = dim5("\u2503");
2705
2810
  const label = yellow2(`Update available for ${PACKAGE_NAME}:`);
2706
2811
  console.log();
2707
- console.log(`${bar} ${label} ${dim4(current)} \u2192 ${green2(latest)}`);
2812
+ console.log(`${bar} ${label} ${dim5(current)} \u2192 ${green3(latest)}`);
2708
2813
  console.log();
2709
2814
  }
2710
2815
 
2711
2816
  // src/cli/startup/startup-tasks.ts
2712
2817
  var STARTUP_TASKS = [checkVersionTask];
2713
- var STATE_FILE = path11.join(os.homedir(), ".root", "startup-tasks.json");
2818
+ var STATE_FILE = path10.join(os2.homedir(), ".root", "startup-tasks.json");
2714
2819
  async function runStartupTasks(ctx) {
2715
2820
  try {
2716
2821
  const state = await readState();
@@ -2761,13 +2866,13 @@ async function writeState(state) {
2761
2866
  }
2762
2867
 
2763
2868
  // src/cli/dev.ts
2764
- var __dirname3 = path12.dirname(fileURLToPath3(import.meta.url));
2869
+ var __dirname4 = path11.dirname(fileURLToPath4(import.meta.url));
2765
2870
  var DEV_SERVER_404_LOG_IGNORE_PATHS = /* @__PURE__ */ new Set([
2766
2871
  "/.well-known/appspecific/com.chrome.devtools.json"
2767
2872
  ]);
2768
2873
  async function dev(rootProjectDir, options) {
2769
2874
  process.env.NODE_ENV = "development";
2770
- const rootDir = path12.resolve(rootProjectDir || process.cwd());
2875
+ const rootDir = path11.resolve(rootProjectDir || process.cwd());
2771
2876
  const defaultPort = parseInt(process.env.PORT || "4007");
2772
2877
  const host = options?.host || "localhost";
2773
2878
  const port = await findOpenPort(defaultPort, defaultPort + 10);
@@ -2783,19 +2888,19 @@ async function dev(rootProjectDir, options) {
2783
2888
  const basePath = rootConfig.base || "";
2784
2889
  const homePagePath = rootConfig.server?.homePagePath || basePath;
2785
2890
  console.log();
2786
- console.log(`${dim5("\u2503")} project: ${rootDir}`);
2787
- console.log(`${dim5("\u2503")} server: http://${host}:${port}${homePagePath}`);
2891
+ console.log(`${dim6("\u2503")} project: ${rootDir}`);
2892
+ console.log(`${dim6("\u2503")} server: http://${host}:${port}${homePagePath}`);
2788
2893
  if (testCmsEnabled(rootConfig)) {
2789
- console.log(`${dim5("\u2503")} cms: http://${host}:${port}/cms/`);
2894
+ console.log(`${dim6("\u2503")} cms: http://${host}:${port}/cms/`);
2790
2895
  }
2791
- console.log(`${dim5("\u2503")} mode: development`);
2896
+ console.log(`${dim6("\u2503")} mode: development`);
2792
2897
  console.log();
2793
2898
  currentServer = server.listen(port, host);
2794
2899
  const rootConfigDependencies = server.get(
2795
2900
  "rootConfigDependencies"
2796
2901
  );
2797
2902
  const dependencies = [
2798
- path12.resolve(rootDir, "root.config.ts"),
2903
+ path11.resolve(rootDir, "root.config.ts"),
2799
2904
  ...rootConfigDependencies
2800
2905
  ];
2801
2906
  viteServer.watcher.add(dependencies);
@@ -2803,7 +2908,7 @@ async function dev(rootProjectDir, options) {
2803
2908
  if (dependencies.includes(file)) {
2804
2909
  console.log(
2805
2910
  `
2806
- ${dim5("\u2503")} root.config.ts changed. restarting server...`
2911
+ ${dim6("\u2503")} root.config.ts changed. restarting server...`
2807
2912
  );
2808
2913
  await restart();
2809
2914
  }
@@ -2823,7 +2928,7 @@ ${dim5("\u2503")} root.config.ts changed. restarting server...`
2823
2928
  await start2();
2824
2929
  }
2825
2930
  async function createDevServer(options) {
2826
- const rootDir = path12.resolve(options?.rootDir || process.cwd());
2931
+ const rootDir = path11.resolve(options?.rootDir || process.cwd());
2827
2932
  const { rootConfig, dependencies } = await loadRootConfigWithDeps(rootDir, {
2828
2933
  command: "dev"
2829
2934
  });
@@ -2838,6 +2943,7 @@ async function createDevServer(options) {
2838
2943
  });
2839
2944
  server.set("viteServer", viteServer);
2840
2945
  server.use(rootProjectMiddleware({ rootConfig }));
2946
+ server.use(securityHeadersMiddleware({ rootConfig }));
2841
2947
  server.use(viteMiddleware);
2842
2948
  server.use(hooksMiddleware());
2843
2949
  const sessionCookieSecret = getSessionCookieSecret(rootConfig, rootDir);
@@ -2859,7 +2965,7 @@ async function createDevServer(options) {
2859
2965
  if (rootConfig.server?.headers) {
2860
2966
  server.use(headersMiddleware({ rootConfig }));
2861
2967
  }
2862
- const publicDir = path12.join(rootDir, "public");
2968
+ const publicDir = path11.join(rootDir, "public");
2863
2969
  if (await dirExists(publicDir)) {
2864
2970
  server.use(rootPublicDirMiddleware({ publicDir, viteServer }));
2865
2971
  }
@@ -2887,10 +2993,10 @@ async function createViteMiddleware(options) {
2887
2993
  return sourceFile.relPath;
2888
2994
  });
2889
2995
  const bundleScripts = [];
2890
- if (await isDirectory(path12.join(rootDir, "bundles"))) {
2891
- const bundleFiles = await glob4("bundles/*", { cwd: rootDir });
2996
+ if (await isDirectory(path11.join(rootDir, "bundles"))) {
2997
+ const bundleFiles = await glob3("bundles/*", { cwd: rootDir });
2892
2998
  bundleFiles.forEach((file) => {
2893
- const parts = path12.parse(file);
2999
+ const parts = path11.parse(file);
2894
3000
  if (isJsFile(parts.base)) {
2895
3001
  bundleScripts.push(file);
2896
3002
  }
@@ -2907,7 +3013,7 @@ async function createViteMiddleware(options) {
2907
3013
  optimizeDeps
2908
3014
  });
2909
3015
  function isInElementsDir(changedFilePath) {
2910
- const filePath = path12.resolve(changedFilePath);
3016
+ const filePath = path11.resolve(changedFilePath);
2911
3017
  const elementsDirs = getElementsDirs(rootConfig, pods);
2912
3018
  return elementsDirs.some((dirPath) => filePath.startsWith(dirPath));
2913
3019
  }
@@ -2926,7 +3032,7 @@ async function createViteMiddleware(options) {
2926
3032
  const viteMiddleware = async (req, res, next) => {
2927
3033
  try {
2928
3034
  req.viteServer = viteServer;
2929
- const renderModulePath = path12.resolve(__dirname3, "./render.js");
3035
+ const renderModulePath = path11.resolve(__dirname4, "./render.js");
2930
3036
  const render = await viteServer.ssrLoadModule(
2931
3037
  renderModulePath
2932
3038
  );
@@ -2950,7 +3056,7 @@ function rootPublicDirMiddleware(options) {
2950
3056
  handler = sirv(publicDir, sirvOptions);
2951
3057
  }, 1e3);
2952
3058
  function isInPublicDir(changedFilePath) {
2953
- const filePath = path12.resolve(changedFilePath);
3059
+ const filePath = path11.resolve(changedFilePath);
2954
3060
  return filePath.startsWith(publicDir);
2955
3061
  }
2956
3062
  const watcher = options.viteServer.watcher;
@@ -2982,7 +3088,7 @@ function rootDevServer404Middleware() {
2982
3088
  }
2983
3089
  if (req.renderer) {
2984
3090
  const url = req.path;
2985
- const ext = path12.extname(url);
3091
+ const ext = path11.extname(url);
2986
3092
  if (!ext) {
2987
3093
  const renderer = req.renderer;
2988
3094
  const data = await renderer.renderDevServer404(req);
@@ -3000,7 +3106,7 @@ function rootDevServer500Middleware() {
3000
3106
  console.error(String(err.stack || err));
3001
3107
  if (req.renderer) {
3002
3108
  const url = req.path;
3003
- const ext = path12.extname(url);
3109
+ const ext = path11.extname(url);
3004
3110
  if (!ext) {
3005
3111
  const renderer = req.renderer;
3006
3112
  const data = await renderer.renderDevServer500(req, err);
@@ -3026,7 +3132,7 @@ function debounce(fn, timeout) {
3026
3132
 
3027
3133
  // src/cli/gae-deploy.ts
3028
3134
  import { execSync } from "node:child_process";
3029
- import fs9 from "node:fs";
3135
+ import fs7 from "node:fs";
3030
3136
  import yaml from "js-yaml";
3031
3137
  async function gaeDeploy(appDir, options) {
3032
3138
  if (!appDir) {
@@ -3040,10 +3146,10 @@ async function gaeDeploy(appDir, options) {
3040
3146
  }
3041
3147
  process.chdir(appDir);
3042
3148
  const appYamlPath = "app.yaml";
3043
- if (!fs9.existsSync(appYamlPath)) {
3149
+ if (!fs7.existsSync(appYamlPath)) {
3044
3150
  throw new Error(`[gae-deplopy] Missing: ${appYamlPath}`);
3045
3151
  }
3046
- const appYaml = yaml.load(fs9.readFileSync(appYamlPath, "utf8"));
3152
+ const appYaml = yaml.load(fs7.readFileSync(appYamlPath, "utf8"));
3047
3153
  const service = appYaml.service;
3048
3154
  if (!service) {
3049
3155
  throw new Error(
@@ -3069,7 +3175,7 @@ async function gaeDeploy(appDir, options) {
3069
3175
  { stdio: "inherit" }
3070
3176
  );
3071
3177
  if (backupAppYaml) {
3072
- fs9.copyFileSync(backupAppYaml, "app.yaml");
3178
+ fs7.copyFileSync(backupAppYaml, "app.yaml");
3073
3179
  }
3074
3180
  if (options?.healthcheckUrl) {
3075
3181
  const healthcheckPassed = await testHealth(
@@ -3156,8 +3262,8 @@ function testIsEnvPlaceholder(envValue) {
3156
3262
  }
3157
3263
  function updateAppYamlEnv(appYamlPath, appYaml) {
3158
3264
  const backupAppYaml = `${appYamlPath}.bak`;
3159
- fs9.copyFileSync(appYamlPath, backupAppYaml);
3160
- let content = fs9.readFileSync(appYamlPath, "utf8");
3265
+ fs7.copyFileSync(appYamlPath, backupAppYaml);
3266
+ let content = fs7.readFileSync(appYamlPath, "utf8");
3161
3267
  const envVars = appYaml.env_variables;
3162
3268
  Object.entries(envVars).forEach(([envVar, envValue]) => {
3163
3269
  if (testIsEnvPlaceholder(envValue)) {
@@ -3170,7 +3276,7 @@ function updateAppYamlEnv(appYamlPath, appYaml) {
3170
3276
  }
3171
3277
  }
3172
3278
  });
3173
- fs9.writeFileSync(appYamlPath, content, "utf8");
3279
+ fs7.writeFileSync(appYamlPath, content, "utf8");
3174
3280
  return backupAppYaml;
3175
3281
  }
3176
3282
  function getTimestamp() {
@@ -3194,33 +3300,33 @@ function testManagedVersion(service, appInfo, prefix) {
3194
3300
  }
3195
3301
 
3196
3302
  // src/cli/preview.ts
3197
- import path13 from "node:path";
3198
- import compression from "compression";
3303
+ import path12 from "node:path";
3199
3304
  import cookieParser2 from "cookie-parser";
3200
3305
  import { default as express2 } from "express";
3201
- import { dim as dim6 } from "kleur/colors";
3306
+ import { dim as dim7 } from "kleur/colors";
3202
3307
  import sirv2 from "sirv";
3203
3308
  async function preview(rootProjectDir, options) {
3204
3309
  process.env.NODE_ENV = "development";
3205
- const rootDir = path13.resolve(rootProjectDir || process.cwd());
3310
+ const rootDir = path12.resolve(rootProjectDir || process.cwd());
3206
3311
  const server = await createPreviewServer({ rootDir });
3207
3312
  const port = parseInt(process.env.PORT || "4007");
3208
3313
  const host = options?.host || "localhost";
3209
3314
  console.log();
3210
- console.log(`${dim6("\u2503")} project: ${rootDir}`);
3211
- console.log(`${dim6("\u2503")} server: http://${host}:${port}`);
3212
- console.log(`${dim6("\u2503")} mode: preview`);
3315
+ console.log(`${dim7("\u2503")} project: ${rootDir}`);
3316
+ console.log(`${dim7("\u2503")} server: http://${host}:${port}`);
3317
+ console.log(`${dim7("\u2503")} mode: preview`);
3213
3318
  console.log();
3214
3319
  server.listen(port, host);
3215
3320
  }
3216
3321
  async function createPreviewServer(options) {
3217
3322
  const rootDir = options.rootDir;
3218
3323
  const rootConfig = await loadBundledConfig(rootDir, { command: "preview" });
3219
- const distDir = path13.join(rootDir, "dist");
3324
+ const distDir = path12.join(rootDir, "dist");
3220
3325
  const server = express2();
3221
3326
  server.disable("x-powered-by");
3222
- server.use(compression());
3327
+ server.use(compressionMiddleware());
3223
3328
  server.use(rootProjectMiddleware({ rootConfig }));
3329
+ server.use(securityHeadersMiddleware({ rootConfig }));
3224
3330
  server.use(await rootPreviewRendererMiddleware({ rootConfig, distDir }));
3225
3331
  server.use(hooksMiddleware());
3226
3332
  const sessionCookieSecret = getSessionCookieSecret(rootConfig, rootDir);
@@ -3242,7 +3348,7 @@ async function createPreviewServer(options) {
3242
3348
  if (rootConfig.server?.headers) {
3243
3349
  server.use(headersMiddleware({ rootConfig }));
3244
3350
  }
3245
- const publicDir = path13.join(distDir, "html");
3351
+ const publicDir = path12.join(distDir, "html");
3246
3352
  server.use(sirv2(publicDir, { dev: false }));
3247
3353
  server.use(trailingSlashMiddleware({ rootConfig }));
3248
3354
  server.use(rootPreviewServerMiddleware());
@@ -3261,14 +3367,14 @@ async function createPreviewServer(options) {
3261
3367
  }
3262
3368
  async function rootPreviewRendererMiddleware(options) {
3263
3369
  const { distDir, rootConfig } = options;
3264
- const render = await import(path13.join(distDir, "server/render.js"));
3265
- const manifestPath2 = path13.join(distDir, ".root/manifest.json");
3370
+ const render = await import(path12.join(distDir, "server/render.js"));
3371
+ const manifestPath2 = path12.join(distDir, ".root/manifest.json");
3266
3372
  if (!await fileExists(manifestPath2)) {
3267
3373
  throw new Error(
3268
3374
  `could not find ${manifestPath2}. run \`root build\` before \`root preview\`.`
3269
3375
  );
3270
3376
  }
3271
- const elementGraphJsonPath = path13.join(distDir, ".root/elements.json");
3377
+ const elementGraphJsonPath = path12.join(distDir, ".root/elements.json");
3272
3378
  if (!await fileExists(elementGraphJsonPath)) {
3273
3379
  throw new Error(
3274
3380
  `could not find ${elementGraphJsonPath}. run \`root build\` before \`root preview\`.`
@@ -3308,7 +3414,7 @@ function rootPreviewServer404Middleware() {
3308
3414
  console.error(`\u2753 404 ${req.originalUrl}`);
3309
3415
  if (req.renderer) {
3310
3416
  const url = req.path;
3311
- const ext = path13.extname(url);
3417
+ const ext = path12.extname(url);
3312
3418
  if (!ext) {
3313
3419
  const renderer = req.renderer;
3314
3420
  const data = await renderer.render404({ currentPath: url });
@@ -3326,7 +3432,7 @@ function rootPreviewServer500Middleware() {
3326
3432
  console.error(String(err.stack || err));
3327
3433
  if (req.renderer) {
3328
3434
  const url = req.path;
3329
- const ext = path13.extname(url);
3435
+ const ext = path12.extname(url);
3330
3436
  if (!ext) {
3331
3437
  const renderer = req.renderer;
3332
3438
  const data = await renderer.renderDevServer500(req, err);
@@ -3340,33 +3446,33 @@ function rootPreviewServer500Middleware() {
3340
3446
  }
3341
3447
 
3342
3448
  // src/cli/start.ts
3343
- import path14 from "node:path";
3344
- import compression2 from "compression";
3449
+ import path13 from "node:path";
3345
3450
  import cookieParser3 from "cookie-parser";
3346
3451
  import { default as express3 } from "express";
3347
- import { dim as dim7 } from "kleur/colors";
3452
+ import { dim as dim8 } from "kleur/colors";
3348
3453
  import sirv3 from "sirv";
3349
3454
  async function start(rootProjectDir, options) {
3350
3455
  process.env.NODE_ENV = "production";
3351
- const rootDir = path14.resolve(rootProjectDir || process.cwd());
3456
+ const rootDir = path13.resolve(rootProjectDir || process.cwd());
3352
3457
  const server = await createProdServer({ rootDir });
3353
3458
  const port = parseInt(process.env.PORT || "4007");
3354
3459
  const host = options?.host || "localhost";
3355
3460
  console.log();
3356
- console.log(`${dim7("\u2503")} project: ${rootDir}`);
3357
- console.log(`${dim7("\u2503")} server: http://${host}:${port}`);
3358
- console.log(`${dim7("\u2503")} mode: production`);
3461
+ console.log(`${dim8("\u2503")} project: ${rootDir}`);
3462
+ console.log(`${dim8("\u2503")} server: http://${host}:${port}`);
3463
+ console.log(`${dim8("\u2503")} mode: production`);
3359
3464
  console.log();
3360
3465
  server.listen(port, host);
3361
3466
  }
3362
3467
  async function createProdServer(options) {
3363
3468
  const rootDir = options.rootDir;
3364
3469
  const rootConfig = await loadBundledConfig(rootDir, { command: "start" });
3365
- const distDir = path14.join(rootDir, "dist");
3470
+ const distDir = path13.join(rootDir, "dist");
3366
3471
  const server = express3();
3367
3472
  server.disable("x-powered-by");
3368
- server.use(compression2());
3473
+ server.use(compressionMiddleware());
3369
3474
  server.use(rootProjectMiddleware({ rootConfig }));
3475
+ server.use(securityHeadersMiddleware({ rootConfig }));
3370
3476
  server.use(await rootProdRendererMiddleware({ rootConfig, distDir }));
3371
3477
  server.use(hooksMiddleware());
3372
3478
  const sessionCookieSecret = getSessionCookieSecret(rootConfig, rootDir);
@@ -3388,7 +3494,7 @@ async function createProdServer(options) {
3388
3494
  if (rootConfig.server?.headers) {
3389
3495
  server.use(headersMiddleware({ rootConfig }));
3390
3496
  }
3391
- const publicDir = path14.join(distDir, "html");
3497
+ const publicDir = path13.join(distDir, "html");
3392
3498
  server.use(sirv3(publicDir, { dev: false }));
3393
3499
  server.use(trailingSlashMiddleware({ rootConfig }));
3394
3500
  server.use(rootProdServerMiddleware());
@@ -3407,14 +3513,14 @@ async function createProdServer(options) {
3407
3513
  }
3408
3514
  async function rootProdRendererMiddleware(options) {
3409
3515
  const { distDir, rootConfig } = options;
3410
- const render = await import(path14.join(distDir, "server/render.js"));
3411
- const manifestPath2 = path14.join(distDir, ".root/manifest.json");
3516
+ const render = await import(path13.join(distDir, "server/render.js"));
3517
+ const manifestPath2 = path13.join(distDir, ".root/manifest.json");
3412
3518
  if (!await fileExists(manifestPath2)) {
3413
3519
  throw new Error(
3414
3520
  `could not find ${manifestPath2}. run \`root build\` before \`root start\`.`
3415
3521
  );
3416
3522
  }
3417
- const elementGraphJsonPath = path14.join(distDir, ".root/elements.json");
3523
+ const elementGraphJsonPath = path13.join(distDir, ".root/elements.json");
3418
3524
  if (!await fileExists(elementGraphJsonPath)) {
3419
3525
  throw new Error(
3420
3526
  `could not find ${elementGraphJsonPath}. run \`root build\` before \`root start\`.`
@@ -3454,7 +3560,7 @@ function rootProdServer404Middleware() {
3454
3560
  console.error(`\u2753 404 ${req.originalUrl}`);
3455
3561
  if (req.renderer) {
3456
3562
  const url = req.path;
3457
- const ext = path14.extname(url);
3563
+ const ext = path13.extname(url);
3458
3564
  if (!ext) {
3459
3565
  const renderer = req.renderer;
3460
3566
  const data = await renderer.render404({ currentPath: url });
@@ -3472,7 +3578,7 @@ function rootProdServer500Middleware() {
3472
3578
  console.error(String(err.stack || err));
3473
3579
  if (req.renderer) {
3474
3580
  const url = req.path;
3475
- const ext = path14.extname(url);
3581
+ const ext = path13.extname(url);
3476
3582
  if (!ext) {
3477
3583
  const renderer = req.renderer;
3478
3584
  const data = await renderer.renderError(err);
@@ -3513,11 +3619,21 @@ var CliRunner = class {
3513
3619
  "-c, --concurrency <num>",
3514
3620
  "number of files to build concurrently",
3515
3621
  "10"
3622
+ ).option(
3623
+ "--threads [num]",
3624
+ 'renders pages using worker threads; pass a number for exactly N workers, or omit the value (or pass "auto") to pick based on cpu cores and page count'
3516
3625
  ).option(
3517
3626
  "--filter <urlPathRegex>",
3518
3627
  'builds the url paths that match the given regex, e.g. "/products/.*"',
3519
3628
  ""
3520
- ).action(build);
3629
+ ).addOption(
3630
+ new Option(
3631
+ "--log <mode>",
3632
+ 'build log output: "progress" (default) shows a progress indicator and a final summary, "verbose" prints one line per output file, "quiet" prints only the final summary'
3633
+ ).choices(["progress", "verbose", "quiet"])
3634
+ ).action(
3635
+ (rootProjectDir, options, cmd) => build(rootProjectDir, cmd.optsWithGlobals())
3636
+ );
3521
3637
  program.command("codegen [type] [name]").description("generates boilerplate code").option("--out <outdir>", "output dir").action(codegen);
3522
3638
  program.command("create-package [path]").alias("package").description(
3523
3639
  "creates a standalone npm package for deployment to various hosting services"
@@ -3579,4 +3695,4 @@ export {
3579
3695
  createProdServer,
3580
3696
  CliRunner
3581
3697
  };
3582
- //# sourceMappingURL=chunk-3EFECH2D.js.map
3698
+ //# sourceMappingURL=chunk-GK3TDYUY.js.map