@jimhoyd/urlcode 0.4.7 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.claude/skills/urlcode-authoring/SKILL.md +8 -0
  2. package/README.md +9 -3
  3. package/dist/BUILD-MANIFEST.json +23 -21
  4. package/dist/agents-guide.js +9 -5
  5. package/dist/authoring.js +36 -3
  6. package/dist/build-cloudflare.js +5 -2
  7. package/dist/build-static.js +1 -0
  8. package/dist/cli.js +47 -15
  9. package/dist/cloudflare.js +6 -3
  10. package/dist/config.js +7 -1
  11. package/dist/context.js +99 -1
  12. package/dist/extension-artifacts.js +147 -0
  13. package/dist/extension-bundles.js +70 -0
  14. package/dist/extensions.js +4 -0
  15. package/dist/functions.js +2 -1
  16. package/dist/index.js +4 -2
  17. package/dist/init-with.js +55 -23
  18. package/dist/interchange.js +1 -1
  19. package/dist/match.js +23 -5
  20. package/dist/mcp.js +11 -4
  21. package/dist/readiness.js +2 -2
  22. package/dist/router.js +20 -8
  23. package/dist/runtime.js +1 -0
  24. package/dist/site.js +19 -1
  25. package/dist/tooling.js +2 -2
  26. package/dist/types/agents-guide.d.ts +6 -1
  27. package/dist/types/authoring.d.ts +1 -1
  28. package/dist/types/cloudflare.d.ts +1 -0
  29. package/dist/types/context.d.ts +56 -0
  30. package/dist/types/extension-artifacts.d.ts +87 -0
  31. package/dist/types/extension-bundles.d.ts +49 -0
  32. package/dist/types/extensions.d.ts +4 -0
  33. package/dist/types/functions.d.ts +4 -0
  34. package/dist/types/index.d.ts +4 -2
  35. package/dist/types/init-with.d.ts +6 -1
  36. package/dist/types/match.d.ts +1 -0
  37. package/dist/types/site.d.ts +2 -0
  38. package/dist/types/tooling.d.ts +2 -2
  39. package/dist/types/types.d.ts +1 -0
  40. package/dist/types.js +1 -1
  41. package/llms-full.txt +188 -24
  42. package/llms.txt +63 -107
  43. package/package.json +11 -3
  44. package/recipes/redirect/README.md +19 -5
  45. package/recipes/redirect/recipe.yaml +12 -10
  46. package/recipes/redirect/tests/requests.json +22 -0
  47. package/recipes/redirect/urlcode.yaml +11 -1
  48. package/skills/urlcode/SKILL.md +2 -0
  49. package/starters/default/AGENTS.md +3 -3
package/llms.txt CHANGED
@@ -4,7 +4,7 @@
4
4
  > A portable runtime for programmable URL behavior, and the framework that grows
5
5
  > from it: routes in YAML, functions and middleware, then accounts, administration and
6
6
  > stored links as operator-installed extensions. Stable project format
7
- > `version: "1"`. Core is Apache-2.0; this revision is `0.4.7`. `function`/`middleware`
7
+ > `version: "1"`. Core is Apache-2.0; this revision is `0.5.0`. `function`/`middleware`
8
8
  > routes are trusted by default with `sandbox: true` as the per-route opt-in. The
9
9
  > auth, admin and ui extension packages in this repository are versioned at the same
10
10
  > revision; confirm what is published with `npm run release:status`.
@@ -15,112 +15,68 @@ or fetch inside a `sandbox: true` function, regex routes, database access,
15
15
  global middleware, YAML interpolation, or packages named in YAML. Secrets need external revision-pinned
16
16
  grants. Unsupported features fail with the route named; nothing degrades silently.
17
17
 
18
- Agents that explicitly want the complete consolidated reference in one fetch should read
19
- [llms-full.txt](llms-full.txt), generated from the documents below (about 50k tokens, estimated).
18
+ ## Start here (read only this section for a first attempt)
20
19
 
