@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,160 @@
1
+ # YAML guide: Functions, inputs and methods
2
+
3
+ Snippets are entries under `routes:` unless stated otherwise; the [guide index](../YAML-GUIDE.md) lists every page.
4
+
5
+ ## 1. A URL that runs code
6
+
7
+ A complete `urlcode.yaml`:
8
+
9
+ ```yaml
10
+ version: "1"
11
+ routes:
12
+ /hello/{name}:
13
+ function: functions/hello.mjs
14
+ env:
15
+ GREETING: {value: Hello}
16
+ ```
17
+
18
+ Create `functions/hello.mjs`:
19
+
20
+ ```js
21
+ export default function hello(request, {args, env}) {
22
+ return Response.json({message: `${env.GREETING}, ${args.name}!`});
23
+ }
24
+ ```
25
+
26
+ GET `/hello/Ada` returns JSON. HEAD invokes the function and suppresses the body.
27
+
28
+ `function: functions/hello.mjs` is the short form. The loader expands it to the
29
+ long form before anything else reads the route: every `{param}` in the path
30
+ becomes a required string input (`minLength: 1`, `maxLength: 128`) and a
31
+ matching `args` entry, so the route above is the same route as
32
+
33
+ ```yaml
34
+ /hello/{name}:
35
+ parameters:
36
+ - name: name
37
+ in: path
38
+ required: true
39
+ schema: {type: string, minLength: 1, maxLength: 128}
40
+ function:
41
+ source: functions/hello.mjs
42
+ args:
43
+ name: {from: path, name: name}
44
+ env:
45
+ GREETING: {value: Hello}
46
+ ```
47
+
48
+ A path parameter the route declares itself keeps its own schema; only the
49
+ undeclared ones get the default. Use the long form when you need a named
50
+ `export`, query, header, `env` or `secret` arguments, or a tighter path schema.
51
+ `routes`, `audit` and `explain` show the expansion. Middleware has the same
52
+ short form: `middleware: [middleware/headers.mjs]` means
53
+ `middleware: [{source: middleware/headers.mjs}]`. The path must be
54
+ project-relative, end in `.mjs` or `.js` and contain no `..` segment.
55
+ Methods default to GET and HEAD. Function paths resolve from the project root,
56
+ not the YAML file's directory. `.js` and `.mjs` ES modules work; TypeScript, Node
57
+ APIs, npm imports, network access and filesystem access do not.
58
+
59
+ ## 4. Input types and constraints
60
+
61
+ Use this list under a route's `parameters` when those inputs are needed:
62
+
63
+ ```yaml
64
+ parameters:
65
+ - name: search
66
+ in: query
67
+ required: true
68
+ schema: {type: string, minLength: 1, maxLength: 200}
69
+ - name: page
70
+ in: query
71
+ schema: {type: integer, minimum: 1, default: 1}
72
+ - name: weight
73
+ in: query
74
+ schema: {type: number, minimum: 0, maximum: 1}
75
+ - name: preview
76
+ in: query
77
+ schema: {type: boolean, default: false}
78
+ - name: category
79
+ in: query
80
+ schema: {type: string, enum: [docs, news], default: docs}
81
+ - name: ids
82
+ in: query
83
+ schema: {type: array, items: {type: integer}, maxItems: 10}
84
+ ```
85
+
86
+ Path inputs must be required strings with no default. Query/header scalar types
87
+ are string, integer, number and boolean; arrays are query-only. Booleans are
88
+ exactly `true`/`false`; numbers do not accept exponent notation or whitespace.
89
+ Defaults apply to absence, not empty strings. Duplicate scalar values fail.
90
+ Required, missing and invalid inputs return 400. This is a documented subset,
91
+ not full OpenAPI/JSON Schema: no `pattern`, `format`, nested input objects,
92
+ `oneOf`, `style` or `explode` in parameter schemas.
93
+
94
+ ## 5. Methods and body validation
95
+
96
+ ```yaml
97
+ /echo:
98
+ methods: [POST]
99
+ request:
100
+ body:
101
+ required: true
102
+ maxBytes: 4096
103
+ contentTypes: [application/json]
104
+ format: json
105
+ function:
106
+ source: functions/echo.mjs
107
+ ```
108
+
109
+ ```js
110
+ export default async function echo(request) {
111
+ return Response.json(await request.json());
112
+ }
113
+ ```
114
+
115
+ This validates JSON syntax/media type/UTF-8 and body size, not an application
116
+ object schema. Validate business fields in code. Empty required body: 400;
117
+ oversized body: 413; wrong media type: 415. For text, use `contentTypes:
118
+ [text/plain]`, `format: text`, and `request.text()`; see the runnable `/text`
119
+ recipe. `maxBytes: 0` can reject nonempty bodies. Bodies are buffered, not streamed.
120
+
121
+ Allowed methods: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS. An explicit list
122
+ replaces the defaults: `[GET]` does not add HEAD. Wrong method returns 405 with
123
+ Allow. Asset handlers accept only GET/HEAD. Body bytes are not automatically
124
+ mapped to function arguments.
125
+
126
+ ## 6. All function argument sources
127
+
128
+ Within `function.args`, supported values are:
129
+
130
+ ```yaml
131
+ args:
132
+ label: campaign
133
+ attempts: 3
134
+ preview: false
135
+ code: {from: path, name: code}
136
+ page: {from: query, name: page}
137
+ channel: {from: header, name: x-channel}
138
+ greeting: {env: GREETING}
139
+ token: {secret: TOKEN}
140
+ ```
141
+
142
+ This is a field-shape illustration: declare the referenced path/query/header
143
+ inputs and route binding aliases before using it. Null, array and arbitrary
144
+ object arguments are not supported. Read `context.args` or directly access
145
+ `context.inputs.path/query/header`, `context.env` and `context.secrets`.
146
+ `function.export` selects a named export; omit it for `default`.
147
+
148
+ For a dynamic redirect, use validated choices instead of accepting any URL:
149
+
150
+ ```js
151
+ export default function choice(request, {args}) {
152
+ const destinations = {docs: 'https://example.com/docs', home: 'https://example.com/'};
153
+ return Response.redirect(destinations[args.destination], 302);
154
+ }
155
+ ```
156
+
157
+ The runnable `/choice` recipe declares an enum query input and binds it to args.
158
+ Functions can return `Response.json(...)`, `new Response('text', {status, headers})`,
159
+ or `Response.redirect(...)`. HTML is a string response with Content-Type text/html;
160
+ escape untrusted values yourself. See the exact [guest API](../SPECIFICATION.md#functions).
@@ -0,0 +1,29 @@
1
+ # YAML guide: Middleware
2
+
3
+ Snippets are entries under `routes:` unless stated otherwise; the [guide index](../YAML-GUIDE.md) lists every page.
4
+
5
+ ## 7. Middleware before and after a handler
6
+
7
+ ```yaml
8
+ /go-with-header:
9
+ middleware:
10
+ - source: middleware/headers.mjs
11
+ export: decorate
12
+ redirect: {url: 'https://example.com/'}
13
+ ```
14
+
15
+ ```js
16
+ export async function decorate(request, context, next) {
17
+ context.state.example = 'cookbook';
18
+ const response = await next();
19
+ response.headers.set('x-middleware', context.state.example);
20
+ return response;
21
+ }
22
+ ```
23
+
24
+ Entries run in declared order before the handler and reverse order afterward.
25
+ Return a Response early to skip downstream code; call `next()` at most once.
26
+ Up to 16 middleware entries share one sandbox/deadline and approved route
27
+ bindings. Native bodies are opaque; preserving them requires retaining original
28
+ status/headers. Return a new Response to replace native content or destination.
29
+ YAML response headers apply last. See [middleware](../MIDDLEWARE.md) for details.
@@ -0,0 +1,74 @@
1
+ # YAML guide: Bindings, split files and tests
2
+
3
+ Snippets are entries under `routes:` unless stated otherwise; the [guide index](../YAML-GUIDE.md) lists every page.
4
+
5
+ ## 12. Environment and secret references
6
+
7
+ Route-level shape (references only, never secret values):
8
+
9
+ ```yaml
10
+ env:
11
+ GREETING: {value: Hello}
12
+ REGION: {env: APP_REGION}
13
+ secrets:
14
+ TOKEN: {secret: APP_TOKEN}
15
+ ```
16
+
17
+ Literal non-secret env needs no grant. External env and secrets require an
18
+ operator-owned policy outside the checkout, granting exact names to the route
19
+ and pinning the reviewed config/code digest. `urlcode permissions --project
20
+ ./my-links` prints a proposed policy; review it and store it outside the app.
21
+ Then pass `--policy /operator/path/policy.json` to validate/dev/test/serve.
22
+ This inspection does not authorize the project or execute its code.
23
+
24
+ Use ignored `.env.local` for local values; process environment wins. Production
25
+ `serve` reads process environment, never `.env.local`. Let your supervisor resolve
26
+ provider secrets and inject them; direct provider secret-store adapters do not
27
+ exist yet. Every config/code change invalidates the grant; rotate values by
28
+ restarting/redeploying. Never return a secret in an example response. Middleware
29
+ and functions on an approved route can read its bindings. See [policy setup](../FUNCTION-SECURITY.md).
30
+
31
+ ## 13. Split files and folders
32
+
33
+ Complete entry point:
34
+
35
+ ```yaml
36
+ version: "1"
37
+ includes:
38
+ - routes/code.yaml
39
+ - routes/marketing/links.yaml
40
+ routes: {}
41
+ ```
42
+
43
+ Each included file contains `version: "1"` and `routes`. No nested includes,
44
+ globs, anchors, merge keys or remote includes. References always use project-root
45
+ paths. Duplicate routes fail; include order is not priority. See [organization](../ORGANIZATION.md)
46
+ and [matching/regex/wildcard rules](../ROUTING.md).
47
+
48
+ ## 14. Assert inputs and outputs
49
+
50
+ Save a JSON array as `tests/requests.json`:
51
+
52
+ ```json
53
+ [
54
+ {"path":"/hello/Ada","status":200,"expectBody":"{\"message\":\"Hello, Ada!\"}"},
55
+ {"path":"/hello/Ada","method":"HEAD","status":200,"expectBody":""},
56
+ {"path":"/go","status":302,"expectHeaders":{"location":"https://example.com/"}},
57
+ {"path":"/go","method":"POST","status":405,"expectHeaders":{"allow":"GET, HEAD"}}
58
+ ]
59
+ ```
60
+
61
+ This fixture targets recipes 1 and 2 together; the runnable cookbook has its own
62
+ matching expectations. Supported test fields: `path`, optional `method`, request
63
+ `headers` and string `body`, required `status`, optional exact string `expectBody`
64
+ and string-map `expectHeaders`. Tests do not follow external redirects. There
65
+ are no route-local YAML test fields or JSON-path assertions yet.
66
+
67
+ Run validate, test, routes, and audit with an intentional expected count. Test
68
+ both positive and negative inputs, every allowed method, HEAD, middleware short
69
+ circuits and relevant asset conditions. Audit needs meaningful body/header
70
+ assertions; a status-only success is insufficient. Benchmarks and recovery drills
71
+ are separate from functional correctness. See [readiness](../READINESS.md).
72
+
73
+ See [organization and readability practices](../BEST-PRACTICES.md) for conventions
74
+ that keep larger projects easy to maintain.
@@ -0,0 +1,37 @@
1
+ # YAML guide: Policies and profiles
2
+
3
+ Snippets are entries under `routes:` unless stated otherwise; the [guide index](../YAML-GUIDE.md) lists every page.
4
+
5
+ ## 16. Hardened profile and per-route overrides
6
+
7
+ ```yaml
8
+ version: "1"
9
+ policies:
10
+ profile: hardened # security, agents, throttle, compression, cache
11
+ throttle: { quota: 60, window: 60 } # tighten one number; the rest stays
12
+ profiles:
13
+ public-api:
14
+ security: { headers: oshp-no-csp, set: { x-robots-tag: noindex } }
15
+ cache: { strategy: swr, maxAge: 30, staleWhileRevalidate: 300 }
16
+ routes:
17
+ /:
18
+ page: {source: pages/index.html}
19
+ /api/lookup/{id}:
20
+ parameters:
21
+ - {name: id, in: path, required: true, schema: {type: string, maxLength: 64}}
22
+ function: {source: functions/lookup.mjs}
23
+ policies:
24
+ profile: public-api # merges over the project layer
25
+ throttle: {quota: 10, window: 60, partition: client-route}
26
+ /healthz:
27
+ respond: {text: ok}
28
+ policies: {throttle: false, agents: false}
29
+ ```
30
+
31
+ Everything under `policies` is optional and off unless declared. The project
32
+ block sets defaults, a route block adjusts them, `false` removes one policy for
33
+ that route and an object merges shallowly over what is below it. `hardened`
34
+ is the only built-in profile; `profiles` defines your own. Serve with
35
+ `--trusted-proxies` when a proxy sits in front so `client` partitioning sees
36
+ the real peer. Not every target accepts every policy: see the per-target
37
+ table in [policies](../POLICIES.md) before deploying the same YAML to an adapter.
@@ -0,0 +1,64 @@
1
+ # YAML guide: Redirects
2
+
3
+ Snippets are entries under `routes:` unless stated otherwise; the [guide index](../YAML-GUIDE.md) lists every page.
4
+
5
+ ## 2. Ordinary and permanent redirects
6
+
7
+ The following snippets are **entries under `routes:`** unless stated otherwise:
8
+
9
+ ```yaml
10
+ /go:
11
+ redirect:
12
+ url: https://example.com/
13
+ /moved:
14
+ redirect:
15
+ url: https://example.com/new
16
+ status: 308
17
+ response:
18
+ headers:
19
+ Cache-Control: public, max-age=60
20
+ ```
21
+
22
+ 302 is the default. Allowed codes are 301, 302, 303, 307 and 308. Choose status
23
+ and cache policy deliberately: a cached permanent redirect can outlive a server
24
+ rollback. Requests' query strings are not forwarded by default. No function or
25
+ middleware means no sandbox execution for these routes.
26
+
27
+ ## 3. Parameterized redirects and explicit query forwarding
28
+
29
+ ```yaml
30
+ /product/{id}:
31
+ parameters:
32
+ - name: id
33
+ in: path
34
+ required: true
35
+ schema: {type: string, minLength: 1, maxLength: 64}
36
+ - name: page
37
+ in: query
38
+ schema: {type: integer, minimum: 1, maximum: 100, default: 1}
39
+ - name: tag
40
+ in: query
41
+ schema: {type: array, items: {type: string}, maxItems: 3}
42
+ - name: x-channel
43
+ in: header
44
+ schema: {type: string, enum: [web, email], default: web}
45
+ redirect:
46
+ url: https://example.com/products/{id}
47
+ query:
48
+ map:
49
+ page: {from: query, name: page}
50
+ label: {from: query, name: tag}
51
+ channel: {from: header, name: x-channel}
52
+ pass: [utm_source]
53
+ ```
54
+
55
+ `/product/abc?page=2&tag=red&tag=blue&utm_source=news&ignored=no` redirects to
56
+ `https://example.com/products/abc?page=2&label=red&label=blue&channel=web&utm_source=news`.
57
+ The unknown `ignored` key is dropped. Missing `page` becomes 1. Arrays produce
58
+ repeated destination keys. Invalid page values return 400 before redirecting.
59
+
60
+ Destination hosts/schemes are literal HTTP(S); path placeholders are safely
61
+ encoded. No arbitrary input-controlled host, credentials, secret interpolation,
62
+ or unrestricted `pass: true`. Mapping/passthrough keys must not collide with
63
+ existing destination keys. Header inputs are client-supplied values, not proof
64
+ of identity. See [input and redirect semantics](../SPECIFICATION.md).
@@ -0,0 +1,57 @@
1
+ # YAML guide: Declared responses, headers and cookies
2
+
3
+ Snippets are entries under `routes:` unless stated otherwise; the [guide index](../YAML-GUIDE.md) lists every page.
4
+
5
+ ## 8. Native responses, headers and cookies
6
+
7
+ ```yaml
8
+ /status:
9
+ respond:
10
+ json: {ok: true, service: my-links}
11
+ /notice:
12
+ respond:
13
+ status: 503
14
+ text: Temporarily unavailable
15
+ response:
16
+ headers:
17
+ Retry-After: "60"
18
+ /empty:
19
+ respond: {status: 204}
20
+ /cookie-demo:
21
+ respond: {text: Non-secret preferences only}
22
+ response:
23
+ headers:
24
+ Set-Cookie:
25
+ - "theme=light; Path=/; Secure; SameSite=Lax"
26
+ - "notice=seen; Path=/; HttpOnly; Secure; SameSite=Lax"
27
+ ```
28
+
29
+ `respond` defaults to 200. Use text or JSON, never both. Omit both for an empty
30
+ body; 204/205 must have no body. 206/304 belong to native asset handling.
31
+ Header values are literal strings; quote numeric-looking values. Only Set-Cookie
32
+ accepts arrays. Do not put live session tokens in YAML. Secure cookies require
33
+ HTTPS at the browser. Functions can create dynamic cookies, but no cookie
34
+ parsing/signing/authentication framework is built in.
35
+
36
+ Do not set Content-Length, Location, Allow, ETag, Content-Range or other
37
+ runtime-owned headers in YAML. Use the corresponding handler. The full reserved
38
+ list and precedence rules are in [HTTP](../HTTP.md).
39
+
40
+ ## 9. Explicit OPTIONS response (not automatic CORS)
41
+
42
+ ```yaml
43
+ /preflight:
44
+ methods: [OPTIONS]
45
+ respond: {status: 204}
46
+ response:
47
+ headers:
48
+ Access-Control-Allow-Origin: https://app.example.com
49
+ Access-Control-Allow-Methods: GET, HEAD
50
+ Access-Control-Allow-Headers: Content-Type
51
+ ```
52
+
53
+ This teaches declared headers only; it is not a working cross-origin GET API.
54
+ For a real API, OPTIONS and the actual methods must be handled on the same URL,
55
+ and actual responses also need the appropriate CORS headers. Because one path
56
+ has one handler, use a function with `[GET, HEAD, OPTIONS]` to branch on method.
57
+ Never reflect arbitrary Origin with credentials. Automatic CORS is unsupported.
@@ -0,0 +1,24 @@
1
+ # YAML guide: Site conventions
2
+
3
+ Snippets are entries under `routes:` unless stated otherwise; the [guide index](../YAML-GUIDE.md) lists every page.
4
+
5
+ ## 17. Site conventions
6
+
7
+ Generate the well-known files from a top-level `site` block instead of writing
8
+ them by hand. Each declared key becomes one native route counted by
9
+ `routes`/`audit`; a declared route at the same path wins.
10
+
11
+ ```yaml
12
+ site:
13
+ robots:
14
+ disallow: [ai-crawlers, /admin]
15
+ sitemap: true # Sitemap line needs --origin
16
+ favicon: public/favicon.svg # served at /favicon.ico
17
+ securityTxt:
18
+ contact: [mailto:security@example.com]
19
+ expires: "2099-01-01T00:00:00Z"
20
+ llms: public/llms.txt # served at /llms.txt
21
+ # sitemap: true # /sitemap.xml; refuses to start without --origin
22
+ ```
23
+
24
+ See [site conventions](../SITE.md) for every field, exclusions and target support.
@@ -0,0 +1,17 @@
1
+ id: assets
2
+ description: Pages, a static directory, a download and one function next to them; the asset snapshot and cache rules in one small runnable project.
3
+ tags: [assets, static, page, download, html, files, cache, snapshot, function]
4
+ complexity: intermediate
5
+ capabilities: [bindings, download, enabled, function, methods, page, parameters, redirect, static]
6
+ targets: {self-hosted: compatible, aws: refused, vercel: refused, cloudflare: refused, static: refused}
7
+ routes: 5
8
+ files: [urlcode.yaml, functions/hello.mjs, public/about.html, public/guide.txt, public/assets/example.txt, tests/requests.json, README.md]
9
+ tests:
10
+ fixtures: tests/requests.json
11
+ commands:
12
+ - node src/cli.ts validate --local --project examples/assets
13
+ - node src/cli.ts test --project examples/assets
14
+ - node src/cli.ts audit --project examples/assets --expect-routes 5
15
+ behavior:
16
+ - native page, static and download routes serve snapshotted files with explicit caching
17
+ - one sandboxed function shows the two kinds of route side by side
@@ -0,0 +1,20 @@
1
+ id: aws
2
+ description: Native-handler project served by an AWS Lambda Function URL through the aws adapter.
3
+ tags: [aws, lambda, serverless, deploy, adapter, native, page, static, redirect, respond, sam]
4
+ complexity: intermediate
5
+ capabilities: [enabled, methods, page, parameters, redirect, respond, static]
6
+ targets: {self-hosted: compatible, aws: compatible, vercel: compatible, cloudflare: refused, static: refused}
7
+ routes: 5
8
+ services:
9
+ - name: AWS Lambda
10
+ description: A Function URL deployed from template.yaml; local tests replay the adapter without an account.
11
+ files: [urlcode.yaml, handler.mjs, template.yaml, public/index.html, public/notes.txt, tests/requests.json, README.md]
12
+ tests:
13
+ fixtures: tests/requests.json
14
+ commands:
15
+ - node src/cli.ts validate --local --project examples/aws
16
+ - node src/cli.ts test --project examples/aws
17
+ - node src/cli.ts audit --project examples/aws --expect-routes 5
18
+ behavior:
19
+ - every route is native, so the project activates on Lambda without the sandbox
20
+ - handler.mjs wraps the project with createLambdaHandler; deployment itself is unverified evidence
@@ -0,0 +1,19 @@
1
+ id: cloudflare
2
+ description: Declarative redirect and respond project compiled into a Cloudflare Worker artifact.
3
+ tags: [cloudflare, workers, wrangler, compile, deploy, serverless, redirect, respond, native]
4
+ complexity: intermediate
5
+ capabilities: [enabled, methods, parameters, redirect, respond, response.headers]
6
+ targets: {self-hosted: compatible, aws: compatible, vercel: compatible, cloudflare: compatible, static: refused}
7
+ routes: 4
8
+ services:
9
+ - name: Cloudflare Workers
10
+ description: wrangler deploys the artifact that urlcode build-cloudflare emits; local tests replay the compiled routes.
11
+ files: [urlcode.yaml, wrangler.toml, tests/requests.json, README.md]
12
+ tests:
13
+ fixtures: tests/requests.json
14
+ commands:
15
+ - node src/cli.ts validate --local --project examples/cloudflare
16
+ - node src/cli.ts test --project examples/cloudflare
17
+ - node src/cli.ts audit --project examples/cloudflare --expect-routes 4
18
+ behavior:
19
+ - only capabilities the Worker lowering supports appear, so the build succeeds on every target
@@ -0,0 +1,11 @@
1
+ id: compliance
2
+ description: An operator rules module for urlcode audit --compliance-rules that adds, disables and re-grades compliance rules.
3
+ tags: [compliance, audit, rules, operator, policy, redirect-hosts, security-headers]
4
+ complexity: advanced
5
+ runnable: false
6
+ files: [rules.mjs, README.md]
7
+ tests:
8
+ commands:
9
+ - node src/cli.ts audit --project examples/cookbook --compliance baseline --compliance-rules "$PWD/examples/compliance/rules.mjs" --compliance-warn
10
+ behavior:
11
+ - three custom rules run alongside the built-in profile; one built-in notice is disabled and one severity raised
@@ -0,0 +1,12 @@
1
+ # Exact routing conditions
2
+
3
+ Run the executable fixture with the operator-owned public origin:
4
+
5
+ ```sh
6
+ urlcode test --project examples/conditions --origin https://conditions.example.test
7
+ ```
8
+
9
+ The 11 requests cover disjoint campaign cases, fallback, header/cookie guards,
10
+ duplicate rejection, HEAD and trusted-origin matching. All data is synthetic.
11
+ Conditions compare request inputs; they do not authorize users or grant guest
12
+ capabilities. See [the condition contract](../../docs/CONDITIONS.md).
@@ -0,0 +1,19 @@
1
+ id: conditions
2
+ description: Exact query, header, cookie, host and method conditions choosing between disjoint redirect and respond branches.
3
+ tags: [conditions, conditional, match, query, header, cookie, host, method, redirect, respond, disjoint]
4
+ complexity: intermediate
5
+ capabilities: [conditional, conditions, enabled, methods, redirect, respond]
6
+ targets: {self-hosted: compatible, aws: compatible, vercel: compatible, cloudflare: refused, static: refused}
7
+ routes: 4
8
+ grants:
9
+ - kind: origin
10
+ description: The public origin passed as --origin, because host conditions compare against it.
11
+ files: [urlcode.yaml, tests/requests.json, README.md]
12
+ tests:
13
+ fixtures: tests/requests.json
14
+ commands:
15
+ - node src/cli.ts validate --local --project examples/conditions --origin https://conditions.example.test
16
+ - node src/cli.ts test --project examples/conditions --origin https://conditions.example.test
17
+ - node src/cli.ts audit --project examples/conditions --expect-routes 4 --origin https://conditions.example.test
18
+ behavior:
19
+ - cases are provably disjoint and every branch stays no-store; no branch is authentication
@@ -0,0 +1,13 @@
1
+ [
2
+ {"path":"/campaign?source=newsletter","status":307,"expectHeaders":{"location":"https://example.test/newsletter","cache-control":"no-store"}},
3
+ {"path":"/campaign?source=partner","status":200,"expectBody":"{\"campaign\":\"partner\"}"},
4
+ {"path":"/campaign","status":200,"expectBody":"Choose a campaign"},
5
+ {"path":"/campaign?source=newsletter&source=partner","status":400},
6
+ {"path":"/campaign","method":"HEAD","status":200,"expectBody":""},
7
+ {"path":"/beta","status":404},
8
+ {"path":"/beta","headers":{"x-beta-user":"true","cookie":"bucket=beta"},"status":200,"expectBody":"beta preview"},
9
+ {"path":"/beta","headers":{"x-beta-user":"true","cookie":"bucket=beta; bucket=other"},"status":400},
10
+ {"path":"/host","headers":{"host":"attacker.example.test","x-forwarded-host":"attacker.example.test"},"status":200,"expectBody":"trusted origin"},
11
+ {"path":"/limited","status":200,"expectBody":"get only"},
12
+ {"path":"/limited","method":"HEAD","status":404,"expectBody":""}
13
+ ]
@@ -0,0 +1,24 @@
1
+ version: "1"
2
+ routes:
3
+ /campaign:
4
+ conditional:
5
+ cases:
6
+ - match: {query: {source: newsletter}}
7
+ redirect: {url: https://example.test/newsletter, status: 307}
8
+ - match: {query: {source: partner}}
9
+ respond: {json: {campaign: partner}}
10
+ fallback:
11
+ respond: {text: Choose a campaign}
12
+ /beta:
13
+ match:
14
+ headers: {x-beta-user: "true"}
15
+ cookies: {bucket: beta}
16
+ respond: {text: beta preview}
17
+ /host:
18
+ match: {host: conditions.example.test}
19
+ respond: {text: trusted origin}
20
+ /limited:
21
+ conditional:
22
+ cases:
23
+ - match: {method: GET}
24
+ respond: {text: get only}
@@ -1,9 +1,11 @@
1
1
  # Runnable YAML cookbook
2
2
 
3
- 25 routes covering all six handlers, middleware, inputs, body validation, response
3
+ 40 routes covering all six handlers, middleware, inputs, body validation, response
4
4
  headers, cookies, methods, expiry, assets, included files and host policies
5
5
  (security headers, agent denial, caching and a request budget) plus generated site
6
- conventions (robots.txt, favicon, security.txt, llms.txt). No credentials or
6
+ conventions (robots.txt, favicon, security.txt, llms.txt). Fifteen of the routes
7
+ exercise the fourteen reusable modules in `middleware/`, described in
8
+ [middleware examples](../../docs/MIDDLEWARE-EXAMPLES.md). No credentials or
7
9
  external services are required. Redirects target example.com; tests never follow them.
8
10
 
9
11
  From the runtime checkout:
@@ -11,11 +13,13 @@ From the runtime checkout:
11
13
  ```sh
12
14
  node src/cli.ts validate --project examples/cookbook
13
15
  node src/cli.ts test --project examples/cookbook
14
- node src/cli.ts audit --project examples/cookbook --expect-routes 25
16
+ node src/cli.ts audit --project examples/cookbook --expect-routes 40
15
17
  node src/cli.ts dev --project examples/cookbook
16
18
  ```
17
19
 
18
20
  The [YAML guide](../../docs/YAML-GUIDE.md) explains the recipes and binding policy.
19
21
  `/notice` deliberately returns 503, `/paused` 404 and `/expired` 410. `/preflight`
20
- is an OPTIONS/header example, not a complete cross-origin API. The small default
22
+ is an OPTIONS/header example, not a complete cross-origin API; `/cors/data` is the
23
+ middleware version. `/maintenance` returns 503 without its bypass header and
24
+ `/inspect` echoes request details only with `X-Debug: 1`. The small default
21
25
  starter remains the recommended starting point for a new application.
@@ -0,0 +1,17 @@
1
+ id: cookbook
2
+ description: Forty runnable routes covering every handler, middleware, inputs, body validation, headers, assets, includes, host policies and site conventions; route-index.json tags each one.
3
+ tags: [cookbook, reference, routes, handlers, middleware, policies, site, includes, parameters, cookies, methods, expiry, assets, everything]
4
+ complexity: advanced
5
+ capabilities: [bindings, download, enabled, expires, function, methods, middleware, page, parameters, policies.agents, policies.cache, policies.security, policies.throttle, redirect, request.body, respond, response.headers, static]
6
+ targets: {self-hosted: compatible, aws: refused, vercel: refused, cloudflare: refused, static: refused}
7
+ routes: 40
8
+ files: [urlcode.yaml, routes/code.yaml, routes/redirects.yaml, routes/responses.yaml, routes/files.yaml, routes/policies.yaml, routes/middleware.yaml, route-index.json, tests/requests.json, README.md]
9
+ tests:
10
+ fixtures: tests/requests.json
11
+ commands:
12
+ - node src/cli.ts validate --local --project examples/cookbook
13
+ - node src/cli.ts test --project examples/cookbook
14
+ - node src/cli.ts audit --project examples/cookbook --expect-routes 40
15
+ behavior:
16
+ - urlcode examples search finds single routes here through the generated route-index.json
17
+ - fifteen routes exercise the fourteen middleware modules that the middleware recipe copies
@@ -0,0 +1,3 @@
1
+ export default function catalog() {
2
+ return Response.json({version: 3, items: ['alpha', 'beta']});
3
+ }
@@ -0,0 +1,4 @@
1
+ export default function fail(request, {args}) {
2
+ if (args.fail) throw new Error('simulated failure');
3
+ return Response.json({healthy: true});
4
+ }
@@ -0,0 +1,3 @@
1
+ export default function items() {
2
+ return Response.json([{id: 1, name: 'first'}, {id: 2, name: 'second'}]);
3
+ }