@jimhoyd/urlcode 0.4.2 → 0.4.6

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 (214) hide show
  1. package/.claude/skills/urlcode-authoring/SKILL.md +57 -14
  2. package/.claude/skills/urlcode-operations/SKILL.md +4 -0
  3. package/README.md +19 -15
  4. package/SECURITY.md +5 -3
  5. package/dist/BUILD-MANIFEST.json +30 -27
  6. package/dist/agent-context.js +82 -0
  7. package/dist/agents-guide.js +42 -42
  8. package/dist/authoring.js +12 -2
  9. package/dist/body-schema.js +159 -0
  10. package/dist/build-cloudflare.js +2 -0
  11. package/dist/capabilities.js +1 -1
  12. package/dist/cli.js +28 -12
  13. package/dist/config.js +74 -6
  14. package/dist/context.js +4 -6
  15. package/dist/errors.js +3 -1
  16. package/dist/examples.js +1 -1
  17. package/dist/extensions.js +60 -2
  18. package/dist/http-policy.js +19 -4
  19. package/dist/http-response.js +2 -2
  20. package/dist/init-with.js +71 -9
  21. package/dist/mcp.js +15 -2
  22. package/dist/pattern-guard.js +32 -0
  23. package/dist/policies/security.js +0 -0
  24. package/dist/project-tests.js +35 -11
  25. package/dist/readiness.js +184 -32
  26. package/dist/recipes.js +1 -1
  27. package/dist/router.js +17 -0
  28. package/dist/runtime.js +26 -3
  29. package/dist/scaffold.js +0 -0
  30. package/dist/server.js +26 -3
  31. package/dist/site.js +0 -0
  32. package/dist/tooling.js +2 -1
  33. package/dist/types/agent-context.d.ts +44 -0
  34. package/dist/types/authoring.d.ts +3 -1
  35. package/dist/types/body-schema.d.ts +58 -0
  36. package/dist/types/config.d.ts +10 -2
  37. package/dist/types/context.d.ts +1 -1
  38. package/dist/types/errors.d.ts +9 -1
  39. package/dist/types/examples.d.ts +1 -1
  40. package/dist/types/extensions.d.ts +47 -2
  41. package/dist/types/http-policy.d.ts +3 -0
  42. package/dist/types/init-with.d.ts +10 -1
  43. package/dist/types/pattern-guard.d.ts +10 -0
  44. package/dist/types/project-tests.d.ts +9 -0
  45. package/dist/types/readiness.d.ts +68 -0
  46. package/dist/types/recipes.d.ts +1 -1
  47. package/dist/types/runtime.d.ts +4 -0
  48. package/dist/types/server.d.ts +9 -1
  49. package/dist/types/tooling.d.ts +1 -0
  50. package/dist/types/types.d.ts +13 -0
  51. package/dist/types.js +10 -2
  52. package/dist/typescript-authoring.js +5 -3
  53. package/dist/verify-deployment.js +16 -10
  54. package/examples/body-validation/README.md +16 -0
  55. package/examples/body-validation/example.yaml +17 -0
  56. package/examples/body-validation/tests/requests.json +15 -0
  57. package/examples/body-validation/urlcode.yaml +34 -0
  58. package/examples/coverage-waiver/README.md +8 -0
  59. package/examples/coverage-waiver/example.yaml +16 -0
  60. package/examples/coverage-waiver/functions/notes.mjs +2 -0
  61. package/examples/coverage-waiver/tests/requests.json +3 -0
  62. package/examples/coverage-waiver/urlcode.yaml +10 -0
  63. package/examples/data-dir/README.md +39 -0
  64. package/examples/data-dir/data/welcome.txt +1 -0
  65. package/examples/data-dir/example.yaml +22 -0
  66. package/examples/data-dir/functions/note.mjs +18 -0
  67. package/examples/data-dir/tests/requests.json +6 -0
  68. package/examples/data-dir/urlcode.yaml +6 -0
  69. package/examples/lifecycle/README.md +15 -0
  70. package/examples/lifecycle/example.yaml +19 -0
  71. package/examples/lifecycle/functions/notes.mjs +29 -0
  72. package/examples/lifecycle/tests/requests.json +12 -0
  73. package/examples/lifecycle/urlcode.yaml +29 -0
  74. package/examples/not-found/README.md +10 -0
  75. package/examples/not-found/example.yaml +17 -0
  76. package/examples/not-found/public/404.html +3 -0
  77. package/examples/not-found/public/index.html +3 -0
  78. package/examples/not-found/tests/requests.json +7 -0
  79. package/examples/not-found/urlcode.yaml +7 -0
  80. package/examples/shared-blocks/README.md +11 -0
  81. package/examples/shared-blocks/example.yaml +17 -0
  82. package/examples/shared-blocks/tests/requests.json +7 -0
  83. package/examples/shared-blocks/urlcode.yaml +37 -0
  84. package/llms-full.txt +364 -49
  85. package/llms.txt +43 -5
  86. package/package.json +15 -22
  87. package/recipes/static-page/README.md +9 -0
  88. package/recipes/static-page/public/index.html +11 -0
  89. package/recipes/static-page/recipe.yaml +21 -0
  90. package/recipes/static-page/tests/requests.json +22 -0
  91. package/recipes/static-page/urlcode.yaml +7 -0
  92. package/recipes/static-plus-api/README.md +6 -0
  93. package/recipes/static-plus-api/urlcode.yaml +4 -0
  94. package/recipes/store-crud/README.md +53 -0
  95. package/recipes/store-crud/recipe.yaml +31 -0
  96. package/recipes/store-crud/tests/requests.json +18 -0
  97. package/recipes/store-crud/urlcode.yaml +18 -0
  98. package/schemas/urlcode.schema.json +125 -60
  99. package/skills/urlcode/SKILL.md +47 -27
  100. package/starters/default/AGENTS.md +43 -43
  101. package/starters/page/README.md +14 -0
  102. package/starters/page/public/index.html +12 -0
  103. package/starters/page/tests/requests.json +17 -0
  104. package/starters/page/urlcode.yaml +6 -0
  105. package/.claude-plugin/marketplace.json +0 -18
  106. package/CONTRIBUTING.md +0 -148
  107. package/ROADMAP.md +0 -70
  108. package/docs/AI-AUTHORING.md +0 -339
  109. package/docs/ASSETS.md +0 -107
  110. package/docs/AUTH-BACKUP.md +0 -32
  111. package/docs/AWS.md +0 -86
  112. package/docs/BEST-PRACTICES.md +0 -276
  113. package/docs/BULK.md +0 -79
  114. package/docs/CAPABILITIES.md +0 -192
  115. package/docs/CAPACITY.md +0 -305
  116. package/docs/CI-FOLLOWUP-2026-09-19.md +0 -97
  117. package/docs/CI-RELEASE-AUDIT-2026-09-19.md +0 -322
  118. package/docs/CI.md +0 -147
  119. package/docs/CLOUDFLARE.md +0 -109
  120. package/docs/CODEBASE-AUDIT-2026-09-20.md +0 -284
  121. package/docs/COMPLIANCE.md +0 -239
  122. package/docs/COMPOSING-A-SITE.md +0 -287
  123. package/docs/CONDITIONS.md +0 -74
  124. package/docs/CONTAINER-PROMOTION.md +0 -74
  125. package/docs/DEPLOYMENT-CHECKS.md +0 -108
  126. package/docs/DEVELOPMENT-PIPELINE.md +0 -304
  127. package/docs/EGRESS.md +0 -125
  128. package/docs/EXTENSIONS.md +0 -403
  129. package/docs/FRAMEWORK.md +0 -217
  130. package/docs/FUNCTION-SECURITY.md +0 -251
  131. package/docs/HTTP.md +0 -129
  132. package/docs/INSTALL.md +0 -128
  133. package/docs/INTERCHANGE.md +0 -134
  134. package/docs/LOAD-TESTING.md +0 -91
  135. package/docs/LOCAL-DEVELOPMENT.md +0 -102
  136. package/docs/MIDDLEWARE-EXAMPLES.md +0 -75
  137. package/docs/MIDDLEWARE.md +0 -102
  138. package/docs/MONITORING.md +0 -115
  139. package/docs/OBSERVABILITY.md +0 -222
  140. package/docs/OPEN-DECISIONS.md +0 -149
  141. package/docs/OPERATIONAL-PROOF.md +0 -41
  142. package/docs/OPERATIONS.md +0 -201
  143. package/docs/ORGANIZATION.md +0 -135
  144. package/docs/PERFORMANCE.md +0 -72
  145. package/docs/PLUGINS.md +0 -271
  146. package/docs/POLICIES.md +0 -211
  147. package/docs/PRERENDER.md +0 -245
  148. package/docs/PROJECT-DIRECTION.md +0 -118
  149. package/docs/PROVIDER-VERIFICATION.md +0 -84
  150. package/docs/READINESS.md +0 -153
  151. package/docs/README.md +0 -93
  152. package/docs/RECIPES.md +0 -99
  153. package/docs/RELEASE-0.4.0-alpha.3.md +0 -50
  154. package/docs/RELEASE-0.4.1.md +0 -73
  155. package/docs/RELEASE-0.4.2.md +0 -30
  156. package/docs/RELEASE-READINESS.md +0 -146
  157. package/docs/RELEASE-SECURITY.md +0 -100
  158. package/docs/RESILIENCE.md +0 -161
  159. package/docs/ROUTING.md +0 -92
  160. package/docs/SANDBOX-REVIEW.md +0 -72
  161. package/docs/SCAFFOLDING.md +0 -70
  162. package/docs/SECURITY-AUDIT.md +0 -164
  163. package/docs/SITE.md +0 -150
  164. package/docs/SPECIFICATION.md +0 -359
  165. package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +0 -288
  166. package/docs/SPIKE-BUSINESS-SUITE.md +0 -1029
  167. package/docs/SPIKE-CORE-LAYERING.md +0 -368
  168. package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +0 -207
  169. package/docs/STANDARDS.md +0 -311
  170. package/docs/STARTERS.md +0 -83
  171. package/docs/STATIC.md +0 -105
  172. package/docs/TOOLING.md +0 -298
  173. package/docs/TUNNELS.md +0 -72
  174. package/docs/TYPESCRIPT-AUTHORING.md +0 -87
  175. package/docs/TYPESCRIPT.md +0 -123
  176. package/docs/VERCEL.md +0 -114
  177. package/docs/VERSION-ALIGNMENT.md +0 -88
  178. package/docs/YAML-GUIDE.md +0 -57
  179. package/docs/YAML-REFERENCE.md +0 -449
  180. package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +0 -68
  181. package/docs/archive/2026-09-19/MANAGEMENT-SECURITY.md +0 -102
  182. package/docs/archive/2026-09-19/NEXT-PHASE-PLAN.md +0 -108
  183. package/docs/archive/2026-09-19/NEXT-STEPS.md +0 -646
  184. package/docs/archive/2026-09-19/OPEN-DECISIONS.md +0 -277
  185. package/docs/archive/2026-09-19/RELEASE-SECURITY.md +0 -186
  186. package/docs/archive/2026-09-19/ROADMAP.md +0 -387
  187. package/docs/archive/2026-09-19/SPIKE-EXTENSION-MODEL.md +0 -430
  188. package/docs/archive/2026-09-19/SPIKE-EXTENSIONS.md +0 -492
  189. package/docs/archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md +0 -365
  190. package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +0 -778
  191. package/docs/archive/2026-09-19/USABILITY-REVIEW.md +0 -139
  192. package/docs/archive/2026-09-20/OPEN-DECISIONS-COMPLETED.md +0 -116
  193. package/docs/archive/README.md +0 -29
  194. package/docs/policies/agents.md +0 -182
  195. package/docs/policies/cache.md +0 -152
  196. package/docs/policies/compression.md +0 -169
  197. package/docs/policies/contract.md +0 -52
  198. package/docs/policies/hardened.md +0 -56
  199. package/docs/policies/interoperability.md +0 -169
  200. package/docs/policies/operations.md +0 -45
  201. package/docs/policies/security.md +0 -161
  202. package/docs/policies/throttle.md +0 -103
  203. package/docs/yaml/assets.md +0 -36
  204. package/docs/yaml/conditions.md +0 -20
  205. package/docs/yaml/functions.md +0 -168
  206. package/docs/yaml/middleware.md +0 -31
  207. package/docs/yaml/organization.md +0 -74
  208. package/docs/yaml/policies.md +0 -37
  209. package/docs/yaml/redirects.md +0 -64
  210. package/docs/yaml/responses.md +0 -57
  211. package/docs/yaml/site.md +0 -24
  212. package/packaging/claude-plugin/.claude-plugin/plugin.json +0 -19
  213. package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +0 -130
  214. package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +0 -108
