@jimhoyd/urlcode 0.5.0 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.claude/skills/urlcode-authoring/SKILL.md +1 -1
  2. package/.claude/skills/urlcode-operations/SKILL.md +1 -1
  3. package/README.md +13 -16
  4. package/dist/BUILD-MANIFEST.json +21 -18
  5. package/dist/agents-guide.js +1 -1
  6. package/dist/authoring.js +50 -9
  7. package/dist/capabilities.js +1 -1
  8. package/dist/cli.js +20 -7
  9. package/dist/ecosystem-cli.js +6 -0
  10. package/dist/explain-cli.js +1 -1
  11. package/dist/explain.js +2 -2
  12. package/dist/extension-artifacts.js +10 -23
  13. package/dist/extension-bundles.js +8 -16
  14. package/dist/extension-transport.js +41 -0
  15. package/dist/feature-plan.js +99 -0
  16. package/dist/index.js +2 -2
  17. package/dist/mcp.js +6 -2
  18. package/dist/policies/agents.js +1 -1
  19. package/dist/policies/security.js +1 -1
  20. package/dist/policies.js +1 -1
  21. package/dist/review.js +206 -0
  22. package/dist/router.js +15 -2
  23. package/dist/scripts/operational-drills.js +1 -1
  24. package/dist/tooling.js +4 -0
  25. package/dist/types/authoring.d.ts +2 -0
  26. package/dist/types/explain.d.ts +3 -0
  27. package/dist/types/extension-artifacts.d.ts +2 -4
  28. package/dist/types/extension-transport.d.ts +31 -0
  29. package/dist/types/feature-plan.d.ts +67 -0
  30. package/dist/types/index.d.ts +2 -2
  31. package/dist/types/review.d.ts +30 -0
  32. package/dist/types/tooling.d.ts +4 -0
  33. package/dist/types/types.d.ts +9 -1
  34. package/dist/types.js +10 -3
  35. package/docs/AI-AUTHORING.md +466 -0
  36. package/docs/FUNCTION-SECURITY.md +251 -0
  37. package/docs/README.md +96 -0
  38. package/docs/TOOLING.md +422 -0
  39. package/docs/YAML-REFERENCE.md +473 -0
  40. package/examples/assets/example.yaml +3 -3
  41. package/examples/aws/example.yaml +3 -3
  42. package/examples/cloudflare/example.yaml +3 -3
  43. package/examples/compliance/README.md +1 -1
  44. package/examples/compliance/example.yaml +1 -1
  45. package/examples/conditions/example.yaml +3 -3
  46. package/examples/cookbook/README.md +4 -4
  47. package/examples/cookbook/example.yaml +3 -3
  48. package/examples/coverage-waiver/example.yaml +3 -3
  49. package/examples/egress/example.yaml +2 -2
  50. package/examples/extensions/example.yaml +1 -1
  51. package/examples/lifecycle/example.yaml +2 -2
  52. package/examples/not-found/README.md +2 -2
  53. package/examples/not-found/example.yaml +3 -3
  54. package/examples/prerender/README.md +4 -4
  55. package/examples/prerender/example.yaml +2 -2
  56. package/examples/provider-conformance/example.yaml +2 -2
  57. package/examples/shared-blocks/example.yaml +3 -3
  58. package/examples/vercel/example.yaml +3 -3
  59. package/llms-full.txt +119 -84
  60. package/llms.txt +28 -19
  61. package/package.json +19 -13
  62. package/recipes/store-crud/README.md +9 -10
  63. package/recipes/store-crud/recipe.yaml +1 -1
  64. package/schemas/urlcode.schema.json +3 -0
  65. package/skills/urlcode/SKILL.md +1 -1
  66. package/starters/default/AGENTS.md +1 -1
  67. package/starters/default/README.md +2 -2
