@jimhoyd/urlcode 0.4.0-alpha.1 → 0.4.0-alpha.3

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 (232) hide show
  1. package/.claude/skills/urlcode-authoring/SKILL.md +36 -22
  2. package/.claude/skills/urlcode-operations/SKILL.md +16 -22
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CONTRIBUTING.md +20 -16
  5. package/README.md +83 -80
  6. package/ROADMAP.md +61 -331
  7. package/SECURITY.md +23 -8
  8. package/dist/BUILD-MANIFEST.json +42 -47
  9. package/dist/agents-guide.js +18 -18
  10. package/dist/build-static.js +136 -0
  11. package/dist/capabilities.js +85 -31
  12. package/dist/capability-query.js +0 -1
  13. package/dist/cli.js +24 -32
  14. package/dist/compliance-rules/baseline.js +2 -10
  15. package/dist/compliance-rules/privacy.js +5 -16
  16. package/dist/compliance-rules/shared.js +0 -2
  17. package/dist/compliance.js +6 -8
  18. package/dist/config.js +15 -8
  19. package/dist/context.js +9 -10
  20. package/dist/examples.js +2 -2
  21. package/dist/explain-cli.js +7 -5
  22. package/dist/explain.js +10 -5
  23. package/dist/extensions.js +61 -2
  24. package/dist/function-sources.js +34 -2
  25. package/dist/function-worker.js +3 -1
  26. package/dist/functions.js +84 -13
  27. package/dist/guest-api.js +29 -3
  28. package/dist/index.js +3 -6
  29. package/dist/manifest.js +11 -7
  30. package/dist/mcp-authoring.js +2 -2
  31. package/dist/mcp.js +1 -1
  32. package/dist/observability.js +1 -21
  33. package/dist/policies/cache.js +8 -3
  34. package/dist/policies.js +3 -1
  35. package/dist/policy.js +28 -9
  36. package/dist/prerender.js +4 -0
  37. package/dist/project-tests.js +3 -3
  38. package/dist/readiness.js +35 -9
  39. package/dist/route-diff.js +12 -5
  40. package/dist/router.js +5 -7
  41. package/dist/runtime.js +77 -58
  42. package/dist/sandbox.js +48 -0
  43. package/dist/scaffold.js +0 -0
  44. package/dist/scripts/operational-drills.js +12 -54
  45. package/dist/server.js +3 -29
  46. package/dist/tooling.js +1 -1
  47. package/dist/trusted-functions.js +210 -0
  48. package/dist/types/build-static.d.ts +43 -0
  49. package/dist/types/capabilities.d.ts +14 -5
  50. package/dist/types/compliance-rules/shared.d.ts +0 -2
  51. package/dist/types/compliance.d.ts +0 -3
  52. package/dist/types/config.d.ts +2 -1
  53. package/dist/types/context.d.ts +2 -1
  54. package/dist/types/examples.d.ts +1 -1
  55. package/dist/types/explain.d.ts +6 -0
  56. package/dist/types/extensions.d.ts +55 -0
  57. package/dist/types/function-sources.d.ts +4 -0
  58. package/dist/types/functions.d.ts +48 -5
  59. package/dist/types/guest-api.d.ts +1 -0
  60. package/dist/types/index.d.ts +3 -6
  61. package/dist/types/manifest.d.ts +5 -3
  62. package/dist/types/observability.d.ts +1 -14
  63. package/dist/types/project-tests.d.ts +1 -2
  64. package/dist/types/readiness.d.ts +12 -3
  65. package/dist/types/router.d.ts +2 -1
  66. package/dist/types/runtime.d.ts +0 -27
  67. package/dist/types/sandbox.d.ts +12 -0
  68. package/dist/types/scaffold.d.ts +0 -2
  69. package/dist/types/server.d.ts +1 -4
  70. package/dist/types/tooling.d.ts +3 -3
  71. package/dist/types/trusted-functions.d.ts +29 -0
  72. package/dist/types/types.d.ts +22 -8
  73. package/dist/types/verify-deployment.d.ts +2 -2
  74. package/dist/types.js +25 -5
  75. package/dist/typescript-authoring.js +67 -18
  76. package/dist/verify-deployment.js +3 -3
  77. package/docs/AI-AUTHORING.md +109 -10
  78. package/docs/ASSETS.md +2 -1
  79. package/docs/AUTH-BACKUP.md +32 -0
  80. package/docs/AWS.md +4 -4
  81. package/docs/BEST-PRACTICES.md +17 -8
  82. package/docs/CAPABILITIES.md +30 -17
  83. package/docs/CAPACITY.md +128 -31
  84. package/docs/CI-FOLLOWUP-2026-09-19.md +97 -0
  85. package/docs/CI-RELEASE-AUDIT-2026-09-19.md +322 -0
  86. package/docs/CI.md +8 -3
  87. package/docs/CLOUDFLARE.md +1 -2
  88. package/docs/CODEBASE-AUDIT-2026-09-20.md +278 -0
  89. package/docs/COMPLIANCE.md +6 -9
  90. package/docs/DEPLOYMENT-CHECKS.md +1 -1
  91. package/docs/DEVELOPMENT-PIPELINE.md +181 -0
  92. package/docs/EXTENSIONS.md +193 -11
  93. package/docs/FRAMEWORK.md +53 -33
  94. package/docs/FUNCTION-SECURITY.md +173 -32
  95. package/docs/INSTALL.md +0 -5
  96. package/docs/LOAD-TESTING.md +4 -4
  97. package/docs/LOCAL-DEVELOPMENT.md +3 -0
  98. package/docs/MIDDLEWARE.md +27 -16
  99. package/docs/MONITORING.md +2 -19
  100. package/docs/OBSERVABILITY.md +7 -16
  101. package/docs/OPEN-DECISIONS.md +184 -0
  102. package/docs/OPERATIONAL-PROOF.md +26 -30
  103. package/docs/OPERATIONS.md +23 -32
  104. package/docs/POLICIES.md +27 -8
  105. package/docs/PRERENDER.md +25 -13
  106. package/docs/PROJECT-DIRECTION.md +35 -10
  107. package/docs/READINESS.md +6 -2
  108. package/docs/README.md +20 -41
  109. package/docs/RELEASE-0.4.0-alpha.3.md +50 -0
  110. package/docs/RELEASE-READINESS.md +50 -30
  111. package/docs/RELEASE-SECURITY.md +72 -86
  112. package/docs/RESILIENCE.md +16 -15
  113. package/docs/ROUTING.md +7 -10
  114. package/docs/SANDBOX-REVIEW.md +19 -6
  115. package/docs/SCAFFOLDING.md +0 -2
  116. package/docs/SECURITY-AUDIT.md +42 -2
  117. package/docs/SPECIFICATION.md +63 -27
  118. package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +288 -0
  119. package/docs/SPIKE-BUSINESS-SUITE.md +16 -0
  120. package/docs/SPIKE-CORE-LAYERING.md +368 -0
  121. package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +211 -0
  122. package/docs/SPIKE-LAMBDA-COMPILE.md +12 -2
  123. package/docs/STARTERS.md +1 -1
  124. package/docs/STATIC.md +105 -0
  125. package/docs/TOOLING.md +17 -12
  126. package/docs/TUNNELS.md +0 -3
  127. package/docs/TYPESCRIPT-AUTHORING.md +32 -12
  128. package/docs/TYPESCRIPT.md +25 -4
  129. package/docs/VERCEL.md +4 -5
  130. package/docs/VERSION-ALIGNMENT.md +46 -0
  131. package/docs/YAML-GUIDE.md +0 -3
  132. package/docs/YAML-REFERENCE.md +16 -6
  133. package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +68 -0
  134. package/docs/{MANAGEMENT-SECURITY.md → archive/2026-09-19/MANAGEMENT-SECURITY.md} +20 -0
  135. package/docs/{NEXT-PHASE-PLAN.md → archive/2026-09-19/NEXT-PHASE-PLAN.md} +26 -8
  136. package/docs/{NEXT-STEPS.md → archive/2026-09-19/NEXT-STEPS.md} +274 -43
  137. package/docs/archive/2026-09-19/OPEN-DECISIONS.md +277 -0
  138. package/docs/archive/2026-09-19/RELEASE-SECURITY.md +186 -0
  139. package/docs/archive/2026-09-19/ROADMAP.md +386 -0
  140. package/docs/{SPIKE-EXTENSION-MODEL.md → archive/2026-09-19/SPIKE-EXTENSION-MODEL.md} +12 -2
  141. package/docs/{SPIKE-EXTENSIONS.md → archive/2026-09-19/SPIKE-EXTENSIONS.md} +28 -12
  142. package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +776 -0
  143. package/docs/{USABILITY-REVIEW.md → archive/2026-09-19/USABILITY-REVIEW.md} +18 -2
  144. package/docs/archive/README.md +27 -0
  145. package/docs/policies/agents.md +1 -1
  146. package/docs/policies/cache.md +13 -0
  147. package/docs/policies/compression.md +3 -2
  148. package/docs/policies/security.md +3 -2
  149. package/examples/assets/example.yaml +2 -2
  150. package/examples/aws/example.yaml +1 -1
  151. package/examples/cloudflare/example.yaml +1 -1
  152. package/examples/conditions/example.yaml +1 -1
  153. package/examples/cookbook/example.yaml +1 -1
  154. package/examples/cookbook/middleware/bucket.mjs +12 -2
  155. package/examples/cookbook/middleware/locale.mjs +7 -3
  156. package/examples/cookbook/route-index.json +1 -1
  157. package/examples/cookbook/routes/middleware.yaml +1 -1
  158. package/examples/egress/example.yaml +1 -1
  159. package/examples/extensions/example.yaml +1 -1
  160. package/examples/prerender/README.md +14 -6
  161. package/examples/prerender/example.yaml +1 -1
  162. package/examples/prerender/functions/page.mjs +4 -2
  163. package/examples/prerender/middleware/template.mjs +1 -1
  164. package/examples/prerender/prerender.mjs +1 -1
  165. package/examples/prerender/urlcode.yaml +8 -4
  166. package/examples/provider-conformance/example.yaml +1 -1
  167. package/examples/vercel/example.yaml +1 -1
  168. package/llms-full.txt +686 -239
  169. package/llms.txt +27 -15
  170. package/package.json +32 -5
  171. package/packaging/claude-plugin/.claude-plugin/plugin.json +2 -2
  172. package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +36 -22
  173. package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +16 -22
  174. package/recipes/authenticated-json-api/README.md +4 -3
  175. package/recipes/authenticated-json-api/functions/profile.mjs +2 -1
  176. package/recipes/authenticated-json-api/recipe.yaml +2 -2
  177. package/recipes/contact-form/functions/contact.mjs +2 -1
  178. package/recipes/contact-form/recipe.yaml +3 -3
  179. package/recipes/cors-api/README.md +2 -2
  180. package/recipes/cors-api/recipe.yaml +2 -2
  181. package/recipes/health-page/README.md +1 -1
  182. package/recipes/health-page/recipe.yaml +1 -1
  183. package/recipes/json-api/README.md +1 -1
  184. package/recipes/json-api/recipe.yaml +4 -4
  185. package/recipes/middleware/README.md +8 -4
  186. package/recipes/middleware/middleware/bucket.mjs +12 -2
  187. package/recipes/middleware/middleware/locale.mjs +7 -3
  188. package/recipes/middleware/recipe.yaml +1 -1
  189. package/recipes/protected-download/README.md +1 -1
  190. package/recipes/protected-download/recipe.yaml +2 -2
  191. package/recipes/redirect/recipe.yaml +1 -1
  192. package/recipes/static-plus-api/README.md +2 -2
  193. package/recipes/static-plus-api/public/index.html +1 -1
  194. package/recipes/static-plus-api/recipe.yaml +2 -2
  195. package/recipes/static-plus-api/urlcode.yaml +1 -1
  196. package/recipes/typescript/README.md +2 -1
  197. package/recipes/typescript/recipe.yaml +5 -5
  198. package/recipes/webhook-receiver/README.md +5 -1
  199. package/recipes/webhook-receiver/recipe.yaml +2 -1
  200. package/recipes/webhook-receiver/urlcode.yaml +7 -0
  201. package/schemas/recipe.schema.json +4 -3
  202. package/schemas/urlcode.schema.json +14 -41
  203. package/skills/urlcode/SKILL.md +32 -11
  204. package/starters/default/AGENTS.md +18 -18
  205. package/starters/default/urlcode.yaml +0 -1
  206. package/dist/link-api.js +0 -136
  207. package/dist/link-cli.js +0 -141
  208. package/dist/link-events.js +0 -76
  209. package/dist/link-records.js +0 -31
  210. package/dist/link-store-worker.js +0 -150
  211. package/dist/link-store.js +0 -250
  212. package/dist/management-policy.js +0 -40
  213. package/dist/sqlite-version.js +0 -6
  214. package/dist/types/link-api.d.ts +0 -30
  215. package/dist/types/link-cli.d.ts +0 -37
  216. package/dist/types/link-events.d.ts +0 -27
  217. package/dist/types/link-records.d.ts +0 -11
  218. package/dist/types/link-store-worker.d.ts +0 -1
  219. package/dist/types/link-store.d.ts +0 -130
  220. package/dist/types/management-policy.d.ts +0 -8
  221. package/dist/types/sqlite-version.d.ts +0 -1
  222. package/docs/DYNAMIC-LINKS.md +0 -61
  223. package/docs/links/cli.md +0 -110
  224. package/docs/links/limits.md +0 -175
  225. package/docs/links/management-api.md +0 -80
  226. package/docs/links/pools.md +0 -75
  227. package/docs/links/setup.md +0 -135
  228. package/docs/yaml/links.md +0 -30
  229. package/examples/live-links/README.md +0 -11
  230. package/examples/live-links/example.yaml +0 -21
  231. package/examples/live-links/tests/requests.json +0 -6
  232. package/examples/live-links/urlcode.yaml +0 -16
