@tyndall/core 0.0.1

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 (59) hide show
  1. package/README.md +34 -0
  2. package/dist/client-router-bootstrap.d.ts +11 -0
  3. package/dist/client-router-bootstrap.d.ts.map +1 -0
  4. package/dist/client-router-bootstrap.js +319 -0
  5. package/dist/config.d.ts +197 -0
  6. package/dist/config.d.ts.map +1 -0
  7. package/dist/config.js +552 -0
  8. package/dist/dynamic-manifest.d.ts +78 -0
  9. package/dist/dynamic-manifest.d.ts.map +1 -0
  10. package/dist/dynamic-manifest.js +189 -0
  11. package/dist/dynamic-page-api.d.ts +34 -0
  12. package/dist/dynamic-page-api.d.ts.map +1 -0
  13. package/dist/dynamic-page-api.js +136 -0
  14. package/dist/errors.d.ts +10 -0
  15. package/dist/errors.d.ts.map +1 -0
  16. package/dist/errors.js +31 -0
  17. package/dist/head.d.ts +3 -0
  18. package/dist/head.d.ts.map +1 -0
  19. package/dist/head.js +33 -0
  20. package/dist/index.d.ts +33 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +18 -0
  23. package/dist/layouts.d.ts +14 -0
  24. package/dist/layouts.d.ts.map +1 -0
  25. package/dist/layouts.js +51 -0
  26. package/dist/manifest.d.ts +81 -0
  27. package/dist/manifest.d.ts.map +1 -0
  28. package/dist/manifest.js +139 -0
  29. package/dist/page-api.d.ts +56 -0
  30. package/dist/page-api.d.ts.map +1 -0
  31. package/dist/page-api.js +100 -0
  32. package/dist/plugin.d.ts +35 -0
  33. package/dist/plugin.d.ts.map +1 -0
  34. package/dist/plugin.js +133 -0
  35. package/dist/program-sandbox.d.ts +18 -0
  36. package/dist/program-sandbox.d.ts.map +1 -0
  37. package/dist/program-sandbox.js +84 -0
  38. package/dist/props.d.ts +2 -0
  39. package/dist/props.d.ts.map +1 -0
  40. package/dist/props.js +14 -0
  41. package/dist/render-policy.d.ts +8 -0
  42. package/dist/render-policy.d.ts.map +1 -0
  43. package/dist/render-policy.js +12 -0
  44. package/dist/resolver-fallback.d.ts +49 -0
  45. package/dist/resolver-fallback.d.ts.map +1 -0
  46. package/dist/resolver-fallback.js +99 -0
  47. package/dist/route-graph.d.ts +9 -0
  48. package/dist/route-graph.d.ts.map +1 -0
  49. package/dist/route-graph.js +157 -0
  50. package/dist/route-meta.d.ts +4 -0
  51. package/dist/route-meta.d.ts.map +1 -0
  52. package/dist/route-meta.js +48 -0
  53. package/dist/router.d.ts +27 -0
  54. package/dist/router.d.ts.map +1 -0
  55. package/dist/router.js +83 -0
  56. package/dist/ui-adapter.d.ts +67 -0
  57. package/dist/ui-adapter.d.ts.map +1 -0
  58. package/dist/ui-adapter.js +27 -0
  59. package/package.json +23 -0
