@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.
- package/.claude/skills/urlcode-authoring/SKILL.md +8 -0
- package/README.md +9 -3
- package/dist/BUILD-MANIFEST.json +23 -21
- package/dist/agents-guide.js +9 -5
- package/dist/authoring.js +36 -3
- package/dist/build-cloudflare.js +5 -2
- package/dist/build-static.js +1 -0
- package/dist/cli.js +47 -15
- package/dist/cloudflare.js +6 -3
- package/dist/config.js +7 -1
- package/dist/context.js +99 -1
- package/dist/extension-artifacts.js +147 -0
- package/dist/extension-bundles.js +70 -0
- package/dist/extensions.js +4 -0
- package/dist/functions.js +2 -1
- package/dist/index.js +4 -2
- package/dist/init-with.js +55 -23
- package/dist/interchange.js +1 -1
- package/dist/match.js +23 -5
- package/dist/mcp.js +11 -4
- package/dist/readiness.js +2 -2
- package/dist/router.js +20 -8
- package/dist/runtime.js +1 -0
- package/dist/site.js +19 -1
- package/dist/tooling.js +2 -2
- package/dist/types/agents-guide.d.ts +6 -1
- package/dist/types/authoring.d.ts +1 -1
- package/dist/types/cloudflare.d.ts +1 -0
- package/dist/types/context.d.ts +56 -0
- package/dist/types/extension-artifacts.d.ts +87 -0
- package/dist/types/extension-bundles.d.ts +49 -0
- package/dist/types/extensions.d.ts +4 -0
- package/dist/types/functions.d.ts +4 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/init-with.d.ts +6 -1
- package/dist/types/match.d.ts +1 -0
- package/dist/types/site.d.ts +2 -0
- package/dist/types/tooling.d.ts +2 -2
- package/dist/types/types.d.ts +1 -0
- package/dist/types.js +1 -1
- package/llms-full.txt +188 -24
- package/llms.txt +63 -107
- package/package.json +11 -3
- package/recipes/redirect/README.md +19 -5
- package/recipes/redirect/recipe.yaml +12 -10
- package/recipes/redirect/tests/requests.json +22 -0
- package/recipes/redirect/urlcode.yaml +11 -1
- package/skills/urlcode/SKILL.md +2 -0
- 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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
- [
|
|
85
|
-
- [
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
- [
|
|
89
|
-
- [
|
|
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.
|
|
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
|
-
#
|
|
1
|
+
# Redirects: fixed, parameterized, query-preserving and 404
|
|
2
2
|
|
|
3
3
|
Run `urlcode validate --local --project .` and `urlcode serve --project .`.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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:
|
|
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:
|
|
7
|
-
routes:
|
|
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
|
|
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
|
|
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
|
-
-
|
|
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:
|
|
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
|
package/skills/urlcode/SKILL.md
CHANGED
|
@@ -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
|
|