@@ -6,11 +6,23 @@ export interface ExtensionDeclaration {
6
6
  config: Record<string, unknown>;
7
7
  }
8
8
  export type ExtensionPolicies = Record<string, Record<string, unknown> | false>;
9
+ /**
10
+ * `root` is the project's resolved absolute directory (the same value
11
+ * `loadDocument()` computes and `functionFile()`/router.ts resolves
12
+ * `function`/`middleware` `source` entries against). It is not the process's
13
+ * `cwd()`: `--project`/`--host-file` are independent paths, a server can be
14
+ * started from any working directory, and the JS API can load a project
15
+ * programmatically with no relationship to `cwd()` at all. An extension that
16
+ * resolves project-relative paths of its own (for example, an alternative
17
+ * middleware source it loads by convention rather than through core's native
18
+ * `middleware:` array) must resolve them against this field, never `cwd()`.
19
+ */
9
20
  export interface ExtensionActivation {
10
21
  origin: string;
11
22
  target: TargetName;
12
23
  projectSha256: string;
13
24
  mounts: readonly string[];
25
+ root: string;
14
26
  }
15
27
  export interface ExtensionRequest {
16
28
  method: string;
@@ -28,6 +40,20 @@ export interface ExtensionRequest {
28
40
  export interface ExtensionInstance {
29
41
  handle(request: ExtensionRequest): HandlerResult | Promise<HandlerResult>;
30
42
  authorize?(requirement: Readonly<Record<string, unknown>>, request: ExtensionRequest): HandlerResult | undefined | Promise<HandlerResult | undefined>;
43
+ /**
44
+ * Wrap semantics, not gate semantics: attached the same way as `authorize`
45
+ * (policies.extensions.<name> on a route, `config` the same validated
46
+ * per-route value `authorize`'s `requirement` receives), but given `next`,
47
+ * a callable invoking the rest of the pipeline for this route (any other
48
+ * declared extension `middleware()` after this one, then the route's own
49
+ * native `middleware:` chain and handler) at most once. Calling it lets
50
+ * this hook run code before and after the rest of the pipeline, inspecting
51
+ * or mutating the `HandlerResult` it resolves to; skipping it short-circuits
52
+ * the rest of the pipeline entirely, the same way `authorize` can. Never
53
+ * touches the native `middleware:` array or its own sandboxed/trusted
54
+ * dispatch, and never runs before `authorize` on the same route.
55
+ */
56
+ middleware?(config: Readonly<Record<string, unknown>>, request: ExtensionRequest, next: () => Promise<HandlerResult>): HandlerResult | Promise<HandlerResult>;
31
57
  close?(): void | Promise<void>;
32
58
  }
33
59
  /** Trusted operator code only. YAML declares names/configuration, never modules. */
@@ -49,6 +75,23 @@ export interface RuntimeExtension {
49
75
  policySchema?: object;
50
76
  credentialHeaders?: string[];
51
77
  immutableAssets?: ExtensionImmutableAssets;
78
+ /**
79
+ * Reviewed, operator-declared cache sensitivity for `policies.extensions.<name>`
80
+ * routes (never for an `extension:` mount, which is always treated as
81
+ * sensitive). Omitted or `true`: the current, safe default — the runtime
82
+ * forces `Cache-Control: no-store`, disables compression and applies the
83
+ * extension response's header/size caps, exactly like `authorize`-gating
84
+ * auth/admin extensions. `false` is an explicit, reviewed opt-in a generic,
85
+ * cache-transparent extension (pure request/response middleware with no
86
+ * gating semantics of its own) makes to say its
87
+ * `middleware()` hook never depends on withholding the response from
88
+ * shared caches: the wrapped route's own declared cache headers pass
89
+ * through unchanged, exactly as the native `middleware:` array already
90
+ * does. A route naming more than one extension is treated as sensitive if
91
+ * any of them is (or leaves this unset) — this can only relax the no-store
92
+ * floor, never weaken it, and it never changes whether `authorize()` runs.
93
+ */
94
+ cacheSensitive?: boolean;
52
95
  activate(config: Readonly<Record<string, unknown>>, context: ExtensionActivation): ExtensionInstance | Promise<ExtensionInstance>;
53
96
  }
54
97
  /** `urlcode init --with <name>` contract: what core hands `@jimhoyd/urlcode-<name>`'s `scaffold` export. Nothing is written by `scaffold`. */
@@ -107,6 +150,18 @@ export declare const immutableCacheControl = "public, max-age=31536000, immutabl
107
150
  export declare function inspectExtensionRevision(project: string): Promise<string>;
108
151
  export declare function effectiveExtensionPolicies(document: ProjectDocument, route: Pick<RouteConfig, 'policies'>): Record<string, Record<string, unknown>>;
109
152
  export declare function hasExtensionPolicy(document: ProjectDocument, route: Pick<RouteConfig, 'policies'>): boolean;
153
+ /**
154
+ * Whether a route naming these `policies.extensions` names must be treated as
155
+ * confidential (forced no-store, no compression, extension response caps).
156
+ * `names` with no entries is never confidential. Without a `registrations`
157
+ * list to consult (a build path with no operator host loaded), every name is
158
+ * treated as sensitive: the safe default this can only relax, never weaken.
159
+ * A name whose registration is missing, or declares no `cacheSensitive` (or
160
+ * `true`), counts as sensitive; only an explicit `cacheSensitive: false`
161
+ * excuses it, and one sensitive name among several makes the whole route
162
+ * confidential.
163
+ */
164
+ export declare function isSensitiveExtensionPolicy(names: readonly string[], registrations?: readonly Pick<RuntimeExtension, 'name' | 'cacheSensitive'>[]): boolean;
110
165
  export declare function prepareExtensions(document: ProjectDocument, routes: Record<string, RouteConfig>, registrations: RuntimeExtension[] | undefined, context: Omit<ExtensionActivation, 'mounts'>): {
111
166
  activate(): Promise<ExtensionRegistry>;
112
167
  };
@@ -23,4 +23,8 @@ export declare const MODULE_LIMIT = 128;
23
23
  export declare const MODULE_BYTE_LIMIT = 1048576;
24
24
  export declare const TOTAL_BYTE_LIMIT = 4194304;
25
25
  export declare function routeFunctions<D>(route: FunctionRoute<D>): D[];
26
+ export declare function collectTrustedSources(routes: FunctionRoute[], root: string): Promise<Record<string, string>>;
27
+ export declare function collectSourcesFor(definitions: FunctionDefinition[], root: string): Promise<FunctionSources>;
28
+ /** Route-shaped convenience over `collectSourcesFor`: every existing caller (policy.ts,
29
+ * prerender.ts, typescript-authoring.ts) keeps working unchanged from `FunctionRoute[]`. */
26
30
  export declare function collectFunctionSources(routes: FunctionRoute[], root: string): Promise<FunctionSources>;
@@ -1,12 +1,28 @@
1
1
  import { Worker } from 'node:worker_threads';
2
- import type { FunctionRoute, FunctionSources } from './function-sources.ts';
2
+ import type { FunctionDefinition, FunctionRoute, FunctionSources } from './function-sources.ts';
3
3
  import type { HandlerResult, HeaderPair } from './http-response.ts';
4
4
  import type { ParameterValue, RequestContext } from './match.ts';
5
5
  import type { LogFn } from './types.ts';
6
6
  import type { GuestRequestPayload } from './guest-api.ts';
7
7
  export type { FunctionDefinition, FunctionRoute } from './function-sources.ts';
8
8
  export type { GuestRequestPayload, GuestResponsePayload } from './guest-api.ts';
9
- export interface FunctionPoolOptions {
9
+ /** A module entry point the sandbox must load: an absolute source file path
10
+ * (already resolved and validated, e.g. via `functionFile()`) plus which
11
+ * export of it needs to be reachable. The same shape a `FunctionDefinition`
12
+ * already uses for `function`/`middleware` routes. */
13
+ export type SandboxEntry = FunctionDefinition;
14
+ /** What one `SandboxPool.execute()` call invokes: the same `{source, export}`
15
+ * shape as a `SandboxEntry`, naming one of the pool's declared entries. */
16
+ export type SandboxTarget = FunctionDefinition;
17
+ /** `entry` runs last (the route's `function`/handler); `chain` runs first, in
18
+ * order, each with `(request, context, next)` — the same wrap/middleware
19
+ * semantics `__invokePipeline` already gives sandboxed routes. Neither is
20
+ * required: an empty invocation with a `native` reply just returns it. */
21
+ export interface SandboxInvocation {
22
+ entry?: SandboxTarget | undefined;
23
+ chain?: SandboxTarget[] | undefined;
24
+ }
25
+ export interface SandboxPoolOptions {
10
26
  root?: string | undefined;
11
27
  snapshot?: FunctionSources | undefined;
12
28
  workers?: number | undefined;
@@ -14,6 +30,7 @@ export interface FunctionPoolOptions {
14
30
  maxBytes?: number | undefined;
15
31
  log?: LogFn | undefined;
16
32
  }
33
+ export type FunctionPoolOptions = SandboxPoolOptions;
17
34
  export interface FunctionWorkerData {
18
35
  sources: Record<string, string>;
19
36
  dependencies: Record<string, string[]>;
@@ -52,6 +69,7 @@ export type FunctionWorkerMessage = {
52
69
  headers: HeaderPair[];
53
70
  body: Uint8Array;
54
71
  nativeBody: boolean;
72
+ contentLength?: number;
55
73
  } | {
56
74
  id: string;
57
75
  error: true;
@@ -67,9 +85,9 @@ interface Slot {
67
85
  ready: boolean;
68
86
  pending: Pending | null;
69
87
  }
70
- export declare class FunctionPool {
88
+ export declare class SandboxPool {
71
89
  root: string | undefined;
72
- routes: FunctionRoute[];
90
+ entries: SandboxEntry[];
73
91
  preparedSnapshot: FunctionSources | undefined;
74
92
  snapshot: FunctionSources | undefined;
75
93
  restarts: Map<number, number>;
@@ -81,12 +99,37 @@ export declare class FunctionPool {
81
99
  size: number;
82
100
  slots: (Slot | undefined)[];
83
101
  closed: boolean;
84
- constructor(routes: FunctionRoute[], { root, snapshot, workers, timeoutMs, maxBytes, log }?: FunctionPoolOptions);
102
+ constructor(entries: SandboxEntry[], { root, snapshot, workers, timeoutMs, maxBytes, log }?: SandboxPoolOptions);
85
103
  start(): Promise<this>;
86
104
  spawn(index: number): Promise<void>;
87
105
  report(status: string, index: number, extra?: object): void;
88
106
  scheduleRespawn(index: number): void;
89
107
  get healthy(): boolean;
108
+ /** `invocation.entry` runs last (chain-wrapped), `invocation.chain` first, in
109
+ * declared order — the same `__invokePipeline` wrap/middleware discipline
110
+ * (`next()` callable at most once) the sandboxed route path already
111
+ * enforces; there is no second dispatch mechanism for this. Every entry and
112
+ * chain source named here must already be one of this pool's declared
113
+ * `entries` (constructor), or the worker's own module allowlist denies it. */
114
+ execute(invocation: SandboxInvocation, request: GuestRequestPayload, context: FunctionContext, native: HandlerResult | undefined): Promise<FunctionResult>;
115
+ close(): Promise<void>;
116
+ }
117
+ export declare class FunctionPool {
118
+ routes: FunctionRoute[];
119
+ private pool;
120
+ constructor(routes: FunctionRoute[], options?: FunctionPoolOptions);
121
+ start(): Promise<this>;
90
122
  execute(route: FunctionRoute, request: GuestRequestPayload, context: FunctionContext, native: HandlerResult | undefined): Promise<FunctionResult>;
123
+ scheduleRespawn(index: number): void;
91
124
  close(): Promise<void>;
125
+ get healthy(): boolean;
126
+ get restarts(): Map<number, number>;
127
+ get restartTimers(): Set<NodeJS.Timeout>;
128
+ get slots(): (Slot | undefined)[];
129
+ get size(): number;
130
+ get closed(): boolean;
131
+ get snapshot(): FunctionSources | undefined;
132
+ get log(): LogFn;
133
+ get timeoutMs(): number;
134
+ get maxBytes(): number;
92
135
  }
@@ -12,5 +12,6 @@ export interface GuestResponsePayload {
12
12
  headers: HeaderPair[];
13
13
  body: string;
14
14
  nativeBody?: boolean;
15
+ contentLength?: number;
15
16
  }
16
17
  export declare const guestBootstrap: string;
@@ -1,13 +1,8 @@
1
1
  export { createRuntime } from './runtime.ts';
2
- export type { Runtime, RuntimeOptions, RuntimeRequest, RequestTrace, LinkReader, LinkStoreBinding, HostPlugin, Observer, TestPlan } from './runtime.ts';
2
+ export type { Runtime, RuntimeOptions, RuntimeRequest, RequestTrace, HostPlugin, Observer, TestPlan } from './runtime.ts';
3
3
  export { startServer } from './server.ts';
4
4
  export type { Server, ServerOptions } from './server.ts';
5
5
  export { loadDocument, validateDocument, parseYaml } from './config.ts';
6
- export { openLinkStore } from './link-store.ts';
7
- export type { LinkStore, LinkRow, LinkStoreOptions } from './link-store.ts';
8
- export { startLinkApi } from './link-api.ts';
9
- export type { LinkApi, LinkApiOptions } from './link-api.ts';
10
- export type { LinkEvent, LinkObserverOptions, LinkObserverStats } from './link-events.ts';
11
6
  export { events as observabilityEvents, validateObservers, createObserverSink, createMetrics, renderPrometheus } from './observability.ts';
12
7
  export { getCapabilities, routeCapabilities, analyzeProjectCapabilities, analyzeCompiledCapabilities, assertTargetCompatibility, normalizeCapabilityTarget } from './capabilities.ts';
13
8
  export { capabilityDetails } from './capabilities.ts';
@@ -35,6 +30,8 @@ export { normalizeMatch, assertDisjointMatches, matchesRoute } from './condition
35
30
  export type { RouteMatch, ConditionRequest } from './conditions.ts';
36
31
  export { buildCloudflare } from './build-cloudflare.ts';
37
32
  export type { BuildOptions as CloudflareBuildOptions, BuildReport as CloudflareBuildReport } from './build-cloudflare.ts';
33
+ export { buildStatic } from './build-static.ts';
34
+ export type { BuildOptions as StaticBuildOptions, BuildReport as StaticBuildReport, StaticRedirect, StaticObject, RedirectManifest, ObjectManifest } from './build-static.ts';
38
35
  export { runProjectTests } from './project-tests.ts';
39
36
  export type { ProjectTestOptions, ProjectTestResult } from './project-tests.ts';
40
37
  export { scaffoldProject } from './scaffold.ts';
@@ -1,7 +1,7 @@
1
1
  import type { CapabilityName, CapabilityTarget } from './capabilities.ts';
2
2
  import type { InspectOptions } from './tooling.ts';
3
3
  import type { RouteExplanation } from './explain.ts';
4
- export declare const MANIFEST_SCHEMA_VERSION = 1;
4
+ export declare const MANIFEST_SCHEMA_VERSION = 3;
5
5
  export interface ManifestRoute {
6
6
  path: string;
7
7
  methods: string[];
@@ -10,6 +10,10 @@ export interface ManifestRoute {
10
10
  expires?: string;
11
11
  generated?: string;
12
12
  description?: string;
13
+ /** Execution mode for the route's whole `function`/`middleware` chain: `true` for the
14
+ * QuickJS sandbox, `false` for trusted in-process execution. */
15
+ sandbox: boolean;
16
+ sandboxReason?: string;
13
17
  middleware: {
14
18
  source: string;
15
19
  export: string;
@@ -63,8 +67,6 @@ export interface Manifest {
63
67
  signals: string[];
64
68
  };
65
69
  extensions: string[];
66
- linkStores: string[];
67
- dynamicLinks: boolean;
68
70
  };
69
71
  functions: ManifestModule[];
70
72
  middleware: ManifestModule[];
@@ -4,12 +4,8 @@ export declare const events: Readonly<{
4
4
  reload: readonly string[];
5
5
  watch: readonly string[];
6
6
  function_worker: readonly string[];
7
- link_store_worker: readonly string[];
8
- link_request: readonly string[];
9
- link_observer: readonly string[];
10
7
  logs_dropped: readonly string[];
11
8
  observer: readonly string[];
12
- management_request: readonly string[];
13
9
  throttle: readonly string[];
14
10
  agents: readonly string[];
15
11
  cache: readonly string[];
@@ -62,21 +58,12 @@ export interface MetricsSnapshot {
62
58
  healthySlots: number;
63
59
  slots: number;
64
60
  };
65
- linkStoreWorkers: {
66
- started: number;
67
- restarts: number;
68
- };
69
61
  policies: {
70
62
  throttle: Counters;
71
63
  agents: Counters;
72
64
  cache: Counters;
73
65
  };
74
66
  signals: Counters;
75
- linkRequests: Counters;
76
- linkObserver: {
77
- failed: number;
78
- dropped: number;
79
- };
80
67
  logsDropped: number;
81
68
  observers: {
82
69
  errors: number;
@@ -99,7 +86,7 @@ export type ObserverSink = ((event: ObserverEvent, context?: RecordContext) => v
99
86
  close(): Promise<void>;
100
87
  };
101
88
  export declare function validateObservers(observers?: unknown): Observer[];
102
- export declare const SNAPSHOT_VERSION = 1;
89
+ export declare const SNAPSHOT_VERSION = 2;
103
90
  export declare function createMetrics(): Metrics;
104
91
  export declare function createObserverSink(observers?: unknown, fallbackLog?: (event: ObserverEvent) => void, metrics?: Metrics): ObserverSink;
105
92
  export declare function renderPrometheus(snapshot: Partial<MetricsSnapshot>): string;
@@ -5,11 +5,10 @@ export interface ProjectTestOptions {
5
5
  plugins?: ServerOptions['plugins'];
6
6
  log?: LogFn | undefined;
7
7
  permissions?: ServerOptions['permissions'];
8
- linkStore?: ServerOptions['linkStore'];
9
8
  origin?: string | undefined;
10
9
  }
11
10
  export interface ProjectTestResult {
12
11
  total: number;
13
12
  failed: number;
14
13
  }
15
- export declare function runProjectTests(project: string, { log, permissions, linkStore, origin, extensions, plugins }?: ProjectTestOptions): Promise<ProjectTestResult>;
14
+ export declare function runProjectTests(project: string, { log, permissions, origin, extensions, plugins }?: ProjectTestOptions): Promise<ProjectTestResult>;
@@ -4,10 +4,15 @@ import type { CompiledRedirect, CompiledRoute, LogFn, PlanInventoryEntry, Policy
4
4
  import type { ComplianceOptions, ComplianceReport } from './compliance.ts';
5
5
  import type { CompiledRoutes } from './match.ts';
6
6
  export type { RouteState } from './types.ts';
7
- export type HandlerName = 'extension' | 'proxy' | 'conditional' | 'redirect' | 'function' | 'page' | 'static' | 'download' | 'respond' | 'link';
7
+ export type HandlerName = 'extension' | 'proxy' | 'conditional' | 'redirect' | 'function' | 'page' | 'static' | 'download' | 'respond';
8
8
  /** One configured route as the inventory reports it: a PlanInventoryEntry with the handler kind named. */
9
9
  export interface RouteInventory extends PlanInventoryEntry {
10
10
  handler: HandlerName | undefined;
11
+ /** Always reported here, so a trust change is visible in `routes` and its diff. */
12
+ sandbox: boolean;
13
+ /** Non-blocking `audit` observations about this route (e.g. a webhook-shaped
14
+ * route with no declared `sandbox`/`sandboxReason`); never affects `ready`. */
15
+ advisories?: string[];
11
16
  }
12
17
  /** One request case: a generated probe or a `tests/requests.json` fixture. */
13
18
  export interface RequestCase {
@@ -40,7 +45,6 @@ export interface AuditableApp {
40
45
  address: AddressInfo;
41
46
  root: string;
42
47
  testPlan(): ProjectPlan & {
43
- dynamicLinks?: boolean;
44
48
  policies?: Record<string, PolicyInventory>;
45
49
  };
46
50
  }
@@ -51,7 +55,6 @@ export interface AuditOptions {
51
55
  compliance?: ComplianceOptions | undefined;
52
56
  }
53
57
  export interface AuditReport {
54
- dynamicLinks: boolean | undefined;
55
58
  elapsedMs: number;
56
59
  ready: boolean;
57
60
  counts: {
@@ -74,6 +77,12 @@ export interface AuditReport {
74
77
  }[];
75
78
  policies: Record<string, PolicyInventory>;
76
79
  compliance: ComplianceReport | null;
80
+ /** Non-blocking `audit` observations, e.g. a route that looks webhook-shaped
81
+ * but declares neither `sandbox: true` nor `sandboxReason`. Never affects `ready`. */
82
+ advisories: {
83
+ route: string;
84
+ message: string;
85
+ }[];
77
86
  }
78
87
  export interface BenchmarkOptions {
79
88
  requests?: number | undefined;
@@ -1,3 +1,4 @@
1
+ import type { RuntimeExtension } from './extensions.ts';
1
2
  import type { CompiledRouteTable, LoadedDocument } from './types.ts';
2
3
  export { parseTarget, matchRoute, contextFor, resolveValue, redirectLocation } from './match.ts';
3
4
  /** The grants compileRoutes consults: a validated OperatorPolicy (policy.ts) or nothing, which denies every binding. */
@@ -8,4 +9,4 @@ export interface BindingPermissions {
8
9
  secrets?: string[];
9
10
  }>;
10
11
  }
11
- export declare function compileRoutes(loaded: LoadedDocument, bindings: Record<string, string | undefined>, permissions?: BindingPermissions, projectSha256?: string): Promise<CompiledRouteTable>;
12
+ export declare function compileRoutes(loaded: LoadedDocument, bindings: Record<string, string | undefined>, permissions?: BindingPermissions, projectSha256?: string, extensions?: readonly Pick<RuntimeExtension, 'name' | 'cacheSensitive'>[]): Promise<CompiledRouteTable>;
@@ -2,34 +2,15 @@ import type { RuntimeExtension } from './extensions.ts';
2
2
  import type { EgressDependencies } from './egress.ts';
3
3
  import type { ProjectPlan } from './readiness.ts';
4
4
  import type { OperatorPolicy } from './policy.ts';
5
- import type { LinkStoreOptions } from './link-store.ts';
6
5
  import type { Plugin } from './plugins.ts';
7
6
  import type { MetricsSnapshot, Observer } from './observability.ts';
8
7
  import type { HandlerResult, HeaderPair } from './http-response.ts';
9
8
  import type { LogFn, PolicyInventory, TargetName } from './types.ts';
10
- /** A stored link as a link store returns it; the runtime validates the fields it uses. */
11
- export interface LinkLookup {
12
- url?: unknown;
13
- status?: unknown;
14
- enabled?: unknown;
15
- expires?: unknown;
16
- }
17
- /** What a `linkStores` binding must provide: a reader and, optionally, its health. */
18
- export interface LinkReader {
19
- get(collection: string, code: string): Promise<LinkLookup | null | undefined>;
20
- readonly readHealthy?: boolean;
21
- readonly healthy?: boolean;
22
- }
23
- /** An operator-owned SQLite link store the runtime opens read-only for one collection. */
24
- export interface LinkStoreBinding extends LinkStoreOptions {
25
- collection: string;
26
- }
27
9
  export type { OperatorPolicy } from './policy.ts';
28
10
  /** An operator plugin (src/plugins.ts). */
29
11
  export type HostPlugin = Plugin;
30
12
  export type { Observer, MetricsSnapshot } from './observability.ts';
31
13
  export interface TestPlan extends ProjectPlan {
32
- dynamicLinks: boolean;
33
14
  policies: Record<string, PolicyInventory>;
34
15
  }
35
16
  export interface RuntimeOptions {
@@ -42,8 +23,6 @@ export interface RuntimeOptions {
42
23
  local?: boolean | undefined;
43
24
  environment?: NodeJS.ProcessEnv | undefined;
44
25
  permissions?: OperatorPolicy | undefined;
45
- linkStore?: LinkStoreBinding | undefined;
46
- linkStores?: Record<string, LinkReader> | undefined;
47
26
  target?: TargetName | undefined;
48
27
  plugins?: HostPlugin[] | undefined;
49
28
  workers?: number | undefined;
@@ -57,16 +36,10 @@ export interface RuntimeOptions {
57
36
  only?: readonly string[] | undefined;
58
37
  }
59
38
  /** Per-request facts the host may read after handle() settles; never request text. */
60
- export interface LinkTrace {
61
- collection: string;
62
- code: string | null;
63
- result: string;
64
- }
65
39
  export interface RequestTrace {
66
40
  route?: string;
67
41
  probe?: boolean;
68
42
  client?: string | null;
69
- link?: LinkTrace;
70
43
  }
71
44
  export interface RuntimeRequest {
72
45
  target: string;
@@ -0,0 +1,12 @@
1
+ export { SandboxPool } from './functions.ts';
2
+ export type { SandboxEntry, SandboxTarget, SandboxInvocation, SandboxPoolOptions, FunctionContext, FunctionResult, } from './functions.ts';
3
+ export type { GuestRequestPayload, GuestResponsePayload } from './guest-api.ts';
4
+ export type { HandlerResult, HeaderPair } from './http-response.ts';
5
+ /**
6
+ * Resolves a project-relative hook source string the same way a native
7
+ * `function`/`middleware` route's `source` is resolved (`.mjs`/`.js` only, no
8
+ * path traversal outside the project root) into the absolute path
9
+ * `SandboxPool`'s entries and `execute()` targets expect. `root` must be the
10
+ * project's resolved directory (`ExtensionActivation.root`, never `cwd()`).
11
+ */
12
+ export { functionFile } from './config.ts';
@@ -1,11 +1,9 @@
1
1
  export interface Unresolved {
2
2
  path?: string;
3
3
  reason: string;
4
- collection?: string;
5
4
  }
6
5
  export interface ScaffoldReport {
7
6
  dryRun: boolean;
8
- dynamicLinks: boolean;
9
7
  created: string[];
10
8
  preserved: string[];
11
9
  unresolved: Unresolved[];
@@ -1,7 +1,6 @@
1
1
  import http from 'node:http';
2
2
  import type { AddressInfo } from 'node:net';
3
3
  import type { RuntimeOptions, TestPlan } from './runtime.ts';
4
- import type { LinkObserverStats } from './link-events.ts';
5
4
  import type { MetricsSnapshot, Observer } from './observability.ts';
6
5
  export interface ServerOptions extends Omit<RuntimeOptions, 'observers'> {
7
6
  project?: string | undefined;
@@ -13,7 +12,6 @@ export interface ServerOptions extends Omit<RuntimeOptions, 'observers'> {
13
12
  maxInFlightHealthRequests?: number | undefined;
14
13
  requestLog?: string | undefined;
15
14
  trustRequestId?: boolean | undefined;
16
- linkEvents?: unknown;
17
15
  trustedProxies?: string | string[] | undefined;
18
16
  observers?: Observer[] | undefined;
19
17
  metrics?: boolean | undefined;
@@ -25,7 +23,6 @@ export interface Server {
25
23
  address: AddressInfo;
26
24
  root: string;
27
25
  testPlan(): TestPlan;
28
- linkEventStats(): LinkObserverStats | undefined;
29
26
  metrics(): MetricsSnapshot;
30
27
  readonly observers: {
31
28
  name: string;
@@ -34,4 +31,4 @@ export interface Server {
34
31
  origin: string;
35
32
  close(): Promise<void>;
36
33
  }
37
- export declare function startServer({ project, host, port, watch, local, log, maxBodyBytes, maxInFlightRequests, maxInFlightHealthRequests, requestLog, trustRequestId, origin, linkEvents, trustedProxies, observers, metrics, metricsIntervalMs, ...runtimeOptions }?: ServerOptions): Promise<Server>;
34
+ export declare function startServer({ project, host, port, watch, local, log, maxBodyBytes, maxInFlightRequests, maxInFlightHealthRequests, requestLog, trustRequestId, origin, trustedProxies, observers, metrics, metricsIntervalMs, ...runtimeOptions }?: ServerOptions): Promise<Server>;
@@ -39,14 +39,14 @@ export declare function inspectProject(project: string, options?: InspectOptions
39
39
  path: string;
40
40
  methods: string[];
41
41
  enabled: boolean;
42
- capabilities: ("function" | "link" | "expires" | "middleware" | "extension" | "proxy" | "signals" | "conditional" | "methods" | "enabled" | "parameters" | "redirect" | "page" | "download" | "static" | "respond" | "dynamicLinks" | "policies.extensions" | "conditions" | "request.body" | "response.headers" | "bindings" | "policies.agents" | "policies.security" | "policies.cache" | "policies.compression" | "policies.throttle")[];
42
+ capabilities: ("function" | "expires" | "middleware" | "extension" | "proxy" | "signals" | "conditional" | "methods" | "enabled" | "parameters" | "redirect" | "page" | "download" | "static" | "respond" | "policies.extensions" | "conditions" | "request.body" | "response.headers" | "bindings" | "policies.agents" | "policies.security" | "policies.cache" | "policies.compression" | "policies.throttle")[];
43
43
  }[];
44
44
  compatibility: {
45
45
  offset: number;
46
46
  limit: number;
47
47
  hasMore: boolean;
48
48
  issues: import("./capabilities.ts").CapabilityRequirement[];
49
- target: "vercel" | "aws" | "cloudflare" | "self-hosted";
49
+ target: "vercel" | "aws" | "cloudflare" | "static" | "self-hosted";
50
50
  compatible: boolean;
51
51
  deployment: "local-runtime" | "unverified";
52
52
  requirementCount: number;
@@ -58,7 +58,7 @@ export declare function validateProject(project: string, options?: InspectOption
58
58
  projectSha256: string;
59
59
  routeCount: number;
60
60
  compatibility: {
61
- target: "vercel" | "aws" | "cloudflare" | "self-hosted";
61
+ target: "vercel" | "aws" | "cloudflare" | "static" | "self-hosted";
62
62
  compatible: boolean;
63
63
  deployment: "local-runtime" | "unverified";
64
64
  requirementCount: number;
@@ -0,0 +1,29 @@
1
+ import type { FunctionRoute } from './function-sources.ts';
2
+ import type { FunctionContext, FunctionResult } from './functions.ts';
3
+ import type { GuestRequestPayload } from './guest-api.ts';
4
+ import type { HandlerResult } from './http-response.ts';
5
+ import type { LogFn } from './types.ts';
6
+ export interface TrustedFunctionsOptions {
7
+ timeoutMs?: number | undefined;
8
+ maxBytes?: number | undefined;
9
+ log?: LogFn | undefined;
10
+ }
11
+ interface TrustedDefinition {
12
+ source: string;
13
+ export: string;
14
+ }
15
+ export type TrustedRoute = FunctionRoute<TrustedDefinition>;
16
+ export declare class TrustedFunctions {
17
+ timeoutMs: number;
18
+ maxBytes: number;
19
+ log: LogFn;
20
+ private readonly epoch;
21
+ constructor({ timeoutMs, maxBytes, log }?: TrustedFunctionsOptions);
22
+ start(routes: TrustedRoute[]): Promise<this>;
23
+ private loadExport;
24
+ execute(route: TrustedRoute, request: GuestRequestPayload, context: FunctionContext, native: HandlerResult | undefined): Promise<FunctionResult>;
25
+ private readBody;
26
+ private invoke;
27
+ close(): Promise<void>;
28
+ }
29
+ export {};
@@ -42,10 +42,6 @@ export type AuthoredRouteConfig = Omit<RouteConfig, 'function' | 'middleware'> &
42
42
  function?: string | FunctionConfig;
43
43
  middleware?: (string | MiddlewareConfig)[];
44
44
  };
45
- export interface LinkConfig {
46
- collection: string;
47
- code: ValueRef;
48
- }
49
45
  export interface PageConfig {
50
46
  file: string;
51
47
  contentType?: string;
@@ -151,6 +147,8 @@ export interface RouteAuthConfig {
151
147
  export interface RouteConfig {
152
148
  extension?: string;
153
149
  auth?: true | RouteAuthConfig;
150
+ /** Route-level `cache` short form: the same object accepted by `policies.cache`, expanded to it before anything else reads the project. */
151
+ cache?: CacheConfig;
154
152
  proxy?: ProxyConfig;
155
153
  signals?: SignalConfig[];
156
154
  match?: RouteMatch;
@@ -159,6 +157,18 @@ export interface RouteConfig {
159
157
  enabled?: boolean;
160
158
  expires?: string;
161
159
  description?: string;
160
+ /** Opt into the isolated QuickJS/WASM worker pool for this route's `function`/`middleware`
161
+ * chain (docs/FUNCTION-SECURITY.md). Default false: trusted, in-process, unsandboxed
162
+ * execution (docs/SPIKE-DEFAULT-TRUST-MODEL.md). Applies uniformly to the whole
163
+ * chain — `function` and any `middleware` on the same route run in the same mode. */
164
+ sandbox?: boolean;
165
+ /** Optional, human-authored justification for this route's sandbox decision — why it
166
+ * needs isolation when `sandbox: true`, or why it is safe to trust when it is not.
167
+ * Parsed and schema-validated (max 500 characters, schemas/urlcode.schema.json), stored
168
+ * on the compiled route and surfaced by `explain`/`context`/manifest next to `sandbox`.
169
+ * Never inferred or enforced: the trust decision remains the author's judgment call
170
+ * (docs/AI-AUTHORING.md, "Deciding when a route needs sandbox: true"). */
171
+ sandboxReason?: string;
162
172
  parameters?: ParameterConfig[];
163
173
  redirect?: RedirectConfig;
164
174
  function?: FunctionConfig;
@@ -175,7 +185,6 @@ export interface RouteConfig {
175
185
  };
176
186
  respond?: RespondSpec;
177
187
  middleware?: MiddlewareConfig[];
178
- link?: LinkConfig;
179
188
  policies?: PoliciesConfig;
180
189
  /** Set by site.ts on a route it generated (`site.<key>`); never declared in YAML. */
181
190
  generated?: string;
@@ -185,7 +194,6 @@ export interface ProjectDocument {
185
194
  extensions?: Record<string, ExtensionDeclaration>;
186
195
  routes: Record<string, RouteConfig>;
187
196
  includes?: string[];
188
- dynamicLinks?: boolean;
189
197
  policies?: PoliciesConfig;
190
198
  profiles?: Record<string, PolicyLayer>;
191
199
  site?: SiteConfig;
@@ -370,7 +378,7 @@ export type PolicyChain = {
370
378
  describe: PolicyInventory;
371
379
  } & Partial<PolicyStates>;
372
380
  export type RouteState = 'active' | 'disabled' | 'expired';
373
- /** One route in the audit inventory: its handler kind, methods and lifecycle state. */
381
+ /** One route in the audit inventory: its handler kind, methods, execution mode and lifecycle state. */
374
382
  export interface PlanInventoryEntry {
375
383
  path: string;
376
384
  handler: string | undefined;
@@ -379,11 +387,17 @@ export interface PlanInventoryEntry {
379
387
  policies: string[];
380
388
  generated?: string;
381
389
  state: RouteState;
390
+ /** The route's execution mode: `true` when its `function`/`middleware` chain runs in the
391
+ * QuickJS sandbox, `false` when it runs trusted in-process. Route-level, because the mode
392
+ * applies to the whole chain — a native handler with `middleware` has one too. Optional
393
+ * only at the report-parsing boundary: reports written before it existed omit it. */
394
+ sandbox?: boolean;
395
+ /** The route's declared `sandboxReason`, when it has one. */
396
+ sandboxReason?: string;
382
397
  }
383
398
  export interface TestPlan {
384
399
  inventory: PlanInventoryEntry[];
385
400
  cases: unknown[];
386
401
  resolve?(path: string): string | undefined;
387
- dynamicLinks?: boolean;
388
402
  policies?: Record<string, PolicyInventory>;
389
403
  }
@@ -12,7 +12,7 @@ export interface VerifyFinding {
12
12
  expected?: string;
13
13
  observed?: string;
14
14
  }
15
- export interface VerifyOptions extends Pick<RuntimeOptions, 'permissions' | 'linkStore'> {
15
+ export interface VerifyOptions extends Pick<RuntimeOptions, 'permissions'> {
16
16
  target: string;
17
17
  origin?: string | undefined;
18
18
  expectRoutes?: number | undefined;
@@ -44,4 +44,4 @@ export interface VerifyReport {
44
44
  compliance: ComplianceReport | null;
45
45
  }
46
46
  export declare const failLevels: readonly FailOn[];
47
- export declare function verifyDeployment(project: string, { target, origin, expectRoutes, timeoutMs, expectMetrics, failOn, compliance, complianceWarn, log, permissions, linkStore }: VerifyOptions): Promise<VerifyReport>;
47
+ export declare function verifyDeployment(project: string, { target, origin, expectRoutes, timeoutMs, expectMetrics, failOn, compliance, complianceWarn, log, permissions }: VerifyOptions): Promise<VerifyReport>;