21
- ## Declarative-first default
20
+ 1. Run `urlcode context --project DIR` (MCP `get_context`). For redirect-only work run
21
+ `urlcode context --project DIR --task redirects`: supported shapes with exact YAML, gaps
22
+ with their exact validation error, a paste-ready `starter`, and this project's own redirects.
23
+ 2. For a new redirect-only project, `urlcode init DIR --template redirects` writes the tested starter (yaml, 404 page, `npm start` honoring `PORT`, fixtures). Otherwise write `urlcode.yaml`, then `urlcode validate --local --project DIR` and `urlcode test`.
24
+ 3. Do not read or grep [llms-full.txt](llms-full.txt) (about 60k tokens) for a routine task; it is the
25
+ deliberate fallback when the steps above do not answer the question.
22
26
 
23
- > Use URLCode's highest-level declarative features whenever possible. Generate custom code only when the framework cannot express the requirement.
24
-
25
- Check the installed version's primitives, YAML configuration, policies, supported
26
- extensions and recipes/templates before writing a custom function or middleware.
27
- Keep necessary custom code focused and report the capability gap; never invent
28
- fields or bypass target limits or operator grants. See [the design principle](docs/PROJECT-DIRECTION.md#design-principle-declarative-first).
29
-
30
- ## Need X? Use this built-in first
31
-
32
- Route a task to the feature before writing code. Every row is off unless declared.
33
-
34
- | I need | Use | Where |
27
+ | Shape | Supported? | Use |
35
28
  |---|---|---|
36
- | Security headers (CSP, HSTS, nosniff, frame and referrer policy) | `policies.security` (`headers: oshp`, or `profile: hardened`) | [security](docs/policies/security.md), [policies](docs/POLICIES.md) |
37
- | Cache headers for a page, download or static folder | `cacheControl`, exactly one of `no-cache` (default), `no-store`, `public, max-age=3600`, `public, max-age=31536000, immutable` | [assets](docs/yaml/assets.md) |
38
- | Cache strategy or origin cache for any route | `policies.cache` | [cache](docs/policies/cache.md) |
39
- | Request body size, required body, content type, JSON syntax check | `request.body` (`maxBytes`, `required`, `contentTypes`, `format`); 413/415/400 | [HTTP](docs/HTTP.md) |
40
- | Restrict or allow HTTP methods | `methods` (default GET/HEAD); 405 with `Allow` | [HTTP](docs/HTTP.md) |
41
- | Rate limiting | `policies.throttle` | [throttle](docs/policies/throttle.md) |
42
- | Block bots or AI crawlers | `policies.agents`; `robots` via `site` | [agents](docs/policies/agents.md), [site](docs/SITE.md) |
43
- | Compression | `policies.compression` | [compression](docs/policies/compression.md) |
44
- | Fixed JSON, text or headers with no code | `respond` and `response.headers` | [HTTP](docs/HTTP.md) |
45
- | robots.txt, sitemap, favicon, security.txt | top-level `site` | [site](docs/SITE.md) |
46
- | Sign-in, roles, protected routes | `extensions.auth`, then `auth: true` on the route | [extensions](docs/EXTENSIONS.md) |
47
-
48
- Which handler serves the response:
49
-
50
- | The response is | Handler | Recipe |
51
- |---|---|---|
52
- | Fixed text or JSON | `respond` | `health-page` |
53
- | A short HTML snippet | `respond` `text` plus `response.headers` `Content-Type: text/html; charset=utf-8` | `docs/HTTP.md` |
54
- | One HTML file | `page` | `static-page` |
55
- | A directory of files | `static` | `static-plus-api` |
56
- | An attachment | `download` | `protected-download` |
57
-
58
- Persistence: core has no storage handler, but the operator-installed store extension serves declared collections as a CRUD API; `urlcode recipes search "crud store persist"` finds the `store-crud` recipe (operator installs the package; `init --with store` is not yet available from npm, #323). Gaps to report rather than invent: a database, filtering or per-record ownership beyond that recipe, CORS, and a native link store. `urlcode context`
59
- prints the same pointer for the project in front of you.
60
-
61
- After a real authoring attempt, draft feedback only for a capability gap,
62
- repeated workaround, documentation/discovery gap or suspected defect. Include
63
- the runtime/target, sanitized YAML, observed validation/test evidence, expected
64
- behavior and a proposed fixture. Search existing URLCode issues first. Never
65
- publish or comment on an issue without the user's explicit approval.
66
-
67
- ## Authoring
68
- - [AI authoring contract](docs/AI-AUTHORING.md): workflow, capability matrix, checks.
69
- - [Authoring skill](.claude/skills/urlcode-authoring/SKILL.md): loadable authoring skill shipped with this revision; see [distribution](docs/AI-AUTHORING.md#agent-skills).
70
- - [Operations skill](.claude/skills/urlcode-operations/SKILL.md): loadable deployment/verification/resilience skill shipped with this revision.
71
- - [YAML guide](docs/YAML-GUIDE.md): recipes for all handlers and common options.
72
- - [JSON Schema](schemas/urlcode.schema.json): accepted fields and types.
73
- - [Field reference](docs/YAML-REFERENCE.md): generated exhaustive field inventory.
74
- - [Semantics](docs/SPECIFICATION.md): validation, defaults and sandbox API.
75
- - [Runnable cookbook](examples/cookbook/README.md): 25 routes with HTTP fixtures.
76
- - [Route matching](docs/ROUTING.md): precedence, non-greedy parameters, updates.
77
- - [Middleware](docs/MIDDLEWARE.md): next(), state, ordering and native body limits.
78
- - [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.
79
- - [Plugins](docs/PLUGINS.md): host hook API operators pass in code; never named in YAML.
80
- - [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.
81
- - [HTTP](docs/HTTP.md): methods, request bodies and response headers.
82
- - [Assets](docs/ASSETS.md): pages, MIME, downloads, cache and ranges.
83
- - [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`.
84
- - [Prerendering](docs/PRERENDER.md): `@jimhoyd/urlcode/prerender` build helper and recipe; render function/middleware routes once into native page routes, no request-time guest code.
85
- - [Organization](docs/ORGANIZATION.md): entry point and included files.
86
-
87
- ## Start here
88
- - [The framework](docs/FRAMEWORK.md): four packages, the ladder from redirects to a full app, the composition contract, the rules an agent must follow.
89
- - [AI authoring contract](docs/AI-AUTHORING.md): workflow, capability matrix, copyable task prompt, checks.
90
- - [JSON Schema](schemas/urlcode.schema.json) and [field reference](docs/YAML-REFERENCE.md): every accepted field.
91
- - [YAML guide](docs/YAML-GUIDE.md) and [runnable cookbook](examples/cookbook/README.md): recipes with HTTP fixtures.
92
- - [Semantics](docs/SPECIFICATION.md): validation, defaults, sandbox API.
93
-
94
- ## Routes and handlers
95
- - [Routing](docs/ROUTING.md): exact and `{param}` paths, precedence, `/*` only on static and extension mounts.
96
- - [HTTP](docs/HTTP.md): methods, validated inputs, bodies, response headers, cookies.
97
- - [Function security](docs/FUNCTION-SECURITY.md): trusted and unsandboxed by default, `sandbox: true` QuickJS/WASM opt-in, Request/Response subset, operator grants.
98
- - [Middleware](docs/MIDDLEWARE.md) and [examples](docs/MIDDLEWARE-EXAMPLES.md): `next()`, state, ordering; also `urlcode recipes add middleware`.
99
- - [Assets](docs/ASSETS.md): pages, static, downloads, MIME, ranges. [Prerender](docs/PRERENDER.md): render once, no request-time code.
100
- - [Conditions](docs/CONDITIONS.md): exact predicates, disjoint redirect/respond cases, no-store.
101
- - [Egress](docs/EGRESS.md): bounded HTTPS proxy and best-effort signals behind operator grants; self-hosted only.
102
- - [Policies](docs/POLICIES.md): `throttle`, `agents`, `security`, `compression`, `cache`; all off unless declared. [Site](docs/SITE.md): robots, sitemap, favicon, security.txt, llms.txt.
103
- - [Organization](docs/ORGANIZATION.md), [best practices](docs/BEST-PRACTICES.md), [scaffolding](docs/SCAFFOLDING.md).
104
- - [Interchange](docs/INTERCHANGE.md), [bulk import](docs/BULK.md), [recipes](docs/RECIPES.md) (`urlcode recipes search`, `examples search`), [TypeScript guests](docs/TYPESCRIPT-AUTHORING.md).
105
-
106
- ## Extensions (accounts, administration, presentation)
107
- - [Extensions](docs/EXTENSIONS.md): `extensions.<name>` blocks, mounts, policies, operator registration, the shared trusted hook primitive and its machine-readable hook contracts. UI/auth/admin project hooks run trusted in-process; contract v1 rejects `sandbox: true`.
108
- - [Composing a site](docs/COMPOSING-A-SITE.md): what `urlcode init site --with ui,auth,admin` wires, which `--with` combinations are supported, the presentation override paths under `ui/`, and per-package lifecycle hook input, verdict, timing and failure semantics. Separates declarative configuration, project functions and the TypeScript a new extension needs.
109
- - [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.
110
- - [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.
111
- - [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.
112
- - 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.
113
- - 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.
114
- - [Plugins](docs/PLUGINS.md): host hook API in operator code, never named in YAML.
115
-
116
- ## Tooling and API
117
- - [Tooling SDK and MCP](docs/TOOLING.md): `urlcode mcp`, read-only inspection, validation, conversion previews.
118
- - [TypeScript](docs/TYPESCRIPT.md): declarations for `@jimhoyd/urlcode` and its `/plugins`, `/policies`, `/observability`, `/compliance`, `/prerender`, `/extensions`, `/sandbox`, `/aws`, `/vercel`, `/cloudflare` entries.
119
- - [Capabilities](docs/CAPABILITIES.md): per-target support; `urlcode capabilities --target NAME`.
120
-
121
- ## Operations
122
- - [Operations](docs/OPERATIONS.md), [install](docs/INSTALL.md), [deployment checks](docs/DEPLOYMENT-CHECKS.md), [CI action](docs/CI.md).
123
- - [Readiness](docs/READINESS.md), [capacity](docs/CAPACITY.md), [resilience](docs/RESILIENCE.md), [observability](docs/OBSERVABILITY.md).
124
- - [Vercel](docs/VERCEL.md), [AWS](docs/AWS.md), [Cloudflare](docs/CLOUDFLARE.md): adapters with local conformance tests; no provider deployment verified yet.
125
- - [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.
126
- - [Release readiness](docs/RELEASE-READINESS.md), [roadmap](ROADMAP.md): what is proven, what is planned.
29
+ | Fixed redirect, 301/302/303/307/308 (302 default) | Yes | `redirect: {url, status}` |
30
+ | Parameterized path (`/users/{id}` to `/profiles/{id}`) | Yes | `{name}` in `redirect.url`, naming a declared path parameter |
31
+ | Root-relative destination (`/users/{id}` to `/profiles/{id}`) | Yes | `redirect.url: /profiles/{id}`: one leading slash, path only |
32
+ | Wildcard suffix (`/legacy/**` to `/modern/{**}`, any depth) | Yes | terminal `/**` key with a literal prefix; `{**}` is the remaining segments (redirect only; not static or Cloudflare) |
33
+ | Query-string preservation | Yes, opt-in | `redirect.query.pass` / `query.map` |
34
+ | Method-preserving redirect | Yes | `methods` plus `status: 307` or `308` |
35
+ | 404 for unmatched paths | Yes | `site.notFound` (project-relative `.html`) |
36
+ | Host or scheme chosen from the request | No | destination is a literal absolute `https://` URL or a root-relative path; report the gap |
37
+
38
+ ## Declarative-first
39
+
40
+ Use the highest-level declarative feature before writing a function or middleware. Custom code
41
+ only when the framework cannot express the requirement; then report the gap. Never invent fields
42
+ or bypass target limits or operator grants ([design principle](docs/PROJECT-DIRECTION.md#design-principle-declarative-first)).
43
+
44
+ | I need | Use |
45
+ |---|---|
46
+ | Security headers | `policies.security` ([security](docs/policies/security.md)) |
47
+ | Cache headers / cache strategy | `cacheControl` / `policies.cache` ([assets](docs/yaml/assets.md), [cache](docs/policies/cache.md)) |
48
+ | Body size, content type, JSON check | `request.body` ([HTTP](docs/HTTP.md)) |
49
+ | Restrict HTTP methods | `methods` (default GET/HEAD; 405 with `Allow`) |
50
+ | Rate limiting, bot/AI-crawler blocking, compression | `policies.throttle`, `policies.agents`, `policies.compression` ([policies](docs/POLICIES.md)) |
51
+ | Fixed JSON/text/headers | `respond` and `response.headers` |
52
+ | robots.txt, sitemap, favicon, security.txt | top-level `site` ([site](docs/SITE.md)) |
53
+ | Sign-in, roles, protected routes | `extensions.auth`, then `auth: true` ([extensions](docs/EXTENSIONS.md)) |
54
+ | Persistence | operator-installed store extension, recipe `store-crud`; a database, filtering and per-record ownership beyond it are gaps |
55
+
56
+ When only code can do it, one function serves one or more routes:
57
+
58
+ ```yaml
59
+ /hello/{name}:
60
+ function: functions/hello.mjs # binds args.name; context.route.pattern is "/hello/{name}"
61
+ ```
62
+ ```js
63
+ export default (request, { args, route }) => Response.json({ hello: args.name });
64
+ ```
65
+
66
+ Handler for the response: fixed text or JSON is `respond`; one HTML file is `page`; a directory is
67
+ `static`; an attachment is `download`; a short HTML snippet is `respond` `text` with a
68
+ `Content-Type: text/html; charset=utf-8` header. Functions are trusted by default; add
69
+ `sandbox: true` to opt a route in ([function security](docs/FUNCTION-SECURITY.md)).
70
+
71
+ After a real attempt, draft feedback only for a capability gap, repeated workaround, documentation
72
+ gap or suspected defect, with sanitized YAML and validation evidence. Search existing issues first;
73
+ never publish or comment without the user's approval.
74
+
75
+ ## Reference (load one only when needed)
76
+
77
+ - Authoring: [AI authoring contract](docs/AI-AUTHORING.md), [YAML guide](docs/YAML-GUIDE.md), [field reference](docs/YAML-REFERENCE.md), [JSON Schema](schemas/urlcode.schema.json), [semantics](docs/SPECIFICATION.md), [cookbook](examples/cookbook/README.md), [recipes](docs/RECIPES.md).
78
+ - Skills: [authoring](.claude/skills/urlcode-authoring/SKILL.md), [operations](.claude/skills/urlcode-operations/SKILL.md).
79
+ - Routes and handlers: [routing](docs/ROUTING.md), [HTTP](docs/HTTP.md), [middleware](docs/MIDDLEWARE.md), [assets](docs/ASSETS.md), [conditions](docs/CONDITIONS.md), [egress](docs/EGRESS.md), [prerender](docs/PRERENDER.md), [site](docs/SITE.md), [organization](docs/ORGANIZATION.md).
80
+ - Extensions: [extensions](docs/EXTENSIONS.md) (including [signed declarative artifacts](docs/EXTENSIONS.md#signed-declarative-artifacts); MCP `get_extension_artifacts`), [composing a site](docs/COMPOSING-A-SITE.md), [urlcode-auth](packages/auth), [urlcode-admin](packages/admin), [urlcode-ui](packages/ui), each with its own llms.txt and in this repository as a workspace package. Per-route middleware is native core (`middleware:` array), not a retired extension; core has no native `link` handler and no supported extension provides stored links, so report them as a gap.
81
+ - Tooling: [tooling and MCP](docs/TOOLING.md), [TypeScript](docs/TYPESCRIPT.md), [capabilities](docs/CAPABILITIES.md), [plugins](docs/PLUGINS.md), [interchange](docs/INTERCHANGE.md), [bulk import](docs/BULK.md).
82
+ - Operations: [operations](docs/OPERATIONS.md), [install](docs/INSTALL.md), [deployment checks](docs/DEPLOYMENT-CHECKS.md), [readiness](docs/READINESS.md), [capacity](docs/CAPACITY.md), [resilience](docs/RESILIENCE.md), [observability](docs/OBSERVABILITY.md), [Vercel](docs/VERCEL.md), [AWS](docs/AWS.md), [Cloudflare](docs/CLOUDFLARE.md), [static hosting](docs/STATIC.md), [release readiness](docs/RELEASE-READINESS.md), [roadmap](ROADMAP.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jimhoyd/urlcode",
3
- "version": "0.4.7",
3
+ "version": "0.5.0",
4
4
  "description": "Portable runtime for programmable URL behavior",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -62,6 +62,10 @@
62
62
  "types": "./dist/types/extensions.d.ts",
63
63
  "default": "./dist/extensions.js"
64
64
  },
65
+ "./extension-bundles": {
66
+ "types": "./dist/types/extension-bundles.d.ts",
67
+ "default": "./dist/extension-bundles.js"
68
+ },
65
69
  "./sandbox": {
66
70
  "types": "./dist/types/sandbox.d.ts",
67
71
  "default": "./dist/sandbox.js"
@@ -69,7 +73,7 @@
69
73
  "./package.json": "./package.json"
70
74
  },
71
75
  "files": [
72
- ".claude",
76
+ ".claude/skills",
73
77
  "dist",
74
78
  "schemas",
75
79
  "data",
@@ -89,7 +93,8 @@
89
93
  "test": "node --conditions=development --test test/*.test.ts",
90
94
  "lint": "eslint .",
91
95
  "check": "npm run check:code && npm run check:docs",
92
- "check:code": "node scripts/check.ts && node scripts/check-release-tags.ts && node scripts/check-workspace-links.ts && npm run release:check",
96
+ "check:code": "node scripts/check.ts && node scripts/check-release-tags.ts && node scripts/check-workspace-links.ts && node scripts/check-issue-labels.ts && npm run release:check",
97
+ "test:workerd": "node scripts/workerd-parity.ts",
93
98
  "verify": "npm run lint && npm run typecheck && npm run check && npm run build && npm test && npm run verify:workspaces",
94
99
  "workspace:styles": "npm run styles --workspace @jimhoyd/urlcode-ui",
95
100
  "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 verify --workspace @jimhoyd/urlcode-store && npm run audit:packages && npm run test:workspace-integration",
@@ -108,6 +113,8 @@
108
113
  "docs:plugin": "node scripts/generate-claude-plugin.ts",
109
114
  "docs:llms": "node scripts/build-llms-full.ts",
110
115
  "docs:cookbook-index": "node scripts/build-cookbook-index.ts",
116
+ "artifacts:prepare": "node --disable-warning=ExperimentalWarning scripts/prepare-extension-artifacts.ts",
117
+ "bundles:prepare": "node --disable-warning=ExperimentalWarning scripts/prepare-extension-bundles.ts",
111
118
  "check:downstream-skills": "node scripts/check-downstream-skill-drift.ts",
112
119
  "sync:agents": "node scripts/sync-agent-lists.ts",
113
120
  "check:docs": "node scripts/check-trust-model-prose.ts && node scripts/check-version-statements.ts && node scripts/check-local-links.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",
@@ -119,6 +126,7 @@
119
126
  "release:run": "node scripts/release-run.ts",
120
127
  "release:peers": "node scripts/release.ts peers",
121
128
  "release:check": "node scripts/release.ts check && node scripts/release-prepare.ts --check",
129
+ "rehearse:release": "node --conditions=development --test test/release-rehearsal.test.ts",
122
130
  "ci:history": "node scripts/ci-history.ts",
123
131
  "ci:report": "node scripts/ci-report.ts",
124
132
  "test:workspace-integration": "node --test test/workspace-scaffold.integration.ts",
@@ -1,7 +1,21 @@
1
- # Permanent documentation redirect
1
+ # Redirects: fixed, parameterized, query-preserving and 404
2
2
 
3
3
  Run `urlcode validate --local --project .` and `urlcode serve --project .`.
4
- `/docs?campaign=launch&private=discarded` redirects to
5
- `https://example.com/documentation?campaign=launch` with status 301.
6
- Replace the example destination before deploying. Incoming query parameters are
7
- not forwarded unless explicitly allowlisted.
4
+
5
+ - `/docs?campaign=launch&private=discarded` redirects (301) to
6
+ `https://example.com/documentation?campaign=launch`. Only the declared
7
+ `campaign` key is forwarded; everything else, including `private`, is
8
+ dropped. Replace the example destination before deploying.
9
+ - `/users/{id}` redirects (308, method- and body-preserving) to
10
+ `https://example.com/profiles/{id}`, substituting the declared path
11
+ parameter into the destination.
12
+ - Any other path, such as `/missing` or `/users/42/extra`, answers the
13
+ runtime's default 404. `DELETE /docs` answers 405 (only GET/HEAD match by
14
+ default). Add top-level `site: {notFound: 404.html}` for a custom 404 page
15
+ instead of the plain default.
16
+
17
+ For the shapes this recipe does not cover — a root-relative or `/**` suffix
18
+ redirect, or a host/scheme-based redirect — run
19
+ `urlcode context --project . --task redirects` (MCP `get_context
20
+ {"task":"redirects"}`) for the exact supported alternative and the exact
21
+ validation error, or see [docs/OPEN-DECISIONS.md](../../docs/OPEN-DECISIONS.md).
@@ -1,25 +1,27 @@
1
1
  id: redirect
2
- description: Permanent redirect that forwards only an allowlisted query key.
3
- tags: [redirect, permanent, "301", query, passthrough, documentation, native]
2
+ description: Fixed, parameterized and query-preserving redirects, and the default 404 for everything else.
3
+ tags: [redirect, permanent, "301", "308", parameterized, query, passthrough, "404", documentation, native]
4
4
  complexity: starter
5
- capabilities: [enabled, methods, redirect]
6
- targets: {self-hosted: compatible, aws: compatible, vercel: compatible, cloudflare: compatible, static: compatible}
7
- routes: 1
5
+ capabilities: [enabled, methods, parameters, redirect]
6
+ targets: {self-hosted: compatible, aws: compatible, vercel: compatible, cloudflare: compatible, static: refused}
7
+ routes: 2
8
8
  inputs:
9
9
  - name: destination
10
10
  file: urlcode.yaml
11
- description: Replace https://example.com/documentation with the real target.
11
+ description: Replace https://example.com/documentation and https://example.com/profiles with the real targets.
12
12
  - name: query.pass
13
13
  file: urlcode.yaml
14
- description: The query keys forwarded to the destination; everything else is dropped.
14
+ description: The query keys forwarded to the /docs destination; everything else is dropped.
15
15
  files: [urlcode.yaml, tests/requests.json, README.md]
16
16
  tests:
17
17
  fixtures: tests/requests.json
18
18
  commands:
19
19
  - urlcode validate --local --project .
20
20
  - urlcode test --project .
21
- - urlcode audit --project . --expect-routes 1
21
+ - urlcode audit --project . --expect-routes 2
22
22
  behavior:
23
23
  - GET /docs?campaign=launch&private=x answers 301 to https://example.com/documentation?campaign=launch
24
- - HEAD answers the same status with an empty body
25
- - unknown paths answer 404
24
+ - HEAD /docs answers the same status with an empty body
25
+ - DELETE /docs answers 405
26
+ - GET /users/42 answers 308 (method- and body-preserving) to https://example.com/profiles/42
27
+ - unknown paths, including /users/42/extra and /missing, answer the runtime's default 404
@@ -12,8 +12,30 @@
12
12
  "status": 301,
13
13
  "expectBody": ""
14
14
  },
15
+ {
16
+ "path": "/users/42",
17
+ "status": 308,
18
+ "expectHeaders": {
19
+ "location": "https://example.com/profiles/42"
20
+ }
21
+ },
22
+ {
23
+ "path": "/users/42/extra",
24
+ "status": 404
25
+ },
26
+ {
27
+ "path": "/users/42",
28
+ "method": "HEAD",
29
+ "status": 308,
30
+ "expectBody": ""
31
+ },
15
32
  {
16
33
  "path": "/missing",
17
34
  "status": 404
35
+ },
36
+ {
37
+ "path": "/docs",
38
+ "method": "DELETE",
39
+ "status": 405
18
40
  }
19
41
  ]
@@ -1,9 +1,19 @@
1
1
  version: "1"
2
2
  routes:
3
3
  /docs:
4
- description: Move documentation and preserve only the declared campaign key.
4
+ description: Permanent move, forwarding only the declared campaign key.
5
5
  redirect:
6
6
  url: https://example.com/documentation
7
7
  status: 301
8
8
  query:
9
9
  pass: [campaign]
10
+ /users/{id}:
11
+ description: Parameterized redirect; the id segment carries straight through.
12
+ parameters:
13
+ - name: id
14
+ in: path
15
+ required: true
16
+ schema: {type: string, minLength: 1, maxLength: 64}
17
+ redirect:
18
+ url: https://example.com/profiles/{id}
19
+ status: 308
@@ -34,6 +34,8 @@ When the MCP server was started with an operator host file, `get_extensions`
34
34
  returns installed extension configuration/policy schemas, declared project
35
35
  hook contracts, supported authoring surfaces and fast checks. Otherwise use `urlcode extensions --project DIR --host-file
36
36
  ABSOLUTE_HOST --json` when the operator has supplied that host file.
37
+ If the project commits `urlcode.extensions.lock.json`, call `get_extension_artifacts`, then `get_extension_artifact` for only the locked schema, example or README needed; without MCP, run `urlcode extension-artifacts inspect --project DIR --json` before reading its cache.
38
+ These are verified, inert authoring inputs, not proof of an installed executable extension. Fetch or update one only when the user requests that project change and names an immutable `extensions@v…` release.
37
39
  Without the server, run the CLI equivalents and read only the output:
38
40
 
39
41
  ```sh
@@ -15,14 +15,14 @@ static serving and authentication. Read this file before changing anything.
15
15
  (limits; `--target NAME` before promising a provider), `get_schema`,
16
16
  `recipes search TEXT`/`search_recipes` then `recipes add NAME --out DIR`,
17
17
  `explain` and, with an operator host file, `get_extensions`. Bare
18
- `capabilities` and `recipes list` are complete catalogs: fallback, not step one.
18
+ `capabilities` and `recipes list` are complete catalogs: fallback, not step one. Do not read or grep `llms-full.txt` or the packaged docs for a routine task.
19
19
  4. Use URLCode's highest-level declarative features whenever possible. Generate custom code only when the framework cannot express the requirement. Check supported extensions and recipes first; explain any capability gap.
20
20
 
21
21
  ## MCP
22
22
 
23
23
  When present, `.mcp.json` registers the read-only `urlcode mcp` server; prefer its
24
24
  tools (also `get_manifest`) to reading documents. Inspect `get_extensions` before
25
- replacing extension behavior. `--allow-authoring` is an operator opt-in; never add it.
25
+ replacing extension behavior. `--allow-authoring` is an operator opt-in; never add it. For a committed artifact lock, use `get_extension_artifacts`/`get_extension_artifact`; they expose verified inert data and never activate an extension.
26
26
 
27
27
  ## What the runtime provides (this version)
28
28
 
@@ -49,7 +49,7 @@ skill only in a React frontend with `components.json`; start with `shadcn info
49
49
  A `function`/`middleware` is trusted, in-process Node with only declared
50
50
  `args`/`env`/`secrets`. Add `sandbox: true` for code needing isolation, not
51
51
  merely untrusted input. The sandbox is text/JSON-only; use `proxy`/a binding and
52
- record the reason in `sandboxReason`.
52
+ record the reason in `sandboxReason`. Try `redirect` (relative or `/**`) or `respond` first; a function gets `context.route.pattern`.
53
53
 
54
54
  ## Checks that count as evidence
55
55