package/docs/ASSETS.md DELETED
@@ -1,107 +0,0 @@
1
- # Pages, static files and downloads
2
-
3
- Native file handlers are implemented in 0.1.0-alpha.3. They work in the local
4
- Node runtime and self-hosted process/container. Provider adapters remain planned.
5
- They do not run user functions or expose filesystem APIs to guest code, trusted
6
- or sandboxed.
7
-
8
- ```yaml
9
- version: "1"
10
- routes:
11
- /about:
12
- page:
13
- file: public/about.html
14
- /assets/*:
15
- static:
16
- directory: public/assets
17
- index: index.html
18
- cacheControl: public, max-age=3600
19
- /guide:
20
- download:
21
- file: public/guide.pdf
22
- filename: getting-started.pdf
23
- contentType: application/pdf
24
- ```
25
-
26
- Create all files/directories before validation or startup. Paths are relative to
27
- the project root. `page` sends one file inline; `download` sends one file as an
28
- attachment; `static` publishes a dedicated directory tree. There is no remote
29
- fetch/proxy, directory listing, automatic trailing-slash redirect, framework
30
- server, SPA fallback or runtime template evaluation. Use prebuilt assets;
31
- [prerendering](PRERENDER.md) is the tested recipe for building them from
32
- function and middleware routes.
33
-
34
- ## Complete handler options
35
-
36
- | Handler | Required | Optional |
37
- |---|---|---|
38
- | `page` | `file` | `contentType`, `cacheControl` |
39
- | `download` | `file` | `filename`, `contentType`, `cacheControl` |
40
- | `static` | `directory` | `index`, `contentType`, `cacheControl` |
41
-
42
- `index` is opt-in, a plain `.html` basename such as `index.html`, served only
43
- when the requested path ends in `/`. A mount `/assets/*` matches `/assets/` and
44
- its descendants, not `/assets`. Missing files return 404. Exact routes precede
45
- parameterized routes; mounts follow, longest prefix first. No fallback between
46
- mounts. Asset routes accept only GET/HEAD (both default); normal enabled/expiry
47
- and declared input validation still apply. Choose exactly one handler per route.
48
-
49
- MIME detection uses the filename extension through `mime-types`; it does not
50
- sniff file bytes. Unknown extensions use `application/octet-stream`. An explicit
51
- `contentType` is a MIME essence such as `text/plain` (no parameters). Known text
52
- charsets are added automatically. On a static mount an override applies to all
53
- its files; normally leave it unset for mixed assets. Responses use `nosniff`.
54
- Download names default to the source basename. Unicode names use a standards-based
55
- Content-Disposition attachment header with UTF-8 encoding and fallback filename.
56
- Path separators and control characters in names are rejected.
57
-
58
- Allowed `cacheControl` values in this release:
59
-
60
- - `no-cache` (default): clients may store but must revalidate.
61
- - `no-store`: clients should not store the response.
62
- - `public, max-age=3600`: one-hour public caching.
63
- - `public, max-age=31536000, immutable`: only for content-versioned URLs you never overwrite.
64
-
65
- Strong content/representation ETags and Last-Modified are emitted. If-Match and
66
- If-Unmodified-Since enforce preconditions (412); If-None-Match and
67
- If-Modified-Since permit 304. ETag conditions take precedence over date conditions.
68
- HEAD returns metadata and the full Content-Length with no body. GET supports a
69
- single byte range, including suffix/open-ended ranges (206); unsatisfiable ranges
70
- return 416 with `Content-Range: bytes */size`. Multiple, malformed or unsupported
71
- ranges are ignored and return the full 200 response. If-Range works with an exact
72
- strong ETag; dates and mismatches return the full representation. Range is ignored
73
- for HEAD and evaluated after preconditions.
74
-
75
- ## Publishing boundary and resource limits
76
-
77
- Assets are an explicit publication surface. Review the files before running an
78
- untrusted project; no filename filter can identify every secret. Keep a dedicated
79
- public directory. Asset declarations reject absolute paths, traversal, dot/hidden
80
- segments, symlinks (including internal ones), hardlinked files and nonregular
81
- files. Static trees skip hidden entries, `node_modules`, `urlcode.yaml/yml`,
82
- `package.json`, `package-lock.json` and `.pem/.key/.p12/.pfx/.env` files. Explicit
83
- references to those names fail. Do not put credentials or private data under
84
- innocent filenames in a public directory. HTML/JavaScript assets are active browser
85
- content; only publish reviewed content on an origin that you control.
86
-
87
- Startup validates and snapshots bytes in memory: **16 MiB per file, 64 MiB total
88
- unique file contents, 10,000 traversed static entries and 20 directory levels**.
89
- These are implementation resource budgets. This is
90
- bounded buffered serving, not arbitrary-size streaming. For larger collections
91
- use an external asset service and redirect, pending provider asset adapters.
92
- Reload can temporarily hold both old and new snapshots; allow memory headroom.
93
-
94
- Requests never open asset paths. Files changed after activation stay unchanged
95
- until a valid reload/restart, preventing request-time path substitution. Keep the
96
- deployment tree operator-owned and stable during compilation; protection against
97
- another host process racing directory changes is not a filesystem sandbox.
98
- `dev` watches declared asset metadata and applies additions, edits and deletions
99
- through validated reloads. A missing required file or invalid tree keeps the last
100
- good snapshot. `serve` is fixed until restart. Function grants remain pinned to
101
- configuration/source, not asset bytes; an asset-only edit does not authorize new
102
- code or new bindings. Asset changes do update the health version and ETags.
103
-
104
- The runnable [asset example](../examples/assets/urlcode.yaml) includes page,
105
- static and download routes with local HTTP assertions. HTTP semantics follow
106
- [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html); MIME mappings use
107
- [mime-types](https://github.com/jshttp/mime-types).
@@ -1,32 +0,0 @@
1
- # Auth backup and restore platform guarantees
2
-
3
- `createBackup({database,destination,projectRoot})` and
4
- `restoreBackup({backup,destination,projectRoot})` use SQLite's online backup API
5
- in a bounded worker. They include committed WAL pages, validate integrity and
6
- foreign keys, and publish to a new path without overwriting an existing file.
7
- Both source and destination must remain outside the served project. See the
8
- [auth operator commands](../packages/auth/README.md#operations-and-recovery).
9
-
10
- The completed snapshot is flushed through a writable file handle before it is
11
- linked into its destination. Windows requires write access for this flush;
12
- opening the snapshot read-only fails with `EPERM`. File flush errors fail the
13
- operation rather than being ignored.
14
-
15
- On POSIX systems the containing directory is also flushed after publication.
16
- Node's filesystem API does not provide the equivalent directory-handle flush
17
- used here on Windows, so Windows does not receive that extra directory-entry
18
- crash-durability guarantee. A successful Windows backup verifies and flushes
19
- file contents; it is not proof that the new filename survives abrupt power
20
- loss. Verify the backup exists and perform an isolated restore rehearsal before
21
- depending on it. CI does not simulate power loss.
22
-
23
- POSIX directory/file modes are checked where supported. Windows operators must
24
- restrict the operator data and backup directories with filesystem ACLs; POSIX
25
- mode bits cannot establish Windows privacy. Keep encryption and CSRF keys and
26
- reviewed configuration separately backed up. Restore to an isolated new path,
27
- and review restored sessions and revocation state before reopening traffic.
28
-
29
- A failed auth service initialization waits for its SQLite worker to terminate
30
- before rejecting. After a configuration rejection, callers can retry or clean up
31
- the database without racing that failed opener's file handle. Configuration
32
- identity checks and their error codes are unchanged.
package/docs/AWS.md DELETED
@@ -1,86 +0,0 @@
1
- # Deploying to AWS Lambda
2
-
3
- The AWS adapter runs a URLCode project as a Lambda function behind a **Function
4
- URL** or an **API Gateway HTTP API**. The same `urlcode.yaml` that runs locally
5
- or in a container serves the deployment.
6
-
7
- **This adapter serves native handlers only** — redirects, validated responses,
8
- pages, static assets and downloads — for the same reasons as
9
- [the Vercel adapter](VERCEL.md): `function` and `middleware` routes need the
10
- self-hosted Node lifecycle, and a `sandbox: true` route would pay worker and
11
- WASM startup on every cold start. Both are refused at activation with the route
12
- named, never per request, trusted or sandboxed alike.
13
-
14
- That refusal is a settled position, not a gap awaiting an adapter: per-route
15
- Lambda compilation was considered and declined
16
- ([the decision](OPEN-DECISIONS.md#accepted-one-node-deployment-per-project),
17
- [the analysis behind it](archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md)). A project that uses
18
- `function` or `middleware` deploys instead as one trusted Node process — a
19
- container or a VM running the project as it runs locally — which supports every
20
- route type today. That process can run on AWS: ECS, EC2 and App Runner all
21
- serve it. The decision is about the execution model, not about avoiding AWS.
22
-
23
- A working project is in [`examples/aws/`](../examples/aws/).
24
-
25
- ## Set it up
26
-
27
- ```js
28
- // handler.mjs
29
- import { createLambdaHandler } from '@jimhoyd/urlcode/aws';
30
-
31
- export const handler = createLambdaHandler({ project: process.env.LAMBDA_TASK_ROOT });
32
- ```
33
-
34
- The deployment package must contain every file the project reads: the entry
35
- YAML, any `includes`, and every page, download and static directory. They are
36
- read at activation, so a missing one fails the deployment rather than one route.
37
- Node 22.13 or newer.
38
-
39
- ## Payload format 2.0 only
40
-
41
- Function URLs and API Gateway **HTTP APIs** send payload format 2.0, which
42
- carries `rawPath` and `rawQueryString` — the request as it arrived.
43
-
44
- A **REST API** sends format 1.0, which supplies the path and query already
45
- decoded. The original bytes cannot be recovered from them, and this runtime
46
- rejects ambiguous encoding deliberately: rebuilding a target from decoded parts
47
- would either re-encode differently than the client sent or quietly accept what
48
- the runtime refuses. Rather than guess, the adapter refuses a 1.0 event and says
49
- why. Front the function with an HTTP API, or run the container image with
50
- `urlcode serve`.
51
-
52
- ## Bindings and origin
53
-
54
- `URLCODE_POLICY` carries the same grant document the operator policy file holds,
55
- validated identically including the `projectSha256` pin — see
56
- [the Vercel guide](VERCEL.md#bindings), which describes the same mechanism.
57
- Store it in the function's environment, or fetch it from Secrets Manager and set
58
- it before the handler is created.
59
-
60
- `URLCODE_PUBLIC_HOST` sets the public hostname for generated URLs. Lambda has no
61
- platform variable naming your domain, so unlike Vercel there is nothing to infer
62
- from: set it when a custom domain or an API Gateway stage prefix is in play.
63
- Forwarded headers stay untrusted.
64
-
65
- ## Limits worth knowing before you deploy
66
-
67
- | Limit | Consequence |
68
- |---|---|
69
- | Lambda response payload is 6 MB | A larger asset cannot be returned. The runtime allows 16 MB per asset, so a project valid self-hosted can exceed what Lambda can send. Keep large files in object storage and redirect to them. |
70
- | Response bodies are base64-encoded | Guessing whether a body is text is how binary assets get corrupted, so every response is encoded. Base64 inflates by about a third, against that 6 MB ceiling. |
71
- | Each execution environment activates independently | Parsing, asset snapshotting and route compilation happen per cold start, and the [capacity limits](CAPACITY.md) apply per instance: a 64 MiB snapshot is 64 MiB in every concurrent one. |
72
- | No reload | A deployment serves the revision it was packaged from. Ship a change by deploying. |
73
- | No `/_urlcode/health` or `/_urlcode/ready` | Those describe a long-lived process. Use Lambda's own metrics; the [monitoring recipes](MONITORING.md) that parse stdout records need adapting for CloudWatch, though the record fields are the same. |
74
-
75
- ## Verification status
76
-
77
- The adapter is tested against the self-hosted runtime for identical status, body
78
- and headers across redirects, parameters, responses, pages, static files and
79
- misses; for format 1.0 and malformed events being refused; for base64 request
80
- bodies and route body policy; for cookies arriving and leaving through the
81
- format 2.0 array; and for the policy pin.
82
-
83
- **It has not been deployed to AWS.** The tests drive the real handler with real
84
- payload format 2.0 events, but no invocation on Lambda has happened. Treat
85
- package contents, cold-start latency, API Gateway's own header handling and the
86
- 6 MB ceiling as unverified until you deploy the example and see it work.
@@ -1,276 +0,0 @@
1
- # Organization and readability
2
-
3
- These are conventions for URLCode applications, not extra schema requirements.
4
- Choose the smallest structure that makes a route easy to find, understand and
5
- test. Preserve an existing project's conventions unless there is a clear reason
6
- to change them. See [file composition](ORGANIZATION.md) for enforced rules and
7
- [the YAML guide](YAML-GUIDE.md) for supported features.
8
-
9
- ## Start small, split by responsibility
10
-
11
- The default starter's two routes do not need a framework or many abstraction
12
- layers. A small project can keep all definitions in `urlcode.yaml`:
13
-
14
- ```text
15
- my-links/
16
- urlcode.yaml
17
- functions/
18
- welcome.mjs
19
- middleware/
20
- response-headers.mjs
21
- public/
22
- guide.txt
23
- tests/
24
- requests.json
25
- README.md
26
- ```
27
-
28
- Create only the folders you use. When navigation or ownership becomes difficult,
29
- group by feature, audience or campaign, rather than creating one YAML file per
30
- short link. There is no universal file-size threshold; split when a reviewer can
31
- no longer follow the changes comfortably. Thousands of redirects usually belong
32
- in a few meaningful collections, not thousands of include entries.
33
-
34
- For a growing application, colocating feature-specific code is often clearer:
35
-
36
- ```text
37
- my-links/
38
- urlcode.yaml
39
- features/
40
- welcome/
41
- routes.yaml
42
- welcome.mjs
43
- greeting.mjs
44
- campaigns/
45
- routes.yaml
46
- middleware/
47
- response-headers.mjs
48
- public/
49
- guide.txt
50
- tests/
51
- requests.json
52
- README.md
53
- ```
54
-
55
- The root remains a short explicit table of contents:
56
-
57
- ```yaml
58
- version: "1"
59
- includes:
60
- - features/welcome/routes.yaml
61
- - features/campaigns/routes.yaml
62
- routes: {}
63
- ```
64
-
65
- Each included file has its own `version: "1"` and `routes`. Do not add recursive
66
- includes, glob patterns or inherited defaults. Folder names never change public
67
- URLs. Keep the two styles available: folders by file type for small apps, or by
68
- feature when it improves ownership. Neither requires forking the runtime.
69
-
70
- ## Make a route readable from top to bottom
71
-
72
- Use two spaces, no tabs, and one consistent key order. A useful order is:
73
- `description`, lifecycle (`enabled`/`expires`), `methods`, `parameters`,
74
- `request`, bindings (`env`/`secrets`), `middleware`, the handler, then `response`.
75
- This is visual organization, not an execution-order declaration.
76
-
77
- Use block style for nested behavior. Short scalar lists such as `[GET, HEAD]`
78
- and small leaf schemas such as `{type: string}` can stay inline. Omit ordinary
79
- defaults unless making them explicit clarifies a decision. Quote timestamps and
80
- numeric-looking header values. Avoid unrelated reformatting when changing a route.
81
-
82
- Example `features/welcome/routes.yaml`:
83
-
84
- ```yaml
85
- version: "1"
86
- routes:
87
- /welcome/{name}:
88
- description: A personalized welcome message with a consistent response header
89
- parameters:
90
- - name: name
91
- in: path
92
- required: true
93
- schema:
94
- type: string
95
- minLength: 1
96
- maxLength: 80
97
- middleware:
98
- - source: middleware/response-headers.mjs
99
- function:
100
- source: features/welcome/welcome.mjs
101
- args:
102
- name: {from: path, name: name}
103
- response:
104
- headers:
105
- Cache-Control: no-store
106
- ```
107
-
108
- Example `features/campaigns/routes.yaml`:
109
-
110
- ```yaml
111
- version: "1"
112
- routes:
113
- /guide:
114
- # Keep this temporary so a campaign destination can change later.
115
- redirect:
116
- url: https://example.com/guide
117
- ```
118
-
119
- Use comments for the reason behind a choice, migration notes or a business rule.
120
- Do not narrate obvious syntax or include secrets, private customer details or
121
- stale deployment instructions. `description` is useful authoring metadata; do
122
- not invent `owner`, `tags` or other unsupported YAML fields. Record ownership in
123
- the README or repository tooling instead.
124
-
125
- Order routes consistently within each collection, such as alphabetically or by
126
- business flow. Do not rely on file order for precedence: exact routes win before
127
- parameter routes, then static mounts. Avoid ambiguous overlaps; see [routing](ROUTING.md).
128
-
129
- ## Keep handlers short and name their job
130
-
131
- Name files for behavior (`welcome.mjs`, `select-destination.mjs`) rather than
132
- `utils2.mjs` or `handler-final.mjs`. Name exports clearly even when using a default
133
- export. Use descriptive variables and early returns for error cases. Separate
134
- business transformations from Request/Response handling when that improves clarity.
135
- Do not extract a one-line helper merely to create more files.
136
-
137
- `features/welcome/welcome.mjs`:
138
-
139
- ```js
140
- import {formatGreeting} from './greeting.mjs';
141
-
142
- export default function welcome(request, {args}) {
143
- return Response.json({message: formatGreeting(args.name)});
144
- }
145
- ```
146
-
147
- `features/welcome/greeting.mjs`:
148
-
149
- ```js
150
- export function formatGreeting(name) {
151
- return `Hello, ${name}!`;
152
- }
153
- ```
154
-
155
- YAML `source` paths are project-root relative. JavaScript imports are relative to
156
- the importing module. Explicit `.mjs` filenames make that distinction clear.
157
- Only relative project JavaScript imports are supported; do not introduce npm,
158
- Node, remote imports or a transpilation requirement accidentally. There is a
159
- project-wide module/source budget; excessive fragmentation has a real limit.
160
-
161
- Use validated `args` and `inputs` instead of parsing query strings again. Return
162
- JSON through `Response.json`; escape user data explicitly when producing HTML.
163
- For dynamic redirects, map a validated enum to known destinations rather than
164
- accepting any user-controlled URL. Keep modules free of top-level work other than
165
- simple definitions: initialization runs during validation and fresh invocations.
166
-
167
- Prefer pure helpers with explicit inputs and outputs. Module globals are not a
168
- cache, database, session store or rate limiter: guest state resets per request
169
- regardless of trust mode. If a route declares `sandbox: true`, review
170
- [sandbox constraints](FUNCTION-SECURITY.md) before choosing dependencies —
171
- trusted (default) routes have ordinary Node module access instead.
172
-
173
- ## Middleware should have one clear responsibility
174
-
175
- `middleware/response-headers.mjs`:
176
-
177
- ```js
178
- export default async function responseHeaders(request, context, next) {
179
- const response = await next();
180
- response.headers.set('x-app', 'my-links');
181
- return response;
182
- }
183
- ```
184
-
185
- Use middleware for reusable behavior around a handler, not to conceal the entire
186
- application flow. Prefer YAML headers for fixed route-specific headers; this
187
- example demonstrates a shared wrapper, but native YAML avoids any
188
- function/middleware invocation overhead when no custom code is needed —
189
- including the extra cost of `sandbox: true` where that is declared. Keep
190
- middleware order explicit in each route.
191
-
192
- Always return a Response. Call `await next()` once when continuing, or return
193
- an early Response when intentionally stopping. Do not launch unawaited work or
194
- assume background tasks will survive. A body read consumes the request body;
195
- if downstream code needs parsed data, agree on a documented `context.state` field.
196
- Use specific field names to avoid collisions among middleware.
197
-
198
- Catch only errors you can handle meaningfully. Do not turn every failure into a
199
- 200 response or include secrets in errors. Keep native body/status/header
200
- preservation rules visible in code review. All middleware share the route's
201
- bindings and execution budget; splitting modules does not create privilege
202
- separation. See [middleware semantics](MIDDLEWARE.md).
203
-
204
- ## Organize tests around observable behavior
205
-
206
- Keep runnable HTTP assertions in `tests/requests.json`, currently the single file
207
- read by the CLI. Group adjacent cases by route: ordinary success, HEAD, boundary
208
- inputs, invalid input, wrong method and relevant early responses. Add meaningful
209
- body/header checks instead of relying only on status codes.
210
-
211
- For the two-route feature layout above:
212
-
213
- ```json
214
- [
215
- {"path":"/welcome/Ada","status":200,"expectBody":"{\"message\":\"Hello, Ada!\"}","expectHeaders":{"x-app":"my-links"}},
216
- {"path":"/welcome/Ada","method":"HEAD","status":200,"expectBody":"","expectHeaders":{"x-app":"my-links"}},
217
- {"path":"/welcome/Ada","method":"POST","status":405,"expectHeaders":{"allow":"GET, HEAD"}},
218
- {"path":"/guide","status":302,"expectHeaders":{"location":"https://example.com/guide"}},
219
- {"path":"/guide","method":"HEAD","status":302,"expectBody":"","expectHeaders":{"location":"https://example.com/guide"}}
220
- ]
221
- ```
222
-
223
- Additional ordinary JavaScript unit tests for pure helpers are your project's
224
- choice. Unit tests alone do not verify runtime compatibility — including the
225
- guest API restrictions of a route declaring `sandbox: true` — or HTTP framing:
226
- always exercise HTTP behavior through URLCode too. Keep large fixture generation explicit and
227
- deterministic if you add your own tooling; nested test directories and JSON
228
- fragments are not automatically discovered or merged by URLCode.
229
-
230
- Assert observable contracts, not incidental timings or internal variable names.
231
- Avoid checking generated request IDs, current Date values or performance numbers
232
- as fixed functional outputs. Test cache/range semantics with controlled assets.
233
- Update expected route counts deliberately when adding or removing a route.
234
-
235
- ## Keep configuration, code, assets and operations separate
236
-
237
- - Git owns behavior and reviewed code. Keep the runtime as a pinned dependency;
238
- upgrading it should not regenerate or overwrite application files.
239
- - Publish only intentionally public files in `public/`. File filters cannot
240
- recognize every secret. A harmless filename is not proof of public content.
241
- - Put local secrets in ignored `.env.local`; production values come from the
242
- operator. Keep operator grants outside the application checkout. Never use
243
- YAML anchors, shell expansion or generated credentials for convenience.
244
- - Deployment limits, TLS, DNS, DDoS filters and worker tuning belong to operations,
245
- not invented route fields. Document them separately from portable behavior.
246
- - Live short-code records need durable storage core does not have. The
247
- `urlcode-dynamic-link` extension provided it and has been retired; its
248
- published `0.1.0-alpha.1` pins core `0.4.0-alpha.1` exactly and so cannot be
249
- installed beside `0.4.0-alpha.2`. Treat stored short links as unsupported
250
- until that work lands somewhere else. General session/application storage
251
- remains future work.
252
-
253
- ## Refactor without changing the public contract
254
-
255
- Move one feature at a time. Update explicit includes and project-root source/asset
256
- paths, then check relative JavaScript imports. Keep public route paths, methods,
257
- headers and bodies stable unless the change is intentional. Avoid mixing URL
258
- renames, dependency upgrades and folder rearrangement in one review.
259
-
260
- Run validate, HTTP tests and the expected-count audit before and after moving
261
- files. Re-review operator grants: code/config changes invalidate the digest even
262
- when intended behavior is unchanged. A successful dev reload is not a production
263
- deployment. Record activation/rollback steps in the project README.
264
-
265
- ## A useful project README
266
-
267
- Document how to install the pinned runtime, start locally, run tests/audit, and
268
- activate a release. Include a small folder map, the owner of each major feature,
269
- public route behavior and required external binding names (never values). State
270
- supported Node/runtime versions and link to matching URLCode docs. Explain any
271
- middleware ordering or surprising defaults that a new maintainer might miss.
272
-
273
- For AI-generated changes, require the assistant to follow existing conventions,
274
- keep diffs focused, add response assertions and report checks actually run. Reject
275
- invented YAML keys, hidden side effects and unnecessary abstractions. The
276
- [AI authoring guide](AI-AUTHORING.md) provides a reusable prompt and capability list.
package/docs/BULK.md DELETED
@@ -1,79 +0,0 @@
1
- # Bulk redirect projects
2
-
3
- Bulk import converts CSV, JSON or YAML redirect rows into an ordinary Git-owned
4
- URLCode project. It delegates semantics and row diagnostics to the strict
5
- [redirect interchange](INTERCHANGE.md) converter. Only literal paths and
6
- absolute HTTP(S) redirect destinations are accepted; unsupported behavior and
7
- duplicate paths fail rather than being discarded or overwritten.
8
-
9
- ```sh
10
- urlcode bulk-import csv redirects.csv --out ./imported --dry-run
11
- urlcode bulk-import csv redirects.csv --out ./imported
12
- urlcode validate --local --project ./imported
13
- ```
14
-
15
- CSV requires the header `path,url,status`; status can be empty for 302. JSON and
16
- YAML are arrays of `{path, url, status?}` rows. Input is limited to 32 MiB and
17
- 100,000 routes. This command is for data import, not arbitrary provider config.
18
- Use explicit provider interchange commands when migrating provider files.
19
-
20
- The SDK provides
21
- `importBulkProject(text, format, output, {dryRun, source})`. `format` is `csv`,
22
- `json` or `yaml`. The report includes `ok`, `routeCount`, source/row diagnostics,
23
- a SHA-256 fingerprint of the exact input text, and a bounded output-file plan.
24
- Invalid input returns `ok: false` without writing files. Filesystem/output
25
- refusals throw. `source` is a caller-supplied provenance label, not a file to read.
26
-
27
- Routes are sorted by literal path and divided into include files with at most
28
- 1,000 routes each. At 100,000 routes this produces 100 include files. The entry
29
- `urlcode.yaml` holds the include list and empty `routes`. `provenance.json`
30
- records the source label, input fingerprint, counts and each shard's first/last
31
- path. It does not embed the original input or claim exact source line mappings
32
- for successful output rows; retain the original file in version control if that
33
- traceability is needed. Invalid row diagnostics preserve their source positions.
34
-
35
- Dry-run performs conversion and output planning without writing the requested
36
- destination. Import requires a new output directory under an existing parent;
37
- there is no implicit merge or overwrite mode. As with recipes, dependencies are
38
- written first and a rename publishes the completed entry YAML last. Failed
39
- writes clean up the directory created by that invocation. Source data is never
40
- interpreted as shell code. The caller must control the output parent while
41
- publishing.
42
-
43
- ## Local scale evidence
44
-
45
- Run each dataset in a fresh process, sequentially, without concurrent builds:
46
-
47
- ```sh
48
- node benchmarks/bulk.ts 1000
49
- node benchmarks/bulk.ts 10000
50
- node benchmarks/bulk.ts 100000
51
- ```
52
-
53
- Observed on 2026-09-17 against the final integrated next-phase working tree,
54
- with the merged TypeScript 6.0.3 dependency lock, Node v26.8.2, macOS arm64,
55
- Apple M4 Pro and 48 GiB system memory. These measurements include the final
56
- capability, revision-pin and egress activation paths. Synthetic routes redirect `/rN` to `https://example.com/items/N`.
57
- Conversion includes input validation, semantic compilation and sharded output
58
- publication; activation uses the normal `createRuntime` loader and compiler.
59
- Lookup measures 5,000 `Runtime.handle` requests at concurrency one after 100
60
- warm-up requests, validating each status and Location. It excludes socket/TLS
61
- transport. Memory is sampled process RSS/heap after phases, not peak memory or
62
- an isolated worker measurement.
63
-
64
- | Routes | Includes | Conversion ms | Activation ms | RSS after activation MiB | Heap MiB | Lookup p95 ms |
65
- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
66
- | 1,000 | 1 | 56 | 139 | 180 | 27 | 0.002292 |
67
- | 10,000 | 10 | 272 | 373 | 288 | 46 | 0.002916 |
68
- | 100,000 | 100 | 5,530 | 8,838 | 537 | 214 | 0.003791 |
69
-
70
- All three sequential fresh-process datasets activated and returned the expected
71
- redirects. These are single observations, not a repeated-run statistical study;
72
- background host load can change startup and memory measurements. Unlike the
73
- previous single-document 100,000-route routing benchmark, the sharded project
74
- fits the existing configuration worker's 256 MiB heap and 10-second deadline.
75
- No loader, route-count, compiler deadline or runtime safety limit was increased.
76
- This local result does not establish cross-platform capacity, provider
77
- performance, peak-memory bounds, concurrency/soak behavior or production SLOs.
78
- The benchmark reports failure phase and error if a limit is reached on another
79
- machine; it does not retry with relaxed limits.