@@ -5,6 +5,6 @@ route, with status 404 and `text/html`. See
5
5
  [site conventions](../../docs/SITE.md#notfound--404html).
6
6
 
7
7
  ```sh
8
- node src/cli.ts test --project examples/not-found
9
- node src/cli.ts build --target static --project examples/not-found # writes 404.html
8
+ node packages/core/src/cli.ts test --project examples/not-found
9
+ node packages/core/src/cli.ts build --target static --project examples/not-found # writes 404.html
10
10
  ```
@@ -9,9 +9,9 @@ files: [urlcode.yaml, public/index.html, public/404.html, tests/requests.json, R
9
9
  tests:
10
10
  fixtures: tests/requests.json
11
11
  commands:
12
- - node src/cli.ts validate --local --project examples/not-found
13
- - node src/cli.ts test --project examples/not-found
14
- - node src/cli.ts audit --project examples/not-found --expect-routes 2
12
+ - node packages/core/src/cli.ts validate --local --project examples/not-found
13
+ - node packages/core/src/cli.ts test --project examples/not-found
14
+ - node packages/core/src/cli.ts audit --project examples/not-found --expect-routes 2
15
15
  behavior:
16
16
  - an unmatched GET or HEAD is answered with the page and status 404
17
17
  - other methods keep the plain-text 404
@@ -14,11 +14,11 @@ runs no code at all.
14
14
  From the runtime checkout:
15
15
 
16
16
  ```sh
17
- node src/cli.ts dev --project examples/prerender # the dynamic source, live
18
- node src/cli.ts test --project examples/prerender # 7 fixtures
17
+ node packages/core/src/cli.ts dev --project examples/prerender # the dynamic source, live
18
+ node packages/core/src/cli.ts test --project examples/prerender # 7 fixtures
19
19
  node examples/prerender/prerender.mjs examples/prerender /absolute/out
20
- node src/cli.ts test --project /absolute/out # fixtures the build wrote
21
- node src/cli.ts audit --project /absolute/out --expect-routes 3
20
+ node packages/core/src/cli.ts test --project /absolute/out # fixtures the build wrote
21
+ node packages/core/src/cli.ts audit --project /absolute/out --expect-routes 3
22
22
  ```
23
23
 
24
24
  The same three URLs answer identically before and after. The difference is what
@@ -9,8 +9,8 @@ files: [urlcode.yaml, functions/page.mjs, middleware/template.mjs, prerender.mjs
9
9
  tests:
10
10
  fixtures: tests/requests.json
11
11
  commands:
12
- - node src/cli.ts test --project examples/prerender
12
+ - node packages/core/src/cli.ts test --project examples/prerender
13
13
  - node examples/prerender/prerender.mjs examples/prerender /absolute/out
14
- - node src/cli.ts audit --project /absolute/out --expect-routes 3
14
+ - node packages/core/src/cli.ts audit --project /absolute/out --expect-routes 3
15
15
  behavior:
16
16
  - the rendered project serves the same bytes from native page routes with no sandbox on the request path
@@ -8,7 +8,7 @@ routes: 5
8
8
  files: [urlcode.yaml, README.md]
9
9
  tests:
10
10
  commands:
11
- - node src/cli.ts validate --local --project examples/provider-conformance
12
- - node src/cli.ts verify-provider --target self-hosted --origin https://owned-fixture.example
11
+ - node packages/core/src/cli.ts validate --local --project examples/provider-conformance
12
+ - node packages/core/src/cli.ts verify-provider --target self-hosted --origin https://owned-fixture.example
13
13
  behavior:
14
14
  - no functions, bindings, assets or policies, so every target activates it; never mix it into a real project
@@ -10,8 +10,8 @@ files: [urlcode.yaml, tests/requests.json, README.md]
10
10
  tests:
11
11
  fixtures: tests/requests.json
12
12
  commands:
13
- - node src/cli.ts validate --local --project examples/shared-blocks
14
- - node src/cli.ts test --project examples/shared-blocks
15
- - node src/cli.ts audit --project examples/shared-blocks --expect-routes 4
13
+ - node packages/core/src/cli.ts validate --local --project examples/shared-blocks
14
+ - node packages/core/src/cli.ts test --project examples/shared-blocks
15
+ - node packages/core/src/cli.ts audit --project examples/shared-blocks --expect-routes 4
16
16
  behavior:
17
17
  - a route's own request or response key replaces the shared block whole; nothing is deep merged
@@ -12,8 +12,8 @@ files: [urlcode.yaml, api/index.js, vercel.json, public/index.html, public/notes
12
12
  tests:
13
13
  fixtures: tests/requests.json
14
14
  commands:
15
- - node src/cli.ts validate --local --project examples/vercel
16
- - node src/cli.ts test --project examples/vercel
17
- - node src/cli.ts audit --project examples/vercel --expect-routes 5
15
+ - node packages/core/src/cli.ts validate --local --project examples/vercel
16
+ - node packages/core/src/cli.ts test --project examples/vercel
17
+ - node packages/core/src/cli.ts audit --project examples/vercel --expect-routes 5
18
18
  behavior:
19
19
  - every route is native, so the project activates on Vercel without the sandbox
package/llms-full.txt CHANGED
@@ -1,5 +1,5 @@
1
1
  <!-- Generated by scripts/build-llms-full.ts (npm run docs:llms). Do not edit; edit the source documents. -->
2
- <!-- Consolidated URLCode authoring reference: 16 documents, about 60,557 tokens (estimate: characters / 4). -->
2
+ <!-- Consolidated URLCode authoring reference: 16 documents, about 61,257 tokens (estimate: characters / 4). -->
3
3
 
4
4
  <!-- urlcode-current-version:start -->
5
5
  # URLCode
@@ -7,10 +7,11 @@
7
7
  > A portable runtime for programmable URL behavior, and the framework that grows
8
8
  > from it: routes in YAML, functions and middleware, then accounts, administration and
9
9
  > stored links as operator-installed extensions. Stable project format
10
- > `version: "1"`. Core is Apache-2.0; this revision is `0.5.0`. `function`/`middleware`
10
+ > `version: "1"`. Core is Apache-2.0; this revision is `0.5.6`. `function`/`middleware`
11
11
  > routes are trusted by default with `sandbox: true` as the per-route opt-in. The
12
- > auth, admin and ui extension packages in this repository are versioned at the same
13
- > revision; confirm what is published with `npm run release:status`.
12
+ > auth, admin, ui and store workspace packages are bundle sources. New sites install
13
+ > core from npm and verified executable extensions from an immutable GitHub Release;
14
+ > see package and channel alignment for the supported release pair.
14
15
  <!-- urlcode-current-version:end -->
15
16
 
16
17
  Use the schema and docs from the runtime revision you run. Do not assume Node
@@ -43,12 +44,12 @@ grants. Unsupported features fail with the route named; nothing degrades silentl
43
44
 
44
45
  Source: https://github.com/jimhoyd-com/urlcode/blob/main/docs/FRAMEWORK.md
45
46
 
46
- One page for people and AI agents. It says what the four packages are, how a
47
+ One page for people and AI agents. It says what the six workspace packages are, how a
47
48
  project grows from a handful of redirects into an application with accounts
48
49
  and an administration console, and which facts an agent must not guess. Every
49
50
  claim here is implemented in the linked repository; nothing is roadmap.
50
51
 
51
- ### Four packages, one project shape
52
+ ### Six workspace packages, one project shape
52
53
 
53
54
  | Package | Source | What it adds | How a project declares it |
54
55
  |---|---|---|---|
@@ -56,12 +57,17 @@ claim here is implemented in the linked repository; nothing is roadmap.
56
57
  | `@jimhoyd/urlcode-ui` | [`packages/ui`](https://github.com/jimhoyd-com/urlcode/blob/main/packages/ui) | Shared presentation: escaped templates, shadcn/ui partials, one stylesheet with light and dark, themes, translations, the `ui` extension that serves the kit's assets | `extensions.ui` plus an asset mount route |
57
58
  | `@jimhoyd/urlcode-auth` | [`packages/auth`](https://github.com/jimhoyd-com/urlcode/blob/main/packages/auth) | Accounts: password, passkeys, OpenID Connect, email codes, TOTP, recovery, sessions, roles, registration modes, account page, operator CLI | `extensions.auth` plus an `/account/*` mount and `policies.extensions.auth` on protected routes |
58
59
  | `@jimhoyd/urlcode-admin` | [`packages/admin`](https://github.com/jimhoyd-com/urlcode/blob/main/packages/admin) | Administration: users, sessions, roles, audit, registration approval, two-person cases, support impersonation, health | `extensions.admin` plus an `/admin/*` mount |
59
-
60
- All four are Apache-2.0. Each package's npm `latest` tag identifies its stable
61
- version. The current publisher records the exact four-package stack tested
62
- together on each new GitHub release and attaches the same information in signed
63
- `train.json` metadata.
64
- A stable npm channel is not an independent assessment: review, deployment
60
+ | `@jimhoyd/urlcode-store` | [`packages/store`](https://github.com/jimhoyd-com/urlcode/blob/main/packages/store) | Durable bounded JSON collections exposed as a typed CRUD API | `extensions.store` plus a protected collection mount |
61
+ | `@jimhoyd/urlcode-forms` | [`packages/forms`](https://github.com/jimhoyd-com/urlcode/blob/main/packages/forms) | Bounded server-rendered form flows: escaped controls, admission, CSRF, validation and a fixed confirmation | `extensions.forms` plus a `GET, HEAD, POST` form mount; it composes with `ui` and optional `auth` |
62
+
63
+ All six are Apache-2.0. Core is published through npm, GitHub Releases and
64
+ Homebrew. The first-party executable extensions are published as signed,
65
+ immutable GitHub Release bundles; their source remains in these workspace
66
+ packages, but new sites do not install them from npm. The forms package is an
67
+ unreleased bundle source and is not implied by the currently recorded bundle
68
+ release. The legacy extension npm
69
+ packages are deprecated migration artifacts. A release channel is not an
70
+ independent assessment: review, deployment
65
71
  evidence and an accessibility assessment are still pending
66
72
  ([issue 58](https://github.com/jimhoyd-com/urlcode/issues/58)). Their status
67
73
  files say exactly what is built: [auth](https://github.com/jimhoyd-com/urlcode/blob/main/packages/auth/IMPLEMENTATION-STATUS.md),
@@ -101,33 +107,40 @@ Each rung's YAML is valid on every rung above it.
101
107
  6. **Your own look.** A shared `presentation` (catalogue and theme variables)
102
108
  restyles auth and admin together; the `ui` extension adds the template kit,
103
109
  project copy, template and stylesheet overrides for kit-rendered pages.
110
+ 7. **Bounded data and forms.** The `store` extension supplies declared durable
111
+ collections; the `forms` extension supplies declared browser form flows over
112
+ the shared UI kit. Both are trusted operator extensions, not core YAML
113
+ handlers. Add `auth: true` where a flow or collection is per-account.
104
114
 
105
115
  Stored short links previously sat here as a native `link` route; that handler
106
116
  was removed from core. A `urlcode-dynamic-link` package owned them the same way
107
117
  `auth`/`admin` own their mounts, but it has been retired and unpublished; no
108
118
  package occupies this rung today.
109
119
 
110
- Rungs 1 to 3 need only the core package. Rungs 4 to 6 need the extension
111
- packages installed from npm and a Node host with a
112
- patched SQLite build; see each package's README ([auth](https://github.com/jimhoyd-com/urlcode/blob/main/packages/auth/README.md),
113
- [admin](https://github.com/jimhoyd-com/urlcode/blob/main/packages/admin/README.md), [ui](https://github.com/jimhoyd-com/urlcode/blob/main/packages/ui/README.md)) for the
120
+ Rungs 1 to 3 need only the core package. Rungs 4 to 7 need a verified extension
121
+ bundle installed into an explicit operator host, once its source package appears
122
+ in a selected catalog. Auth and admin additionally need the Node/SQLite runtime
123
+ their packages document; forms declares Node, AWS and Vercel targets, while store
124
+ is currently Node-only. See each package's README ([auth](https://github.com/jimhoyd-com/urlcode/blob/main/packages/auth/README.md),
125
+ [admin](https://github.com/jimhoyd-com/urlcode/blob/main/packages/admin/README.md), [ui](https://github.com/jimhoyd-com/urlcode/blob/main/packages/ui/README.md),
126
+ [store](https://github.com/jimhoyd-com/urlcode/blob/main/packages/store/README.md), [forms](https://github.com/jimhoyd-com/urlcode/blob/main/packages/forms/README.md)) for the
114
127
  exact requirement.
115
128
 
116
129
  ### The composition contract
117
130
 
118
- An extended project starts from the current stable packages and one command.
119
- `--save-exact` records the concrete versions selected from the independent
120
- `latest` channels:
131
+ An extended project starts from core and an immutable bundle release. The
132
+ release is an explicit operator choice; use the current verified tag from
133
+ [package and channel alignment](https://github.com/jimhoyd-com/urlcode/blob/main/docs/VERSION-ALIGNMENT.md):
121
134
 
122
135
  ```sh
123
- npm install --save-exact @jimhoyd/urlcode@latest @jimhoyd/urlcode-ui@latest @jimhoyd/urlcode-auth@latest @jimhoyd/urlcode-admin@latest
124
- urlcode init my-site --with ui,auth,admin
136
+ npm install @jimhoyd/urlcode
137
+ npx urlcode init my-site --with ui,auth,admin --bundle-release extension-bundles@v…
125
138
  ```
126
139
 
127
- Installing from npm is the normal path; `scripts/pack-sources.mjs` still builds
128
- local tarballs from a reviewed checkout for operators who install only source
129
- they have read one revision now covers core and every extension. Three files make an extended
130
- project. Nothing else is discovered by convention.
140
+ This produces a manifest with core only and a bundle lockfile for extensions.
141
+ `scripts/pack-sources.mjs` remains available to review reproducible source
142
+ inputs. Three files make an extended project. Nothing else is discovered by
143
+ convention.
131
144
 
132
145
  ```
133
146
  site/
@@ -199,36 +212,32 @@ package behavior.
199
212
  A signed declarative artifact is a separate, optional authoring input, not a
200
213
  fifth way to compose executable behavior. A project may lock an attested
201
214
  schema/example bundle and expose it through MCP `get_extension_artifacts` and
202
- `get_extension_artifact`; the npm package and operator host remain the only
203
- executable extension path. See [signed declarative artifacts](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EXTENSIONS.md#signed-declarative-artifacts).
215
+ `get_extension_artifact`; the verified executable bundle and explicit operator
216
+ host remain the executable extension path. See [signed declarative artifacts](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EXTENSIONS.md#signed-declarative-artifacts).
204
217
 
205
218
  ```sh
206
219
  urlcode serve --project /absolute/site --host-file /absolute/operator/host.mjs --origin https://site.example
207
220
  ```
208
221
 
209
- `urlcode init <dir> --with ui,auth,admin` writes this layout in one step: by
210
- default it resolves each installed `@jimhoyd/urlcode-<name>` from the current
211
- directory; with `--bundle-release extension-bundles@v…` it verifies and locks
212
- the named GitHub Release bundles instead. In either mode it calls the verified
213
- module's `scaffold` export and merges fragments into `app/urlcode.yaml`, one
222
+ `urlcode init <dir> --with ui,auth,admin --bundle-release
223
+ extension-bundles@v…` writes this layout in one step. It verifies and locks the
224
+ named GitHub Release bundles, calls each verified module's `scaffold` export,
225
+ and merges fragments into `app/urlcode.yaml`, one
214
226
  explicit `host.mjs` and one `README.md`, refusing before writing a site when a
215
- package/bundle is missing or two fragments collide (the contract is documented under
227
+ bundle is missing or two fragments collide (the contract is documented under
216
228
  [scaffolding](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EXTENSIONS.md#scaffolding-with-init---with)). `urlcode-auth init`
217
229
  and `urlcode-admin init` write the same layout for a single package; `urlcode-auth bootstrap` creates the first
218
230
  administrator from JSON on stdin. `inspectExtensionRevision(project)` prints
219
231
  the SHA-256 that `projectSha256` must carry; changing extension YAML, policies
220
232
  or mounts changes the revision and needs an explicit operator reapproval.
221
233
 
222
- The presentation tooling composes the same way, by naming packages rather than
223
- depending on them. `urlcode-ui` is the kit alone until
224
- `--extensions @jimhoyd/urlcode-auth,@jimhoyd/urlcode-admin` names the packages
225
- that ship the other namespaces: each is resolved from `--project` with Node
226
- package resolution and imported for the namespace it exports, so `list`,
234
+ The presentation tooling composes the same way, by naming logical extensions
235
+ rather than npm dependencies. The UI bundle is the kit until the locked auth
236
+ and admin bundles add their namespaces, so `list`,
227
237
  `doctor`, `eject`, `preview` and `copy --missing` cover the `auth/*` and
228
238
  `admin/*` templates and copy the host registers, and a project override of an
229
- extension template is checked against the shipped view model. A package that is
230
- not installed is skipped. `urlcode init --with` writes the commands with the
231
- flag already set; `@jimhoyd/urlcode-ui` still depends on neither peer.
239
+ extension template is checked against the shipped view model. `urlcode init
240
+ --with --bundle-release` writes the release pin into the generated README.
232
241
 
233
242
  ### Rules an agent must follow
234
243
 
@@ -313,6 +322,13 @@ needed schema, example or README. Context is a summary with the
313
322
  constraints and exact commands, not a schema dump, and it never hides a
314
323
  capability limit: ask `capabilities NAME` before promising a feature.
315
324
 
325
+ When a goal spans routes, persistence or extensions, the next bounded query can
326
+ be `urlcode plan-feature "goal" --project DIR --json` (MCP `plan_feature`). It
327
+ matches only the current compiled project, capability catalog, bundled recipes,
328
+ locked inert artifacts and registrations already available to the session. Read
329
+ its operator prerequisites and explicit gaps as constraints, not as permission
330
+ to select packages, storage, keys or grants in project YAML.
331
+
316
332
  The complete catalogs (`urlcode capabilities`, `recipes list`), the compact
317
333
  [llms.txt](https://github.com/jimhoyd-com/urlcode/blob/main/llms.txt) index and the generated [llms-full.txt](https://github.com/jimhoyd-com/urlcode/blob/main/llms-full.txt)
318
334
  stay available as deliberate fallback and reference, not as the opening move.
@@ -423,7 +439,7 @@ urlcode benchmark --project ./my-links --requests 100 --concurrency 2
423
439
  ```
424
440
 
425
441
  Use the intentional actual count, not always 2. Runtime checkout users can replace
426
- `urlcode` with `node src/cli.ts`. Template users can use the equivalent npm scripts.
442
+ `urlcode` with `node packages/core/src/cli.ts`. Template users can use the equivalent npm scripts.
427
443
  External bindings require an already reviewed policy; add `--policy` where needed.
428
444
  The benchmark operates locally; it is not a load test of an external deployment.
429
445
 
@@ -536,9 +552,9 @@ Which handler serves the response:
536
552
  Data persistence has no native handler. The operator-installed `store` extension
537
553
  serves declared collections as a CRUD API, and `urlcode recipes search "crud store
538
554
  persist"` finds the `store-crud` recipe. It needs the operator to install
539
- `@jimhoyd/urlcode-store` package or an attested executable bundle and a host
540
- file. `init --with ui,auth,store --bundle-release extension-bundles@v…`
541
- scaffolds the npm-free bundle form; a no-auth
555
+ an attested executable bundle and a host file. `init --with
556
+ ui,auth,store --bundle-release extension-bundles@v…` scaffolds the supported
557
+ npm-free form; a no-auth
542
558
  `--with store` needs `--ack store:public-write`, which only a core release after the
543
559
  store's first publication has, so say so rather than promising it. Report anything beyond that recipe (filtering, sorting, per-record
544
560
  ownership, a database) as a gap. `urlcode context` lists the same built-ins so
@@ -757,7 +773,7 @@ programmatic compatibility analysis and provider verification limits.
757
773
  Source: https://github.com/jimhoyd-com/urlcode/blob/main/docs/YAML-GUIDE.md
758
774
 
759
775
  <!-- urlcode-current-version:start -->
760
- This guide targets URLCode 0.5.0. Start with the function example below,
776
+ This guide targets URLCode 0.5.6. Start with the function example below,
761
777
  then add only the fields your route needs. The authoritative machine-readable
762
778
  shape is [JSON Schema](https://github.com/jimhoyd-com/urlcode/blob/main/schemas/urlcode.schema.json); semantic rules are in the
763
779
  [specification](https://github.com/jimhoyd-com/urlcode/blob/main/docs/SPECIFICATION.md). Unsupported fields fail validation.
@@ -772,10 +788,10 @@ From the runtime checkout:
772
788
 
773
789
  ```sh
774
790
  npm ci
775
- node src/cli.ts validate --project examples/cookbook
776
- node src/cli.ts test --project examples/cookbook
777
- node src/cli.ts audit --project examples/cookbook --expect-routes 40
778
- node src/cli.ts dev --project examples/cookbook
791
+ node packages/core/src/cli.ts validate --project examples/cookbook
792
+ node packages/core/src/cli.ts test --project examples/cookbook
793
+ node packages/core/src/cli.ts audit --project examples/cookbook --expect-routes 40
794
+ node packages/core/src/cli.ts dev --project examples/cookbook
779
795
  ```
780
796
 
781
797
  The cookbook is a larger learning project. The normal `urlcode init ../my-links`
@@ -898,6 +914,7 @@ schema-valid combinations activate successfully.
898
914
  | `routes.*.env.* (option 1).value` | string | yes | — |
899
915
  | `routes.*.env.* (option 2)` | object | no | unknown keys rejected |
900
916
  | `routes.*.env.* (option 2).env` | string | yes | pattern: "^[A-Za-z_][A-Za-z0-9_]*$" |
917
+ | `routes.*.env.* (option 2).default` | string | no | — |
901
918
  | `routes.*.secrets` | object | no | — |
902
919
  | `routes.*.secrets.*` | object | no | unknown keys rejected |
903
920
  | `routes.*.secrets.*.secret` | string | yes | pattern: "^[A-Za-z_][A-Za-z0-9_]*$" |
@@ -1593,12 +1610,17 @@ parity for a `sandbox: true` route; a trusted route has no such restriction.
1593
1610
 
1594
1611
  Context contains `inputs.path/query/header`, `args`, `env`, `secrets`. Arguments
1595
1612
  may be scalar literals, input references, `{env: alias}` or `{secret: alias}`.
1596
- Bindings use `{value: "literal"}`, `{env: EXTERNAL_NAME}` or `{secret: logical_name}`.
1597
- Literal non-secret values need no grant. Every external environment or secret
1598
- binding is denied unless an operator policy grants that exact name to the route
1599
- and matches the SHA-256 of the current configuration/source snapshot.
1600
- A project cannot grant itself capabilities. See [policy setup](https://github.com/jimhoyd-com/urlcode/blob/main/docs/FUNCTION-SECURITY.md).
1601
- Missing bindings also reject activation. Inspection parses source without running it.
1613
+ Bindings use `{value: "literal"}`, `{env: EXTERNAL_NAME}`,
1614
+ `{value: "default", env: EXTERNAL_NAME}` or `{secret: logical_name}`. The
1615
+ combined form resolves to the process environment variable when set and
1616
+ non-empty, else `value`; it needs the same operator grant as `{env: ...}`
1617
+ alone. Literal non-secret values need no grant. Every external environment or
1618
+ secret binding is denied unless an operator policy grants that exact name to
1619
+ the route and matches the SHA-256 of the current configuration/source
1620
+ snapshot. A project cannot grant itself capabilities. See
1621
+ [policy setup](https://github.com/jimhoyd-com/urlcode/blob/main/docs/FUNCTION-SECURITY.md). Missing bindings reject activation
1622
+ (the combined form always resolves, via its default). Inspection parses
1623
+ source without running it.
1602
1624
 
1603
1625
  Development may read `.env.local`; process values win. Serving never reads it.
1604
1626
  Dotenv supports single-line NAME=value, paired single/double quotes, blank lines
@@ -2833,7 +2855,7 @@ Extensions are trusted operator modules, separate from a project's own
2833
2855
  `function`/`middleware` code. Auth
2834
2856
  and admin implementations live in `urlcode-auth` and `urlcode-admin`; the runtime
2835
2857
  supplies only the generic integration contract. No project file can import a host
2836
- extension or choose its npm package.
2858
+ extension, choose a bundle release, or choose an npm package.
2837
2859
 
2838
2860
  Stored short links moved out of core this way too: a `urlcode-dynamic-link`
2839
2861
  package (mount-based, like `auth`/`admin`) owned the durable link store, its CLI
@@ -2845,6 +2867,13 @@ The `store` extension is the data-owning counterpart: it serves declared,
2845
2867
  bounded collections as a CRUD API from an operator-owned directory. See
2846
2868
  [data store](https://github.com/jimhoyd-com/urlcode/blob/main/docs/STORE.md).
2847
2869
 
2870
+ The `forms` extension is the browser-flow counterpart: it renders bounded
2871
+ declared fields through the `ui` kit, validates URL-encoded submissions with
2872
+ its host-supplied CSRF secret, and redirects a successful submission to a
2873
+ fixed confirmation page. It is a trusted operator extension, needs `ui`, and
2874
+ may be mounted with `auth: true`; its optional `onSubmit` hook is trusted
2875
+ project code rather than a sandbox bridge. See the [forms package](https://github.com/jimhoyd-com/urlcode/blob/main/packages/forms/README.md).
2876
+
2848
2877
  A project declares versioned configuration and exclusive route mounts:
2849
2878
 
2850
2879
  ```yaml
@@ -3229,29 +3258,24 @@ Assembly rules, in the resolved order:
3229
3258
 
3230
3259
  #### Recorded versions
3231
3260
 
3232
- `init --with` also writes `<directory>/package.json`: a private manifest that
3233
- pins, at exactly the version that was resolved, the running runtime, every
3234
- extension named in `--with`, and every package those extensions declare in
3235
- `peerDependencies` (so `@jimhoyd/urlcode-ui` is pinned for an `auth,admin` site
3236
- although nobody named it). Before anything is written, the whole set is checked
3237
- against every declared peer range; an incompatible combination or a missing
3238
- required peer refuses and names it, leaving no directory behind.
3261
+ `init --with --bundle-release extension-bundles@v…` writes a private
3262
+ `<directory>/package.json` pinning the running core and an extension bundle
3263
+ lockfile naming the verified archives. It does not add extension npm
3264
+ dependencies. Before anything is written, the selected catalog checks every
3265
+ required extension and core compatibility; a missing requirement or incompatible
3266
+ bundle refuses and names it, leaving no directory behind.
3239
3267
 
3240
3268
  Nothing is installed. The generated site has no `node_modules` and no
3241
- `package-lock.json` until you run `npm install` in it yourself, which the
3242
- command and the generated README both state as the next step. Reproducibility
3243
- comes from that install, not from generation.
3269
+ `package-lock.json` until you run `npm install` in it yourself for core, which
3270
+ the command and generated README both state as the next step. Bundle
3271
+ reproducibility comes from the committed lockfile and frozen cache, not npm.
3244
3272
 
3245
3273
  - `--no-manifest` generates the site without a `package.json`, for a site whose
3246
3274
  dependencies are managed elsewhere. Plain `urlcode init` is unchanged and
3247
3275
  still writes no manifest; add `--manifest` to pin the runtime for a
3248
3276
  route-only project too.
3249
- - `--pin <package>=<specifier>` records a specifier instead of the resolved
3250
- version, for local tarball or offline development
3251
- (`--pin @jimhoyd/urlcode-auth=file:/abs/urlcode-auth-0.1.0-alpha.6.tgz`). A
3252
- package installed from a local path or tarball is detected from npm's own
3253
- install record and pinned by that path without any flag; the README says so,
3254
- because such a pin only reproduces where that path exists.
3277
+ - `--pin <package>=<specifier>` is only for reviewed local source development.
3278
+ New first-party extension installs use the signed bundle release instead.
3255
3279
 
3256
3280
  There is no upgrade command. Moving a generated project to newer versions today
3257
3281
  means editing its `package.json` and re-running `npm install` yourself; nothing
@@ -3323,8 +3347,8 @@ requirements.
3323
3347
 
3324
3348
  ### Signed executable extension bundles
3325
3349
 
3326
- Official executable extensions are migrating away from consumer npm installs.
3327
- They use a separate, immutable `extension-bundles@v…` GitHub Release namespace;
3350
+ Official executable extensions are delivered through a separate, immutable
3351
+ `extension-bundles@v…` GitHub Release namespace;
3328
3352
  it is intentionally disjoint from the permanently data-only `extensions@v…`
3329
3353
  artifact channel above. A bundle is a bounded, frozen Node module tree produced
3330
3354
  from reviewed first-party source, not a general extension marketplace and not
@@ -3377,10 +3401,14 @@ export default { extensions: [storeExtension({ directory: '/srv/site-data', proj
3377
3401
  ```
3378
3402
 
3379
3403
  This does not make bundle code sandboxed and does not alter a route that
3380
- declares `sandbox: true`; those remain distinct execution modes. npm packages
3381
- remain the migration fallback until the first signed bundle release and the
3382
- fresh composed consumer flow have been released and proven. Do not unpublish a
3383
- package merely because its data-only artifact exists.
3404
+ declares `sandbox: true`; those remain distinct execution modes. The signed
3405
+ bundle path is the supported distribution for first-party executable
3406
+ extensions. The legacy `@jimhoyd/urlcode-ui`, `@jimhoyd/urlcode-auth`,
3407
+ `@jimhoyd/urlcode-admin`, and `@jimhoyd/urlcode-store` npm packages are
3408
+ deprecated migration artifacts: existing projects may retain their locked
3409
+ copies, but new projects must use a verified bundle release. Their npm
3410
+ retention status is not a promise that they are available or supported for new
3411
+ installs.
3384
3412
 
3385
3413
  ---
3386
3414
 
@@ -3392,10 +3420,16 @@ One command produces a site that already has accounts, an administration
3392
3420
  console and a presentation kit wired together:
3393
3421
 
3394
3422
  ```sh
3395
- npm install @jimhoyd/urlcode @jimhoyd/urlcode-ui @jimhoyd/urlcode-auth @jimhoyd/urlcode-admin
3396
- urlcode init site --with ui,auth,admin
3423
+ npm install @jimhoyd/urlcode
3424
+ npx urlcode init site --with ui,auth,admin --bundle-release extension-bundles@v…
3397
3425
  ```
3398
3426
 
3427
+ Use the immutable bundle release recorded in [package and channel
3428
+ alignment](https://github.com/jimhoyd-com/urlcode/blob/main/docs/VERSION-ALIGNMENT.md). This creates a site whose npm manifest pins
3429
+ core only; UI, auth and admin are verified, locked GitHub Release bundles.
3430
+ The legacy extension npm packages are deprecated migration artifacts. New sites
3431
+ obtain extensions from the verified bundle release.
3432
+
3399
3433
  This page is the map of what you may then change, and with which tool. It
3400
3434
  covers three different activities that are easy to confuse:
3401
3435
 
@@ -3573,9 +3607,10 @@ not run it. With the packages named:
3573
3607
  and those translations do not currently reach the console
3574
3608
  ([#227](https://github.com/jimhoyd-com/urlcode/issues/227)).
3575
3609
 
3576
- `urlcode init <directory> --with ui,auth,admin` writes these commands into the
3577
- generated README with the flag already set. `@jimhoyd/urlcode-ui` depends on
3578
- neither peer; the operator names them.
3610
+ `urlcode init <directory> --with ui,auth,admin --bundle-release
3611
+ extension-bundles@v…` writes these commands into the generated README with the
3612
+ verified bundle release pinned. The operator names the logical extensions; it
3613
+ does not add extension npm dependencies.
3579
3614
 
3580
3615
  Run the extension's published `fastChecks` while editing. Theme and copy changes
3581
3616
  need no framework build. Template and CSS checks load only the UI kit and named
package/llms.txt CHANGED
@@ -4,10 +4,11 @@
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.5.0`. `function`/`middleware`
7
+ > `version: "1"`. Core is Apache-2.0; this revision is `0.5.6`. `function`/`middleware`
8
8
  > routes are trusted by default with `sandbox: true` as the per-route opt-in. The
9
- > auth, admin and ui extension packages in this repository are versioned at the same
10
- > revision; confirm what is published with `npm run release:status`.
9
+ > auth, admin, ui and store workspace packages are bundle sources. New sites install
10
+ > core from npm and verified executable extensions from an immutable GitHub Release;
11
+ > see package and channel alignment for the supported release pair.
11
12
  <!-- urlcode-current-version:end -->
12
13
 
13
14
  Use the schema and docs from the runtime revision you run. Do not assume Node
@@ -17,10 +18,10 @@ grants. Unsupported features fail with the route named; nothing degrades silentl
17
18
 
18
19
  ## Start here (read only this section for a first attempt)
19
20
 
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
21
+ 1. Run `npx --no --package @jimhoyd/urlcode urlcode context --project DIR` (MCP `get_context`). For redirect-only work run
22
+ `npx --no --package @jimhoyd/urlcode urlcode context --project DIR --task redirects`: supported shapes with exact YAML, gaps
22
23
  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
+ 2. After installing `@jimhoyd/urlcode`, run `npx --no --package @jimhoyd/urlcode urlcode init . --template redirects` (it works in a directory holding only `package.json`, `package-lock.json`, `node_modules` or `.git`, and merges `scripts.start` into your `package.json`): yaml, 404 page, `npm start` honoring `PORT`, fixtures. Otherwise write `urlcode.yaml`, then run `npx --no --package @jimhoyd/urlcode urlcode validate --local --project DIR` and `npx --no --package @jimhoyd/urlcode urlcode test`. `--no` refuses a registry fetch, so it always uses the installed scoped runtime.
24
25
  3. Do not read or grep [llms-full.txt](llms-full.txt) (about 60k tokens) for a routine task; it is the
25
26
  deliberate fallback when the steps above do not answer the question.
26
27
 
@@ -39,19 +40,20 @@ grants. Unsupported features fail with the route named; nothing degrades silentl
39
40
 
40
41
  Use the highest-level declarative feature before writing a function or middleware. Custom code
41
42
  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
+ or bypass target limits or operator grants ([design principle](https://github.com/jimhoyd-com/urlcode/blob/main/docs/PROJECT-DIRECTION.md#design-principle-declarative-first)).
43
44
 
44
45
  | I need | Use |
45
46
  |---|---|
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)) |
47
+ | Security headers | `policies.security` ([security](https://github.com/jimhoyd-com/urlcode/blob/main/docs/policies/security.md)) |
48
+ | Cache headers / cache strategy | `cacheControl` / `policies.cache` ([assets](https://github.com/jimhoyd-com/urlcode/blob/main/docs/yaml/assets.md), [cache](https://github.com/jimhoyd-com/urlcode/blob/main/docs/policies/cache.md)) |
49
+ | Body size, content type, JSON check | `request.body` ([HTTP](https://github.com/jimhoyd-com/urlcode/blob/main/docs/HTTP.md)) |
49
50
  | 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
+ | Rate limiting, bot/AI-crawler blocking, compression | `policies.throttle`, `policies.agents`, `policies.compression` ([policies](https://github.com/jimhoyd-com/urlcode/blob/main/docs/POLICIES.md)) |
51
52
  | 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)) |
53
+ | robots.txt, sitemap, favicon, security.txt | top-level `site` ([site](https://github.com/jimhoyd-com/urlcode/blob/main/docs/SITE.md)) |
54
+ | Sign-in, roles, protected routes | `extensions.auth`, then `auth: true` ([extensions](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EXTENSIONS.md)) |
54
55
  | Persistence | operator-installed store extension, recipe `store-crud`; a database, filtering and per-record ownership beyond it are gaps |
56
+ | Stored short links (create, redirect, click count) | operator-installed store extension's `shortLinks` capability: a collection `key`, an HTTP(S) `destination` field, one `increments` counter, and a public `/go`-style redirect mount; no function needed (`urlcode docs search "shortLinks"`, MCP `search_docs`) |
55
57
 
56
58
  When only code can do it, one function serves one or more routes:
57
59
 
@@ -63,10 +65,17 @@ When only code can do it, one function serves one or more routes:
63
65
  export default (request, { args, route }) => Response.json({ hello: args.name });
64
66
  ```
65
67
 
68
+ Function handler shape: a module whose `export default` is `(request, { args, env, route })` and returns a
69
+ `Response` (or a promise of one). `args` holds the route's declared path inputs; the string form
70
+ `function: functions/hello.mjs` and the long form `function: {source: ...}` both bind every declared path input
71
+ (write `args: {}` to bind none). `sandbox: true` is a separate route option. `route.pattern` is the matched pattern; `env` holds route-scoped values only.
72
+ Search the docs with bounded output using `npx --no --package @jimhoyd/urlcode urlcode docs search "TEXT"` (MCP `search_docs`)
73
+ rather than grepping llms-full.txt.
74
+
66
75
  Handler for the response: fixed text or JSON is `respond`; one HTML file is `page`; a directory is
67
76
  `static`; an attachment is `download`; a short HTML snippet is `respond` `text` with a
68
77
  `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)).
78
+ `sandbox: true` to opt a route in ([function security](https://github.com/jimhoyd-com/urlcode/blob/main/docs/FUNCTION-SECURITY.md)).
70
79
 
71
80
  After a real attempt, draft feedback only for a capability gap, repeated workaround, documentation
72
81
  gap or suspected defect, with sanitized YAML and validation evidence. Search existing issues first;
@@ -74,9 +83,9 @@ never publish or comment without the user's approval.
74
83
 
75
84
  ## Reference (load one only when needed)
76
85
 
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).
86
+ - Authoring: [AI authoring contract](https://github.com/jimhoyd-com/urlcode/blob/main/docs/AI-AUTHORING.md), [YAML guide](https://github.com/jimhoyd-com/urlcode/blob/main/docs/YAML-GUIDE.md), [field reference](https://github.com/jimhoyd-com/urlcode/blob/main/docs/YAML-REFERENCE.md), [JSON Schema](schemas/urlcode.schema.json), [semantics](https://github.com/jimhoyd-com/urlcode/blob/main/docs/SPECIFICATION.md), [cookbook](examples/cookbook/README.md), [recipes](https://github.com/jimhoyd-com/urlcode/blob/main/docs/RECIPES.md).
78
87
  - 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).
88
+ - Routes and handlers: [routing](https://github.com/jimhoyd-com/urlcode/blob/main/docs/ROUTING.md), [HTTP](https://github.com/jimhoyd-com/urlcode/blob/main/docs/HTTP.md), [middleware](https://github.com/jimhoyd-com/urlcode/blob/main/docs/MIDDLEWARE.md), [assets](https://github.com/jimhoyd-com/urlcode/blob/main/docs/ASSETS.md), [conditions](https://github.com/jimhoyd-com/urlcode/blob/main/docs/CONDITIONS.md), [egress](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EGRESS.md), [prerender](https://github.com/jimhoyd-com/urlcode/blob/main/docs/PRERENDER.md), [site](https://github.com/jimhoyd-com/urlcode/blob/main/docs/SITE.md), [organization](https://github.com/jimhoyd-com/urlcode/blob/main/docs/ORGANIZATION.md).
89
+ - Extensions: [extensions](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EXTENSIONS.md) (including [signed declarative artifacts](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EXTENSIONS.md#signed-declarative-artifacts); MCP `get_extension_artifacts`), [composing a site](https://github.com/jimhoyd-com/urlcode/blob/main/docs/COMPOSING-A-SITE.md), [package and channel alignment](https://github.com/jimhoyd-com/urlcode/blob/main/docs/VERSION-ALIGNMENT.md). New first-party executable extension installs use an attested `extension-bundles@v…` GitHub Release and an explicit host; do not add deprecated extension npm packages to a new project. [urlcode-auth](https://github.com/jimhoyd-com/urlcode/tree/main/packages/auth), [urlcode-admin](https://github.com/jimhoyd-com/urlcode/tree/main/packages/admin), [urlcode-ui](https://github.com/jimhoyd-com/urlcode/tree/main/packages/ui), [urlcode-store](https://github.com/jimhoyd-com/urlcode/tree/main/packages/store), and [urlcode-forms](https://github.com/jimhoyd-com/urlcode/tree/main/packages/forms) are workspace bundle sources. Per-route middleware is native core (`middleware:` array), not a retired extension; core has no native `link` handler, but the store extension's `shortLinks` capability covers create, invalid-destination, redirect, missing-code and click-count for stored short links without a function declare it under `extensions.store.config.shortLinks` (`urlcode docs search "shortLinks"`, MCP `search_docs`). Only report a gap for stored-link needs beyond that (custom redirect status, non-HTTP(S) destinations, per-record ownership).
90
+ - Tooling: [tooling and MCP](https://github.com/jimhoyd-com/urlcode/blob/main/docs/TOOLING.md), [TypeScript](https://github.com/jimhoyd-com/urlcode/blob/main/docs/TYPESCRIPT.md), [capabilities](https://github.com/jimhoyd-com/urlcode/blob/main/docs/CAPABILITIES.md), [plugins](https://github.com/jimhoyd-com/urlcode/blob/main/docs/PLUGINS.md), [interchange](https://github.com/jimhoyd-com/urlcode/blob/main/docs/INTERCHANGE.md), [bulk import](https://github.com/jimhoyd-com/urlcode/blob/main/docs/BULK.md).
91
+ - Operations: [operations](https://github.com/jimhoyd-com/urlcode/blob/main/docs/OPERATIONS.md), [install](https://github.com/jimhoyd-com/urlcode/blob/main/docs/INSTALL.md), [deployment checks](https://github.com/jimhoyd-com/urlcode/blob/main/docs/DEPLOYMENT-CHECKS.md), [readiness](https://github.com/jimhoyd-com/urlcode/blob/main/docs/READINESS.md), [capacity](https://github.com/jimhoyd-com/urlcode/blob/main/docs/CAPACITY.md), [resilience](https://github.com/jimhoyd-com/urlcode/blob/main/docs/RESILIENCE.md), [observability](https://github.com/jimhoyd-com/urlcode/blob/main/docs/OBSERVABILITY.md), [Vercel](https://github.com/jimhoyd-com/urlcode/blob/main/docs/VERCEL.md), [AWS](https://github.com/jimhoyd-com/urlcode/blob/main/docs/AWS.md), [Cloudflare](https://github.com/jimhoyd-com/urlcode/blob/main/docs/CLOUDFLARE.md), [static hosting](https://github.com/jimhoyd-com/urlcode/blob/main/docs/STATIC.md), [release readiness](https://github.com/jimhoyd-com/urlcode/blob/main/docs/RELEASE-READINESS.md), [roadmap](https://github.com/jimhoyd-com/urlcode/blob/main/ROADMAP.md).