@jimhoyd/urlcode 0.4.0-alpha.1 → 0.4.0-alpha.3
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 +36 -22
- package/.claude/skills/urlcode-operations/SKILL.md +16 -22
- package/.claude-plugin/marketplace.json +1 -1
- package/CONTRIBUTING.md +20 -16
- package/README.md +83 -80
- package/ROADMAP.md +61 -331
- package/SECURITY.md +23 -8
- package/dist/BUILD-MANIFEST.json +42 -47
- package/dist/agents-guide.js +18 -18
- package/dist/build-static.js +136 -0
- package/dist/capabilities.js +85 -31
- package/dist/capability-query.js +0 -1
- package/dist/cli.js +24 -32
- package/dist/compliance-rules/baseline.js +2 -10
- package/dist/compliance-rules/privacy.js +5 -16
- package/dist/compliance-rules/shared.js +0 -2
- package/dist/compliance.js +6 -8
- package/dist/config.js +15 -8
- package/dist/context.js +9 -10
- package/dist/examples.js +2 -2
- package/dist/explain-cli.js +7 -5
- package/dist/explain.js +10 -5
- package/dist/extensions.js +61 -2
- package/dist/function-sources.js +34 -2
- package/dist/function-worker.js +3 -1
- package/dist/functions.js +84 -13
- package/dist/guest-api.js +29 -3
- package/dist/index.js +3 -6
- package/dist/manifest.js +11 -7
- package/dist/mcp-authoring.js +2 -2
- package/dist/mcp.js +1 -1
- package/dist/observability.js +1 -21
- package/dist/policies/cache.js +8 -3
- package/dist/policies.js +3 -1
- package/dist/policy.js +28 -9
- package/dist/prerender.js +4 -0
- package/dist/project-tests.js +3 -3
- package/dist/readiness.js +35 -9
- package/dist/route-diff.js +12 -5
- package/dist/router.js +5 -7
- package/dist/runtime.js +77 -58
- package/dist/sandbox.js +48 -0
- package/dist/scaffold.js +0 -0
- package/dist/scripts/operational-drills.js +12 -54
- package/dist/server.js +3 -29
- package/dist/tooling.js +1 -1
- package/dist/trusted-functions.js +210 -0
- package/dist/types/build-static.d.ts +43 -0
- package/dist/types/capabilities.d.ts +14 -5
- package/dist/types/compliance-rules/shared.d.ts +0 -2
- package/dist/types/compliance.d.ts +0 -3
- package/dist/types/config.d.ts +2 -1
- package/dist/types/context.d.ts +2 -1
- package/dist/types/examples.d.ts +1 -1
- package/dist/types/explain.d.ts +6 -0
- package/dist/types/extensions.d.ts +55 -0
- package/dist/types/function-sources.d.ts +4 -0
- package/dist/types/functions.d.ts +48 -5
- package/dist/types/guest-api.d.ts +1 -0
- package/dist/types/index.d.ts +3 -6
- package/dist/types/manifest.d.ts +5 -3
- package/dist/types/observability.d.ts +1 -14
- package/dist/types/project-tests.d.ts +1 -2
- package/dist/types/readiness.d.ts +12 -3
- package/dist/types/router.d.ts +2 -1
- package/dist/types/runtime.d.ts +0 -27
- package/dist/types/sandbox.d.ts +12 -0
- package/dist/types/scaffold.d.ts +0 -2
- package/dist/types/server.d.ts +1 -4
- package/dist/types/tooling.d.ts +3 -3
- package/dist/types/trusted-functions.d.ts +29 -0
- package/dist/types/types.d.ts +22 -8
- package/dist/types/verify-deployment.d.ts +2 -2
- package/dist/types.js +25 -5
- package/dist/typescript-authoring.js +67 -18
- package/dist/verify-deployment.js +3 -3
- package/docs/AI-AUTHORING.md +109 -10
- package/docs/ASSETS.md +2 -1
- package/docs/AUTH-BACKUP.md +32 -0
- package/docs/AWS.md +4 -4
- package/docs/BEST-PRACTICES.md +17 -8
- package/docs/CAPABILITIES.md +30 -17
- package/docs/CAPACITY.md +128 -31
- package/docs/CI-FOLLOWUP-2026-09-19.md +97 -0
- package/docs/CI-RELEASE-AUDIT-2026-09-19.md +322 -0
- package/docs/CI.md +8 -3
- package/docs/CLOUDFLARE.md +1 -2
- package/docs/CODEBASE-AUDIT-2026-09-20.md +278 -0
- package/docs/COMPLIANCE.md +6 -9
- package/docs/DEPLOYMENT-CHECKS.md +1 -1
- package/docs/DEVELOPMENT-PIPELINE.md +181 -0
- package/docs/EXTENSIONS.md +193 -11
- package/docs/FRAMEWORK.md +53 -33
- package/docs/FUNCTION-SECURITY.md +173 -32
- package/docs/INSTALL.md +0 -5
- package/docs/LOAD-TESTING.md +4 -4
- package/docs/LOCAL-DEVELOPMENT.md +3 -0
- package/docs/MIDDLEWARE.md +27 -16
- package/docs/MONITORING.md +2 -19
- package/docs/OBSERVABILITY.md +7 -16
- package/docs/OPEN-DECISIONS.md +184 -0
- package/docs/OPERATIONAL-PROOF.md +26 -30
- package/docs/OPERATIONS.md +23 -32
- package/docs/POLICIES.md +27 -8
- package/docs/PRERENDER.md +25 -13
- package/docs/PROJECT-DIRECTION.md +35 -10
- package/docs/READINESS.md +6 -2
- package/docs/README.md +20 -41
- package/docs/RELEASE-0.4.0-alpha.3.md +50 -0
- package/docs/RELEASE-READINESS.md +50 -30
- package/docs/RELEASE-SECURITY.md +72 -86
- package/docs/RESILIENCE.md +16 -15
- package/docs/ROUTING.md +7 -10
- package/docs/SANDBOX-REVIEW.md +19 -6
- package/docs/SCAFFOLDING.md +0 -2
- package/docs/SECURITY-AUDIT.md +42 -2
- package/docs/SPECIFICATION.md +63 -27
- package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +288 -0
- package/docs/SPIKE-BUSINESS-SUITE.md +16 -0
- package/docs/SPIKE-CORE-LAYERING.md +368 -0
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +211 -0
- package/docs/SPIKE-LAMBDA-COMPILE.md +12 -2
- package/docs/STARTERS.md +1 -1
- package/docs/STATIC.md +105 -0
- package/docs/TOOLING.md +17 -12
- package/docs/TUNNELS.md +0 -3
- package/docs/TYPESCRIPT-AUTHORING.md +32 -12
- package/docs/TYPESCRIPT.md +25 -4
- package/docs/VERCEL.md +4 -5
- package/docs/VERSION-ALIGNMENT.md +46 -0
- package/docs/YAML-GUIDE.md +0 -3
- package/docs/YAML-REFERENCE.md +16 -6
- package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +68 -0
- package/docs/{MANAGEMENT-SECURITY.md → archive/2026-09-19/MANAGEMENT-SECURITY.md} +20 -0
- package/docs/{NEXT-PHASE-PLAN.md → archive/2026-09-19/NEXT-PHASE-PLAN.md} +26 -8
- package/docs/{NEXT-STEPS.md → archive/2026-09-19/NEXT-STEPS.md} +274 -43
- package/docs/archive/2026-09-19/OPEN-DECISIONS.md +277 -0
- package/docs/archive/2026-09-19/RELEASE-SECURITY.md +186 -0
- package/docs/archive/2026-09-19/ROADMAP.md +386 -0
- package/docs/{SPIKE-EXTENSION-MODEL.md → archive/2026-09-19/SPIKE-EXTENSION-MODEL.md} +12 -2
- package/docs/{SPIKE-EXTENSIONS.md → archive/2026-09-19/SPIKE-EXTENSIONS.md} +28 -12
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +776 -0
- package/docs/{USABILITY-REVIEW.md → archive/2026-09-19/USABILITY-REVIEW.md} +18 -2
- package/docs/archive/README.md +27 -0
- package/docs/policies/agents.md +1 -1
- package/docs/policies/cache.md +13 -0
- package/docs/policies/compression.md +3 -2
- package/docs/policies/security.md +3 -2
- package/examples/assets/example.yaml +2 -2
- package/examples/aws/example.yaml +1 -1
- package/examples/cloudflare/example.yaml +1 -1
- package/examples/conditions/example.yaml +1 -1
- package/examples/cookbook/example.yaml +1 -1
- package/examples/cookbook/middleware/bucket.mjs +12 -2
- package/examples/cookbook/middleware/locale.mjs +7 -3
- package/examples/cookbook/route-index.json +1 -1
- package/examples/cookbook/routes/middleware.yaml +1 -1
- package/examples/egress/example.yaml +1 -1
- package/examples/extensions/example.yaml +1 -1
- package/examples/prerender/README.md +14 -6
- package/examples/prerender/example.yaml +1 -1
- package/examples/prerender/functions/page.mjs +4 -2
- package/examples/prerender/middleware/template.mjs +1 -1
- package/examples/prerender/prerender.mjs +1 -1
- package/examples/prerender/urlcode.yaml +8 -4
- package/examples/provider-conformance/example.yaml +1 -1
- package/examples/vercel/example.yaml +1 -1
- package/llms-full.txt +686 -239
- package/llms.txt +27 -15
- package/package.json +32 -5
- package/packaging/claude-plugin/.claude-plugin/plugin.json +2 -2
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +36 -22
- package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +16 -22
- package/recipes/authenticated-json-api/README.md +4 -3
- package/recipes/authenticated-json-api/functions/profile.mjs +2 -1
- package/recipes/authenticated-json-api/recipe.yaml +2 -2
- package/recipes/contact-form/functions/contact.mjs +2 -1
- package/recipes/contact-form/recipe.yaml +3 -3
- package/recipes/cors-api/README.md +2 -2
- package/recipes/cors-api/recipe.yaml +2 -2
- package/recipes/health-page/README.md +1 -1
- package/recipes/health-page/recipe.yaml +1 -1
- package/recipes/json-api/README.md +1 -1
- package/recipes/json-api/recipe.yaml +4 -4
- package/recipes/middleware/README.md +8 -4
- package/recipes/middleware/middleware/bucket.mjs +12 -2
- package/recipes/middleware/middleware/locale.mjs +7 -3
- package/recipes/middleware/recipe.yaml +1 -1
- package/recipes/protected-download/README.md +1 -1
- package/recipes/protected-download/recipe.yaml +2 -2
- package/recipes/redirect/recipe.yaml +1 -1
- package/recipes/static-plus-api/README.md +2 -2
- package/recipes/static-plus-api/public/index.html +1 -1
- package/recipes/static-plus-api/recipe.yaml +2 -2
- package/recipes/static-plus-api/urlcode.yaml +1 -1
- package/recipes/typescript/README.md +2 -1
- package/recipes/typescript/recipe.yaml +5 -5
- package/recipes/webhook-receiver/README.md +5 -1
- package/recipes/webhook-receiver/recipe.yaml +2 -1
- package/recipes/webhook-receiver/urlcode.yaml +7 -0
- package/schemas/recipe.schema.json +4 -3
- package/schemas/urlcode.schema.json +14 -41
- package/skills/urlcode/SKILL.md +32 -11
- package/starters/default/AGENTS.md +18 -18
- package/starters/default/urlcode.yaml +0 -1
- package/dist/link-api.js +0 -136
- package/dist/link-cli.js +0 -141
- package/dist/link-events.js +0 -76
- package/dist/link-records.js +0 -31
- package/dist/link-store-worker.js +0 -150
- package/dist/link-store.js +0 -250
- package/dist/management-policy.js +0 -40
- package/dist/sqlite-version.js +0 -6
- package/dist/types/link-api.d.ts +0 -30
- package/dist/types/link-cli.d.ts +0 -37
- package/dist/types/link-events.d.ts +0 -27
- package/dist/types/link-records.d.ts +0 -11
- package/dist/types/link-store-worker.d.ts +0 -1
- package/dist/types/link-store.d.ts +0 -130
- package/dist/types/management-policy.d.ts +0 -8
- package/dist/types/sqlite-version.d.ts +0 -1
- package/docs/DYNAMIC-LINKS.md +0 -61
- package/docs/links/cli.md +0 -110
- package/docs/links/limits.md +0 -175
- package/docs/links/management-api.md +0 -80
- package/docs/links/pools.md +0 -75
- package/docs/links/setup.md +0 -135
- package/docs/yaml/links.md +0 -30
- package/examples/live-links/README.md +0 -11
- package/examples/live-links/example.yaml +0 -21
- package/examples/live-links/tests/requests.json +0 -6
- package/examples/live-links/urlcode.yaml +0 -16
package/llms.txt
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
# URLCode
|
|
2
2
|
|
|
3
3
|
> A portable runtime for programmable URL behavior, and the framework that grows
|
|
4
|
-
> from it: routes in YAML,
|
|
5
|
-
>
|
|
6
|
-
> `version: "1"`. Core is Apache-2.0; `0.4.0-alpha.
|
|
4
|
+
> from it: routes in YAML, functions and middleware, then accounts, administration and
|
|
5
|
+
> stored links as operator-installed extensions. Stable project format
|
|
6
|
+
> `version: "1"`. Core is Apache-2.0; this revision is `0.4.0-alpha.2`, which makes
|
|
7
|
+
> `function`/`middleware` routes trusted by default with `sandbox: true` as the
|
|
8
|
+
> per-route opt-in; `0.4.0-alpha.1` is the newest alpha published to npm, on top of
|
|
7
9
|
> the `0.3.0` release. The auth, admin and ui extension packages are on npm as
|
|
8
|
-
> `0.1.0-alpha.
|
|
10
|
+
> `0.1.0-alpha.x`, source-complete, review pending.
|
|
9
11
|
|
|
10
12
|
Use the schema and docs from the runtime revision you run. Do not assume Node
|
|
11
|
-
or fetch
|
|
12
|
-
interpolation, or packages named in YAML. Secrets need external revision-pinned
|
|
13
|
+
or fetch inside a `sandbox: true` function, regex routes, database access,
|
|
14
|
+
global middleware, YAML interpolation, or packages named in YAML. Secrets need external revision-pinned
|
|
13
15
|
grants. Unsupported features fail with the route named; nothing degrades silently.
|
|
14
16
|
|
|
15
17
|
Agents that explicitly want the complete consolidated reference in one fetch should read
|
|
16
18
|
[llms-full.txt](llms-full.txt), generated from the documents below (about 50k tokens, estimated).
|
|
17
19
|
|
|
20
|
+
## Declarative-first default
|
|
21
|
+
|
|
22
|
+
> Use URLCode's highest-level declarative features whenever possible. Generate custom code only when the framework cannot express the requirement.
|
|
23
|
+
|
|
24
|
+
Check the installed version's primitives, YAML configuration, policies, supported
|
|
25
|
+
extensions and recipes/templates before writing a custom function or middleware.
|
|
26
|
+
Keep necessary custom code focused and report the capability gap; never invent
|
|
27
|
+
fields or bypass target limits or operator grants. See [the design principle](docs/PROJECT-DIRECTION.md#design-principle-declarative-first).
|
|
28
|
+
|
|
18
29
|
## Authoring
|
|
19
30
|
- [AI authoring contract](docs/AI-AUTHORING.md): workflow, capability matrix, checks.
|
|
20
31
|
- [Authoring skill](.claude/skills/urlcode-authoring/SKILL.md): loadable authoring skill shipped with this revision; see [distribution](docs/AI-AUTHORING.md#agent-skills).
|
|
@@ -24,12 +35,11 @@ Agents that explicitly want the complete consolidated reference in one fetch sho
|
|
|
24
35
|
- [Field reference](docs/YAML-REFERENCE.md): generated exhaustive field inventory.
|
|
25
36
|
- [Semantics](docs/SPECIFICATION.md): validation, defaults and sandbox API.
|
|
26
37
|
- [Runnable cookbook](examples/cookbook/README.md): 25 routes with HTTP fixtures.
|
|
27
|
-
- [Dynamic links](docs/DYNAMIC-LINKS.md): optional SQLite, live mutations, management API and limits.
|
|
28
38
|
- [Route matching](docs/ROUTING.md): precedence, non-greedy parameters, updates.
|
|
29
39
|
- [Middleware](docs/MIDDLEWARE.md): next(), state, ordering and native body limits.
|
|
30
40
|
- [Policies](docs/POLICIES.md): optional host-enforced `policies`/`profiles` keys, all off by default: `throttle`, `agents`, `security`, `compression`, `cache`; merge rules and per-target support.
|
|
31
41
|
- [Plugins](docs/PLUGINS.md): host hook API operators pass in code; never named in YAML.
|
|
32
|
-
- [TypeScript](docs/TYPESCRIPT.md): the package ships declarations for every export (`urlcode`, `@jimhoyd/urlcode/plugins`, `@jimhoyd/urlcode/policies`, `@jimhoyd/urlcode/observability`, `@jimhoyd/urlcode/compliance`, `@jimhoyd/urlcode/prerender`, `@jimhoyd/urlcode/aws`, `@jimhoyd/urlcode/vercel`, `@jimhoyd/urlcode/cloudflare`); the runtime source is TypeScript, `dist/` is its stripped JavaScript.
|
|
42
|
+
- [TypeScript](docs/TYPESCRIPT.md): the package ships declarations for every export (`urlcode`, `@jimhoyd/urlcode/plugins`, `@jimhoyd/urlcode/policies`, `@jimhoyd/urlcode/observability`, `@jimhoyd/urlcode/compliance`, `@jimhoyd/urlcode/prerender`, `@jimhoyd/urlcode/sandbox`, `@jimhoyd/urlcode/aws`, `@jimhoyd/urlcode/vercel`, `@jimhoyd/urlcode/cloudflare`); the runtime source is TypeScript, `dist/` is its stripped JavaScript.
|
|
33
43
|
- [HTTP](docs/HTTP.md): methods, request bodies and response headers.
|
|
34
44
|
- [Assets](docs/ASSETS.md): pages, MIME, downloads, cache and ranges.
|
|
35
45
|
- [Site conventions](docs/SITE.md): optional top-level `site` key, all off by default: `robots`, `sitemap`, `favicon`, `securityTxt`, `llms` generate native routes; declared routes win; absolute URLs need `--origin`.
|
|
@@ -46,10 +56,9 @@ Agents that explicitly want the complete consolidated reference in one fetch sho
|
|
|
46
56
|
## Routes and handlers
|
|
47
57
|
- [Routing](docs/ROUTING.md): exact and `{param}` paths, precedence, `/*` only on static and extension mounts.
|
|
48
58
|
- [HTTP](docs/HTTP.md): methods, validated inputs, bodies, response headers, cookies.
|
|
49
|
-
- [Function security](docs/FUNCTION-SECURITY.md): QuickJS/WASM
|
|
59
|
+
- [Function security](docs/FUNCTION-SECURITY.md): trusted and unsandboxed by default, `sandbox: true` QuickJS/WASM opt-in, Request/Response subset, operator grants.
|
|
50
60
|
- [Middleware](docs/MIDDLEWARE.md) and [examples](docs/MIDDLEWARE-EXAMPLES.md): `next()`, state, ordering; also `urlcode recipes add middleware`.
|
|
51
61
|
- [Assets](docs/ASSETS.md): pages, static, downloads, MIME, ranges. [Prerender](docs/PRERENDER.md): render once, no request-time code.
|
|
52
|
-
- [Dynamic links](docs/DYNAMIC-LINKS.md): `link` handler, SQLite store, CLI, private management API.
|
|
53
62
|
- [Conditions](docs/CONDITIONS.md): exact predicates, disjoint redirect/respond cases, no-store.
|
|
54
63
|
- [Egress](docs/EGRESS.md): bounded HTTPS proxy and best-effort signals behind operator grants; self-hosted only.
|
|
55
64
|
- [Policies](docs/POLICIES.md): `throttle`, `agents`, `security`, `compression`, `cache`; all off unless declared. [Site](docs/SITE.md): robots, sitemap, favicon, security.txt, llms.txt.
|
|
@@ -57,19 +66,22 @@ Agents that explicitly want the complete consolidated reference in one fetch sho
|
|
|
57
66
|
- [Interchange](docs/INTERCHANGE.md), [bulk import](docs/BULK.md), [recipes](docs/RECIPES.md) (`urlcode recipes search`, `examples search`), [TypeScript guests](docs/TYPESCRIPT-AUTHORING.md).
|
|
58
67
|
|
|
59
68
|
## Extensions (accounts, administration, presentation)
|
|
60
|
-
- [Extensions](docs/EXTENSIONS.md): `extensions.<name>` blocks, `extension` mounts, `policies.extensions`, the operator host file, `@jimhoyd/urlcode/extensions`.
|
|
61
|
-
- [urlcode-auth](
|
|
62
|
-
- [urlcode-admin](
|
|
63
|
-
- [urlcode-ui](
|
|
69
|
+
- [Extensions](docs/EXTENSIONS.md): `extensions.<name>` blocks, `extension` mounts, `policies.extensions`, the operator host file, `@jimhoyd/urlcode/extensions`. Project-level lifecycle hooks run trusted via plain `import()`, or sandboxed via `@jimhoyd/urlcode/sandbox`'s `SandboxPool`.
|
|
70
|
+
- [urlcode-auth](packages/auth): npm: @jimhoyd/urlcode-auth; accounts, sessions, MFA, roles, account page; its own llms.txt. Lives in this repository as a workspace package.
|
|
71
|
+
- [urlcode-admin](packages/admin): npm: @jimhoyd/urlcode-admin; users, sessions, roles, audit, cases; its own llms.txt. Lives in this repository as a workspace package.
|
|
72
|
+
- [urlcode-ui](packages/ui): npm: @jimhoyd/urlcode-ui; escaped templates, shadcn/ui partials, themes, translations; its own llms.txt. Lives in this repository as a workspace package, not in a separate repository.
|
|
73
|
+
- urlcode-middleware (retired): per-route middleware briefly lived here as an extension-seam alternative; the package was unpublished at 0.1.0-alpha.2 and its repository deleted. This is not a gap — per-route middleware is native to core via the `middleware:` array (docs/MIDDLEWARE.md); use that.
|
|
74
|
+
- urlcode-dynamic-link (retired): stored short links briefly lived here as a mount-based extension; the package was unpublished and its repository deleted. Core has no native `link` handler and no supported extension provides stored links — report them as a gap.
|
|
64
75
|
- [Plugins](docs/PLUGINS.md): host hook API in operator code, never named in YAML.
|
|
65
76
|
|
|
66
77
|
## Tooling and API
|
|
67
78
|
- [Tooling SDK and MCP](docs/TOOLING.md): `urlcode mcp`, read-only inspection, validation, conversion previews.
|
|
68
|
-
- [TypeScript](docs/TYPESCRIPT.md): declarations for `@jimhoyd/urlcode` and its `/plugins`, `/policies`, `/observability`, `/compliance`, `/prerender`, `/extensions`, `/aws`, `/vercel`, `/cloudflare` entries.
|
|
79
|
+
- [TypeScript](docs/TYPESCRIPT.md): declarations for `@jimhoyd/urlcode` and its `/plugins`, `/policies`, `/observability`, `/compliance`, `/prerender`, `/extensions`, `/sandbox`, `/aws`, `/vercel`, `/cloudflare` entries.
|
|
69
80
|
- [Capabilities](docs/CAPABILITIES.md): per-target support; `urlcode capabilities --target NAME`.
|
|
70
81
|
|
|
71
82
|
## Operations
|
|
72
83
|
- [Operations](docs/OPERATIONS.md), [install](docs/INSTALL.md), [deployment checks](docs/DEPLOYMENT-CHECKS.md), [CI action](docs/CI.md).
|
|
73
84
|
- [Readiness](docs/READINESS.md), [capacity](docs/CAPACITY.md), [performance](docs/PERFORMANCE.md), [resilience](docs/RESILIENCE.md), [observability](docs/OBSERVABILITY.md).
|
|
74
85
|
- [Vercel](docs/VERCEL.md), [AWS](docs/AWS.md), [Cloudflare](docs/CLOUDFLARE.md): adapters with local conformance tests; no provider deployment verified yet.
|
|
86
|
+
- [Static hosting](docs/STATIC.md): `urlcode build --target static` compiles redirects/pages/static/downloads to S3 + CloudFront objects and redirect metadata; no server, so function/middleware/extension/proxy/signals/conditions/parameters/bindings/policies are all refused; GitHub Pages is explicitly out of scope.
|
|
75
87
|
- [Release readiness](docs/RELEASE-READINESS.md), [roadmap](ROADMAP.md): what is proven, what is planned.
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jimhoyd/urlcode",
|
|
3
|
-
"version": "0.4.0-alpha.
|
|
3
|
+
"version": "0.4.0-alpha.3",
|
|
4
4
|
"description": "Portable runtime for programmable URL behavior",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"workspaces": [
|
|
7
|
+
"packages/*"
|
|
8
|
+
],
|
|
6
9
|
"license": "Apache-2.0",
|
|
7
10
|
"engines": {
|
|
8
11
|
"node": ">=22.13.0"
|
|
@@ -69,6 +72,11 @@
|
|
|
69
72
|
"development": "./src/extensions.ts",
|
|
70
73
|
"default": "./dist/extensions.js"
|
|
71
74
|
},
|
|
75
|
+
"./sandbox": {
|
|
76
|
+
"types": "./dist/types/sandbox.d.ts",
|
|
77
|
+
"development": "./src/sandbox.ts",
|
|
78
|
+
"default": "./dist/sandbox.js"
|
|
79
|
+
},
|
|
72
80
|
"./package.json": "./package.json"
|
|
73
81
|
},
|
|
74
82
|
"files": [
|
|
@@ -96,8 +104,10 @@
|
|
|
96
104
|
"typecheck": "tsc -p tsconfig.json",
|
|
97
105
|
"test": "node --conditions=development --test test/*.test.ts",
|
|
98
106
|
"lint": "eslint .",
|
|
99
|
-
"check": "node scripts/check.ts && node scripts/generate-yaml-reference.ts --check && node scripts/build-llms-full.ts --check && node scripts/build-cookbook-index.ts --check && node scripts/generate-claude-plugin.ts --check",
|
|
100
|
-
"verify": "npm run lint && npm run typecheck && npm run check && npm run build && npm test",
|
|
107
|
+
"check": "node scripts/check.ts && node scripts/check-trust-model-prose.ts && node scripts/check-guidance-claims.ts && node scripts/check-release-tags.ts && node scripts/check-workspace-links.ts && node scripts/generate-yaml-reference.ts --check && node scripts/build-llms-full.ts --check && node scripts/build-cookbook-index.ts --check && node scripts/generate-claude-plugin.ts --check && npm run release:check",
|
|
108
|
+
"verify": "npm run lint && npm run typecheck && npm run check && npm run build && npm test && npm run verify:workspaces",
|
|
109
|
+
"workspace:styles": "npm run styles --workspace @jimhoyd/urlcode-ui",
|
|
110
|
+
"verify:workspaces": "npm run verify --workspace @jimhoyd/urlcode-ui && npm run verify --workspace @jimhoyd/urlcode-auth && npm run verify --workspace @jimhoyd/urlcode-admin && npm run test:workspace-integration",
|
|
101
111
|
"benchmark": "node benchmarks/routing.ts",
|
|
102
112
|
"test:package": "npm run build && node scripts/package-smoke.ts",
|
|
103
113
|
"dev": "node src/cli.ts dev --project starters/default",
|
|
@@ -113,9 +123,23 @@
|
|
|
113
123
|
"docs:plugin": "node scripts/generate-claude-plugin.ts",
|
|
114
124
|
"docs:llms": "node scripts/build-llms-full.ts",
|
|
115
125
|
"docs:cookbook-index": "node scripts/build-cookbook-index.ts",
|
|
126
|
+
"check:downstream-skills": "node scripts/check-downstream-skill-drift.ts",
|
|
116
127
|
"benchmark:bulk": "node benchmarks/bulk.ts",
|
|
128
|
+
"benchmark:sandbox-vs-trusted": "node benchmarks/sandbox-vs-trusted.ts",
|
|
117
129
|
"benchmark:agent": "node benchmarks/agent/run.ts",
|
|
118
|
-
"sync:agents": "node scripts/sync-agent-lists.ts"
|
|
130
|
+
"sync:agents": "node scripts/sync-agent-lists.ts",
|
|
131
|
+
"check:docs": "node scripts/check-trust-model-prose.ts && node scripts/check-guidance-claims.ts && node scripts/generate-yaml-reference.ts --check && node scripts/build-llms-full.ts --check && node scripts/build-cookbook-index.ts --check && node scripts/generate-claude-plugin.ts --check",
|
|
132
|
+
"ci:plan": "node scripts/ci-plan.ts",
|
|
133
|
+
"test:package:built": "node scripts/package-smoke.ts",
|
|
134
|
+
"release:status": "node scripts/release.ts status",
|
|
135
|
+
"release:plan": "node scripts/release.ts plan",
|
|
136
|
+
"release:publish": "node scripts/release.ts publish",
|
|
137
|
+
"release:run": "node scripts/release-run.ts",
|
|
138
|
+
"release:peers": "node scripts/release.ts peers",
|
|
139
|
+
"release:check": "node scripts/release.ts check",
|
|
140
|
+
"ci:history": "node scripts/ci-history.ts",
|
|
141
|
+
"ci:report": "node scripts/ci-report.ts",
|
|
142
|
+
"test:workspace-integration": "node --test test/workspace-scaffold.integration.ts"
|
|
119
143
|
},
|
|
120
144
|
"repository": {
|
|
121
145
|
"type": "git",
|
|
@@ -131,14 +155,17 @@
|
|
|
131
155
|
"yaml": "2.9.1"
|
|
132
156
|
},
|
|
133
157
|
"devDependencies": {
|
|
158
|
+
"@changesets/cli": "3.0.3",
|
|
134
159
|
"@eslint/js": "10.0.1",
|
|
135
160
|
"@types/mime-types": "3.0.1",
|
|
136
161
|
"@types/node": "26.5.1",
|
|
162
|
+
"@types/semver": "7.8.0",
|
|
137
163
|
"eslint": "10.10.0",
|
|
138
164
|
"globals": "17.12.0",
|
|
165
|
+
"semver": "7.8.5",
|
|
139
166
|
"typescript-eslint": "8.70.0"
|
|
140
167
|
},
|
|
141
|
-
"homepage": "https://github.com/jimhoyd-com/urlcode
|
|
168
|
+
"homepage": "https://github.com/jimhoyd-com/urlcode#readme",
|
|
142
169
|
"bugs": {
|
|
143
170
|
"url": "https://github.com/jimhoyd-com/urlcode/issues"
|
|
144
171
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "urlcode",
|
|
3
3
|
"description": "Authoring and operating URLCode projects: the implemented YAML contract, capability limits, deployment and verification commands for the pinned runtime revision.",
|
|
4
|
-
"version": "0.4.0-alpha.
|
|
4
|
+
"version": "0.4.0-alpha.3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "jimhoyd-com",
|
|
7
7
|
"url": "https://github.com/jimhoyd-com"
|
|
@@ -14,6 +14,6 @@
|
|
|
14
14
|
"routing",
|
|
15
15
|
"yaml",
|
|
16
16
|
"redirects",
|
|
17
|
-
"
|
|
17
|
+
"middleware"
|
|
18
18
|
]
|
|
19
19
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: urlcode-authoring
|
|
3
|
-
description: Author or modify a URLCode project — write and edit urlcode.yaml routes, function and middleware modules, pages, static assets
|
|
3
|
+
description: Author or modify a URLCode project — write and edit urlcode.yaml routes, function and middleware modules, pages, static assets and downloads, then validate and test them. Use whenever a urlcode.yaml file is present or referenced, when the user mentions URLCode, @jimhoyd/urlcode, urlcode routes/handlers/policies/site keys, or asks for redirects or request functions in a URLCode project. Loads the implemented capability matrix so unsupported features are reported as gaps instead of invented.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Authoring URLCode projects
|
|
@@ -11,23 +11,27 @@ validates. Features outside that contract do not silently degrade — they fail
|
|
|
11
11
|
validation. So the cost of guessing is a broken project, and the whole job here
|
|
12
12
|
is to author only what the pinned revision implements and then prove it.
|
|
13
13
|
|
|
14
|
+
## Declarative-first default
|
|
15
|
+
|
|
16
|
+
> Use URLCode's highest-level declarative features whenever possible. Generate custom code only when the framework cannot express the requirement.
|
|
17
|
+
|
|
18
|
+
Check the installed version's primitives, YAML configuration, policies, supported
|
|
19
|
+
extensions and recipes/templates before writing a custom function or middleware.
|
|
20
|
+
Keep necessary custom code focused and report the capability gap; never invent
|
|
21
|
+
fields or bypass target limits or operator grants. See `docs/PROJECT-DIRECTION.md` in the installed runtime.
|
|
22
|
+
|
|
14
23
|
## Read the contract before writing YAML
|
|
15
24
|
|
|
16
25
|
Documentation, schema and runtime must come from the **same revision**. Read from
|
|
17
26
|
the project's installed runtime (`node_modules/@jimhoyd/urlcode/`) or the
|
|
18
27
|
checkout you are working in — never from memory of another version.
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
5. `docs/ROUTING.md`, `docs/HTTP.md`, `docs/MIDDLEWARE.md`, `docs/ASSETS.md` —
|
|
27
|
-
matching precedence, methods, composition, MIME and ranges.
|
|
28
|
-
6. `docs/FUNCTION-SECURITY.md` — the sandbox and operator binding policy.
|
|
29
|
-
|
|
30
|
-
`llms.txt` at the repository root is a compact index of all of the above.
|
|
29
|
+
Start with `urlcode context --project <dir> --budget 4000`, then retrieve the
|
|
30
|
+
capability, schema fragment, recipe or example relevant to the change. Use the
|
|
31
|
+
read-only MCP equivalents when available. `llms.txt` is the index; read the
|
|
32
|
+
matching task guide from `docs/` when a query needs more explanation.
|
|
33
|
+
`docs/SPECIFICATION.md` and `schemas/urlcode.schema.json` resolve contract
|
|
34
|
+
questions. Archived plans are historical, not valid YAML guidance.
|
|
31
35
|
|
|
32
36
|
## Workflow
|
|
33
37
|
|
|
@@ -35,15 +39,16 @@ checkout you are working in — never from memory of another version.
|
|
|
35
39
|
tests and the pinned runtime version. Preserve the user's organization,
|
|
36
40
|
naming and unrelated routes.
|
|
37
41
|
- Choose exactly one handler per route — `function`, `redirect`, `respond`,
|
|
38
|
-
`page`, `static`, `download`
|
|
39
|
-
Prefer a native handler when code is
|
|
42
|
+
`page`, `static`, `download`, `conditional`, `proxy` or an `extension` mount
|
|
43
|
+
— plus optional ordered middleware. Prefer a native handler when code is
|
|
44
|
+
unnecessary.
|
|
40
45
|
- Declare each path placeholder as a required string. Paths match whole
|
|
41
46
|
segments: no regex, no greedy captures, no wildcard handlers.
|
|
42
47
|
- Bind typed inputs through `args` or context. There is no `${...}`
|
|
43
48
|
interpolation anywhere in the format.
|
|
44
49
|
- Create every referenced module, page and asset **before** validating. All
|
|
45
|
-
paths resolve from the project root
|
|
46
|
-
|
|
50
|
+
source paths resolve from the project root. Trusted modules can import Node built-ins and npm packages;
|
|
51
|
+
only `sandbox: true` modules are restricted to the relative snapshotted graph.
|
|
47
52
|
- Write exact response fixtures for success and failure, covering every active
|
|
48
53
|
method, middleware behavior, HEAD, and any range or cache semantics.
|
|
49
54
|
- Follow `docs/BEST-PRACTICES.md` for layout and readability as the project grows.
|
|
@@ -56,11 +61,20 @@ mistakes that recur:
|
|
|
56
61
|
- No YAML anchors, aliases, template interpolation or remote includes.
|
|
57
62
|
- No recursive includes or glob discovery; includes are explicit.
|
|
58
63
|
- No regex, optional or greedy route segments, and no host-based routing.
|
|
59
|
-
-
|
|
60
|
-
npm
|
|
64
|
+
- `function`/`middleware` routes run trusted and unsandboxed by default: full
|
|
65
|
+
Node, npm, filesystem and `fetch` access, in-process, like any other project
|
|
66
|
+
code. `sandbox: true` opts a route into isolation — reach for it when that
|
|
67
|
+
route's own code warrants it (unreviewed or third-party code, a secret whose
|
|
68
|
+
blast radius matters, complex logic), not reflexively on every route and
|
|
69
|
+
never merely because it handles request data -- that is untrusted in both
|
|
70
|
+
modes and must be validated either way. A
|
|
71
|
+
`sandbox: true` route gets a text/JSON `Request`/`Response` sandbox only:
|
|
72
|
+
**no** `fetch`, Node or npm APIs, filesystem, WebSocket, streaming or crypto
|
|
73
|
+
API.
|
|
61
74
|
- No global middleware, Express compatibility or automatic auth.
|
|
62
75
|
- `policies` accepts only `throttle`, `agents`, `security`, `compression` and
|
|
63
|
-
`cache`,
|
|
76
|
+
`cache`, plus registered extension requirements under `extensions`;
|
|
77
|
+
the built-in policies are off unless declared; `hardened` is the only built-in
|
|
64
78
|
profile. Check the per-target table in `docs/POLICIES.md` before declaring
|
|
65
79
|
one for a serverless or Cloudflare deployment — an unsupported policy refuses
|
|
66
80
|
activation rather than degrading.
|
|
@@ -68,9 +82,9 @@ mistakes that recur:
|
|
|
68
82
|
only and off unless declared; a declared route at the same path wins. Its
|
|
69
83
|
generated routes count toward `--expect-routes`, and `site.sitemap` needs
|
|
70
84
|
`--origin` on every command that activates the project.
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
|
|
85
|
+
- There is no native `link` handler or `dynamicLinks` project flag, and no
|
|
86
|
+
supported extension package provides one; report stored short links as a gap,
|
|
87
|
+
never invent a `link` field.
|
|
74
88
|
- Infrastructure (proxy ranges, storage URLs, vendor rule identifiers) is an
|
|
75
89
|
operator flag, never route YAML.
|
|
76
90
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: urlcode-operations
|
|
3
|
-
description: Deploy, verify, monitor and operate a URLCode project — process/container deployment, release readiness, verifying a live deployment against the project, capacity/audit/benchmark, observability, DDoS/overload resilience, and
|
|
3
|
+
description: Deploy, verify, monitor and operate a URLCode project — process/container deployment, release readiness, verifying a live deployment against the project, capacity/audit/benchmark, observability, DDoS/overload resilience, and operator binding grants. Use when the user asks to deploy, check readiness, verify a running deployment, size/benchmark a project, monitor it, plan for overload, or manage bindings. Reports operational limits and unimplemented capabilities as gaps instead of inventing mitigations.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Operating a URLCode deployment
|
|
@@ -30,9 +30,7 @@ another version.
|
|
|
30
30
|
overload and DDoS; what layer each defense belongs to.
|
|
31
31
|
6. `docs/MONITORING.md` and `docs/OBSERVABILITY.md` — health/ready probes,
|
|
32
32
|
logs, metrics format, what is and is not exported.
|
|
33
|
-
7. `docs/
|
|
34
|
-
policy shape, scope, loopback-only binding.
|
|
35
|
-
8. `docs/POLICIES.md` and `docs/FUNCTION-SECURITY.md` — per-target policy
|
|
33
|
+
7. `docs/POLICIES.md` and `docs/FUNCTION-SECURITY.md` — per-target policy
|
|
36
34
|
support and the operator binding-grant process, needed whenever a
|
|
37
35
|
deployment or verification step touches either.
|
|
38
36
|
|
|
@@ -71,28 +69,25 @@ urlcode verify-deployment --project ./my-links --target https://links.example \
|
|
|
71
69
|
Run the actual commands and report actual results, never "should work" or
|
|
72
70
|
"should be reachable". `verify-deployment` needs a real target; do not
|
|
73
71
|
simulate its output. In a runtime checkout, substitute `node src/cli.ts` for
|
|
74
|
-
`urlcode`. Pass `--policy
|
|
75
|
-
|
|
72
|
+
`urlcode`. Pass `--policy` where a snapshot needs bindings already reviewed
|
|
73
|
+
by the operator.
|
|
76
74
|
|
|
77
75
|
## Hard limits — report these as gaps, never invent around them
|
|
78
76
|
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
operator
|
|
77
|
+
- Provider adapters exist with different capability limits; query
|
|
78
|
+
`urlcode capabilities --target NAME`. Automatic TLS/DNS, distributed rate
|
|
79
|
+
limiting, metrics exporters and durable delivery require operator infrastructure.
|
|
82
80
|
- No orchestration, traffic switching or automated rollback; recovery is an
|
|
83
81
|
explicit snapshot reload from a known-good artifact.
|
|
84
82
|
- `verify-deployment` has no infrastructure access, uses no credential,
|
|
85
83
|
follows no redirect and offers no `--insecure`. It cannot check anything a
|
|
86
84
|
read-only HTTP probe cannot observe.
|
|
87
|
-
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- Sandbox concurrency, worker slots and execution deadlines are shared across
|
|
94
|
-
every programmable route in a snapshot; there is no per-route fairness or
|
|
95
|
-
reserved capacity, and awaiting a guest timer still occupies a slot.
|
|
85
|
+
- Core has no durable store and no private management API of its own, and no
|
|
86
|
+
supported extension package provides stored short links.
|
|
87
|
+
- Only `sandbox: true` routes share the sandbox worker slots and forced
|
|
88
|
+
execution deadlines. Trusted routes run in Node under HTTP admission limits;
|
|
89
|
+
their cooperative timeout cannot stop blocking JavaScript. A guest timer still
|
|
90
|
+
occupies a sandbox slot. Size both modes from `docs/CAPACITY.md`.
|
|
96
91
|
- `throttle` and `agents` policy counters are per instance, not distributed;
|
|
97
92
|
they are a second layer behind the edge, never a replacement for it.
|
|
98
93
|
|
|
@@ -102,10 +97,9 @@ responsibility that covers it instead of inventing a flag.
|
|
|
102
97
|
|
|
103
98
|
## Boundaries
|
|
104
99
|
|
|
105
|
-
- Never generate or approve an operator binding grant
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
real secret.
|
|
100
|
+
- Never generate or approve an operator binding grant on the user's behalf.
|
|
101
|
+
That is the operator's own reviewed decision; produce the shape and let
|
|
102
|
+
them fill in and store the real secret.
|
|
109
103
|
- Keep every credential, token and policy file out of source, examples and
|
|
110
104
|
Git. A synthetic example value is fine; a real one is never committed.
|
|
111
105
|
- Do not deploy, expose a service, rotate a credential, or run
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Authenticated JSON API
|
|
2
2
|
|
|
3
|
-
`/api/profile` is a
|
|
3
|
+
`/api/profile` is a function behind `auth: true`, the route-level short
|
|
4
4
|
form that expands to `policies.extensions.auth: {}`. The project declares the
|
|
5
|
-
`auth` extension; it never chooses or loads the module that implements it. Authorization happens in trusted operator code before the
|
|
6
|
-
runs, and the
|
|
5
|
+
`auth` extension; it never chooses or loads the module that implements it. Authorization happens in trusted operator code before the route's function
|
|
6
|
+
runs, and the host strips `Authorization` and `Cookie` before dispatch -- for
|
|
7
|
+
trusted and `sandbox: true` routes alike.
|
|
7
8
|
|
|
8
9
|
This recipe does not activate on its own. Every command needs an operator host
|
|
9
10
|
file outside the project plus the canonical origin:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// The auth extension has already authorized this request. Credentials never
|
|
2
|
-
// reach
|
|
2
|
+
// reach this function: the host strips Authorization and Cookie before
|
|
3
|
+
// dispatch, in both execution modes.
|
|
3
4
|
export default function profile() {
|
|
4
5
|
return Response.json({signedIn: true, profile: {name: 'Ada', plan: 'team'}});
|
|
5
6
|
}
|
|
@@ -3,7 +3,7 @@ description: JSON endpoint protected by the operator-installed auth extension th
|
|
|
3
3
|
tags: [auth, authenticated, protected, signed-in, bearer, json, api, function, extension, "401", private]
|
|
4
4
|
complexity: advanced
|
|
5
5
|
capabilities: [enabled, extension, function, methods, policies.extensions]
|
|
6
|
-
targets: {self-hosted:
|
|
6
|
+
targets: {self-hosted: conditional, aws: refused, vercel: refused, cloudflare: refused, static: refused}
|
|
7
7
|
routes: 1
|
|
8
8
|
services:
|
|
9
9
|
- name: auth extension
|
|
@@ -28,7 +28,7 @@ tests:
|
|
|
28
28
|
- urlcode test --project . --host-file /operator/host.mjs --origin https://api.example.com
|
|
29
29
|
- urlcode audit --project . --expect-routes 1 --host-file /operator/host.mjs --origin https://api.example.com
|
|
30
30
|
behavior:
|
|
31
|
-
- GET /api/profile without credentials answers 401 from the extension before the
|
|
31
|
+
- GET /api/profile without credentials answers 401 from the extension before the function runs
|
|
32
32
|
- GET with the credential the extension accepts answers 200 JSON with Cache-Control no-store
|
|
33
33
|
- HEAD mirrors both cases with an empty body; POST answers 405
|
|
34
34
|
- the project declares the extension and the requirement; it never chooses or loads the implementing module
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Field checks the runtime does not do: the body limit and JSON syntax are
|
|
2
2
|
// enforced before this runs. The declared signal fires after a response with
|
|
3
3
|
// a fixed payload (route, method, status); the message itself never leaves
|
|
4
|
-
// the
|
|
4
|
+
// the signal, so a form store or mailer belongs behind the granted hook rather
|
|
5
|
+
// than an ad-hoc call from here.
|
|
5
6
|
const emailPattern = /^[^\s@]{1,64}@[^\s@]{1,255}$/;
|
|
6
7
|
export default async function contact(request) {
|
|
7
8
|
const body = await request.json();
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
id: contact-form
|
|
2
|
-
description: Validate a JSON contact message in
|
|
2
|
+
description: Validate a JSON contact message in a function and notify an operator-granted hook with a fixed signal after the response.
|
|
3
3
|
tags: [contact, form, message, email, post, json, validation, signal, notify, hook, egress, "422"]
|
|
4
4
|
complexity: intermediate
|
|
5
5
|
capabilities: [enabled, function, methods, request.body, response.headers, signals]
|
|
6
|
-
targets: {self-hosted: compatible, aws: refused, vercel: refused, cloudflare: refused}
|
|
6
|
+
targets: {self-hosted: compatible, aws: refused, vercel: refused, cloudflare: refused, static: refused}
|
|
7
7
|
routes: 1
|
|
8
8
|
services:
|
|
9
9
|
- name: hook endpoint
|
|
@@ -30,4 +30,4 @@ behavior:
|
|
|
30
30
|
- POST /contact with a valid name, email and message answers 202 {"accepted":true}
|
|
31
31
|
- invalid fields answer 422 with one error line per field; a non-object body answers 422
|
|
32
32
|
- form-encoded bodies answer 415 and GET answers 405
|
|
33
|
-
- the signal carries route, method and status only, best effort; the message itself never
|
|
33
|
+
- the signal carries route, method and status only, best effort; the message itself is never included
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Run `urlcode validate --local --project .`, `urlcode test --project .` and
|
|
4
4
|
`urlcode audit --project . --expect-routes 1`.
|
|
5
5
|
|
|
6
|
-
`/api/items` returns JSON from a
|
|
6
|
+
`/api/items` returns JSON from a function (trusted, the default). `middleware/cors.mjs` runs
|
|
7
7
|
around it: it answers `OPTIONS` preflight itself with 204 and adds
|
|
8
8
|
`Access-Control-Allow-Origin` to responses for origins in its allowlist. Other
|
|
9
9
|
origins get `Vary: Origin` and no allow header, so browsers refuse them.
|
|
@@ -11,6 +11,6 @@ origins get `Vary: Origin` and no allow header, so browsers refuse them.
|
|
|
11
11
|
Edit `allowedOrigins` in the middleware and the function body. The route must
|
|
12
12
|
list `OPTIONS` in `methods`, or the runtime answers 405 before the middleware
|
|
13
13
|
runs. CORS is not a host policy in this contract; middleware is the supported
|
|
14
|
-
place for it, and
|
|
14
|
+
place for it, and middleware needs the self-hosted Node lifecycle, so serverless targets refuse this
|
|
15
15
|
project. A browser-facing API that also needs credentials must add
|
|
16
16
|
`Access-Control-Allow-Credentials` deliberately, never with a wildcard origin.
|
|
@@ -3,7 +3,7 @@ description: JSON endpoint for one allowed browser origin; middleware answers pr
|
|
|
3
3
|
tags: [cors, preflight, options, browser, origin, api, json, middleware, access-control, vary]
|
|
4
4
|
complexity: intermediate
|
|
5
5
|
capabilities: [enabled, function, methods, middleware, response.headers]
|
|
6
|
-
targets: {self-hosted: compatible, aws: refused, vercel: refused, cloudflare: refused}
|
|
6
|
+
targets: {self-hosted: compatible, aws: refused, vercel: refused, cloudflare: refused, static: refused}
|
|
7
7
|
routes: 1
|
|
8
8
|
inputs:
|
|
9
9
|
- name: allowedOrigins
|
|
@@ -23,4 +23,4 @@ behavior:
|
|
|
23
23
|
- OPTIONS /api/items from https://app.example.com answers 204 with Access-Control-Allow-Origin, -Methods and -Headers
|
|
24
24
|
- GET from the allowed origin carries Access-Control-Allow-Origin; other origins get Vary Origin and no allow header
|
|
25
25
|
- the route lists OPTIONS in methods, otherwise the runtime answers 405 before the middleware runs
|
|
26
|
-
- CORS is middleware, not a host policy, so the route needs the
|
|
26
|
+
- CORS is middleware, not a host policy, so the route needs the self-hosted Node lifecycle
|
|
@@ -4,7 +4,7 @@ Run `urlcode validate --local --project .`, `urlcode test --project .` and
|
|
|
4
4
|
`urlcode audit --project . --expect-routes 2`.
|
|
5
5
|
|
|
6
6
|
`/health` answers `ok` as text and `/status` answers a small JSON document. Both
|
|
7
|
-
are native `respond` routes with `Cache-Control: no-store`, so no
|
|
7
|
+
are native `respond` routes with `Cache-Control: no-store`, so no project code runs
|
|
8
8
|
and every target supports them. Change `service` in `urlcode.yaml` to your
|
|
9
9
|
service name.
|
|
10
10
|
|
|
@@ -3,7 +3,7 @@ description: Plain-text liveness and JSON readiness answers served natively with
|
|
|
3
3
|
tags: [health, status, readiness, liveness, uptime, probe, monitoring, respond, native, json]
|
|
4
4
|
complexity: starter
|
|
5
5
|
capabilities: [enabled, methods, respond, response.headers]
|
|
6
|
-
targets: {self-hosted: compatible, aws: compatible, vercel: compatible, cloudflare: compatible}
|
|
6
|
+
targets: {self-hosted: compatible, aws: compatible, vercel: compatible, cloudflare: compatible, static: refused}
|
|
7
7
|
routes: 2
|
|
8
8
|
inputs:
|
|
9
9
|
- name: service
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# JSON echo API
|
|
2
2
|
|
|
3
3
|
Run `urlcode validate --local --project .` and `urlcode serve --project .`.
|
|
4
|
-
POST JSON with `Content-Type: application/json` to `/echo`. The
|
|
4
|
+
POST JSON with `Content-Type: application/json` to `/echo`. The function returns
|
|
5
5
|
`{"received": ...}`. Other methods are refused. Do not submit credentials to an
|
|
6
6
|
echo endpoint. This recipe grants no network or filesystem access.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
id: json-api
|
|
2
|
-
description: Bounded JSON request body echoed back by a
|
|
3
|
-
tags: [json, api, post, echo, function, body, validation
|
|
2
|
+
description: Bounded JSON request body echoed back by a function (trusted, the default).
|
|
3
|
+
tags: [json, api, post, echo, function, body, validation]
|
|
4
4
|
complexity: starter
|
|
5
5
|
capabilities: [enabled, function, methods, request.body]
|
|
6
|
-
targets: {self-hosted: compatible, aws: refused, vercel: refused, cloudflare: refused}
|
|
6
|
+
targets: {self-hosted: compatible, aws: refused, vercel: refused, cloudflare: refused, static: refused}
|
|
7
7
|
routes: 1
|
|
8
8
|
inputs:
|
|
9
9
|
- name: handler
|
|
@@ -21,5 +21,5 @@ tests:
|
|
|
21
21
|
- urlcode audit --project . --expect-routes 1
|
|
22
22
|
behavior:
|
|
23
23
|
- POST /echo with application/json answers 200 {"received":<body>}
|
|
24
|
-
- malformed JSON answers 400 and another content type 415 before the
|
|
24
|
+
- malformed JSON answers 400 and another content type 415 before the function runs
|
|
25
25
|
- GET answers 405
|
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
Fourteen reusable middleware modules on fifteen routes (`auth.mjs` serves two),
|
|
4
4
|
covering what Express, Koa,
|
|
5
5
|
Hono, Next.js and edge runtimes usually ship as middleware, rewritten for
|
|
6
|
-
URLCode's
|
|
6
|
+
URLCode's middleware contract. These modules run trusted and in-process, the
|
|
7
|
+
default; a `sandbox: true` route sees a smaller guest API instead. Run `urlcode validate --local --project .`
|
|
7
8
|
and `urlcode serve --project .`, then keep the routes you need and delete the
|
|
8
9
|
rest; every module is independent.
|
|
9
10
|
|
|
10
11
|
| Route | Module | Pattern |
|
|
11
12
|
| --- | --- | --- |
|
|
12
13
|
| `/api/private` | `middleware/auth.mjs` (`bearer`) | Bearer token with constant-time comparison |
|
|
13
|
-
| `/admin/panel` | `middleware/auth.mjs` (`basic`) | HTTP Basic decoded in
|
|
14
|
+
| `/admin/panel` | `middleware/auth.mjs` (`basic`) | HTTP Basic decoded in trusted middleware |
|
|
14
15
|
| `/cors/data` | `middleware/cors.mjs` | Origin allowlist and preflight |
|
|
15
16
|
| `/traced` | `middleware/request-id.mjs` | Caller correlation id and Server-Timing |
|
|
16
17
|
| `/maintenance` | `middleware/maintenance.mjs` | 503 kill switch with bypass header |
|
|
@@ -28,7 +29,10 @@ rest; every module is independent.
|
|
|
28
29
|
Tokens, passwords and switches are literal `env` values so the recipe runs
|
|
29
30
|
without grants. Before deploying, move credentials to `{secret: name}` bindings
|
|
30
31
|
with an operator grant, replace the example.com destinations, and delete the
|
|
31
|
-
`/inspect` route or set `DEBUG` to `false`.
|
|
32
|
+
`/inspect` route or set `DEBUG` to `false`. These modules run trusted, so
|
|
33
|
+
`node:crypto` is available; the examples deliberately stay to request shaping
|
|
34
|
+
and are not a security boundary. Under `sandbox: true` there is no crypto API
|
|
35
|
+
at all, so none
|
|
32
36
|
of these modules can verify signatures or hash passwords; treat them as request
|
|
33
37
|
shaping, not as a security boundary. See the runtime's middleware documentation
|
|
34
|
-
for the full list of
|
|
38
|
+
for the full list of sandboxed-route limits.
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// Sticky A/B bucketing through a cookie, as on Vercel or Cloudflare edge examples.
|
|
2
2
|
// Bucket b gets a different destination; everyone keeps their bucket for a week.
|
|
3
|
+
// `Response.redirect()`'s headers are immutable (per the Fetch standard a
|
|
4
|
+
// trusted route's real `Response` enforces this, unlike the sandbox's guest
|
|
5
|
+
// API), so build that branch's headers up front instead of mutating the
|
|
6
|
+
// result afterward.
|
|
3
7
|
function cookie(request, name) {
|
|
4
8
|
for (const part of (request.headers.get('cookie') || '').split(';')) {
|
|
5
9
|
const [key, ...rest] = part.trim().split('=');
|
|
@@ -12,8 +16,14 @@ export default async function bucket(request, context, next) {
|
|
|
12
16
|
const fresh = assigned !== 'a' && assigned !== 'b';
|
|
13
17
|
if (fresh) assigned = Math.random() < 0.5 ? 'a' : 'b';
|
|
14
18
|
context.state.bucket = assigned;
|
|
15
|
-
const
|
|
19
|
+
const setCookie = 'bucket=' + assigned + '; Path=/; Max-Age=604800; SameSite=Lax';
|
|
20
|
+
if (assigned === 'b') {
|
|
21
|
+
const headers = new Headers({ location: context.env.VARIANT_URL, vary: 'cookie' });
|
|
22
|
+
if (fresh) headers.append('set-cookie', setCookie);
|
|
23
|
+
return new Response(null, { status: 302, headers });
|
|
24
|
+
}
|
|
25
|
+
const response = await next();
|
|
16
26
|
response.headers.set('vary', 'cookie');
|
|
17
|
-
if (fresh) response.headers.append('set-cookie',
|
|
27
|
+
if (fresh) response.headers.append('set-cookie', setCookie);
|
|
18
28
|
return response;
|
|
19
29
|
}
|