@jimhoyd/urlcode 0.4.1 → 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.
- package/.claude/skills/urlcode-authoring/SKILL.md +60 -7
- package/.claude/skills/urlcode-operations/SKILL.md +4 -0
- package/README.md +19 -15
- package/SECURITY.md +5 -3
- package/dist/BUILD-MANIFEST.json +31 -28
- package/dist/agent-context.js +82 -0
- package/dist/agents-guide.js +42 -42
- package/dist/authoring.js +12 -2
- package/dist/body-schema.js +159 -0
- package/dist/build-cloudflare.js +2 -0
- package/dist/capabilities.js +1 -1
- package/dist/cli.js +29 -12
- package/dist/config.js +74 -6
- package/dist/context.js +4 -6
- package/dist/errors.js +3 -1
- package/dist/examples.js +1 -1
- package/dist/extensions.js +138 -3
- package/dist/http-policy.js +19 -4
- package/dist/http-response.js +2 -2
- package/dist/init-with.js +71 -9
- package/dist/mcp.js +15 -2
- package/dist/pattern-guard.js +32 -0
- package/dist/policies/security.js +0 -0
- package/dist/policy.js +16 -0
- package/dist/project-tests.js +35 -11
- package/dist/readiness.js +188 -32
- package/dist/recipes.js +1 -1
- package/dist/router.js +17 -0
- package/dist/runtime.js +26 -3
- package/dist/scaffold.js +0 -0
- package/dist/server.js +26 -3
- package/dist/site.js +0 -0
- package/dist/tooling.js +3 -1
- package/dist/types/agent-context.d.ts +44 -0
- package/dist/types/authoring.d.ts +3 -1
- package/dist/types/body-schema.d.ts +58 -0
- package/dist/types/config.d.ts +10 -2
- package/dist/types/context.d.ts +1 -1
- package/dist/types/errors.d.ts +9 -1
- package/dist/types/examples.d.ts +1 -1
- package/dist/types/extensions.d.ts +105 -2
- package/dist/types/http-policy.d.ts +3 -0
- package/dist/types/init-with.d.ts +10 -1
- package/dist/types/pattern-guard.d.ts +10 -0
- package/dist/types/project-tests.d.ts +9 -0
- package/dist/types/readiness.d.ts +71 -0
- package/dist/types/recipes.d.ts +1 -1
- package/dist/types/runtime.d.ts +4 -0
- package/dist/types/server.d.ts +9 -1
- package/dist/types/tooling.d.ts +2 -0
- package/dist/types/types.d.ts +13 -0
- package/dist/types.js +10 -2
- package/dist/typescript-authoring.js +5 -3
- package/dist/verify-deployment.js +16 -10
- package/examples/body-validation/README.md +16 -0
- package/examples/body-validation/example.yaml +17 -0
- package/examples/body-validation/tests/requests.json +15 -0
- package/examples/body-validation/urlcode.yaml +34 -0
- package/examples/coverage-waiver/README.md +8 -0
- package/examples/coverage-waiver/example.yaml +16 -0
- package/examples/coverage-waiver/functions/notes.mjs +2 -0
- package/examples/coverage-waiver/tests/requests.json +3 -0
- package/examples/coverage-waiver/urlcode.yaml +10 -0
- package/examples/data-dir/README.md +39 -0
- package/examples/data-dir/data/welcome.txt +1 -0
- package/examples/data-dir/example.yaml +22 -0
- package/examples/data-dir/functions/note.mjs +18 -0
- package/examples/data-dir/tests/requests.json +6 -0
- package/examples/data-dir/urlcode.yaml +6 -0
- package/examples/lifecycle/README.md +15 -0
- package/examples/lifecycle/example.yaml +19 -0
- package/examples/lifecycle/functions/notes.mjs +29 -0
- package/examples/lifecycle/tests/requests.json +12 -0
- package/examples/lifecycle/urlcode.yaml +29 -0
- package/examples/not-found/README.md +10 -0
- package/examples/not-found/example.yaml +17 -0
- package/examples/not-found/public/404.html +3 -0
- package/examples/not-found/public/index.html +3 -0
- package/examples/not-found/tests/requests.json +7 -0
- package/examples/not-found/urlcode.yaml +7 -0
- package/examples/shared-blocks/README.md +11 -0
- package/examples/shared-blocks/example.yaml +17 -0
- package/examples/shared-blocks/tests/requests.json +7 -0
- package/examples/shared-blocks/urlcode.yaml +37 -0
- package/llms-full.txt +436 -149
- package/llms.txt +44 -6
- package/package.json +15 -22
- package/recipes/static-page/README.md +9 -0
- package/recipes/static-page/public/index.html +11 -0
- package/recipes/static-page/recipe.yaml +21 -0
- package/recipes/static-page/tests/requests.json +22 -0
- package/recipes/static-page/urlcode.yaml +7 -0
- package/recipes/static-plus-api/README.md +6 -0
- package/recipes/static-plus-api/urlcode.yaml +4 -0
- package/recipes/store-crud/README.md +53 -0
- package/recipes/store-crud/recipe.yaml +31 -0
- package/recipes/store-crud/tests/requests.json +18 -0
- package/recipes/store-crud/urlcode.yaml +18 -0
- package/schemas/urlcode.schema.json +125 -60
- package/skills/urlcode/SKILL.md +53 -26
- package/starters/default/AGENTS.md +43 -43
- package/starters/page/README.md +14 -0
- package/starters/page/public/index.html +12 -0
- package/starters/page/tests/requests.json +17 -0
- package/starters/page/urlcode.yaml +6 -0
- package/.claude-plugin/marketplace.json +0 -18
- package/CONTRIBUTING.md +0 -112
- package/ROADMAP.md +0 -66
- package/docs/AI-AUTHORING.md +0 -338
- package/docs/ASSETS.md +0 -107
- package/docs/AUTH-BACKUP.md +0 -32
- package/docs/AWS.md +0 -86
- package/docs/BEST-PRACTICES.md +0 -276
- package/docs/BULK.md +0 -79
- package/docs/CAPABILITIES.md +0 -192
- package/docs/CAPACITY.md +0 -305
- package/docs/CI-FOLLOWUP-2026-09-19.md +0 -97
- package/docs/CI-RELEASE-AUDIT-2026-09-19.md +0 -322
- package/docs/CI.md +0 -147
- package/docs/CLOUDFLARE.md +0 -109
- package/docs/CODEBASE-AUDIT-2026-09-20.md +0 -284
- package/docs/COMPLIANCE.md +0 -239
- package/docs/COMPOSING-A-SITE.md +0 -278
- package/docs/CONDITIONS.md +0 -74
- package/docs/DEPLOYMENT-CHECKS.md +0 -108
- package/docs/DEVELOPMENT-PIPELINE.md +0 -270
- package/docs/EGRESS.md +0 -125
- package/docs/EXTENSIONS.md +0 -438
- package/docs/FRAMEWORK.md +0 -217
- package/docs/FUNCTION-SECURITY.md +0 -254
- package/docs/HTTP.md +0 -129
- package/docs/INSTALL.md +0 -128
- package/docs/INTERCHANGE.md +0 -134
- package/docs/LOAD-TESTING.md +0 -91
- package/docs/LOCAL-DEVELOPMENT.md +0 -102
- package/docs/MIDDLEWARE-EXAMPLES.md +0 -75
- package/docs/MIDDLEWARE.md +0 -102
- package/docs/MONITORING.md +0 -115
- package/docs/OBSERVABILITY.md +0 -222
- package/docs/OPEN-DECISIONS.md +0 -224
- package/docs/OPERATIONAL-PROOF.md +0 -41
- package/docs/OPERATIONS.md +0 -201
- package/docs/ORGANIZATION.md +0 -135
- package/docs/PERFORMANCE.md +0 -72
- package/docs/PLUGINS.md +0 -271
- package/docs/POLICIES.md +0 -211
- package/docs/PRERENDER.md +0 -245
- package/docs/PROJECT-DIRECTION.md +0 -118
- package/docs/PROVIDER-VERIFICATION.md +0 -84
- package/docs/READINESS.md +0 -150
- package/docs/README.md +0 -87
- package/docs/RECIPES.md +0 -99
- package/docs/RELEASE-0.4.0-alpha.3.md +0 -50
- package/docs/RELEASE-0.4.1.md +0 -73
- package/docs/RELEASE-READINESS.md +0 -117
- package/docs/RELEASE-SECURITY.md +0 -96
- package/docs/RESILIENCE.md +0 -161
- package/docs/ROUTING.md +0 -92
- package/docs/SANDBOX-REVIEW.md +0 -72
- package/docs/SCAFFOLDING.md +0 -70
- package/docs/SECURITY-AUDIT.md +0 -164
- package/docs/SITE.md +0 -150
- package/docs/SPECIFICATION.md +0 -359
- package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +0 -288
- package/docs/SPIKE-BUSINESS-SUITE.md +0 -1029
- package/docs/SPIKE-CORE-LAYERING.md +0 -368
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +0 -211
- package/docs/STANDARDS.md +0 -311
- package/docs/STARTERS.md +0 -83
- package/docs/STATIC.md +0 -105
- package/docs/TOOLING.md +0 -298
- package/docs/TUNNELS.md +0 -72
- package/docs/TYPESCRIPT-AUTHORING.md +0 -87
- package/docs/TYPESCRIPT.md +0 -123
- package/docs/VERCEL.md +0 -114
- package/docs/VERSION-ALIGNMENT.md +0 -80
- package/docs/YAML-GUIDE.md +0 -57
- package/docs/YAML-REFERENCE.md +0 -449
- package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +0 -68
- package/docs/archive/2026-09-19/MANAGEMENT-SECURITY.md +0 -102
- package/docs/archive/2026-09-19/NEXT-PHASE-PLAN.md +0 -108
- package/docs/archive/2026-09-19/NEXT-STEPS.md +0 -646
- package/docs/archive/2026-09-19/OPEN-DECISIONS.md +0 -277
- package/docs/archive/2026-09-19/RELEASE-SECURITY.md +0 -186
- package/docs/archive/2026-09-19/ROADMAP.md +0 -387
- package/docs/archive/2026-09-19/SPIKE-EXTENSION-MODEL.md +0 -430
- package/docs/archive/2026-09-19/SPIKE-EXTENSIONS.md +0 -492
- package/docs/archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md +0 -365
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +0 -778
- package/docs/archive/2026-09-19/USABILITY-REVIEW.md +0 -139
- package/docs/archive/README.md +0 -28
- package/docs/policies/agents.md +0 -182
- package/docs/policies/cache.md +0 -152
- package/docs/policies/compression.md +0 -169
- package/docs/policies/contract.md +0 -52
- package/docs/policies/hardened.md +0 -56
- package/docs/policies/interoperability.md +0 -169
- package/docs/policies/operations.md +0 -45
- package/docs/policies/security.md +0 -161
- package/docs/policies/throttle.md +0 -103
- package/docs/yaml/assets.md +0 -36
- package/docs/yaml/conditions.md +0 -20
- package/docs/yaml/functions.md +0 -168
- package/docs/yaml/middleware.md +0 -31
- package/docs/yaml/organization.md +0 -74
- package/docs/yaml/policies.md +0 -37
- package/docs/yaml/redirects.md +0 -64
- package/docs/yaml/responses.md +0 -57
- package/docs/yaml/site.md +0 -24
- package/packaging/claude-plugin/.claude-plugin/plugin.json +0 -19
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +0 -120
- package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +0 -108
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.
|
package/docs/BEST-PRACTICES.md
DELETED
|
@@ -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.
|
package/docs/CAPABILITIES.md
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
# Capabilities and normalized route representation
|
|
2
|
-
|
|
3
|
-
URLCode is a portable runtime for programmable URL behavior: **URL behavior as
|
|
4
|
-
code**. YAML describes behavior; operators supply infrastructure and authority.
|
|
5
|
-
|
|
6
|
-
## Inspect target support
|
|
7
|
-
|
|
8
|
-
```sh
|
|
9
|
-
urlcode capabilities
|
|
10
|
-
urlcode capabilities --target self-hosted
|
|
11
|
-
urlcode capabilities --target cloudflare --json
|
|
12
|
-
urlcode capabilities --target aws
|
|
13
|
-
urlcode capabilities --target vercel
|
|
14
|
-
urlcode capabilities --target static
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
This command needs no project or credentials. `node` is an alias for
|
|
18
|
-
`self-hosted`, matching the existing embedding API. Unknown target names fail.
|
|
19
|
-
JSON has `format: 1`, target deployment evidence and capability rows.
|
|
20
|
-
`doctor` also reports `capabilityTargets`; its `providers` list remains empty
|
|
21
|
-
because no provider deployment has been verified. Canonical
|
|
22
|
-
names follow the schema (`respond`, `extension`, `policies.security`), not
|
|
23
|
-
marketing synonyms. `proxy` and `signals` are self-hosted capabilities requiring
|
|
24
|
-
external revision-pinned origin grants. `conditions` (`match`) and `conditional`
|
|
25
|
-
(disjoint cases) are supported by self-hosted/AWS/Vercel and refused by
|
|
26
|
-
Cloudflare (no artifact lowering yet) and by `static` (no server to match a
|
|
27
|
-
request against). `extension`/`policies.extensions` report per-extension
|
|
28
|
-
support from the registered extension's own declared `targets` when a
|
|
29
|
-
`--host-file` is supplied; without one they report `conditional`/`unknown`
|
|
30
|
-
rather than a blanket answer. See [egress](EGRESS.md) and
|
|
31
|
-
[conditions](CONDITIONS.md).
|
|
32
|
-
|
|
33
|
-
| Support | Meaning |
|
|
34
|
-
| --- | --- |
|
|
35
|
-
| native | Implemented by the local runtime or Node adapter |
|
|
36
|
-
| compiled | Implemented by the Cloudflare or static-hosting compiler and its runtime/build output |
|
|
37
|
-
| conditional | Depends on configuration; inspect the actual project |
|
|
38
|
-
| delegated | Existing policy contract relies on provider behavior |
|
|
39
|
-
| refused | No implementation that this target can activate |
|
|
40
|
-
| unknown | No support evidence; fail closed during project analysis |
|
|
41
|
-
|
|
42
|
-
`native` and `compiled` describe local implementation tests. AWS, Vercel,
|
|
43
|
-
Cloudflare and static deployment evidence remains **unverified**. This is not a
|
|
44
|
-
blanket exact-portability promise. Cloudflare coalesces duplicate headers and
|
|
45
|
-
receives a normalized Request target; AWS accepts payload v2 only; `static` has
|
|
46
|
-
no server at all, so it refuses every capability that needs one (parameters,
|
|
47
|
-
request bodies, response headers, bindings, every `policies.*`) in addition to
|
|
48
|
-
`function`/`middleware`. See [Cloudflare](CLOUDFLARE.md), [AWS](AWS.md),
|
|
49
|
-
[Vercel](VERCEL.md) and [static hosting](STATIC.md) for transport and fidelity
|
|
50
|
-
limits. Compression is explicitly delegated, not verified equivalent to
|
|
51
|
-
operator-selected settings. Route throttle counters and caches remain per
|
|
52
|
-
instance. No supported entry bypasses semantic validation, required operator
|
|
53
|
-
grants or deployment prerequisites.
|
|
54
|
-
|
|
55
|
-
## One capability or one schema fragment
|
|
56
|
-
|
|
57
|
-
```sh
|
|
58
|
-
urlcode capabilities redirect
|
|
59
|
-
urlcode capabilities policies.throttle --json
|
|
60
|
-
urlcode schema route
|
|
61
|
-
urlcode schema policies.cache --json
|
|
62
|
-
urlcode schema site.sitemap --yaml
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
`urlcode capabilities <name>` prints one catalog entry: its kind (handler,
|
|
66
|
-
policy, routing, request, binding, egress, middleware or project), a summary,
|
|
67
|
-
the resolved schema fragment(s), constraints, the operator grants the capability
|
|
68
|
-
needs at activation, support per target, the targets that refuse it, and the
|
|
69
|
-
bundled recipes and cookbook routes that use it. Names are the catalog names
|
|
70
|
-
(`redirect`, `bindings`, `policies.cache`); an unknown name fails with exit 1
|
|
71
|
-
and lists the valid names. `--target` applies to the full catalog only.
|
|
72
|
-
|
|
73
|
-
`urlcode schema <path>` prints only that fragment of
|
|
74
|
-
`schemas/urlcode.schema.json` with local `$ref`s resolved inline. Paths are
|
|
75
|
-
top-level document keys (`routes`, `policies`, `site`, `extensions`), `route`,
|
|
76
|
-
or a route property (`redirect`, `middleware`, `match`, `env`), optionally
|
|
77
|
-
followed by nested property names (`policies.cache`, `request.body`,
|
|
78
|
-
`site.sitemap`). Resolution is bounded and cycle-safe; where a nested object is
|
|
79
|
-
its own path (`route` inside `routes`, `policies` inside `route`) it is
|
|
80
|
-
summarized with a `$comment` naming that path so every fragment stays under
|
|
81
|
-
16 KiB. Fragments describe shape only: they carry no defaults, validation
|
|
82
|
-
result or operator authority, and the full schema remains the contract.
|
|
83
|
-
|
|
84
|
-
Both commands read bundled package data and need no project, credentials or
|
|
85
|
-
network. The SDK exposes them as `getCapability(name)` and
|
|
86
|
-
`getSchemaFragment(path)`; the MCP server as `get_capability` and `get_schema`
|
|
87
|
-
(see [tooling](TOOLING.md)). Grant descriptions name the operator flag or policy
|
|
88
|
-
involved, never binding values.
|
|
89
|
-
|
|
90
|
-
## Programmatic analysis
|
|
91
|
-
|
|
92
|
-
The main package exports `getCapabilities`, `routeCapabilities`,
|
|
93
|
-
`analyzeProjectCapabilities`, `analyzeCompiledCapabilities`,
|
|
94
|
-
`assertTargetCompatibility`, `normalizeCapabilityTarget` and their report types.
|
|
95
|
-
|
|
96
|
-
```js
|
|
97
|
-
import { loadDocument, analyzeProjectCapabilities,
|
|
98
|
-
assertTargetCompatibility } from '@jimhoyd/urlcode';
|
|
99
|
-
|
|
100
|
-
const loaded = await loadDocument('./project');
|
|
101
|
-
const report = analyzeProjectCapabilities(loaded, 'cloudflare');
|
|
102
|
-
console.log(report.issues); // path, capability, support, reason; never binding values
|
|
103
|
-
assertTargetCompatibility(report);
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
This low-level example examines declared routes. Runtime activation and builds
|
|
107
|
-
first expand `site` conventions using the operator origin, then analyze all
|
|
108
|
-
routes including generated ones. A report is a compatibility preflight, **not**
|
|
109
|
-
a substitute for compilation/validation. Disabled and expired routes are still
|
|
110
|
-
checked. `analyzeProjectCapabilities`/`analyzeCompiledCapabilities` take an
|
|
111
|
-
optional resolved extension registration set (the same shape `--host-file`
|
|
112
|
-
loads); pass it to get per-extension `refused`/`native` from that extension's
|
|
113
|
-
own `targets` instead of the generic `conditional`/`unknown` answer.
|
|
114
|
-
`compatible` means there are no refused, unknown or unresolved conditional
|
|
115
|
-
requirements. Explicit delegation and transport limitations still apply.
|
|
116
|
-
|
|
117
|
-
Requirements include effective inherited/profile policies after route overrides
|
|
118
|
-
and `false` removals. Policy modules' existing `targets(config)` functions remain
|
|
119
|
-
the source of policy decisions. The catalog says serverless throttle is
|
|
120
|
-
conditional; a project report resolves `partition: route` to native and the
|
|
121
|
-
client partitions to refused. Reports contain paths and capability facts, not
|
|
122
|
-
sources, destinations, binding names/values, code, validator closures or assets.
|
|
123
|
-
|
|
124
|
-
Build/activation refusals aggregate all incompatible requirements and name each
|
|
125
|
-
route, capability, target and reason before any artifact files are written.
|
|
126
|
-
Unsupported bindings on Cloudflare fail before credentials are resolved.
|
|
127
|
-
|
|
128
|
-
## Existing IR, formalized
|
|
129
|
-
|
|
130
|
-
The implementation already has a useful internal representation:
|
|
131
|
-
|
|
132
|
-
```text
|
|
133
|
-
strict YAML + schema validation + includes
|
|
134
|
-
→ site expansion
|
|
135
|
-
→ shared capability preflight (declarations, no credentials)
|
|
136
|
-
→ semantic route compilation
|
|
137
|
-
→ CompiledRouteTable / CompiledRoute
|
|
138
|
-
→ capability analysis / target lowering
|
|
139
|
-
→ host assets + policy chains + trusted or isolated function dispatch, or Worker artifact
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
`CompiledRoute` in `src/types.ts` extends shared `MatchableRoute` with validated
|
|
143
|
-
parameters, normalized HTTP replies/headers, resolved bindings and source
|
|
144
|
-
references. `CompiledRouteTable` indexes literal paths, parameter buckets and
|
|
145
|
-
static mounts. `router.ts` owns precedence, collision checks, default methods,
|
|
146
|
-
input/reference validation and normalization. `match.ts` supplies portable
|
|
147
|
-
request-time matching to both runtime and Worker. No second parser or competing
|
|
148
|
-
route IR is introduced.
|
|
149
|
-
|
|
150
|
-
`routeCapabilities` is a value-free projection used for both declaration
|
|
151
|
-
preflight and compiled analysis. Preflight deliberately precedes full semantic
|
|
152
|
-
validation to report unsupported features before reading missing assets or
|
|
153
|
-
resolving secrets; it cannot authorize or validate a project. Normal compilation
|
|
154
|
-
and policy validation still run. AWS/Vercel consume the shared runtime IR;
|
|
155
|
-
Cloudflare additionally analyzes the compiled table before serializing its
|
|
156
|
-
allowlisted artifact fields and standalone validators.
|
|
157
|
-
|
|
158
|
-
The compiled table is internal, mutable during activation and **not serializable
|
|
159
|
-
as an interchange contract**: bindings contain resolved secrets, validators are
|
|
160
|
-
functions, assets contain bytes and policy chains own host state. The existing
|
|
161
|
-
Cloudflare artifact is a separate versioned lowering, not a replacement IR.
|
|
162
|
-
[Interchange](INTERCHANGE.md) projects a validated literal-redirect subset and
|
|
163
|
-
explicitly reports unsupported semantics; it never dumps compiled routes.
|
|
164
|
-
The next-phase schema extends this same IR with normalized condition cases,
|
|
165
|
-
proxy headers and signal definitions. Resolved egress headers are private runtime
|
|
166
|
-
state and must never be serialized. Capability analysis itself adds no authority
|
|
167
|
-
and does not run in the request path.
|
|
168
|
-
|
|
169
|
-
See the [repository review and incremental plan](archive/2026-09-19/NEXT-PHASE-PLAN.md) for the
|
|
170
|
-
implementation status. Provider deployment tests, independent security review and
|
|
171
|
-
operational soak/recovery proof are separate work.
|
|
172
|
-
|
|
173
|
-
## Local performance check
|
|
174
|
-
|
|
175
|
-
2026-09-17, Node 26.8.2, macOS arm64, Apple M4 Pro, 48 GiB RAM.
|
|
176
|
-
`npm run benchmark -- <count>` runs 5,000 loopback requests at concurrency 16.
|
|
177
|
-
Single runs against baseline `1a00294` and this change, not a statistical study:
|
|
178
|
-
|
|
179
|
-
| Routes | Startup ms before / after | RSS MiB before / after | Heap MiB before / after | Requests/s before / after | p95 ms before / after |
|
|
180
|
-
| --- | --- | --- | --- | --- | --- |
|
|
181
|
-
| 1,000 | 135 / 138 | 183 / 182 | 29 / 34 | 24,645 / 24,701 | 1.18 / 1.23 |
|
|
182
|
-
| 10,000 | 312 / 303 | 211 / 207 | 55 / 63 | 6,762 / 6,829 | 4.08 / 4.09 |
|
|
183
|
-
|
|
184
|
-
Both baseline and updated 100,000-route runs failed with `Configuration worker
|
|
185
|
-
resource limit or failure` before route compilation. The worker's existing
|
|
186
|
-
resource bounds are unchanged; the configured 100k route ceiling is not evidence
|
|
187
|
-
that every 100k YAML document fits those bounds. Bulk-scale remediation and
|
|
188
|
-
repeatable memory profiling were subsequent work at Phase A. The new
|
|
189
|
-
[bulk sharding benchmark](BULK.md) successfully activates 100,000 routes within
|
|
190
|
-
the unchanged worker limits. Capability analysis adds
|
|
191
|
-
linear activation work and temporary report allocations; no request-time checks
|
|
192
|
-
were added. These measurements are not provider, soak or capacity certification.
|