@jimhoyd/urlcode 0.3.0

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 (254) hide show
  1. package/CONTRIBUTING.md +80 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +20 -0
  4. package/README.md +342 -0
  5. package/ROADMAP.md +248 -0
  6. package/SECURITY.md +40 -0
  7. package/data/agents/LICENSES/ai-robots-txt.txt +24 -0
  8. package/data/agents/LICENSES/crawler-user-agents.txt +23 -0
  9. package/data/agents/ai-crawlers.json +1240 -0
  10. package/data/agents/crawlers.json +10515 -0
  11. package/data/agents/index.d.ts +3 -0
  12. package/data/agents/index.js +2453 -0
  13. package/data/agents/monitoring.json +1520 -0
  14. package/data/agents/seo.json +3865 -0
  15. package/dist/BUILD-MANIFEST.json +60 -0
  16. package/dist/adapters.js +59 -0
  17. package/dist/agent-lists.js +59 -0
  18. package/dist/assets.js +129 -0
  19. package/dist/authoring.js +68 -0
  20. package/dist/aws.js +110 -0
  21. package/dist/build-cloudflare.js +164 -0
  22. package/dist/cli.js +185 -0
  23. package/dist/client-address.js +85 -0
  24. package/dist/cloudflare.js +165 -0
  25. package/dist/compliance-rules/baseline.js +98 -0
  26. package/dist/compliance-rules/privacy.js +38 -0
  27. package/dist/compliance-rules/shared.js +62 -0
  28. package/dist/compliance-rules/strict.js +61 -0
  29. package/dist/compliance.js +219 -0
  30. package/dist/config-worker.js +9 -0
  31. package/dist/config.js +172 -0
  32. package/dist/errors.js +8 -0
  33. package/dist/function-sources.js +54 -0
  34. package/dist/function-worker.js +105 -0
  35. package/dist/functions.js +174 -0
  36. package/dist/guest-api.js +114 -0
  37. package/dist/header-validation.js +18 -0
  38. package/dist/http-policy.js +74 -0
  39. package/dist/http-response.js +84 -0
  40. package/dist/index.js +11 -0
  41. package/dist/link-api.js +136 -0
  42. package/dist/link-cli.js +141 -0
  43. package/dist/link-events.js +76 -0
  44. package/dist/link-records.js +31 -0
  45. package/dist/link-store-worker.js +150 -0
  46. package/dist/link-store.js +250 -0
  47. package/dist/logging.js +22 -0
  48. package/dist/management-policy.js +41 -0
  49. package/dist/match.js +124 -0
  50. package/dist/observability.js +242 -0
  51. package/dist/plugins.js +74 -0
  52. package/dist/policies/agents.js +248 -0
  53. package/dist/policies/cache.js +297 -0
  54. package/dist/policies/compression.js +187 -0
  55. package/dist/policies/security.js +0 -0
  56. package/dist/policies/throttle.js +131 -0
  57. package/dist/policies.js +142 -0
  58. package/dist/policy.js +55 -0
  59. package/dist/prerender.js +165 -0
  60. package/dist/project-tests.js +23 -0
  61. package/dist/readiness.js +216 -0
  62. package/dist/router.js +166 -0
  63. package/dist/runtime.js +244 -0
  64. package/dist/scaffold.js +0 -0
  65. package/dist/scripts/operational-drills.js +75 -0
  66. package/dist/server.js +270 -0
  67. package/dist/site.js +0 -0
  68. package/dist/sqlite-version.js +6 -0
  69. package/dist/types/adapters.d.ts +12 -0
  70. package/dist/types/agent-lists.d.ts +5 -0
  71. package/dist/types/assets.d.ts +13 -0
  72. package/dist/types/authoring.d.ts +2 -0
  73. package/dist/types/aws.d.ts +35 -0
  74. package/dist/types/build-cloudflare.d.ts +14 -0
  75. package/dist/types/cli.d.ts +2 -0
  76. package/dist/types/client-address.d.ts +9 -0
  77. package/dist/types/cloudflare.d.ts +75 -0
  78. package/dist/types/compliance-rules/baseline.d.ts +12 -0
  79. package/dist/types/compliance-rules/privacy.d.ts +6 -0
  80. package/dist/types/compliance-rules/shared.d.ts +24 -0
  81. package/dist/types/compliance-rules/strict.d.ts +9 -0
  82. package/dist/types/compliance.d.ts +119 -0
  83. package/dist/types/config-worker.d.ts +1 -0
  84. package/dist/types/config.d.ts +21 -0
  85. package/dist/types/errors.d.ts +7 -0
  86. package/dist/types/function-sources.d.ts +21 -0
  87. package/dist/types/function-worker.d.ts +1 -0
  88. package/dist/types/functions.d.ts +92 -0
  89. package/dist/types/guest-api.d.ts +16 -0
  90. package/dist/types/header-validation.d.ts +2 -0
  91. package/dist/types/http-policy.d.ts +39 -0
  92. package/dist/types/http-response.d.ts +43 -0
  93. package/dist/types/index.d.ts +11 -0
  94. package/dist/types/link-api.d.ts +30 -0
  95. package/dist/types/link-cli.d.ts +36 -0
  96. package/dist/types/link-events.d.ts +27 -0
  97. package/dist/types/link-records.d.ts +11 -0
  98. package/dist/types/link-store-worker.d.ts +1 -0
  99. package/dist/types/link-store.d.ts +130 -0
  100. package/dist/types/logging.d.ts +9 -0
  101. package/dist/types/management-policy.d.ts +9 -0
  102. package/dist/types/match.d.ts +81 -0
  103. package/dist/types/observability.d.ts +104 -0
  104. package/dist/types/plugins.d.ts +27 -0
  105. package/dist/types/policies/agents.d.ts +86 -0
  106. package/dist/types/policies/cache.d.ts +82 -0
  107. package/dist/types/policies/compression.d.ts +41 -0
  108. package/dist/types/policies/security.d.ts +37 -0
  109. package/dist/types/policies/throttle.d.ts +55 -0
  110. package/dist/types/policies.d.ts +36 -0
  111. package/dist/types/policy.d.ts +21 -0
  112. package/dist/types/prerender.d.ts +31 -0
  113. package/dist/types/project-tests.d.ts +13 -0
  114. package/dist/types/readiness.d.ts +121 -0
  115. package/dist/types/router.d.ts +11 -0
  116. package/dist/types/runtime.d.ts +91 -0
  117. package/dist/types/scaffold.d.ts +17 -0
  118. package/dist/types/server.d.ts +37 -0
  119. package/dist/types/site.d.ts +12 -0
  120. package/dist/types/sqlite-version.d.ts +1 -0
  121. package/dist/types/types.d.ts +332 -0
  122. package/dist/types/vercel.d.ts +12 -0
  123. package/dist/types.js +177 -0
  124. package/dist/vercel.js +65 -0
  125. package/docs/AI-AUTHORING.md +113 -0
  126. package/docs/ASSETS.md +106 -0
  127. package/docs/AWS.md +77 -0
  128. package/docs/BEST-PRACTICES.md +267 -0
  129. package/docs/CAPACITY.md +208 -0
  130. package/docs/CLOUDFLARE.md +110 -0
  131. package/docs/COMPLIANCE.md +242 -0
  132. package/docs/DYNAMIC-LINKS.md +561 -0
  133. package/docs/FUNCTION-SECURITY.md +113 -0
  134. package/docs/HTTP.md +129 -0
  135. package/docs/INSTALL.md +90 -0
  136. package/docs/LOAD-TESTING.md +91 -0
  137. package/docs/LOCAL-DEVELOPMENT.md +99 -0
  138. package/docs/MANAGEMENT-SECURITY.md +82 -0
  139. package/docs/MIDDLEWARE.md +83 -0
  140. package/docs/MONITORING.md +132 -0
  141. package/docs/OBSERVABILITY.md +229 -0
  142. package/docs/OPERATIONAL-PROOF.md +42 -0
  143. package/docs/OPERATIONS.md +207 -0
  144. package/docs/ORGANIZATION.md +135 -0
  145. package/docs/PERFORMANCE.md +72 -0
  146. package/docs/PLUGINS.md +234 -0
  147. package/docs/POLICIES.md +489 -0
  148. package/docs/PRERENDER.md +193 -0
  149. package/docs/PROJECT-DIRECTION.md +51 -0
  150. package/docs/READINESS.md +125 -0
  151. package/docs/README.md +58 -0
  152. package/docs/RELEASE-READINESS.md +95 -0
  153. package/docs/RELEASE-SECURITY.md +72 -0
  154. package/docs/RESILIENCE.md +160 -0
  155. package/docs/ROUTING.md +95 -0
  156. package/docs/SANDBOX-REVIEW.md +59 -0
  157. package/docs/SCAFFOLDING.md +72 -0
  158. package/docs/SECURITY-AUDIT.md +124 -0
  159. package/docs/SITE.md +150 -0
  160. package/docs/SPECIFICATION.md +232 -0
  161. package/docs/SPIKE-EXTENSIONS.md +475 -0
  162. package/docs/STANDARDS.md +303 -0
  163. package/docs/STARTERS.md +51 -0
  164. package/docs/TUNNELS.md +75 -0
  165. package/docs/TYPESCRIPT.md +102 -0
  166. package/docs/VERCEL.md +107 -0
  167. package/docs/YAML-GUIDE.md +521 -0
  168. package/docs/YAML-REFERENCE.md +328 -0
  169. package/docs/policies/agents.md +182 -0
  170. package/docs/policies/cache.md +139 -0
  171. package/docs/policies/compression.md +168 -0
  172. package/docs/policies/security.md +160 -0
  173. package/docs/policies/throttle.md +103 -0
  174. package/examples/assets/.env.example +1 -0
  175. package/examples/assets/.gitattributes +2 -0
  176. package/examples/assets/Makefile +30 -0
  177. package/examples/assets/README.md +18 -0
  178. package/examples/assets/functions/hello.mjs +3 -0
  179. package/examples/assets/gitignore.template +7 -0
  180. package/examples/assets/public/about.html +2 -0
  181. package/examples/assets/public/assets/example.txt +1 -0
  182. package/examples/assets/public/guide.txt +1 -0
  183. package/examples/assets/tests/requests.json +44 -0
  184. package/examples/assets/urlcode.yaml +27 -0
  185. package/examples/aws/README.md +13 -0
  186. package/examples/aws/handler.mjs +5 -0
  187. package/examples/aws/public/index.html +3 -0
  188. package/examples/aws/public/notes.txt +1 -0
  189. package/examples/aws/template.yaml +33 -0
  190. package/examples/aws/tests/requests.json +9 -0
  191. package/examples/aws/urlcode.yaml +19 -0
  192. package/examples/cloudflare/README.md +18 -0
  193. package/examples/cloudflare/package.json +14 -0
  194. package/examples/cloudflare/tests/requests.json +9 -0
  195. package/examples/cloudflare/urlcode.yaml +24 -0
  196. package/examples/cloudflare/wrangler.toml +5 -0
  197. package/examples/compliance/README.md +20 -0
  198. package/examples/compliance/rules.mjs +65 -0
  199. package/examples/cookbook/README.md +21 -0
  200. package/examples/cookbook/functions/choice.mjs +4 -0
  201. package/examples/cookbook/functions/echo.mjs +3 -0
  202. package/examples/cookbook/functions/hello.mjs +3 -0
  203. package/examples/cookbook/functions/text.mjs +3 -0
  204. package/examples/cookbook/middleware/headers.mjs +6 -0
  205. package/examples/cookbook/public/about.html +1 -0
  206. package/examples/cookbook/public/assets/index.html +1 -0
  207. package/examples/cookbook/public/assets/site.css +1 -0
  208. package/examples/cookbook/public/favicon.svg +1 -0
  209. package/examples/cookbook/public/guide.txt +1 -0
  210. package/examples/cookbook/public/llms.txt +6 -0
  211. package/examples/cookbook/routes/code.yaml +55 -0
  212. package/examples/cookbook/routes/files.yaml +17 -0
  213. package/examples/cookbook/routes/policies.yaml +28 -0
  214. package/examples/cookbook/routes/redirects.yaml +38 -0
  215. package/examples/cookbook/routes/responses.yaml +26 -0
  216. package/examples/cookbook/tests/requests.json +254 -0
  217. package/examples/cookbook/urlcode.yaml +22 -0
  218. package/examples/live-links/README.md +11 -0
  219. package/examples/live-links/tests/requests.json +6 -0
  220. package/examples/live-links/urlcode.yaml +16 -0
  221. package/examples/monitoring/blackbox-jobs.yaml +37 -0
  222. package/examples/monitoring/prometheus-rules.yaml +88 -0
  223. package/examples/monitoring/prometheus-scrape.yaml +20 -0
  224. package/examples/monitoring/vector.toml +65 -0
  225. package/examples/prerender/README.md +48 -0
  226. package/examples/prerender/functions/page.mjs +6 -0
  227. package/examples/prerender/middleware/template.mjs +29 -0
  228. package/examples/prerender/prerender.d.mts +4 -0
  229. package/examples/prerender/prerender.mjs +63 -0
  230. package/examples/prerender/tests/requests.json +39 -0
  231. package/examples/prerender/urlcode.yaml +50 -0
  232. package/examples/tunnel/dev-with-ngrok.sh +61 -0
  233. package/examples/vercel/README.md +13 -0
  234. package/examples/vercel/api/index.js +5 -0
  235. package/examples/vercel/package.json +8 -0
  236. package/examples/vercel/public/index.html +3 -0
  237. package/examples/vercel/public/notes.txt +1 -0
  238. package/examples/vercel/tests/requests.json +48 -0
  239. package/examples/vercel/urlcode.yaml +19 -0
  240. package/examples/vercel/vercel.json +10 -0
  241. package/llms.txt +46 -0
  242. package/package.json +123 -0
  243. package/schemas/urlcode.schema.json +1230 -0
  244. package/starters/default/.gitattributes +1 -0
  245. package/starters/default/Makefile +30 -0
  246. package/starters/default/README.md +34 -0
  247. package/starters/default/functions/hello.mjs +3 -0
  248. package/starters/default/gitignore.template +8 -0
  249. package/starters/default/middleware/headers.mjs +6 -0
  250. package/starters/default/routes/functions.yaml +20 -0
  251. package/starters/default/routes/marketing/links.yaml +7 -0
  252. package/starters/default/starter.json +5 -0
  253. package/starters/default/tests/requests.json +56 -0
  254. package/starters/default/urlcode.yaml +8 -0