package/dist/config.js ADDED
@@ -0,0 +1,552 @@
1
+ import { access, readFile } from "node:fs/promises";
2
+ import { extname, join } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { HyperError } from "./errors.js";
5
+ const CONFIG_FILES = [
6
+ "hyper.config.ts",
7
+ "hyper.config.mjs",
8
+ "hyper.config.js",
9
+ "hyper.config.cjs",
10
+ "hyper.config.json",
11
+ ];
12
+ const defaultConfig = {
13
+ mode: "ssg",
14
+ ssr: false,
15
+ outDir: "dist",
16
+ basePath: "",
17
+ assetsDir: "assets",
18
+ publicDir: "public",
19
+ routesDir: "src/pages",
20
+ jsxRuntime: "automatic",
21
+ target: "es2017",
22
+ build: {
23
+ version: "",
24
+ },
25
+ lint: { tool: "biome" },
26
+ format: { tool: "biome" },
27
+ plugins: [],
28
+ ssrAdapters: ["node"],
29
+ revalidateDefault: false,
30
+ dev: {
31
+ onDemandRoutes: true,
32
+ prewarmRoutes: [],
33
+ },
34
+ ui: {
35
+ adapter: "react",
36
+ options: {},
37
+ },
38
+ render: {
39
+ mode: "auto",
40
+ },
41
+ routing: {
42
+ nestedLayouts: true,
43
+ routeGroups: true,
44
+ routeMeta: true,
45
+ navigation: "url",
46
+ clientRender: "payload",
47
+ ssrClientRouting: false,
48
+ },
49
+ cache: {
50
+ enabled: true,
51
+ dir: ".hyper/cache",
52
+ maxSizeMB: 2048,
53
+ maxAgeDays: 30,
54
+ propsTTLSeconds: false,
55
+ renderCache: true,
56
+ chunkCache: true,
57
+ },
58
+ testing: {
59
+ e2e: {
60
+ enabled: false,
61
+ defaultMode: "dev",
62
+ headless: true,
63
+ retry: 0,
64
+ reporters: ["console"],
65
+ },
66
+ },
67
+ logging: {
68
+ level: "info",
69
+ },
70
+ modules: {
71
+ dynamicManifest: {
72
+ enabled: false,
73
+ program: {
74
+ integrityRequired: true,
75
+ allow: [],
76
+ timeoutMs: 50,
77
+ memoryMb: 32,
78
+ },
79
+ },
80
+ },
81
+ engine: {
82
+ build: "bun",
83
+ runtime: "node",
84
+ },
85
+ bundler: "bun",
86
+ transpile: "typescript-native",
87
+ legacy: {
88
+ enabled: false,
89
+ polyfills: "auto",
90
+ chunking: "auto",
91
+ test: false,
92
+ },
93
+ };
94
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
95
+ const assertKnownKeys = (value, allowed, path) => {
96
+ for (const key of Object.keys(value)) {
97
+ if (!allowed.has(key)) {
98
+ throw new HyperError("CONFIG_UNKNOWN_KEY", `Unknown config key: ${path}${key}`, {
99
+ path: `${path}${key}`,
100
+ });
101
+ }
102
+ }
103
+ };
104
+ const expectType = (value, typeName, path) => {
105
+ throw new HyperError("CONFIG_INVALID", `Invalid config value at ${path}: expected ${typeName}`, {
106
+ path,
107
+ expected: typeName,
108
+ });
109
+ };
110
+ const ensureString = (value, path) => {
111
+ if (typeof value !== "string") {
112
+ return expectType(value, "string", path);
113
+ }
114
+ return value;
115
+ };
116
+ const ensureBoolean = (value, path) => {
117
+ if (typeof value !== "boolean") {
118
+ return expectType(value, "boolean", path);
119
+ }
120
+ return value;
121
+ };
122
+ const ensureNumber = (value, path) => {
123
+ if (typeof value !== "number" || Number.isNaN(value)) {
124
+ return expectType(value, "number", path);
125
+ }
126
+ return value;
127
+ };
128
+ const ensureStringArray = (value, path) => {
129
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
130
+ return expectType(value, "string[]", path);
131
+ }
132
+ return value;
133
+ };
134
+ const ensureEnum = (value, allowed, path) => {
135
+ if (typeof value !== "string" || !allowed.includes(value)) {
136
+ return expectType(value, allowed.join(" | "), path);
137
+ }
138
+ return value;
139
+ };
140
+ const ensureNumberOrFalse = (value, path) => {
141
+ if (value === false) {
142
+ return false;
143
+ }
144
+ return ensureNumber(value, path);
145
+ };
146
+ const loadConfigFile = async (cwd) => {
147
+ // Important: config resolution must be deterministic to avoid environment-specific behavior.
148
+ for (const file of CONFIG_FILES) {
149
+ const candidate = join(cwd, file);
150
+ try {
151
+ await access(candidate);
152
+ }
153
+ catch {
154
+ continue;
155
+ }
156
+ const extension = extname(candidate);
157
+ if (extension === ".json") {
158
+ const raw = await readFile(candidate, "utf-8");
159
+ return JSON.parse(raw);
160
+ }
161
+ const module = await import(pathToFileURL(candidate).href);
162
+ return module.default ?? module;
163
+ }
164
+ return {};
165
+ };
166
+ export const loadConfig = async (cwd) => {
167
+ const rawConfig = await loadConfigFile(cwd);
168
+ if (!isRecord(rawConfig)) {
169
+ throw new Error("Config must export an object.");
170
+ }
171
+ return resolveConfig(rawConfig);
172
+ };
173
+ const resolveConfig = (rawConfig) => {
174
+ // Merge defaults first so optional blocks get full shape before overrides apply.
175
+ const resolved = {
176
+ ...defaultConfig,
177
+ dev: { ...defaultConfig.dev },
178
+ ui: { ...defaultConfig.ui },
179
+ render: { ...defaultConfig.render },
180
+ routing: { ...defaultConfig.routing },
181
+ build: { ...defaultConfig.build },
182
+ cache: { ...defaultConfig.cache },
183
+ testing: { e2e: { ...defaultConfig.testing.e2e } },
184
+ logging: { ...defaultConfig.logging },
185
+ engine: { ...defaultConfig.engine },
186
+ lint: { ...defaultConfig.lint },
187
+ format: { ...defaultConfig.format },
188
+ legacy: { ...defaultConfig.legacy },
189
+ };
190
+ const rootKeys = new Set([
191
+ "mode",
192
+ "ssr",
193
+ "outDir",
194
+ "basePath",
195
+ "assetsDir",
196
+ "publicDir",
197
+ "routesDir",
198
+ "jsxRuntime",
199
+ "target",
200
+ "build",
201
+ "lint",
202
+ "format",
203
+ "plugins",
204
+ "ssrAdapters",
205
+ "revalidateDefault",
206
+ "dev",
207
+ "ui",
208
+ "render",
209
+ "routing",
210
+ "cache",
211
+ "testing",
212
+ "logging",
213
+ "modules",
214
+ "engine",
215
+ "bundler",
216
+ "transpile",
217
+ "legacy",
218
+ ]);
219
+ // Critical: fail fast on unknown keys to avoid silent misconfiguration.
220
+ assertKnownKeys(rawConfig, rootKeys, "");
221
+ if ("mode" in rawConfig) {
222
+ resolved.mode = ensureEnum(rawConfig.mode, ["ssg", "ssr"], "mode");
223
+ }
224
+ if ("ssr" in rawConfig) {
225
+ resolved.ssr = ensureBoolean(rawConfig.ssr, "ssr");
226
+ }
227
+ if ("outDir" in rawConfig) {
228
+ resolved.outDir = ensureString(rawConfig.outDir, "outDir");
229
+ }
230
+ if ("basePath" in rawConfig) {
231
+ resolved.basePath = ensureString(rawConfig.basePath, "basePath");
232
+ }
233
+ if ("assetsDir" in rawConfig) {
234
+ resolved.assetsDir = ensureString(rawConfig.assetsDir, "assetsDir");
235
+ }
236
+ if ("publicDir" in rawConfig) {
237
+ resolved.publicDir = ensureString(rawConfig.publicDir, "publicDir");
238
+ }
239
+ if ("routesDir" in rawConfig) {
240
+ resolved.routesDir = ensureString(rawConfig.routesDir, "routesDir");
241
+ }
242
+ if ("jsxRuntime" in rawConfig) {
243
+ resolved.jsxRuntime = ensureEnum(rawConfig.jsxRuntime, ["classic", "automatic"], "jsxRuntime");
244
+ }
245
+ if ("target" in rawConfig) {
246
+ resolved.target = ensureString(rawConfig.target, "target");
247
+ }
248
+ if ("build" in rawConfig) {
249
+ if (!isRecord(rawConfig.build)) {
250
+ expectType(rawConfig.build, "object", "build");
251
+ }
252
+ const build = rawConfig.build;
253
+ assertKnownKeys(build, new Set(["version"]), "build.");
254
+ if ("version" in build) {
255
+ resolved.build.version = ensureString(build.version, "build.version");
256
+ }
257
+ }
258
+ if ("lint" in rawConfig) {
259
+ if (!isRecord(rawConfig.lint)) {
260
+ expectType(rawConfig.lint, "object", "lint");
261
+ }
262
+ const lint = rawConfig.lint;
263
+ if ("tool" in lint) {
264
+ resolved.lint.tool = ensureEnum(lint.tool, ["biome", "eslint"], "lint.tool");
265
+ }
266
+ resolved.lint = { ...resolved.lint, ...lint };
267
+ }
268
+ if ("format" in rawConfig) {
269
+ if (!isRecord(rawConfig.format)) {
270
+ expectType(rawConfig.format, "object", "format");
271
+ }
272
+ const format = rawConfig.format;
273
+ if ("tool" in format) {
274
+ resolved.format.tool = ensureEnum(format.tool, ["biome", "eslint", "prettier"], "format.tool");
275
+ }
276
+ resolved.format = { ...resolved.format, ...format };
277
+ }
278
+ if ("plugins" in rawConfig) {
279
+ if (!Array.isArray(rawConfig.plugins)) {
280
+ expectType(rawConfig.plugins, "Plugin[]", "plugins");
281
+ }
282
+ resolved.plugins = rawConfig.plugins;
283
+ }
284
+ if ("ssrAdapters" in rawConfig) {
285
+ const adapters = ensureStringArray(rawConfig.ssrAdapters, "ssrAdapters");
286
+ const allowed = new Set(["node", "express", "fastify"]);
287
+ for (const adapter of adapters) {
288
+ if (!allowed.has(adapter)) {
289
+ throw new Error(`Invalid config value at ssrAdapters: ${adapter}`);
290
+ }
291
+ }
292
+ resolved.ssrAdapters = adapters;
293
+ }
294
+ if ("revalidateDefault" in rawConfig) {
295
+ resolved.revalidateDefault = ensureNumberOrFalse(rawConfig.revalidateDefault, "revalidateDefault");
296
+ }
297
+ if ("dev" in rawConfig) {
298
+ if (!isRecord(rawConfig.dev)) {
299
+ expectType(rawConfig.dev, "object", "dev");
300
+ }
301
+ const dev = rawConfig.dev;
302
+ assertKnownKeys(dev, new Set(["onDemandRoutes", "prewarmRoutes"]), "dev.");
303
+ if ("onDemandRoutes" in dev) {
304
+ resolved.dev.onDemandRoutes = ensureBoolean(dev.onDemandRoutes, "dev.onDemandRoutes");
305
+ }
306
+ if ("prewarmRoutes" in dev) {
307
+ resolved.dev.prewarmRoutes = ensureStringArray(dev.prewarmRoutes, "dev.prewarmRoutes");
308
+ }
309
+ }
310
+ if ("ui" in rawConfig) {
311
+ if (!isRecord(rawConfig.ui)) {
312
+ expectType(rawConfig.ui, "object", "ui");
313
+ }
314
+ const ui = rawConfig.ui;
315
+ assertKnownKeys(ui, new Set(["adapter", "options"]), "ui.");
316
+ if ("adapter" in ui) {
317
+ if (typeof ui.adapter !== "string" && typeof ui.adapter !== "function") {
318
+ expectType(ui.adapter, "string | UIAdapterFactory", "ui.adapter");
319
+ }
320
+ resolved.ui.adapter = ui.adapter;
321
+ }
322
+ if ("options" in ui) {
323
+ if (!isRecord(ui.options)) {
324
+ expectType(ui.options, "object", "ui.options");
325
+ }
326
+ resolved.ui.options = ui.options;
327
+ }
328
+ }
329
+ if ("render" in rawConfig) {
330
+ if (!isRecord(rawConfig.render)) {
331
+ expectType(rawConfig.render, "object", "render");
332
+ }
333
+ const render = rawConfig.render;
334
+ assertKnownKeys(render, new Set(["mode"]), "render.");
335
+ if ("mode" in render) {
336
+ resolved.render.mode = ensureEnum(render.mode, ["ssg", "ssr", "auto"], "render.mode");
337
+ }
338
+ }
339
+ if ("routing" in rawConfig) {
340
+ if (!isRecord(rawConfig.routing)) {
341
+ expectType(rawConfig.routing, "object", "routing");
342
+ }
343
+ const routing = rawConfig.routing;
344
+ assertKnownKeys(routing, new Set([
345
+ "nestedLayouts",
346
+ "routeGroups",
347
+ "routeMeta",
348
+ "navigation",
349
+ "clientRender",
350
+ "ssrClientRouting",
351
+ ]), "routing.");
352
+ if ("nestedLayouts" in routing) {
353
+ resolved.routing.nestedLayouts = ensureBoolean(routing.nestedLayouts, "routing.nestedLayouts");
354
+ }
355
+ if ("routeGroups" in routing) {
356
+ resolved.routing.routeGroups = ensureBoolean(routing.routeGroups, "routing.routeGroups");
357
+ }
358
+ if ("routeMeta" in routing) {
359
+ resolved.routing.routeMeta = ensureBoolean(routing.routeMeta, "routing.routeMeta");
360
+ }
361
+ if ("navigation" in routing) {
362
+ resolved.routing.navigation = ensureEnum(routing.navigation, ["url", "client"], "routing.navigation");
363
+ }
364
+ if ("clientRender" in routing) {
365
+ resolved.routing.clientRender = ensureEnum(routing.clientRender, ["payload", "module"], "routing.clientRender");
366
+ }
367
+ if ("ssrClientRouting" in routing) {
368
+ resolved.routing.ssrClientRouting = ensureBoolean(routing.ssrClientRouting, "routing.ssrClientRouting");
369
+ }
370
+ }
371
+ if ("cache" in rawConfig) {
372
+ if (!isRecord(rawConfig.cache)) {
373
+ expectType(rawConfig.cache, "object", "cache");
374
+ }
375
+ const cache = rawConfig.cache;
376
+ assertKnownKeys(cache, new Set([
377
+ "enabled",
378
+ "dir",
379
+ "maxSizeMB",
380
+ "maxAgeDays",
381
+ "propsTTLSeconds",
382
+ "renderCache",
383
+ "chunkCache",
384
+ ]), "cache.");
385
+ if ("enabled" in cache) {
386
+ resolved.cache.enabled = ensureBoolean(cache.enabled, "cache.enabled");
387
+ }
388
+ if ("dir" in cache) {
389
+ resolved.cache.dir = ensureString(cache.dir, "cache.dir");
390
+ }
391
+ if ("maxSizeMB" in cache) {
392
+ resolved.cache.maxSizeMB = ensureNumber(cache.maxSizeMB, "cache.maxSizeMB");
393
+ }
394
+ if ("maxAgeDays" in cache) {
395
+ resolved.cache.maxAgeDays = ensureNumber(cache.maxAgeDays, "cache.maxAgeDays");
396
+ }
397
+ if ("propsTTLSeconds" in cache) {
398
+ resolved.cache.propsTTLSeconds = ensureNumberOrFalse(cache.propsTTLSeconds, "cache.propsTTLSeconds");
399
+ }
400
+ if ("renderCache" in cache) {
401
+ resolved.cache.renderCache = ensureBoolean(cache.renderCache, "cache.renderCache");
402
+ }
403
+ if ("chunkCache" in cache) {
404
+ resolved.cache.chunkCache = ensureBoolean(cache.chunkCache, "cache.chunkCache");
405
+ }
406
+ }
407
+ if ("testing" in rawConfig) {
408
+ if (!isRecord(rawConfig.testing)) {
409
+ expectType(rawConfig.testing, "object", "testing");
410
+ }
411
+ const testing = rawConfig.testing;
412
+ assertKnownKeys(testing, new Set(["e2e"]), "testing.");
413
+ if ("e2e" in testing) {
414
+ if (!isRecord(testing.e2e)) {
415
+ expectType(testing.e2e, "object", "testing.e2e");
416
+ }
417
+ const e2e = testing.e2e;
418
+ assertKnownKeys(e2e, new Set(["enabled", "defaultMode", "headless", "retry", "reporters", "environmentScript"]), "testing.e2e.");
419
+ if ("enabled" in e2e) {
420
+ resolved.testing.e2e.enabled = ensureBoolean(e2e.enabled, "testing.e2e.enabled");
421
+ }
422
+ if ("defaultMode" in e2e) {
423
+ resolved.testing.e2e.defaultMode = ensureEnum(e2e.defaultMode, ["dev", "ssg", "ssr"], "testing.e2e.defaultMode");
424
+ }
425
+ if ("headless" in e2e) {
426
+ resolved.testing.e2e.headless = ensureBoolean(e2e.headless, "testing.e2e.headless");
427
+ }
428
+ if ("retry" in e2e) {
429
+ const retry = ensureNumber(e2e.retry, "testing.e2e.retry");
430
+ if (retry < 0) {
431
+ throw new HyperError("CONFIG_INVALID", "Invalid config value at testing.e2e.retry: must be >= 0", { path: "testing.e2e.retry", expected: "number >= 0" });
432
+ }
433
+ resolved.testing.e2e.retry = retry;
434
+ }
435
+ if ("reporters" in e2e) {
436
+ const reporters = ensureStringArray(e2e.reporters, "testing.e2e.reporters");
437
+ const allowed = new Set(["console", "json", "html"]);
438
+ for (const reporter of reporters) {
439
+ if (!allowed.has(reporter)) {
440
+ throw new Error(`Invalid config value at testing.e2e.reporters: ${reporter}`);
441
+ }
442
+ }
443
+ resolved.testing.e2e.reporters = reporters;
444
+ }
445
+ if ("environmentScript" in e2e) {
446
+ resolved.testing.e2e.environmentScript = ensureString(e2e.environmentScript, "testing.e2e.environmentScript");
447
+ }
448
+ }
449
+ }
450
+ if ("logging" in rawConfig) {
451
+ if (!isRecord(rawConfig.logging)) {
452
+ expectType(rawConfig.logging, "object", "logging");
453
+ }
454
+ const logging = rawConfig.logging;
455
+ assertKnownKeys(logging, new Set(["level"]), "logging.");
456
+ if ("level" in logging) {
457
+ resolved.logging.level = ensureEnum(logging.level, ["fatal", "error", "warn", "info"], "logging.level");
458
+ }
459
+ }
460
+ if ("modules" in rawConfig) {
461
+ if (!isRecord(rawConfig.modules)) {
462
+ expectType(rawConfig.modules, "object", "modules");
463
+ }
464
+ const modules = rawConfig.modules;
465
+ assertKnownKeys(modules, new Set(["dynamicManifest"]), "modules.");
466
+ if ("dynamicManifest" in modules) {
467
+ if (!isRecord(modules.dynamicManifest)) {
468
+ expectType(modules.dynamicManifest, "object", "modules.dynamicManifest");
469
+ }
470
+ const dynamicManifest = modules.dynamicManifest;
471
+ assertKnownKeys(dynamicManifest, new Set(["enabled", "program"]), "modules.dynamicManifest.");
472
+ if ("enabled" in dynamicManifest) {
473
+ resolved.modules.dynamicManifest.enabled = ensureBoolean(dynamicManifest.enabled, "modules.dynamicManifest.enabled");
474
+ }
475
+ if ("program" in dynamicManifest) {
476
+ if (!isRecord(dynamicManifest.program)) {
477
+ expectType(dynamicManifest.program, "object", "modules.dynamicManifest.program");
478
+ }
479
+ const program = dynamicManifest.program;
480
+ assertKnownKeys(program, new Set(["integrityRequired", "allow", "timeoutMs", "memoryMb"]), "modules.dynamicManifest.program.");
481
+ if ("integrityRequired" in program) {
482
+ resolved.modules.dynamicManifest.program.integrityRequired = ensureBoolean(program.integrityRequired, "modules.dynamicManifest.program.integrityRequired");
483
+ }
484
+ if ("allow" in program) {
485
+ resolved.modules.dynamicManifest.program.allow = ensureStringArray(program.allow, "modules.dynamicManifest.program.allow");
486
+ }
487
+ if ("timeoutMs" in program) {
488
+ resolved.modules.dynamicManifest.program.timeoutMs = ensureNumber(program.timeoutMs, "modules.dynamicManifest.program.timeoutMs");
489
+ }
490
+ if ("memoryMb" in program) {
491
+ resolved.modules.dynamicManifest.program.memoryMb = ensureNumber(program.memoryMb, "modules.dynamicManifest.program.memoryMb");
492
+ }
493
+ }
494
+ }
495
+ }
496
+ if ("engine" in rawConfig) {
497
+ // Important: allow both shorthand string and object form for engine overrides.
498
+ const engine = rawConfig.engine;
499
+ if (typeof engine === "string") {
500
+ resolved.engine.build = ensureEnum(engine, ["bun", "node"], "engine");
501
+ }
502
+ else if (isRecord(engine)) {
503
+ assertKnownKeys(engine, new Set(["runtime", "build"]), "engine.");
504
+ if ("build" in engine) {
505
+ resolved.engine.build = ensureEnum(engine.build, ["bun", "node"], "engine.build");
506
+ }
507
+ if ("runtime" in engine) {
508
+ resolved.engine.runtime = ensureEnum(engine.runtime, ["bun", "node"], "engine.runtime");
509
+ }
510
+ }
511
+ else {
512
+ expectType(engine, "'bun' | 'node' | object", "engine");
513
+ }
514
+ }
515
+ if ("bundler" in rawConfig) {
516
+ resolved.bundler = ensureEnum(rawConfig.bundler, ["bun", "esbuild", "swc"], "bundler");
517
+ }
518
+ if ("transpile" in rawConfig) {
519
+ resolved.transpile = ensureEnum(rawConfig.transpile, ["typescript-native", "tsc"], "transpile");
520
+ }
521
+ if ("legacy" in rawConfig) {
522
+ if (!isRecord(rawConfig.legacy)) {
523
+ expectType(rawConfig.legacy, "object", "legacy");
524
+ }
525
+ const legacy = rawConfig.legacy;
526
+ assertKnownKeys(legacy, new Set(["enabled", "polyfills", "chunking", "test"]), "legacy.");
527
+ if ("enabled" in legacy) {
528
+ resolved.legacy.enabled = ensureBoolean(legacy.enabled, "legacy.enabled");
529
+ }
530
+ if ("polyfills" in legacy) {
531
+ if (legacy.polyfills !== "auto" && !Array.isArray(legacy.polyfills)) {
532
+ expectType(legacy.polyfills, "'auto' | string[]", "legacy.polyfills");
533
+ }
534
+ if (Array.isArray(legacy.polyfills)) {
535
+ resolved.legacy.polyfills = ensureStringArray(legacy.polyfills, "legacy.polyfills");
536
+ }
537
+ else {
538
+ resolved.legacy.polyfills = legacy.polyfills;
539
+ }
540
+ }
541
+ if ("chunking" in legacy) {
542
+ if (legacy.chunking !== "auto" && legacy.chunking !== "safe") {
543
+ expectType(legacy.chunking, "'auto' | 'safe'", "legacy.chunking");
544
+ }
545
+ resolved.legacy.chunking = legacy.chunking;
546
+ }
547
+ if ("test" in legacy) {
548
+ resolved.legacy.test = ensureBoolean(legacy.test, "legacy.test");
549
+ }
550
+ }
551
+ return resolved;
552
+ };
@@ -0,0 +1,78 @@
1
+ export type DynamicPageMode = "meta" | "program";
2
+ export interface DynamicPageCache {
3
+ ttlSeconds?: number;
4
+ etag?: string;
5
+ swrSeconds?: number;
6
+ }
7
+ export interface DynamicPagePolicy {
8
+ render?: "csr" | "ssr" | "redirect" | "auto";
9
+ auth?: string;
10
+ seo?: string;
11
+ headers?: Record<string, string>;
12
+ redirect?: string;
13
+ [key: string]: unknown;
14
+ }
15
+ export interface MetaNode {
16
+ type: string;
17
+ props?: Record<string, unknown>;
18
+ children?: Array<MetaNode | string>;
19
+ }
20
+ export interface MetaDefinition {
21
+ kind: "meta";
22
+ root: MetaNode;
23
+ }
24
+ export interface ProgramDefinition {
25
+ kind: "program";
26
+ entry: {
27
+ code: string;
28
+ };
29
+ integrity?: {
30
+ alg?: string;
31
+ signature?: string;
32
+ };
33
+ sandbox?: {
34
+ timeoutMs?: number;
35
+ memoryMb?: number;
36
+ };
37
+ }
38
+ export type DynamicPageDefinition = MetaDefinition | ProgramDefinition;
39
+ export interface DynamicPageEnvelope {
40
+ pageId: string;
41
+ version: string | number;
42
+ mode: DynamicPageMode;
43
+ cache?: DynamicPageCache;
44
+ policy?: DynamicPagePolicy;
45
+ pageProps?: Record<string, unknown>;
46
+ definition: DynamicPageDefinition;
47
+ }
48
+ export declare const dynamicManifestSchema: {
49
+ readonly $schema: "http://json-schema.org/draft-07/schema#";
50
+ readonly type: "object";
51
+ readonly required: readonly ["pageId", "version", "mode", "definition"];
52
+ readonly additionalProperties: false;
53
+ readonly properties: {
54
+ readonly pageId: {
55
+ readonly type: "string";
56
+ };
57
+ readonly version: {
58
+ readonly type: readonly ["string", "number"];
59
+ };
60
+ readonly mode: {
61
+ readonly enum: readonly ["meta", "program"];
62
+ };
63
+ readonly cache: {
64
+ readonly type: "object";
65
+ };
66
+ readonly policy: {
67
+ readonly type: "object";
68
+ };
69
+ readonly pageProps: {
70
+ readonly type: "object";
71
+ };
72
+ readonly definition: {
73
+ readonly type: "object";
74
+ };
75
+ };
76
+ };
77
+ export declare const validateDynamicManifest: (input: unknown) => DynamicPageEnvelope;
78
+ //# sourceMappingURL=dynamic-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-manifest.d.ts","sourceRoot":"","sources":["../src/dynamic-manifest.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,SAAS,CAAC,EAAE;QACV,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,MAAM,qBAAqB,GAAG,cAAc,GAAG,iBAAiB,CAAC;AAEvE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,EAAE,qBAAqB,CAAC;CACnC;AAED,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAcxB,CAAC;AA+NX,eAAO,MAAM,uBAAuB,GAAI,OAAO,OAAO,KAAG,mBAmCxD,CAAC"}