@jimhoyd/urlcode 0.3.0 → 0.4.0-alpha.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 (380) hide show
  1. package/.claude/skills/urlcode-authoring/SKILL.md +122 -0
  2. package/.claude/skills/urlcode-operations/SKILL.md +108 -0
  3. package/.claude-plugin/marketplace.json +18 -0
  4. package/CONTRIBUTING.md +30 -2
  5. package/README.md +195 -255
  6. package/ROADMAP.md +143 -15
  7. package/SECURITY.md +31 -9
  8. package/dist/BUILD-MANIFEST.json +72 -47
  9. package/dist/adapters.js +4 -23
  10. package/dist/agent-lists.js +1 -1
  11. package/dist/agents-guide.js +113 -0
  12. package/dist/authoring-files.js +60 -0
  13. package/dist/authoring.js +11 -1
  14. package/dist/aws.js +4 -3
  15. package/dist/build-cloudflare.js +11 -24
  16. package/dist/build-static.js +134 -0
  17. package/dist/bulk.js +37 -0
  18. package/dist/capabilities.js +262 -0
  19. package/dist/capability-query.js +71 -0
  20. package/dist/catalog.js +105 -0
  21. package/dist/cli.js +165 -34
  22. package/dist/client-address.js +1 -1
  23. package/dist/compliance-rules/baseline.js +9 -17
  24. package/dist/compliance-rules/privacy.js +7 -18
  25. package/dist/compliance-rules/shared.js +0 -2
  26. package/dist/compliance-rules/strict.js +5 -5
  27. package/dist/compliance.js +6 -8
  28. package/dist/conditions.js +88 -0
  29. package/dist/config.js +69 -6
  30. package/dist/context.js +155 -0
  31. package/dist/ecosystem-cli.js +88 -0
  32. package/dist/egress.js +98 -0
  33. package/dist/examples.js +92 -0
  34. package/dist/explain-cli.js +64 -0
  35. package/dist/explain.js +131 -0
  36. package/dist/extensions.js +231 -0
  37. package/dist/function-sources.js +49 -5
  38. package/dist/function-worker.js +3 -1
  39. package/dist/functions.js +84 -13
  40. package/dist/guest-api.js +29 -3
  41. package/dist/index.js +40 -6
  42. package/dist/init-with.js +165 -0
  43. package/dist/interchange-cli.js +42 -0
  44. package/dist/interchange.js +189 -0
  45. package/dist/manifest.js +109 -0
  46. package/dist/match.js +2 -2
  47. package/dist/mcp-authoring.js +147 -0
  48. package/dist/mcp.js +97 -0
  49. package/dist/observability.js +7 -21
  50. package/dist/operator-host.js +29 -0
  51. package/dist/plugins.js +12 -0
  52. package/dist/policies/agents.js +2 -2
  53. package/dist/policies/cache.js +8 -3
  54. package/dist/policies/compression.js +2 -1
  55. package/dist/policies/security.js +0 -0
  56. package/dist/policies.js +1 -1
  57. package/dist/policy.js +56 -15
  58. package/dist/prerender.js +100 -41
  59. package/dist/project-tests.js +3 -3
  60. package/dist/provider-verification.js +92 -0
  61. package/dist/proxy.js +44 -0
  62. package/dist/readiness.js +34 -11
  63. package/dist/recipes.js +41 -0
  64. package/dist/route-diff.js +106 -0
  65. package/dist/router.js +45 -7
  66. package/dist/runtime.js +164 -64
  67. package/dist/sandbox.js +48 -0
  68. package/dist/scaffold.js +0 -0
  69. package/dist/schema-query.js +62 -0
  70. package/dist/scripts/operational-drills.js +12 -54
  71. package/dist/server.js +3 -29
  72. package/dist/signals.js +24 -0
  73. package/dist/site.js +0 -0
  74. package/dist/tooling.js +96 -0
  75. package/dist/trusted-functions.js +210 -0
  76. package/dist/types/adapters.d.ts +7 -4
  77. package/dist/types/agent-lists.d.ts +0 -1
  78. package/dist/types/agents-guide.d.ts +17 -0
  79. package/dist/types/authoring-files.d.ts +10 -0
  80. package/dist/types/aws.d.ts +3 -1
  81. package/dist/types/build-cloudflare.d.ts +1 -0
  82. package/dist/types/build-static.d.ts +43 -0
  83. package/dist/types/bulk.d.ts +27 -0
  84. package/dist/types/capabilities.d.ts +64 -0
  85. package/dist/types/capability-query.d.ts +24 -0
  86. package/dist/types/catalog.d.ts +65 -0
  87. package/dist/types/client-address.d.ts +0 -1
  88. package/dist/types/compliance-rules/baseline.d.ts +1 -9
  89. package/dist/types/compliance-rules/privacy.d.ts +1 -4
  90. package/dist/types/compliance-rules/shared.d.ts +0 -2
  91. package/dist/types/compliance-rules/strict.d.ts +0 -5
  92. package/dist/types/compliance.d.ts +0 -3
  93. package/dist/types/conditions.d.ts +19 -0
  94. package/dist/types/config.d.ts +21 -2
  95. package/dist/types/context.d.ts +66 -0
  96. package/dist/types/ecosystem-cli.d.ts +17 -0
  97. package/dist/types/egress.d.ts +46 -0
  98. package/dist/types/examples.d.ts +50 -0
  99. package/dist/types/explain-cli.d.ts +11 -0
  100. package/dist/types/explain.d.ts +95 -0
  101. package/dist/types/extensions.d.ts +177 -0
  102. package/dist/types/function-sources.d.ts +9 -0
  103. package/dist/types/functions.d.ts +48 -5
  104. package/dist/types/guest-api.d.ts +1 -0
  105. package/dist/types/index.d.ts +36 -6
  106. package/dist/types/init-with.d.ts +30 -0
  107. package/dist/types/interchange-cli.d.ts +16 -0
  108. package/dist/types/interchange.d.ts +42 -0
  109. package/dist/types/manifest.d.ts +79 -0
  110. package/dist/types/match.d.ts +1 -0
  111. package/dist/types/mcp-authoring.d.ts +92 -0
  112. package/dist/types/mcp.d.ts +12 -0
  113. package/dist/types/observability.d.ts +3 -14
  114. package/dist/types/operator-host.d.ts +8 -0
  115. package/dist/types/plugins.d.ts +2 -0
  116. package/dist/types/policies/agents.d.ts +0 -2
  117. package/dist/types/policies/compression.d.ts +2 -0
  118. package/dist/types/policies/security.d.ts +0 -1
  119. package/dist/types/policy.d.ts +15 -4
  120. package/dist/types/project-tests.d.ts +3 -2
  121. package/dist/types/provider-verification.d.ts +53 -0
  122. package/dist/types/proxy.d.ts +21 -0
  123. package/dist/types/readiness.d.ts +10 -3
  124. package/dist/types/recipes.d.ts +30 -0
  125. package/dist/types/route-diff.d.ts +27 -0
  126. package/dist/types/router.d.ts +2 -1
  127. package/dist/types/runtime.d.ts +11 -27
  128. package/dist/types/sandbox.d.ts +12 -0
  129. package/dist/types/scaffold.d.ts +0 -2
  130. package/dist/types/schema-query.d.ts +12 -0
  131. package/dist/types/server.d.ts +1 -4
  132. package/dist/types/signals.d.ts +25 -0
  133. package/dist/types/site.d.ts +0 -1
  134. package/dist/types/tooling.d.ts +115 -0
  135. package/dist/types/trusted-functions.d.ts +29 -0
  136. package/dist/types/types.d.ts +71 -7
  137. package/dist/types/typescript-authoring.d.ts +12 -0
  138. package/dist/types/vercel.d.ts +3 -1
  139. package/dist/types/verify-deployment.d.ts +47 -0
  140. package/dist/types.js +37 -5
  141. package/dist/typescript-authoring.js +142 -0
  142. package/dist/vercel.js +4 -3
  143. package/dist/verify-deployment.js +270 -0
  144. package/docs/AI-AUTHORING.md +232 -15
  145. package/docs/AWS.md +4 -4
  146. package/docs/BEST-PRACTICES.md +3 -2
  147. package/docs/BULK.md +79 -0
  148. package/docs/CAPABILITIES.md +192 -0
  149. package/docs/CAPACITY.md +129 -32
  150. package/docs/CI.md +142 -0
  151. package/docs/CLOUDFLARE.md +1 -2
  152. package/docs/COMPLIANCE.md +6 -9
  153. package/docs/CONDITIONS.md +74 -0
  154. package/docs/DEPLOYMENT-CHECKS.md +108 -0
  155. package/docs/EGRESS.md +125 -0
  156. package/docs/EXTENSIONS.md +398 -0
  157. package/docs/FRAMEWORK.md +198 -0
  158. package/docs/FUNCTION-SECURITY.md +129 -32
  159. package/docs/INSTALL.md +45 -12
  160. package/docs/INTERCHANGE.md +134 -0
  161. package/docs/LOAD-TESTING.md +4 -4
  162. package/docs/MIDDLEWARE-EXAMPLES.md +75 -0
  163. package/docs/MIDDLEWARE.md +29 -16
  164. package/docs/MONITORING.md +2 -19
  165. package/docs/NEXT-PHASE-PLAN.md +98 -0
  166. package/docs/NEXT-STEPS.md +634 -0
  167. package/docs/OBSERVABILITY.md +11 -18
  168. package/docs/OPEN-DECISIONS.md +212 -0
  169. package/docs/OPERATIONAL-PROOF.md +30 -31
  170. package/docs/OPERATIONS.md +29 -35
  171. package/docs/PLUGINS.md +37 -0
  172. package/docs/POLICIES.md +23 -309
  173. package/docs/PRERENDER.md +41 -1
  174. package/docs/PROJECT-DIRECTION.md +75 -8
  175. package/docs/PROVIDER-VERIFICATION.md +84 -0
  176. package/docs/READINESS.md +21 -1
  177. package/docs/README.md +87 -34
  178. package/docs/RECIPES.md +99 -0
  179. package/docs/RELEASE-READINESS.md +57 -35
  180. package/docs/RELEASE-SECURITY.md +116 -7
  181. package/docs/RESILIENCE.md +16 -15
  182. package/docs/ROUTING.md +8 -10
  183. package/docs/SANDBOX-REVIEW.md +19 -6
  184. package/docs/SCAFFOLDING.md +0 -2
  185. package/docs/SECURITY-AUDIT.md +41 -1
  186. package/docs/SPECIFICATION.md +150 -29
  187. package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +287 -0
  188. package/docs/SPIKE-BUSINESS-SUITE.md +1021 -0
  189. package/docs/SPIKE-CORE-LAYERING.md +337 -0
  190. package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +209 -0
  191. package/docs/SPIKE-EXTENSION-MODEL.md +419 -0
  192. package/docs/SPIKE-EXTENSIONS.md +6 -0
  193. package/docs/SPIKE-LAMBDA-COMPILE.md +201 -0
  194. package/docs/SPIKE-MONOREPO.md +322 -0
  195. package/docs/STANDARDS.md +150 -142
  196. package/docs/STARTERS.md +21 -1
  197. package/docs/STATIC.md +94 -0
  198. package/docs/TOOLING.md +295 -0
  199. package/docs/TUNNELS.md +0 -3
  200. package/docs/TYPESCRIPT-AUTHORING.md +82 -0
  201. package/docs/TYPESCRIPT.md +25 -4
  202. package/docs/USABILITY-REVIEW.md +129 -0
  203. package/docs/VERCEL.md +4 -5
  204. package/docs/VERSION-ALIGNMENT.md +205 -0
  205. package/docs/YAML-GUIDE.md +15 -479
  206. package/docs/YAML-REFERENCE.md +143 -22
  207. package/docs/policies/agents.md +1 -1
  208. package/docs/policies/cache.md +13 -0
  209. package/docs/policies/contract.md +52 -0
  210. package/docs/policies/hardened.md +56 -0
  211. package/docs/policies/interoperability.md +169 -0
  212. package/docs/policies/operations.md +45 -0
  213. package/docs/yaml/assets.md +36 -0
  214. package/docs/yaml/conditions.md +20 -0
  215. package/docs/yaml/functions.md +160 -0
  216. package/docs/yaml/middleware.md +29 -0
  217. package/docs/yaml/organization.md +74 -0
  218. package/docs/yaml/policies.md +37 -0
  219. package/docs/yaml/redirects.md +64 -0
  220. package/docs/yaml/responses.md +57 -0
  221. package/docs/yaml/site.md +24 -0
  222. package/examples/assets/example.yaml +17 -0
  223. package/examples/aws/example.yaml +20 -0
  224. package/examples/cloudflare/example.yaml +19 -0
  225. package/examples/compliance/example.yaml +11 -0
  226. package/examples/conditions/README.md +12 -0
  227. package/examples/conditions/example.yaml +19 -0
  228. package/examples/conditions/tests/requests.json +13 -0
  229. package/examples/conditions/urlcode.yaml +24 -0
  230. package/examples/cookbook/README.md +8 -4
  231. package/examples/cookbook/example.yaml +17 -0
  232. package/examples/cookbook/functions/catalog.mjs +3 -0
  233. package/examples/cookbook/functions/fail.mjs +4 -0
  234. package/examples/cookbook/functions/items.mjs +3 -0
  235. package/examples/cookbook/functions/profile.mjs +3 -0
  236. package/examples/cookbook/functions/resource.mjs +3 -0
  237. package/examples/cookbook/functions/status.mjs +3 -0
  238. package/examples/cookbook/middleware/auth.mjs +48 -0
  239. package/examples/cookbook/middleware/body.mjs +15 -0
  240. package/examples/cookbook/middleware/bucket.mjs +29 -0
  241. package/examples/cookbook/middleware/cors.mjs +21 -0
  242. package/examples/cookbook/middleware/debug.mjs +13 -0
  243. package/examples/cookbook/middleware/envelope.mjs +11 -0
  244. package/examples/cookbook/middleware/errors.mjs +11 -0
  245. package/examples/cookbook/middleware/etag.mjs +18 -0
  246. package/examples/cookbook/middleware/locale.mjs +20 -0
  247. package/examples/cookbook/middleware/maintenance.mjs +10 -0
  248. package/examples/cookbook/middleware/methods.mjs +15 -0
  249. package/examples/cookbook/middleware/negotiate.mjs +20 -0
  250. package/examples/cookbook/middleware/referer.mjs +12 -0
  251. package/examples/cookbook/middleware/request-id.mjs +16 -0
  252. package/examples/cookbook/route-index.json +676 -0
  253. package/examples/cookbook/routes/middleware.yaml +126 -0
  254. package/examples/cookbook/tests/requests.json +526 -0
  255. package/examples/cookbook/urlcode.yaml +1 -0
  256. package/examples/egress/README.md +22 -0
  257. package/examples/egress/example.yaml +19 -0
  258. package/examples/egress/urlcode.yaml +19 -0
  259. package/examples/extensions/README.md +7 -0
  260. package/examples/extensions/example.yaml +21 -0
  261. package/examples/extensions/urlcode.yaml +25 -0
  262. package/examples/monitoring/example.yaml +8 -0
  263. package/examples/prerender/README.md +2 -2
  264. package/examples/prerender/example.yaml +16 -0
  265. package/examples/provider-conformance/README.md +12 -0
  266. package/examples/provider-conformance/example.yaml +14 -0
  267. package/examples/provider-conformance/urlcode.yaml +34 -0
  268. package/examples/tunnel/example.yaml +8 -0
  269. package/examples/vercel/example.yaml +19 -0
  270. package/llms-full.txt +3084 -0
  271. package/llms.txt +61 -21
  272. package/package.json +36 -7
  273. package/packaging/claude-plugin/.claude-plugin/plugin.json +19 -0
  274. package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +122 -0
  275. package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +108 -0
  276. package/recipes/authenticated-json-api/README.md +51 -0
  277. package/recipes/authenticated-json-api/functions/profile.mjs +5 -0
  278. package/recipes/authenticated-json-api/recipe.yaml +34 -0
  279. package/recipes/authenticated-json-api/tests/requests.json +39 -0
  280. package/recipes/authenticated-json-api/urlcode.yaml +12 -0
  281. package/recipes/contact-form/README.md +25 -0
  282. package/recipes/contact-form/functions/contact.mjs +17 -0
  283. package/recipes/contact-form/recipe.yaml +33 -0
  284. package/recipes/contact-form/tests/requests.json +47 -0
  285. package/recipes/contact-form/urlcode.yaml +18 -0
  286. package/recipes/cors-api/README.md +16 -0
  287. package/recipes/cors-api/functions/items.mjs +3 -0
  288. package/recipes/cors-api/middleware/cors.mjs +21 -0
  289. package/recipes/cors-api/recipe.yaml +26 -0
  290. package/recipes/cors-api/tests/requests.json +65 -0
  291. package/recipes/cors-api/urlcode.yaml +12 -0
  292. package/recipes/health-page/README.md +13 -0
  293. package/recipes/health-page/recipe.yaml +23 -0
  294. package/recipes/health-page/tests/requests.json +36 -0
  295. package/recipes/health-page/urlcode.yaml +19 -0
  296. package/recipes/json-api/README.md +6 -0
  297. package/recipes/json-api/functions/echo.mjs +3 -0
  298. package/recipes/json-api/recipe.yaml +25 -0
  299. package/recipes/json-api/tests/requests.json +34 -0
  300. package/recipes/json-api/urlcode.yaml +12 -0
  301. package/recipes/middleware/README.md +34 -0
  302. package/recipes/middleware/functions/catalog.mjs +3 -0
  303. package/recipes/middleware/functions/fail.mjs +4 -0
  304. package/recipes/middleware/functions/items.mjs +3 -0
  305. package/recipes/middleware/functions/profile.mjs +3 -0
  306. package/recipes/middleware/functions/resource.mjs +3 -0
  307. package/recipes/middleware/functions/status.mjs +3 -0
  308. package/recipes/middleware/middleware/auth.mjs +48 -0
  309. package/recipes/middleware/middleware/body.mjs +15 -0
  310. package/recipes/middleware/middleware/bucket.mjs +29 -0
  311. package/recipes/middleware/middleware/cors.mjs +21 -0
  312. package/recipes/middleware/middleware/debug.mjs +13 -0
  313. package/recipes/middleware/middleware/envelope.mjs +11 -0
  314. package/recipes/middleware/middleware/errors.mjs +11 -0
  315. package/recipes/middleware/middleware/etag.mjs +18 -0
  316. package/recipes/middleware/middleware/locale.mjs +20 -0
  317. package/recipes/middleware/middleware/maintenance.mjs +10 -0
  318. package/recipes/middleware/middleware/methods.mjs +15 -0
  319. package/recipes/middleware/middleware/negotiate.mjs +20 -0
  320. package/recipes/middleware/middleware/referer.mjs +12 -0
  321. package/recipes/middleware/middleware/request-id.mjs +16 -0
  322. package/recipes/middleware/public/guide.txt +1 -0
  323. package/recipes/middleware/recipe.yaml +50 -0
  324. package/recipes/middleware/tests/requests.json +528 -0
  325. package/recipes/middleware/urlcode.yaml +127 -0
  326. package/recipes/protected-download/README.md +22 -0
  327. package/recipes/protected-download/files/report.txt +1 -0
  328. package/recipes/protected-download/recipe.yaml +31 -0
  329. package/recipes/protected-download/tests/requests.json +32 -0
  330. package/recipes/protected-download/urlcode.yaml +15 -0
  331. package/recipes/redirect/README.md +7 -0
  332. package/recipes/redirect/recipe.yaml +25 -0
  333. package/recipes/redirect/tests/requests.json +19 -0
  334. package/recipes/redirect/urlcode.yaml +9 -0
  335. package/recipes/static-plus-api/README.md +15 -0
  336. package/recipes/static-plus-api/functions/info.mjs +3 -0
  337. package/recipes/static-plus-api/public/assets/index.html +3 -0
  338. package/recipes/static-plus-api/public/assets/site.css +1 -0
  339. package/recipes/static-plus-api/public/index.html +8 -0
  340. package/recipes/static-plus-api/recipe.yaml +29 -0
  341. package/recipes/static-plus-api/tests/requests.json +56 -0
  342. package/recipes/static-plus-api/urlcode.yaml +23 -0
  343. package/recipes/typescript/README.md +8 -0
  344. package/recipes/typescript/functions/hello.ts +5 -0
  345. package/recipes/typescript/recipe.yaml +23 -0
  346. package/recipes/typescript/tests/requests.json +18 -0
  347. package/recipes/typescript/urlcode.yaml +5 -0
  348. package/recipes/webhook-receiver/README.md +20 -0
  349. package/recipes/webhook-receiver/functions/receive.mjs +16 -0
  350. package/recipes/webhook-receiver/recipe.yaml +27 -0
  351. package/recipes/webhook-receiver/tests/requests.json +59 -0
  352. package/recipes/webhook-receiver/urlcode.yaml +23 -0
  353. package/schemas/recipe.schema.json +139 -0
  354. package/schemas/urlcode.schema.json +659 -110
  355. package/skills/urlcode/SKILL.md +119 -0
  356. package/starters/default/.github/workflows/urlcode.yml +23 -0
  357. package/starters/default/.mcp.json +12 -0
  358. package/starters/default/AGENTS.md +79 -0
  359. package/starters/default/urlcode.yaml +0 -1
  360. package/dist/link-api.js +0 -136
  361. package/dist/link-cli.js +0 -141
  362. package/dist/link-events.js +0 -76
  363. package/dist/link-records.js +0 -31
  364. package/dist/link-store-worker.js +0 -150
  365. package/dist/link-store.js +0 -250
  366. package/dist/management-policy.js +0 -41
  367. package/dist/sqlite-version.js +0 -6
  368. package/dist/types/link-api.d.ts +0 -30
  369. package/dist/types/link-cli.d.ts +0 -36
  370. package/dist/types/link-events.d.ts +0 -27
  371. package/dist/types/link-records.d.ts +0 -11
  372. package/dist/types/link-store-worker.d.ts +0 -1
  373. package/dist/types/link-store.d.ts +0 -130
  374. package/dist/types/management-policy.d.ts +0 -9
  375. package/dist/types/sqlite-version.d.ts +0 -1
  376. package/docs/DYNAMIC-LINKS.md +0 -561
  377. package/docs/MANAGEMENT-SECURITY.md +0 -82
  378. package/examples/live-links/README.md +0 -11
  379. package/examples/live-links/tests/requests.json +0 -6
  380. package/examples/live-links/urlcode.yaml +0 -16