package/docs/SITE.md ADDED
@@ -0,0 +1,150 @@
1
+ # Site conventions
2
+
3
+ The optional top-level `site` block generates the small well-known files a
4
+ site is expected to serve. Every key is off unless declared, and each declared
5
+ key becomes one ordinary native route (`respond` or `page`) that is merged into
6
+ the route table before compilation. Everything downstream is unchanged: the
7
+ route appears in `urlcode routes` with `generated: "site.<key>"`, the audit
8
+ generates fixtures for it, host policies apply to it and every deployment
9
+ target compiles it like a route the project wrote by hand. `site` is accepted
10
+ only in the entry `urlcode.yaml`, not in included files.
11
+
12
+ ```yaml
13
+ version: "1"
14
+ site:
15
+ robots:
16
+ disallow: [ai-crawlers, /admin]
17
+ allow: [/admin/public]
18
+ sitemap: true
19
+ sitemap:
20
+ exclude: [/drafts/*]
21
+ changefreq: weekly
22
+ favicon: public/favicon.svg
23
+ securityTxt:
24
+ contact: [mailto:security@example.com]
25
+ expires: "2027-01-01T00:00:00Z"
26
+ policy: [https://example.com/security-policy]
27
+ preferredLanguages: [en, fr]
28
+ llms: llms.txt
29
+ routes:
30
+ /: {page: {file: public/index.html}}
31
+ ```
32
+
33
+ The [cookbook](../examples/cookbook/urlcode.yaml) declares `robots`, `favicon`,
34
+ `securityTxt` and `llms` with request fixtures; `sitemap` is exercised by the
35
+ unit tests because it needs a public origin (below).
36
+
37
+ ## Precedence with declared routes
38
+
39
+ A route the project declares at a generated path always wins. The generated
40
+ route is dropped and the activation log records
41
+ `{"event":"site","key":"robots","path":"/robots.txt","status":"shadowed"}`.
42
+ This is how a project keeps a hand-written `robots.txt` while still using
43
+ `site.favicon`, or migrates one file at a time.
44
+
45
+ ## The public origin
46
+
47
+ `robots.txt`'s `Sitemap:` line and every `<loc>` in `sitemap.xml` are absolute
48
+ URLs, so the runtime needs to know the origin the site is served from. It
49
+ never guesses from a request: a generated file is a fixed response compiled at
50
+ activation, and a `Host` header is client-controlled. The origin is the
51
+ operator's `--origin https://links.example` flag on `serve`, `dev`,
52
+ `validate`, `test`, `routes`, `audit`, `benchmark` and `build`, or the
53
+ `origin` option of `startServer`/`createRuntime`/`buildCloudflare`.
54
+
55
+ - `site.robots.sitemap: true` without an origin omits the `Sitemap:` line and
56
+ logs `{"event":"site","key":"robots","severity":"info",...}` at activation.
57
+ - `site.sitemap` without an origin **refuses activation**: a sitemap of relative
58
+ URLs is invalid under the protocol, so there is no useful degraded form.
59
+
60
+ ## Keys
61
+
62
+ ### `robots` → `/robots.txt` (RFC 9309)
63
+
64
+ | Field | Meaning |
65
+ | --- | --- |
66
+ | `disallow` | bundled agent list names (`ai-crawlers`, `crawlers`, `seo`, `monitoring`, the same lists as the [`agents` policy](policies/agents.md)) and/or paths starting with `/` |
67
+ | `allow` | the same shapes, emitted as `Allow:` |
68
+ | `sitemap` | `true` appends `Sitemap: <origin>/sitemap.xml` when the origin is known |
69
+ | `extra` | literal lines appended verbatim (comments, `Crawl-delay`, ...) |
70
+
71
+ List names become one group of `User-agent:` lines followed by `Disallow: /`
72
+ (or `Allow: /`). Paths go under `User-agent: *`; with no paths that group is
73
+ `Allow: /`. A list entry whose name is not usable as a product token (it has
74
+ spaces or pattern metacharacters) is skipped and counted in an info log line.
75
+ The result is served as `text/plain; charset=utf-8`.
76
+
77
+ ```
78
+ User-agent: GPTBot
79
+ User-agent: ClaudeBot
80
+ Disallow: /
81
+
82
+ User-agent: *
83
+ Disallow: /admin
84
+ Allow: /admin/public
85
+
86
+ Sitemap: https://links.example/sitemap.xml
87
+ ```
88
+
89
+ ### `sitemap` → `/sitemap.xml` (sitemaps.org protocol 0.9)
90
+
91
+ `true`, or `{ exclude, changefreq, priority }`. The sitemap lists every active
92
+ literal GET route that serves HTML: a `page` whose file is `.html`/`.htm` or
93
+ whose `contentType` is `text/html`; a `respond` route whose `Content-Type`
94
+ response header is `text/html`; and the `.html` files under a `static` mount
95
+ (the mount's `index` file is listed as its directory URL). Excluded:
96
+ parameterized routes, redirects, functions, downloads and stored links,
97
+ disabled or expired routes, `/robots.txt` and `/sitemap.xml`, any route whose
98
+ `response.headers` set `X-Robots-Tag` containing `noindex`, and anything an
99
+ `exclude` pattern matches (an exact path, or a prefix ending in `/*`).
100
+ `lastmod` is the file's modification date for asset routes and omitted
101
+ otherwise. URLs are sorted and XML-escaped; the response is
102
+ `application/xml; charset=utf-8`.
103
+
104
+ More than 50,000 URLs, or a document over the 1 MiB declared-response limit,
105
+ refuses activation with a message saying to generate the file at build time
106
+ and serve it as a static asset instead; the runtime does not split or index
107
+ sitemaps.
108
+
109
+ ### `favicon` → `/favicon.ico`
110
+
111
+ A project-relative `.ico`, `.svg` or `.png` file, served as a `page` route
112
+ with `image/x-icon`, `image/svg+xml` or `image/png` and
113
+ `cacheControl: public, max-age=3600` (the closest value in the
114
+ [allowed vocabulary](ASSETS.md)). The route has the usual asset semantics:
115
+ ETag, Last-Modified, conditional requests and ranges.
116
+
117
+ ### `securityTxt` → `/.well-known/security.txt` (RFC 9116)
118
+
119
+ | Field | Rule |
120
+ | --- | --- |
121
+ | `contact` (required) | one or more `mailto:`, `tel:` or `https:` URIs |
122
+ | `expires` (required) | UTC ISO timestamp; must be in the future at activation; more than a year away logs a warning (the RFC recommends less than a year) |
123
+ | `policy`, `acknowledgments`, `canonical` | `https:` URIs |
124
+ | `encryption` | `https:`, `dns:` or `openpgp4fpr:` URIs |
125
+ | `preferredLanguages` | language tags, emitted as one comma-separated line |
126
+
127
+ Fields are emitted in the RFC's order (Acknowledgments, Canonical, Contact,
128
+ Encryption, Expires, Policy, Preferred-Languages), one value per line. The
129
+ file is unsigned; add a signed copy as a declared route if you need one. The
130
+ `.well-known` segment is an ordinary route segment (only `.` and `..` are
131
+ refused).
132
+
133
+ ### `llms` → `/llms.txt`
134
+
135
+ A project-relative text file served as a `page` route with
136
+ `text/plain; charset=utf-8` and the default `no-cache`.
137
+
138
+ ## Per-target support
139
+
140
+ | Target | `robots`, `sitemap`, `securityTxt` (`respond`) | `favicon`, `llms` (`page`) |
141
+ | --- | --- | --- |
142
+ | self-hosted, Vercel, AWS | served | served |
143
+ | Cloudflare | compiled into the artifact (`build --origin` for absolute URLs) | refused at build time like any `page` route: the target has no asset binding; serve them from the platform's static assets |
144
+
145
+ ## Not in this release
146
+
147
+ No per-route `noindex` field (use `response.headers: {X-Robots-Tag: noindex}`,
148
+ which the sitemap honours), no sitemap index or split files, no `humans.txt`,
149
+ no signed `security.txt`, and no project `.json` agent lists in `robots`
150
+ (bundled names only).
@@ -0,0 +1,232 @@
1
+ # Implemented 0.1 contract
2
+
3
+ This document and [JSON Schema](../schemas/urlcode.schema.json) describe
4
+ 0.2.0. `version: "1"` is the stable project-format contract for the 0.1 release
5
+ line. Later planned features are rejected until implemented.
6
+
7
+ ## Files and validation
8
+
9
+ `urlcode.yaml` contains `version`, `routes`, and optional `includes` (an explicit
10
+ list of project-relative YAML files). Included files have the same version/routes
11
+ shape and cannot recursively include files. Duplicate paths across files fail.
12
+ See [organization examples](ORGANIZATION.md) for one-file, multi-file and mixed
13
+ layouts. All references resolve from the project root, not the including file.
14
+ File references resolve inside the project; escaping symlinks fail. No glob,
15
+ remote config or arbitrary infrastructure configuration.
16
+
17
+ YAML 1.2 JSON-compatible values only: string mapping keys, finite numbers,
18
+ booleans and null. No duplicate keys, aliases, anchors, tags, merge keys,
19
+ multiple documents, reserved prototype keys or nesting of 40+ levels. Unknown
20
+ schema fields fail. Files are limited to 32 MiB each, 256 includes and 100,000
21
+ routes total, with a 64 MiB aggregate YAML source cap. Loading runs in a worker
22
+ with a 256 MiB old-generation heap, a 10-second wall deadline and at most two
23
+ concurrent loads per Node isolate. These are not a total process RSS bound.
24
+ Route compilation yields every 64 routes and checks a 10-second cooperative
25
+ deadline; individual synchronous operations are not preempted. At most 1,000 parameterized routes and 1,024 distinct input schemas.
26
+
27
+ ## Routes
28
+
29
+ Keys are absolute case-sensitive paths. Trailing slashes are significant.
30
+ Parameters occupy whole segments, e.g. `/p/{id}`, with distinct identifier names.
31
+ Each parameter matches exactly one nonempty segment, never across `/`; it is not
32
+ greedy. No regex paths, host matching or dot segments. Only static directory mounts
33
+ support a terminal `/*` wildcard with an otherwise literal path. Route keys cannot contain
34
+ percent encoding, spaces, backslashes or query strings. Path length is limited
35
+ to 2,048 characters and 32 segments. `/_urlcode` is reserved.
36
+
37
+ One handler per route: `function`, `redirect`, `page`, `static`, `download`, `respond` or `link`.
38
+ See [asset configuration](ASSETS.md) for file handlers. Optional properties:
39
+
40
+ - `methods`: unique HTTP methods; default GET and HEAD. Explicit lists are exact;
41
+ adding GET does not implicitly add HEAD. Wrong method returns 405 plus Allow.
42
+ - `enabled`: false returns 404, the same as unknown paths.
43
+ - `expires`: UTC ISO timestamp (`...ssZ` or `...ss.sssZ`); expired routes return 410.
44
+ - `description`: optional authoring metadata.
45
+ - `middleware`: ordered list of up to 16 `{source, export?}` modules wrapping any
46
+ handler. See [middleware](MIDDLEWARE.md) for the portable contract.
47
+ - `parameters`, `env`, `secrets`: inputs and explicit binding references.
48
+
49
+ Literal paths win; parameter routes with more literal segments win next;
50
+ static mounts follow, longest prefix first. A missing file in the selected mount
51
+ returns 404 without falling back to a shorter mount.
52
+ Equally specific overlapping patterns fail even if methods differ. Match a route
53
+ before checking its methods; do not fall back to a less specific route for 405.
54
+ Requests decode the path once; invalid UTF-8/percent encoding, encoded slashes or
55
+ backslashes, control characters and dot segments return 400. Query values decode
56
+ once. Incoming query data is not automatically forwarded.
57
+
58
+ See [route matching and new links](ROUTING.md) for examples, precedence, wildcard
59
+ limits, reload behavior and the distinction between YAML routes and live link data.
60
+
61
+ The optional top-level `site` block (entry file only) generates native routes
62
+ for site conventions: `robots` → `/robots.txt`, `sitemap` → `/sitemap.xml`,
63
+ `favicon` → `/favicon.ico`, `securityTxt` → `/.well-known/security.txt` and
64
+ `llms` → `/llms.txt`. Each is an ordinary `respond` or `page` route merged in
65
+ before compilation and counted by `routes`/`audit`; a declared route at the same
66
+ path wins and the generated one is logged as shadowed. Absolute URLs come from
67
+ the operator's `--origin`; `sitemap` refuses activation without one. See
68
+ [site conventions](SITE.md).
69
+
70
+ ## HTTP request/response configuration
71
+
72
+ Routes accept `request.body` validation and `response.headers` overrides. The
73
+ `respond` handler serves declared text/JSON with a status without running code.
74
+ See [HTTP configuration](HTTP.md) for the exact supported fields, precedence,
75
+ security restrictions and examples.
76
+
77
+ ## Policies
78
+
79
+ Optional top-level `policies` and `profiles` keys, and `routes.<path>.policies`,
80
+ declare host-enforced behavior around a route: `throttle`, `agents`, `security`,
81
+ `compression` and `cache`. All are off unless declared; a route's keys merge
82
+ over the project's, `false` disables one, and a target that cannot enforce a
83
+ policy refuses activation naming the route. The five policies are implemented
84
+ for the self-hosted server; Vercel and AWS accept `agents`, `security`, `cache`
85
+ and route-partitioned `throttle`; the Cloudflare build compiles `agents` and
86
+ `security` only. See [policies](POLICIES.md) for the pipeline position, merge
87
+ rules and the per-target table, and [plugins](PLUGINS.md) for the host hook API
88
+ operators pass in code.
89
+
90
+ ## Inputs
91
+
92
+ Declare each path placeholder as a required string. Query/header inputs may be
93
+ string, integer, number or boolean. Query arrays declare scalar `items`; repeated
94
+ keys retain order (maximum 100 values). Header names are case-insensitive.
95
+ Duplicate scalar query/header inputs return 400. Required missing inputs return
96
+ 400; defaults apply only to absent inputs. Empty strings are present values.
97
+
98
+ Supported validation: `type`, scalar `enum`, `default`, string `minLength`/
99
+ `maxLength`, numeric `minimum`/`maximum`, query array `items` and `maxItems`.
100
+ Regular-expression patterns, structured bodies, cookies, nested inputs and
101
+ OpenAPI `style`/`explode` fields are not implemented. This uses a documented
102
+ OpenAPI-like input subset; it is not an OpenAPI document or full JSON Schema
103
+ input vocabulary. String limits are at most 8,192 characters.
104
+
105
+ Integers use `-?(0|[1-9][0-9]*)` and must be safe JavaScript integers. Numbers
106
+ allow the same grammar plus a fractional suffix; no exponent, plus sign, leading
107
+ zeros, whitespace, NaN or infinity. Booleans are exactly `true` or `false`.
108
+ Unknown query keys are ignored unless explicitly passed by a redirect.
109
+
110
+ ## Redirects
111
+
112
+ `redirect.url` is an absolute HTTP(S) URL with literal scheme/host and no embedded
113
+ credentials or whitespace/control characters. `{pathInput}` placeholders are
114
+ allowed only in the destination pathname and encoded as single components.
115
+ No environment/secret interpolation. Status defaults to 302; allowed values are
116
+ 301, 302, 303, 307 and 308.
117
+
118
+ `redirect.query.map` maps output keys to `{from: path|query|header, name: input}`
119
+ references to declared inputs. Typed/defaulted values are used. Arrays produce
120
+ repeated output keys. Absent optional inputs are omitted.
121
+ `redirect.query.pass` is false or an explicit allowlist; unrestricted `true` is
122
+ not supported. Conflicts between destination keys, maps and passthrough fail.
123
+ Declared passthrough inputs use validated/defaulted values; undeclared allowlisted
124
+ keys preserve repeated values. Headers are forwarded only through explicit maps.
125
+
126
+ ## Stored links
127
+
128
+ The entry `urlcode.yaml` must opt in with `dynamicLinks: true` (default false).
129
+ Included files cannot set this project-level flag. Parameterized redirects and
130
+ functions do not require it.
131
+
132
+ `link: {collection: links, code: {from: path, name: code}}` resolves a declared
133
+ path input against an operator-bound store. GET/HEAD only. The logical collection
134
+ is portable; file paths and store credentials are external deployment bindings.
135
+ No general storage capability is exposed to guest code. See [dynamic links](DYNAMIC-LINKS.md)
136
+ for validation, persistence, mutation, expiry and read-after-write behavior.
137
+
138
+ ## Functions
139
+
140
+ ```yaml
141
+ version: "1"
142
+ routes:
143
+ /hello/{name}:
144
+ parameters:
145
+ - name: name
146
+ in: path
147
+ required: true
148
+ schema: {type: string, minLength: 1}
149
+ function:
150
+ source: functions/hello.mjs
151
+ export: default
152
+ args:
153
+ name: {from: path, name: name}
154
+ env:
155
+ GREETING: {value: Hello}
156
+ ```
157
+
158
+ ```js
159
+ export default function hello(request, { args, env }) {
160
+ return Response.json({ message: `${env.GREETING}, ${args.name}!` });
161
+ }
162
+ ```
163
+
164
+ ES modules only (`.mjs` or `.js`, independent of Node package settings).
165
+ TypeScript is not included. `export` defaults to `default`. Functions execute
166
+ inside QuickJS/WASM, never through Node imports. Only relative `.js`/`.mjs`
167
+ project imports are supported, with a snapshotted dependency graph. No bare/npm,
168
+ Node built-in, remote, dynamic source imports or `import.meta`. Runtime-created
169
+ imports remain restricted to the route's middleware and handler dependency graphs; there is no fallback.
170
+ Source limits: 128 modules, 1 MiB per module, 4 MiB total.
171
+
172
+ The current guest API is a **text/JSON subset**, not the complete native Fetch
173
+ API: Request `url`, `method`, `headers`, `text()`, `json()`; Headers append/set/
174
+ delete/get/has/entries/getSetCookie; Response constructor with string/null body,
175
+ `status`, `headers`, `ok`, `text()`, `json()`, static `json()` and `redirect()`.
176
+ Requests decode body bytes as UTF-8. Binary/streaming bodies, URL helpers,
177
+ fetch/WebSocket, crypto and filesystem are not exposed. Promise/async and
178
+ bounded timers (128 pending per invocation) work inside the guest. Unsupported
179
+ APIs fail; they never execute on the host. Do not claim full browser/Node API parity.
180
+
181
+ Context contains `inputs.path/query/header`, `args`, `env`, `secrets`. Arguments
182
+ may be scalar literals, input references, `{env: alias}` or `{secret: alias}`.
183
+ Bindings use `{value: "literal"}`, `{env: EXTERNAL_NAME}` or `{secret: logical_name}`.
184
+ Literal non-secret values need no grant. Every external environment or secret
185
+ binding is denied unless an operator policy grants that exact name to the route
186
+ and matches the SHA-256 of the current configuration/source snapshot.
187
+ A project cannot grant itself capabilities. See [policy setup](FUNCTION-SECURITY.md).
188
+ Missing bindings also reject activation. Inspection parses source without running it.
189
+
190
+ Development may read `.env.local`; process values win. Serving never reads it.
191
+ Dotenv supports single-line NAME=value, paired single/double quotes, blank lines
192
+ and full-line comments, without expansion/escapes/shell execution. Loading a
193
+ value does not authorize exposing it to a function; the policy still applies.
194
+
195
+ Every invocation has a fresh guest heap and module state. No cross-request
196
+ counters, cached secrets or prototype mutation. QuickJS heap limit is 32 MiB,
197
+ stack limit 512 KiB; outer worker and deployment limits are additional defenses,
198
+ not a claim that total process RSS is capped at 32 MiB. Two workers, no queue;
199
+ saturation returns 503. The independent 5-second deadline terminates a worker
200
+ and returns 504. Generic failures return 502; worker replacement is bounded.
201
+
202
+ HEAD invokes the handler as HEAD and suppresses body output. Code must guard
203
+ its own application side effects when future brokered integrations are enabled.
204
+ Guest console output is discarded. Functions see the configured public origin,
205
+ not arbitrary Host/forwarded headers. Request/response bodies default to 1 MiB;
206
+ response headers 16 KiB, maximum 256 pairs. Hop-by-hop headers are stripped;
207
+ cookies are preserved individually. Default response cache policy is `no-store`.
208
+
209
+ No unrestricted host execution option exists. Network integrations and persistent
210
+ state need future explicit, tested capability brokers. Approved secrets can be
211
+ returned by code that receives them; isolation does not automatically enforce
212
+ information-flow rules on authorized inputs. Keep grants narrow and review the
213
+ exact pinned revision. The sandbox still needs independent security review before hostile multi-tenant use.
214
+
215
+ ## Reload and status
216
+
217
+ `dev` polls project YAML/JSON/JS and `.env.local` every 500 ms, plus declared
218
+ asset files/directories (including binary assets and explicit build directories).
219
+ Asset polling uses file metadata; production assets stay fixed until restart.
220
+ The general source scan excludes common
221
+ build/dependency directories and hidden files. Includes and source dependencies
222
+ must be normal watched files; changes in symlink targets or `node_modules`
223
+ require restart. A candidate fully validates and initializes its functions
224
+ and snapshots its assets before activation. Invalid candidates leave the old snapshot serving. In-flight
225
+ function calls finish on their original snapshot; new requests use the new one.
226
+ Production `serve` is a fixed snapshot; restart/redeploy for code, secret or
227
+ operator-policy changes. Config/code edits invalidate old binding grants.
228
+
229
+ The health `version` combines route-definition and asset-representation digests,
230
+ not a full artifact digest
231
+ or secret fingerprint. Production release identity should be the Git commit and
232
+ container image digest. See [operations](OPERATIONS.md).