@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
@@ -0,0 +1,19 @@
1
+ version: "1"
2
+ routes:
3
+ /go:
4
+ redirect:
5
+ url: https://example.com/target
6
+ /u/{id}:
7
+ parameters:
8
+ - {name: id, in: path, required: true, schema: {type: string}}
9
+ redirect:
10
+ url: https://example.com/users/{id}
11
+ /health:
12
+ respond:
13
+ json: {ok: true}
14
+ /:
15
+ page:
16
+ file: public/index.html
17
+ /files/*:
18
+ static:
19
+ directory: public
@@ -0,0 +1,18 @@
1
+ # URLCode on Cloudflare Workers
2
+
3
+ A declarative project compiled to a Worker. See
4
+ [the target guide](../../docs/CLOUDFLARE.md) for what is and is not supported.
5
+
6
+ ```sh
7
+ urlcode test --project . # the same assertions run against the local runtime
8
+ npm run build # writes dist/{index,artifact,validators}.js
9
+ npx wrangler deploy
10
+ ```
11
+
12
+ The build is the deploy unit: `dist/` is generated, never edited, and never
13
+ committed. Rebuild after every change to `urlcode.yaml` — the Worker reads the
14
+ artifact, not the YAML.
15
+
16
+ `wrangler.toml` sets no `nodejs_compat` flag on purpose. The runtime and the
17
+ precompiled schema validators use Web standards only, so nothing here needs a
18
+ Node compatibility layer.
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "urlcode-cloudflare-example",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "urlcode build --target cloudflare --project . --out dist"
7
+ },
8
+ "dependencies": {
9
+ "@jimhoyd/urlcode": "^0.3.0"
10
+ },
11
+ "devDependencies": {
12
+ "wrangler": "^4"
13
+ }
14
+ }
@@ -0,0 +1,9 @@
1
+ [
2
+ {"path": "/go", "status": 302, "expectHeaders": {"location": "https://example.com/target"}},
3
+ {"path": "/u/42", "status": 302, "expectHeaders": {"location": "https://example.com/users/42"}},
4
+ {"path": "/search?q=links", "status": 302, "expectHeaders": {"location": "https://example.com/search?q=links"}},
5
+ {"path": "/search", "status": 302, "expectHeaders": {"location": "https://example.com/search?q=urlcode"}},
6
+ {"path": "/health", "status": 200, "expectBody": "{\"ok\":true}"},
7
+ {"path": "/nothing-here", "status": 404},
8
+ {"path": "/go", "method": "HEAD", "status": 302, "expectHeaders": {"location": "https://example.com/target"}, "expectBody": ""}
9
+ ]
@@ -0,0 +1,24 @@
1
+ version: "1"
2
+ routes:
3
+ /go:
4
+ redirect:
5
+ url: https://example.com/target
6
+ /u/{id}:
7
+ parameters:
8
+ - {name: id, in: path, required: true, schema: {type: string}}
9
+ redirect:
10
+ url: https://example.com/users/{id}
11
+ /search:
12
+ parameters:
13
+ - {name: q, in: query, required: false, schema: {type: string, maxLength: 64, default: urlcode}}
14
+ redirect:
15
+ url: https://example.com/search
16
+ query:
17
+ map:
18
+ q: {from: query, name: q}
19
+ /health:
20
+ respond:
21
+ json: {ok: true}
22
+ response:
23
+ headers:
24
+ cache-control: no-store
@@ -0,0 +1,5 @@
1
+ name = "urlcode-example"
2
+ main = "dist/index.js"
3
+ compatibility_date = "2025-09-01"
4
+ # No nodejs_compat: the runtime and the generated validators use Web standards
5
+ # only, so the Worker starts without a Node compatibility layer.
@@ -0,0 +1,20 @@
1
+ # Custom compliance rules
2
+
3
+ `rules.mjs` is an operator rules module for `urlcode audit --compliance-rules`.
4
+ It adds three rules (an approved redirect host list, a description on every
5
+ route, a reminder to declare the origin), disables the built-in expired-routes
6
+ notice and raises the security-headers rule to `high`. See
7
+ [docs/COMPLIANCE.md](../../docs/COMPLIANCE.md) for the contract.
8
+
9
+ Run it against the cookbook from the runtime checkout. The module must be an
10
+ absolute path outside the audited project:
11
+
12
+ ```sh
13
+ node src/cli.ts audit --project examples/cookbook \
14
+ --compliance baseline --compliance-rules "$PWD/examples/compliance/rules.mjs" --compliance-warn
15
+ ```
16
+
17
+ Without `--compliance-warn` the run exits 1: the override makes the cookbook's
18
+ routes without `policies.security` high findings. The report's `compliance`
19
+ section lists every finding with its rule, severity, route, message,
20
+ remediation and the standard it cites.
@@ -0,0 +1,65 @@
1
+ // Operator compliance rules for `urlcode audit --compliance-rules`.
2
+ // Runnable against the cookbook from the runtime checkout:
3
+ //
4
+ // node src/cli.js audit --project examples/cookbook \
5
+ // --compliance baseline --compliance-rules "$PWD/examples/compliance/rules.mjs" --compliance-warn
6
+ //
7
+ // The file must sit outside the audited project (it is trusted host code, the
8
+ // same rule as --policy). It exports `rules` to add, `disable` to drop
9
+ // built-in rules by id, and `override` to change fields of an existing rule.
10
+ // Every check reads only the context runCompliance hands it: the parsed YAML
11
+ // (`document`, `routes`), the runtime's test plan (`plan`), the effective
12
+ // policies per route (`policies`, `effective`), the declared `origin` and the
13
+ // declared `host` settings.
14
+
15
+ // Which redirect hosts this operator considers their own.
16
+ const allowedHosts = ['example.com', 'www.example.com'];
17
+
18
+ export const rules = [
19
+ {
20
+ id: 'acme/redirect-hosts',
21
+ title: 'Redirects only leave for approved hosts',
22
+ standard: { name: 'ACME link policy', reference: 'https://example.com/policies/links', section: 'Outbound redirects' },
23
+ severity: 'high',
24
+ appliesTo: 'route',
25
+ check({ route, config }) {
26
+ const url = config.redirect?.url;
27
+ if (route.state !== 'active' || typeof url !== 'string') return [];
28
+ // Templates such as https://host/{id} still start with a fixed host.
29
+ let host;
30
+ try { host = new URL(url.replace(/\{[^}]*\}/g, 'x')).hostname; } catch { return [{ message: `${route.path} redirect target could not be parsed`, remediation: 'Use an absolute https URL' }]; }
31
+ if (allowedHosts.includes(host)) return [];
32
+ return [{ message: `${route.path} redirects to ${host}, which is not an approved host`, remediation: `Redirect only to ${allowedHosts.join(', ')} or extend the allow list in rules.mjs` }];
33
+ },
34
+ },
35
+ {
36
+ id: 'acme/described-routes',
37
+ title: 'Every active route carries a description',
38
+ standard: { name: 'ACME YAML conventions', reference: 'https://example.com/policies/yaml' },
39
+ severity: 'low',
40
+ appliesTo: 'route',
41
+ check({ route, config }) {
42
+ if (route.state !== 'active' || typeof config.description === 'string') return [];
43
+ return [{ message: `${route.path} has no description`, remediation: 'Add a one-line description so audits and reviews can name the route by purpose' }];
44
+ },
45
+ },
46
+ {
47
+ id: 'acme/declared-origin',
48
+ title: 'The audit declares the public origin',
49
+ standard: { name: 'ACME deployment checklist', reference: 'https://example.com/policies/deploy' },
50
+ severity: 'info',
51
+ appliesTo: 'project',
52
+ check({ origin }) {
53
+ if (origin) return [];
54
+ return [{ message: 'No --origin was declared, so origin-dependent rules cannot confirm https', remediation: 'Pass --origin https://your.host to audit' }];
55
+ },
56
+ },
57
+ ];
58
+
59
+ // The cookbook keeps an expired route on purpose to show a 410.
60
+ export const disable = ['rfc9110/expired-routes'];
61
+
62
+ // This operator treats a missing security profile as blocking.
63
+ export const override = {
64
+ 'oshp/security-headers': { severity: 'high' },
65
+ };
@@ -0,0 +1,21 @@
1
+ # Runnable YAML cookbook
2
+
3
+ 25 routes covering all six handlers, middleware, inputs, body validation, response
4
+ headers, cookies, methods, expiry, assets, included files and host policies
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
7
+ external services are required. Redirects target example.com; tests never follow them.
8
+
9
+ From the runtime checkout:
10
+
11
+ ```sh
12
+ node src/cli.ts validate --project examples/cookbook
13
+ node src/cli.ts test --project examples/cookbook
14
+ node src/cli.ts audit --project examples/cookbook --expect-routes 25
15
+ node src/cli.ts dev --project examples/cookbook
16
+ ```
17
+
18
+ The [YAML guide](../../docs/YAML-GUIDE.md) explains the recipes and binding policy.
19
+ `/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
21
+ starter remains the recommended starting point for a new application.
@@ -0,0 +1,4 @@
1
+ export default function choice(request, {args}) {
2
+ const destinations = {docs: 'https://example.com/docs', home: 'https://example.com/'};
3
+ return Response.redirect(destinations[args.destination], 302);
4
+ }
@@ -0,0 +1,3 @@
1
+ export default async function echo(request) {
2
+ return Response.json(await request.json());
3
+ }
@@ -0,0 +1,3 @@
1
+ export function hello(request, {args}) {
2
+ return Response.json({message: `${args.greeting}, ${args.name}${args.excited ? args.punctuation : '.'}`});
3
+ }
@@ -0,0 +1,3 @@
1
+ export default async function text(request) {
2
+ return new Response(await request.text());
3
+ }
@@ -0,0 +1,6 @@
1
+ export async function decorate(request, context, next) {
2
+ context.state.example = 'cookbook';
3
+ const response = await next();
4
+ response.headers.set('x-middleware', context.state.example);
5
+ return response;
6
+ }
@@ -0,0 +1 @@
1
+ <!doctype html><title>About URLCode</title>About
@@ -0,0 +1 @@
1
+ <!doctype html><title>Assets</title>Assets
@@ -0,0 +1 @@
1
+ body { color: navy; }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect width="16" height="16" rx="3" fill="#1d4ed8"/><text x="8" y="12" font-size="10" text-anchor="middle" fill="#fff" font-family="sans-serif">U</text></svg>
@@ -0,0 +1 @@
1
+ URLCode guide
@@ -0,0 +1,6 @@
1
+ # Cookbook
2
+
3
+ > A runnable URLCode example project. Routes are declared in urlcode.yaml.
4
+
5
+ - /status: JSON health response
6
+ - /about: HTML page
@@ -0,0 +1,55 @@
1
+ version: "1"
2
+ routes:
3
+ /hello/{name}:
4
+ description: Validated input, named export, arguments, literal environment and middleware
5
+ parameters:
6
+ - name: name
7
+ in: path
8
+ required: true
9
+ schema: {type: string, minLength: 1, maxLength: 80}
10
+ - name: excited
11
+ in: query
12
+ schema: {type: boolean, default: false}
13
+ env:
14
+ GREETING: {value: Hello}
15
+ middleware:
16
+ - source: middleware/headers.mjs
17
+ export: decorate
18
+ function:
19
+ source: functions/hello.mjs
20
+ export: hello
21
+ args:
22
+ name: {from: path, name: name}
23
+ excited: {from: query, name: excited}
24
+ greeting: {env: GREETING}
25
+ punctuation: "!"
26
+ response:
27
+ headers:
28
+ X-App: cookbook
29
+ /echo:
30
+ methods: [POST]
31
+ request:
32
+ body:
33
+ required: true
34
+ maxBytes: 4096
35
+ contentTypes: [application/json]
36
+ format: json
37
+ function: {source: functions/echo.mjs}
38
+ /text:
39
+ methods: [POST]
40
+ request:
41
+ body:
42
+ required: true
43
+ maxBytes: 1024
44
+ contentTypes: [text/plain]
45
+ format: text
46
+ function: {source: functions/text.mjs}
47
+ /choice:
48
+ parameters:
49
+ - name: destination
50
+ in: query
51
+ schema: {type: string, enum: [docs, home], default: docs}
52
+ function:
53
+ source: functions/choice.mjs
54
+ args:
55
+ destination: {from: query, name: destination}
@@ -0,0 +1,17 @@
1
+ version: "1"
2
+ routes:
3
+ /about:
4
+ page:
5
+ file: public/about.html
6
+ cacheControl: no-cache
7
+ /assets/*:
8
+ static:
9
+ directory: public/assets
10
+ index: index.html
11
+ cacheControl: public, max-age=3600
12
+ /download:
13
+ download:
14
+ file: public/guide.txt
15
+ filename: urlcode-guide.txt
16
+ contentType: text/plain
17
+ cacheControl: no-store
@@ -0,0 +1,28 @@
1
+ version: "1"
2
+ routes:
3
+ /secure:
4
+ description: OWASP Secure Headers profile on a declared response; explicit headers still win
5
+ respond: {text: Hardened response}
6
+ policies:
7
+ security: {headers: oshp, unset: [Cross-Origin-Embedder-Policy]}
8
+ response:
9
+ headers:
10
+ Referrer-Policy: no-referrer
11
+ /no-bots:
12
+ description: Deny the bundled AI-crawler list and any curl; allow one named crawler
13
+ redirect: {url: https://example.com/article}
14
+ policies:
15
+ agents:
16
+ deny: [ai-crawlers]
17
+ denyPatterns: ["^curl/"]
18
+ allowPatterns: ["^Mozilla/5\\.0 \\(compatible; Googlebot/"]
19
+ /cached:
20
+ description: Public caching with a named strategy; the origin does not cache
21
+ respond: {json: {cached: true}}
22
+ policies:
23
+ cache: {strategy: public, maxAge: 60}
24
+ /budget:
25
+ description: Per-route request budget with standard RateLimit headers
26
+ respond: {text: Budgeted}
27
+ policies:
28
+ throttle: {quota: 100, window: 60, partition: route}
@@ -0,0 +1,38 @@
1
+ version: "1"
2
+ routes:
3
+ /go:
4
+ redirect: {url: 'https://example.com/'}
5
+ /moved:
6
+ redirect: {url: 'https://example.com/new', status: 308}
7
+ response:
8
+ headers:
9
+ Cache-Control: public, max-age=60
10
+ /product/{id}:
11
+ parameters:
12
+ - name: id
13
+ in: path
14
+ required: true
15
+ schema: {type: string, minLength: 1, maxLength: 64}
16
+ - name: page
17
+ in: query
18
+ schema: {type: integer, minimum: 1, maximum: 100, default: 1}
19
+ - name: tag
20
+ in: query
21
+ schema: {type: array, items: {type: string}, maxItems: 3}
22
+ - name: x-channel
23
+ in: header
24
+ schema: {type: string, enum: [web, email], default: web}
25
+ redirect:
26
+ url: https://example.com/products/{id}
27
+ query:
28
+ map:
29
+ page: {from: query, name: page}
30
+ label: {from: query, name: tag}
31
+ channel: {from: header, name: x-channel}
32
+ pass: [utm_source]
33
+ /paused:
34
+ enabled: false
35
+ redirect: {url: 'https://example.com/'}
36
+ /expired:
37
+ expires: "2020-01-01T00:00:00Z"
38
+ redirect: {url: 'https://example.com/'}
@@ -0,0 +1,26 @@
1
+ version: "1"
2
+ routes:
3
+ /status:
4
+ respond: {json: {ok: true, service: cookbook}}
5
+ /notice:
6
+ respond: {text: Temporarily unavailable, status: 503}
7
+ response:
8
+ headers:
9
+ Retry-After: "60"
10
+ /empty:
11
+ respond: {status: 204}
12
+ /cookie-demo:
13
+ respond: {text: Non-secret preferences only}
14
+ response:
15
+ headers:
16
+ Set-Cookie:
17
+ - "theme=light; Path=/; Secure; SameSite=Lax"
18
+ - "notice=seen; Path=/; HttpOnly; Secure; SameSite=Lax"
19
+ /preflight:
20
+ methods: [OPTIONS]
21
+ respond: {status: 204}
22
+ response:
23
+ headers:
24
+ Access-Control-Allow-Origin: https://app.example.com
25
+ Access-Control-Allow-Methods: GET, HEAD
26
+ Access-Control-Allow-Headers: Content-Type