@@ -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 {};
@@ -1,3 +1,7 @@
1
+ import type { ExtensionDeclaration, ExtensionPolicies } from './extensions.ts';
2
+ import type { ProxyDefinition } from './proxy.ts';
3
+ import type { SignalDefinition } from './signals.ts';
4
+ import type { RouteMatch } from './conditions.ts';
1
5
  import type { HandlerResult, HeaderPair } from './http-response.ts';
2
6
  import type { CompiledParameter, MatchableRoute, ParameterLocation, ParameterSchema, RedirectSpec, Scalar, ValueRef } from './match.ts';
3
7
  import type { HttpRoute, Reply, RequestBodyPolicy, RespondSpec } from './http-policy.ts';
@@ -33,10 +37,11 @@ export interface MiddlewareConfig {
33
37
  source: string;
34
38
  export?: string;
35
39
  }
36
- export interface LinkConfig {
37
- collection: string;
38
- code: ValueRef;
39
- }
40
+ /** A route as YAML may spell it before normalization: `function` and middleware entries may be short-form module paths. */
41
+ export type AuthoredRouteConfig = Omit<RouteConfig, 'function' | 'middleware'> & {
42
+ function?: string | FunctionConfig;
43
+ middleware?: (string | MiddlewareConfig)[];
44
+ };
40
45
  export interface PageConfig {
41
46
  file: string;
42
47
  contentType?: string;
@@ -73,6 +78,8 @@ export interface PolicyConfigs {
73
78
  /** One layer of policy configuration (profile, project or route); a layer may declare part of a policy, the merge supplies the rest. */
74
79
  export type PolicyLayer = {
75
80
  [K in PolicyName]?: Partial<PolicyConfigs[K]> | false;
81
+ } & {
82
+ extensions?: ExtensionPolicies | false;
76
83
  };
77
84
  export interface PoliciesConfig extends PolicyLayer {
78
85
  profile?: string;
@@ -108,11 +115,60 @@ export interface SiteConfig {
108
115
  llms?: string;
109
116
  }
110
117
  /** One route as declared in YAML (plus `generated`, which site.ts stamps on the routes it adds). */
118
+ export interface ConditionalReply {
119
+ redirect?: RedirectConfig;
120
+ respond?: RespondSpec;
121
+ }
122
+ export interface ConditionalConfig {
123
+ cases: (ConditionalReply & {
124
+ match: RouteMatch;
125
+ })[];
126
+ fallback?: ConditionalReply;
127
+ }
128
+ export type EgressHeaders = Record<string, string | {
129
+ secret: string;
130
+ }>;
131
+ export interface ProxyConfig extends Omit<ProxyDefinition, 'headers'> {
132
+ headers?: EgressHeaders;
133
+ }
134
+ export interface SignalConfig {
135
+ url: string;
136
+ headers?: EgressHeaders;
137
+ }
138
+ /** Route-level `auth` short form. Keys other than `required` mirror the auth extension's policy schema and expand to `policies.extensions.auth`. */
139
+ export interface RouteAuthConfig {
140
+ required?: boolean;
141
+ role?: string;
142
+ permission?: string;
143
+ verified?: boolean;
144
+ freshWithinSeconds?: number;
145
+ onDeny?: 401 | 403 | 404 | 'sign-in';
146
+ }
111
147
  export interface RouteConfig {
148
+ extension?: string;
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;
152
+ proxy?: ProxyConfig;
153
+ signals?: SignalConfig[];
154
+ match?: RouteMatch;
155
+ conditional?: ConditionalConfig;
112
156
  methods?: string[];
113
157
  enabled?: boolean;
114
158
  expires?: string;
115
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;
116
172
  parameters?: ParameterConfig[];
117
173
  redirect?: RedirectConfig;
118
174
  function?: FunctionConfig;
@@ -129,16 +185,15 @@ export interface RouteConfig {
129
185
  };
130
186
  respond?: RespondSpec;
131
187
  middleware?: MiddlewareConfig[];
132
- link?: LinkConfig;
133
188
  policies?: PoliciesConfig;
134
189
  /** Set by site.ts on a route it generated (`site.<key>`); never declared in YAML. */
135
190
  generated?: string;
136
191
  }
137
192
  export interface ProjectDocument {
138
193
  version: '1';
194
+ extensions?: Record<string, ExtensionDeclaration>;
139
195
  routes: Record<string, RouteConfig>;
140
196
  includes?: string[];
141
- dynamicLinks?: boolean;
142
197
  policies?: PoliciesConfig;
143
198
  profiles?: Record<string, PolicyLayer>;
144
199
  site?: SiteConfig;
@@ -210,6 +265,16 @@ export interface CompiledRoute extends Omit<RouteConfig, 'methods' | 'parameters
210
265
  policy?: PolicyChain | null;
211
266
  /** Attached by build-cloudflare: the compiled policy states shipped in the Worker artifact. */
212
267
  compiledPolicies?: Record<string, unknown>;
268
+ extensionPolicyNames?: string[];
269
+ compiledProxy?: ProxyDefinition;
270
+ compiledSignals?: SignalDefinition[];
271
+ conditionalRoutes?: {
272
+ cases: {
273
+ match: RouteMatch;
274
+ route: CompiledRoute;
275
+ }[];
276
+ fallback?: CompiledRoute;
277
+ };
213
278
  }
214
279
  export interface CompiledRouteTable {
215
280
  exact: Map<string, CompiledRoute>;
@@ -327,6 +392,5 @@ export interface TestPlan {
327
392
  inventory: PlanInventoryEntry[];
328
393
  cases: unknown[];
329
394
  resolve?(path: string): string | undefined;
330
- dynamicLinks?: boolean;
331
395
  policies?: Record<string, PolicyInventory>;
332
396
  }
@@ -0,0 +1,12 @@
1
+ export interface TypeScriptBuildReport {
2
+ output: string;
3
+ dryRun: boolean;
4
+ modules: string[];
5
+ files: string[];
6
+ typeChecked: false;
7
+ }
8
+ /** Transpile a project snapshot ahead of runtime. No project compiler settings,
9
+ * plugins, package resolution, subprocesses or guest code execution are used. */
10
+ export declare function buildTypeScriptProject(project: string, output: string, { dryRun }?: {
11
+ dryRun?: boolean | undefined;
12
+ }): Promise<TypeScriptBuildReport>;
@@ -1,3 +1,4 @@
1
+ import type { RuntimeExtension } from './extensions.ts';
1
2
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
3
  import type { Environment } from './adapters.ts';
3
4
  import type { HostPlugin } from './runtime.ts';
@@ -7,6 +8,7 @@ export interface VercelHandlerOptions {
7
8
  environment?: Environment | undefined;
8
9
  maxBodyBytes?: number | undefined;
9
10
  plugins?: HostPlugin[] | undefined;
11
+ extensions?: RuntimeExtension[] | undefined;
10
12
  }
11
13
  export type VercelHandler = (req: IncomingMessage, res: ServerResponse) => Promise<void>;
12
- export declare function createVercelHandler({ project, origin, environment, maxBodyBytes, plugins }?: VercelHandlerOptions): VercelHandler;
14
+ export declare function createVercelHandler({ project, origin, environment, maxBodyBytes, plugins, extensions }?: VercelHandlerOptions): VercelHandler;
@@ -0,0 +1,47 @@
1
+ import type { RuntimeOptions } from './runtime.ts';
2
+ import type { ComplianceOptions, ComplianceReport, Severity } from './compliance.ts';
3
+ import type { LogFn } from './types.ts';
4
+ export type { Severity } from './compliance.ts';
5
+ export type FailOn = Severity | 'none';
6
+ export type CheckName = 'probes' | 'fixtures' | 'security' | 'cache' | 'compression' | 'agents' | 'throttle' | 'site' | 'methods' | 'errors' | 'head' | 'transport';
7
+ export interface VerifyFinding {
8
+ check: CheckName;
9
+ severity: Severity;
10
+ route?: string;
11
+ message: string;
12
+ expected?: string;
13
+ observed?: string;
14
+ }
15
+ export interface VerifyOptions extends Pick<RuntimeOptions, 'permissions'> {
16
+ target: string;
17
+ origin?: string | undefined;
18
+ expectRoutes?: number | undefined;
19
+ timeoutMs?: number | undefined;
20
+ expectMetrics?: boolean | undefined;
21
+ failOn?: FailOn | undefined;
22
+ compliance?: ComplianceOptions | undefined;
23
+ complianceWarn?: boolean | undefined;
24
+ log?: LogFn | undefined;
25
+ }
26
+ export interface VerifyReport {
27
+ target: string;
28
+ version: {
29
+ local: string;
30
+ observed: string | null;
31
+ };
32
+ routes: {
33
+ local: number;
34
+ observed: number | null;
35
+ expected: number | null;
36
+ };
37
+ requests: number;
38
+ checks: number;
39
+ findings: VerifyFinding[];
40
+ counts: Record<Severity, number>;
41
+ notes: string[];
42
+ failOn: FailOn;
43
+ pass: boolean;
44
+ compliance: ComplianceReport | null;
45
+ }
46
+ export declare const failLevels: readonly FailOn[];
47
+ export declare function verifyDeployment(project: string, { target, origin, expectRoutes, timeoutMs, expectMetrics, failOn, compliance, complianceWarn, log, permissions }: VerifyOptions): Promise<VerifyReport>;
package/dist/types.js CHANGED
@@ -1,3 +1,7 @@
1
+
2
+
3
+
4
+
1
5
  // Types shared across modules: the validated YAML document, the compiled
2
6
  // route the router produces from it, and the host-side policy contract.
3
7
  // This file is type-only (nothing here exists at run time) and append-only:
@@ -28,7 +32,8 @@
28
32
 
29
33
 
30
34
 
31
-
35
+ /** A route as YAML may spell it before normalization: `function` and middleware entries may be short-form module paths. */
36
+
32
37
 
33
38
 
34
39
 
@@ -39,7 +44,7 @@
39
44
 
40
45
  /** One `policies` block or profile layer: every policy optional, `false` disables it. */
41
46
  /** One layer of policy configuration (profile, project or route); a layer may declare part of a policy, the merge supplies the rest. */
42
-
47
+
43
48
 
44
49
  /** The result of layering profiles and route keys: what compiles, per policy. */
45
50
 
@@ -52,18 +57,42 @@
52
57
 
53
58
 
54
59
  /** One route as declared in YAML (plus `generated`, which site.ts stamps on the routes it adds). */
60
+
61
+
62
+
63
+
64
+
65
+ /** Route-level `auth` short form. Keys other than `required` mirror the auth extension's policy schema and expand to `policies.extensions.auth`. */
66
+
55
67
 
68
+
69
+
70
+
71
+
72
+
56
73
 
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
57
86
 
58
87
 
59
88
 
60
89
 
61
-
90
+
62
91
 
63
92
 
64
93
 
65
94
 
66
-
95
+
67
96
 
68
97
 
69
98
  /** What config.ts returns: the entry document, the merged route table and the files it came from. */
@@ -101,6 +130,9 @@
101
130
 
102
131
 
103
132
 
133
+
134
+
135
+
104
136
 
105
137
 
106
138
 
@@ -173,5 +205,5 @@
173
205
 
174
206
 
175
207
 
176
-
208
+
177
209
 
@@ -0,0 +1,142 @@
1
+ import {realpath,readdir,mkdtemp,rm} from 'node:fs/promises';
2
+ import {join,posix} from 'node:path';
3
+ import {tmpdir} from 'node:os';
4
+
5
+ import {init,parse} from 'es-module-lexer';
6
+ import {stringify} from 'yaml';
7
+ import {loadDocument} from './config.js';
8
+ import {collectFunctionSources,routeFunctions} from './function-sources.js';
9
+ import {authoringPath,authoringFile,readAuthoringFile,publishAuthoringProject} from './authoring-files.js';
10
+ import {assert} from './errors.js';
11
+
12
+
13
+ const emitted=(path )=>path.replace(/\.ts$/,'.js');
14
+ // Sandboxed-guest budget (docs/FUNCTION-SECURITY.md): what one QuickJS snapshot
15
+ // may cost. Trusted (non-`sandbox: true`) routes run through Node's own module
16
+ // resolution at serve time, so neither this budget nor the relative/static
17
+ // import-only rule below applies to them; only their file-read size is capped,
18
+ // generously, to bound authoring-time memory use.
19
+ const SANDBOX_MODULE_LIMIT=128,SANDBOX_MODULE_BYTE_LIMIT=1048576,SANDBOX_TOTAL_BYTE_LIMIT=4194304,TRUSTED_MODULE_BYTE_LIMIT=16*1048576;
20
+
21
+ /** Transpile a project snapshot ahead of runtime. No project compiler settings,
22
+ * plugins, package resolution, subprocesses or guest code execution are used. */
23
+ export async function buildTypeScriptProject(project ,output ,{dryRun=false} ={}) {
24
+ const root=await realpath(project),loaded=await loadDocument(root),files=new Map (),modules=new Map ();
25
+ const {default:ts}=await import('typescript');
26
+ // Per-path trust, so a module visited once (recursively, from its importer)
27
+ // is never reprocessed under a different trust level.
28
+ const moduleTrust=new Map ();
29
+ let sandboxedSourceBytes=0,sandboxedModuleCount=0,assetBytes=0;
30
+ await init;
31
+ async function collect(path ,sandboxed ) {
32
+ authoringPath(path);assert(/\.(?:ts|js|mjs)$/.test(path) && !path.endsWith('.d.ts'),'Guest source must be .ts, .js or .mjs');
33
+ const seen=moduleTrust.get(path);
34
+ if(seen!==undefined){assert(seen===sandboxed,'Module is imported by both a sandboxed and a trusted route');return;}
35
+ moduleTrust.set(path,sandboxed);
36
+ let bytes ;
37
+ if(sandboxed){
38
+ assert(sandboxedModuleCount<SANDBOX_MODULE_LIMIT,'Function module limit exceeded');sandboxedModuleCount++;
39
+ bytes=await readAuthoringFile(root,path,SANDBOX_MODULE_BYTE_LIMIT);sandboxedSourceBytes+=bytes.length;
40
+ assert(sandboxedSourceBytes<=SANDBOX_TOTAL_BYTE_LIMIT,'Function source limit exceeded');
41
+ } else {
42
+ bytes=await readAuthoringFile(root,path,TRUSTED_MODULE_BYTE_LIMIT);
43
+ }
44
+ const source=new TextDecoder('utf-8',{fatal:true}).decode(bytes);
45
+ modules.set(path,emitted(path));
46
+ let code=source;
47
+ if(path.endsWith('.ts')){
48
+ const ast=ts.createSourceFile(path,source,ts.ScriptTarget.ES2022,true,ts.ScriptKind.TS);
49
+ // Bare imports are forbidden even if they would be erased as type-only —
50
+ // but only for the sandboxed guest surface; a trusted route resolves
51
+ // them through Node like any other module once served.
52
+ const inspect=(node ) =>{
53
+ assert(!ts.isImportEqualsDeclaration(node) && !(ts.isExportAssignment(node) && node.isExportEquals),'CommonJS TypeScript module syntax is unsupported');
54
+ if(sandboxed && (ts.isImportDeclaration(node)||ts.isExportDeclaration(node)) && node.moduleSpecifier){
55
+ assert(ts.isStringLiteral(node.moduleSpecifier),'Only literal relative imports are supported');
56
+ const name=node.moduleSpecifier.text;
57
+ assert((name.startsWith('./')||name.startsWith('../')) && /\.(?:ts|js|mjs)$/.test(name),'Only relative guest module imports are supported');
58
+ }
59
+ ts.forEachChild(node,inspect);
60
+ };
61
+ inspect(ast);
62
+ const result=ts.transpileModule(source,{fileName:path,reportDiagnostics:true,compilerOptions:{
63
+ target:ts.ScriptTarget.ES2022,module:ts.ModuleKind.ESNext,isolatedModules:true,verbatimModuleSyntax:true,
64
+ sourceMap:false,inlineSourceMap:false,removeComments:false,
65
+ }});
66
+ assert(!result.diagnostics?.some(d=>d.category===ts.DiagnosticCategory.Error),'Invalid TypeScript guest source');
67
+ code=result.outputText;
68
+ }
69
+ const [imports]=parse(code);const edits =[];
70
+ for(const item of imports){
71
+ if(sandboxed){
72
+ assert(item.type==='static' && typeof item.specifier==='string' && !item.attributes && !item.phase,'Dynamic imports and import.meta are unsupported in sandbox functions');
73
+ assert(item.specifier.startsWith('./')||item.specifier.startsWith('../'),'Only relative guest module imports are supported');
74
+ assert(!/[\\\u0000-\u001f]/u.test(item.specifier),'Invalid guest import path');
75
+ const dependency=posix.normalize(posix.join(posix.dirname(path),item.specifier));
76
+ await collect(dependency,true);
77
+ edits.push({start:item.start-1,end:item.end+1,value:JSON.stringify(emitted(item.specifier))});
78
+ } else {
79
+ // Trusted routes get full Node module resolution at serve time: bare/
80
+ // npm specifiers, dynamic import() and import.meta pass through
81
+ // untouched. Only literal relative imports of project .ts/.js/.mjs
82
+ // modules are rewritten and recursively collected.
83
+ if(typeof item.specifier!=='string')continue;
84
+ if(!(item.specifier.startsWith('./')||item.specifier.startsWith('../')))continue;
85
+ assert(!/[\\\u0000-\u001f]/u.test(item.specifier),'Invalid guest import path');
86
+ if(!/\.(?:ts|js|mjs)$/.test(item.specifier))continue;
87
+ const dependency=posix.normalize(posix.join(posix.dirname(path),item.specifier));
88
+ await collect(dependency,false);
89
+ // A static specifier's start/end exclude its quotes; a dynamic
90
+ // import()'s already include them (es-module-lexer's own asymmetry).
91
+ const [start,end]=item.type==='dynamic'?[item.start,item.end]:[item.start-1,item.end+1];
92
+ edits.push({start,end,value:JSON.stringify(emitted(item.specifier))});
93
+ }
94
+ }
95
+ for(const edit of edits.sort((a,b)=>b.start-a.start))code=code.slice(0,edit.start)+edit.value+code.slice(edit.end);
96
+ const target=emitted(path);assert(!files.has(target),'Guest output module collision');files.set(target,code);
97
+ }
98
+ for(const route of Object.values(loaded.routes))for(const definition of routeFunctions(route)){
99
+ await collect(definition.source,!!route.sandbox);definition.source=emitted(definition.source);
100
+ }
101
+ async function asset(path ) {
102
+ if(files.has(path))return;assert(files.size<10000,'Authoring file limit exceeded');
103
+ const bytes=await readAuthoringFile(root,path,16*1048576);assetBytes+=bytes.length;
104
+ assert(assetBytes<=64*1048576,'Authoring asset limit exceeded');files.set(path,bytes);
105
+ }
106
+ async function directory(path ) {
107
+ const absolute=await authoringFile(root,path,true);
108
+ const entries=await readdir(absolute,{withFileTypes:true});
109
+ assert(entries.length>0,'Empty static directories need an asset before authoring build');
110
+ for(const entry of entries){
111
+ const child=path+'/'+entry.name;authoringPath(child);
112
+ assert(!entry.isSymbolicLink(),'Authoring through symlinks is forbidden');
113
+ if(entry.isDirectory())await directory(child);else await asset(child);
114
+ }
115
+ }
116
+ for(const route of Object.values(loaded.routes)){
117
+ for(const ref of [route.page,route.download])if(ref)await asset(ref.file);
118
+ if(route.static)await directory(route.static.directory);
119
+ }
120
+ for(const ref of [loaded.document.site?.favicon,loaded.document.site?.llms])if(ref)await asset(ref);
121
+ // Request fixtures describe behavior the emitted project must keep, so they travel with it.
122
+ try{await asset('tests/requests.json');}catch(error){if(!(error instanceof Error && 'code' in error && error.code==='ENOENT'))throw error;}
123
+ // Flatten includes using the loader's duplicate-checked route table. Original
124
+ // project metadata stays in the entry; executable sources are the only edits.
125
+ const document={...loaded.document,routes:loaded.routes};delete document.includes;
126
+ assert(!files.has('urlcode.yaml'),'Guest source conflicts with entry configuration');
127
+ files.set('urlcode.yaml',stringify(document));
128
+ // Reuse the exact runtime module parser/path/source-budget validation on the
129
+ // emitted snapshot before touching the requested destination. This is the
130
+ // sandboxed guest's own parser/budget contract, so it only applies to
131
+ // `sandbox: true` routes; a trusted route's emitted module runs through
132
+ // Node's own resolution and carries none of that budget.
133
+ const temporary=await mkdtemp(join(tmpdir(),'urlcode-ts-'));
134
+ try {
135
+ const validation=await publishAuthoringProject(join(temporary,'project'),files);
136
+ await collectFunctionSources(Object.values(loaded.routes).filter(route=>route.sandbox).map(route=>({
137
+ middleware:(route.middleware||[]).map(def=>({source:join(validation,def.source),export:def.export||'default'})),
138
+ ...(route.function?{function:{source:join(validation,route.function.source),export:route.function.export||'default'}}:{}),
139
+ })),validation);
140
+ }finally{await rm(temporary,{recursive:true,force:true});}
141
+ return {output:await publishAuthoringProject(output,files,dryRun),dryRun,modules:[...modules.values()].sort(),files:[...files.keys()].sort(),typeChecked:false};
142
+ }
package/dist/vercel.js CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  import { randomUUID } from 'node:crypto';
2
3
 
3
4
  import { activateNativeOnly, lazyRuntime, resolveOrigin } from './adapters.js';
@@ -6,7 +7,7 @@ import { activateNativeOnly, lazyRuntime, resolveOrigin } from './adapters.js';
6
7
  import { writeResponse, writeError } from './http-response.js';
7
8
  import { assert, HttpError } from './errors.js';
8
9
 
9
-
10
+
10
11
 
11
12
 
12
13
  const platformOrigins = ['VERCEL_PROJECT_PRODUCTION_URL','VERCEL_URL','VERCEL_BRANCH_URL'];
@@ -31,9 +32,9 @@ function readBody(req , limit ) {
31
32
  // instance and reused across warm invocations; a failed activation is not
32
33
  // cached, so a fixed deployment recovers without a code change.
33
34
  export function createVercelHandler({ project = process.cwd(), origin, environment = process.env,
34
- maxBodyBytes = 1048576, plugins } = {}) {
35
+ maxBodyBytes = 1048576, plugins, extensions } = {}) {
35
36
  assert(Number.isInteger(maxBodyBytes) && maxBodyBytes >= 1 && maxBodyBytes <= 16777216, 'Request limit must be 1–16777216 bytes');
36
- const ready = lazyRuntime(() => activateNativeOnly(project, environment, { target: 'vercel', plugins }));
37
+ const ready = lazyRuntime(() => activateNativeOnly(project, environment, { target: 'vercel', plugins, extensions, origin:resolveOrigin(origin,environment,platformOrigins) }));
37
38
 
38
39
  return async function handler(req,res) {
39
40
  const requestId = randomUUID();