@jimhoyd/urlcode 0.4.8 → 0.5.5
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/README.md +18 -18
- package/dist/BUILD-MANIFEST.json +27 -23
- package/dist/agents-guide.js +8 -4
- package/dist/authoring.js +81 -7
- package/dist/build-cloudflare.js +1 -0
- package/dist/build-static.js +1 -0
- package/dist/cli.js +39 -14
- package/dist/config.js +7 -1
- package/dist/context.js +32 -4
- package/dist/ecosystem-cli.js +6 -0
- package/dist/explain-cli.js +1 -1
- package/dist/explain.js +2 -2
- package/dist/extension-artifacts.js +28 -34
- package/dist/extension-bundles.js +62 -0
- package/dist/extension-transport.js +41 -0
- package/dist/extensions.js +4 -0
- package/dist/feature-plan.js +99 -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 +6 -2
- package/dist/readiness.js +2 -2
- package/dist/review.js +206 -0
- package/dist/router.js +35 -10
- package/dist/runtime.js +1 -0
- package/dist/tooling.js +4 -0
- package/dist/types/agents-guide.d.ts +6 -1
- package/dist/types/authoring.d.ts +3 -1
- package/dist/types/context.d.ts +12 -0
- package/dist/types/explain.d.ts +3 -0
- package/dist/types/extension-artifacts.d.ts +15 -4
- package/dist/types/extension-bundles.d.ts +49 -0
- package/dist/types/extension-transport.d.ts +31 -0
- package/dist/types/extensions.d.ts +4 -0
- package/dist/types/feature-plan.d.ts +67 -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/review.d.ts +30 -0
- package/dist/types/tooling.d.ts +4 -0
- package/dist/types/types.d.ts +10 -1
- package/dist/types.js +10 -3
- package/docs/AI-AUTHORING.md +466 -0
- package/docs/FUNCTION-SECURITY.md +251 -0
- package/docs/README.md +96 -0
- package/docs/TOOLING.md +422 -0
- package/docs/YAML-REFERENCE.md +473 -0
- package/llms-full.txt +190 -83
- package/llms.txt +73 -128
- package/package.json +15 -4
- package/recipes/redirect/README.md +2 -2
- package/recipes/store-crud/README.md +9 -10
- package/recipes/store-crud/recipe.yaml +1 -1
- package/schemas/urlcode.schema.json +3 -0
- package/starters/default/AGENTS.md +2 -2
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
|
|
2
|
+
<!-- Consolidated URLCode authoring reference: 16 documents, about 61,239 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.
|
|
10
|
+
> `version: "1"`. Core is Apache-2.0; this revision is `0.5.5`. `function`/`middleware`
|
|
11
11
|
> routes are trusted by default with `sandbox: true` as the per-route opt-in. The
|
|
12
|
-
> auth, admin and
|
|
13
|
-
>
|
|
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
|
|
@@ -18,9 +19,6 @@ or fetch inside a `sandbox: true` function, regex routes, database access,
|
|
|
18
19
|
global middleware, YAML interpolation, or packages named in YAML. Secrets need external revision-pinned
|
|
19
20
|
grants. Unsupported features fail with the route named; nothing degrades silently.
|
|
20
21
|
|
|
21
|
-
Agents that explicitly want the complete consolidated reference in one fetch should read
|
|
22
|
-
[llms-full.txt](https://github.com/jimhoyd-com/urlcode/blob/main/llms-full.txt), generated from the documents below (about 50k tokens, estimated).
|
|
23
|
-
|
|
24
22
|
## Contents
|
|
25
23
|
|
|
26
24
|
1. [The URLCode framework](#the-urlcode-framework)
|
|
@@ -46,12 +44,12 @@ Agents that explicitly want the complete consolidated reference in one fetch sho
|
|
|
46
44
|
|
|
47
45
|
Source: https://github.com/jimhoyd-com/urlcode/blob/main/docs/FRAMEWORK.md
|
|
48
46
|
|
|
49
|
-
One page for people and AI agents. It says what the
|
|
47
|
+
One page for people and AI agents. It says what the six workspace packages are, how a
|
|
50
48
|
project grows from a handful of redirects into an application with accounts
|
|
51
49
|
and an administration console, and which facts an agent must not guess. Every
|
|
52
50
|
claim here is implemented in the linked repository; nothing is roadmap.
|
|
53
51
|
|
|
54
|
-
###
|
|
52
|
+
### Six workspace packages, one project shape
|
|
55
53
|
|
|
56
54
|
| Package | Source | What it adds | How a project declares it |
|
|
57
55
|
|---|---|---|---|
|
|
@@ -59,12 +57,17 @@ claim here is implemented in the linked repository; nothing is roadmap.
|
|
|
59
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 |
|
|
60
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 |
|
|
61
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 |
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
68
71
|
evidence and an accessibility assessment are still pending
|
|
69
72
|
([issue 58](https://github.com/jimhoyd-com/urlcode/issues/58)). Their status
|
|
70
73
|
files say exactly what is built: [auth](https://github.com/jimhoyd-com/urlcode/blob/main/packages/auth/IMPLEMENTATION-STATUS.md),
|
|
@@ -104,33 +107,40 @@ Each rung's YAML is valid on every rung above it.
|
|
|
104
107
|
6. **Your own look.** A shared `presentation` (catalogue and theme variables)
|
|
105
108
|
restyles auth and admin together; the `ui` extension adds the template kit,
|
|
106
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.
|
|
107
114
|
|
|
108
115
|
Stored short links previously sat here as a native `link` route; that handler
|
|
109
116
|
was removed from core. A `urlcode-dynamic-link` package owned them the same way
|
|
110
117
|
`auth`/`admin` own their mounts, but it has been retired and unpublished; no
|
|
111
118
|
package occupies this rung today.
|
|
112
119
|
|
|
113
|
-
Rungs 1 to 3 need only the core package. Rungs 4 to
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
117
127
|
exact requirement.
|
|
118
128
|
|
|
119
129
|
### The composition contract
|
|
120
130
|
|
|
121
|
-
An extended project starts from
|
|
122
|
-
|
|
123
|
-
|
|
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):
|
|
124
134
|
|
|
125
135
|
```sh
|
|
126
|
-
npm install
|
|
127
|
-
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…
|
|
128
138
|
```
|
|
129
139
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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.
|
|
134
144
|
|
|
135
145
|
```
|
|
136
146
|
site/
|
|
@@ -202,34 +212,32 @@ package behavior.
|
|
|
202
212
|
A signed declarative artifact is a separate, optional authoring input, not a
|
|
203
213
|
fifth way to compose executable behavior. A project may lock an attested
|
|
204
214
|
schema/example bundle and expose it through MCP `get_extension_artifacts` and
|
|
205
|
-
`get_extension_artifact`; the
|
|
206
|
-
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).
|
|
207
217
|
|
|
208
218
|
```sh
|
|
209
219
|
urlcode serve --project /absolute/site --host-file /absolute/operator/host.mjs --origin https://site.example
|
|
210
220
|
```
|
|
211
221
|
|
|
212
|
-
`urlcode init <dir> --with ui,auth,admin
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
|
226
|
+
explicit `host.mjs` and one `README.md`, refusing before writing a site when a
|
|
227
|
+
bundle is missing or two fragments collide (the contract is documented under
|
|
217
228
|
[scaffolding](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EXTENSIONS.md#scaffolding-with-init---with)). `urlcode-auth init`
|
|
218
229
|
and `urlcode-admin init` write the same layout for a single package; `urlcode-auth bootstrap` creates the first
|
|
219
230
|
administrator from JSON on stdin. `inspectExtensionRevision(project)` prints
|
|
220
231
|
the SHA-256 that `projectSha256` must carry; changing extension YAML, policies
|
|
221
232
|
or mounts changes the revision and needs an explicit operator reapproval.
|
|
222
233
|
|
|
223
|
-
The presentation tooling composes the same way, by naming
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
that ship the other namespaces: each is resolved from `--project` with Node
|
|
227
|
-
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`,
|
|
228
237
|
`doctor`, `eject`, `preview` and `copy --missing` cover the `auth/*` and
|
|
229
238
|
`admin/*` templates and copy the host registers, and a project override of an
|
|
230
|
-
extension template is checked against the shipped view model.
|
|
231
|
-
|
|
232
|
-
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.
|
|
233
241
|
|
|
234
242
|
### Rules an agent must follow
|
|
235
243
|
|
|
@@ -314,6 +322,13 @@ needed schema, example or README. Context is a summary with the
|
|
|
314
322
|
constraints and exact commands, not a schema dump, and it never hides a
|
|
315
323
|
capability limit: ask `capabilities NAME` before promising a feature.
|
|
316
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
|
+
|
|
317
332
|
The complete catalogs (`urlcode capabilities`, `recipes list`), the compact
|
|
318
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)
|
|
319
334
|
stay available as deliberate fallback and reference, not as the opening move.
|
|
@@ -508,12 +523,12 @@ this project's own redirects — cheaper than this table or the recipe catalog.
|
|
|
508
523
|
|---|---|---|
|
|
509
524
|
| Fixed redirect (301/302/303/307/308, 302 default) | `redirect: {url, status}` | [redirects](https://github.com/jimhoyd-com/urlcode/blob/main/docs/yaml/redirects.md) |
|
|
510
525
|
| Parameterized path redirect (`/users/{id}` to `/profiles/{id}`) | `{name}` placeholder in `redirect.url` naming a declared path parameter | [redirects](https://github.com/jimhoyd-com/urlcode/blob/main/docs/yaml/redirects.md) |
|
|
511
|
-
|
|
|
526
|
+
| Root-relative redirect (`/users/{id}` to `/profiles/{id}`) | `redirect.url: /profiles/{id}`: one leading slash, path only, `{name}` placeholders | [redirects](https://github.com/jimhoyd-com/urlcode/blob/main/docs/yaml/redirects.md) |
|
|
527
|
+
| Wildcard/suffix redirect (`/legacy/**` to `/modern/{**}`, any depth) | terminal `/**` route key with a literal prefix, `{**}` in the destination path; redirect only, not static or Cloudflare | [redirects](https://github.com/jimhoyd-com/urlcode/blob/main/docs/yaml/redirects.md) |
|
|
512
528
|
| Redirect that preserves query keys | `redirect.query.pass` (explicit allowlist) or `query.map` | [redirects](https://github.com/jimhoyd-com/urlcode/blob/main/docs/yaml/redirects.md) |
|
|
513
529
|
| Redirect that keeps the method/body (POST) | `methods` plus `status: 307` or `308` | [redirects](https://github.com/jimhoyd-com/urlcode/blob/main/docs/yaml/redirects.md) |
|
|
514
530
|
| 404 for unmatched paths | `site.notFound` (a project-relative `.html` file) | [site](https://github.com/jimhoyd-com/urlcode/blob/main/docs/SITE.md) |
|
|
515
|
-
|
|
|
516
|
-
| Host-based, scheme-based or relative-URL redirect — **gap** | not expressible; destination must be a literal absolute `https://host/path` | [open decision](https://github.com/jimhoyd-com/urlcode/blob/main/docs/OPEN-DECISIONS.md) |
|
|
531
|
+
| Host-based or scheme-based redirect — **gap** | not expressible; destination is a literal absolute `https://host/path` or a root-relative path | [open decision](https://github.com/jimhoyd-com/urlcode/blob/main/docs/OPEN-DECISIONS.md) |
|
|
517
532
|
| Security headers (CSP, HSTS, frame and referrer policy) | `policies.security: {headers: oshp}` or `policies.profile: hardened` | [security](https://github.com/jimhoyd-com/urlcode/blob/main/docs/policies/security.md) |
|
|
518
533
|
| Cache headers on a page, download or static mount | `cacheControl`: `no-cache` (default), `no-store`, `public, max-age=3600` or `public, max-age=31536000, immutable`; nothing else validates | [assets](https://github.com/jimhoyd-com/urlcode/blob/main/docs/yaml/assets.md) |
|
|
519
534
|
| A cache strategy on any route | `policies.cache` | [cache](https://github.com/jimhoyd-com/urlcode/blob/main/docs/policies/cache.md) |
|
|
@@ -537,8 +552,9 @@ Which handler serves the response:
|
|
|
537
552
|
Data persistence has no native handler. The operator-installed `store` extension
|
|
538
553
|
serves declared collections as a CRUD API, and `urlcode recipes search "crud store
|
|
539
554
|
persist"` finds the `store-crud` recipe. It needs the operator to install
|
|
540
|
-
|
|
541
|
-
ui,auth,store
|
|
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.
|
|
776
|
+
This guide targets URLCode 0.5.5. 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.
|
|
@@ -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_]*$" |
|
|
@@ -1327,7 +1344,8 @@ Keys are absolute case-sensitive paths. Trailing slashes are significant.
|
|
|
1327
1344
|
Parameters occupy whole segments, e.g. `/p/{id}`, with distinct identifier names.
|
|
1328
1345
|
Each parameter matches exactly one nonempty segment, never across `/`; it is not
|
|
1329
1346
|
greedy. No regex paths, client-controlled host dispatch or dot segments. Only static directory mounts
|
|
1330
|
-
support a terminal `/*` wildcard with an otherwise literal path
|
|
1347
|
+
support a terminal `/*` wildcard with an otherwise literal path; a `redirect` alone supports a
|
|
1348
|
+
terminal `/**` (one or more remaining segments, at least a one-segment literal prefix, see [route matching](https://github.com/jimhoyd-com/urlcode/blob/main/docs/ROUTING.md)). Route keys cannot contain
|
|
1331
1349
|
percent encoding, spaces, backslashes or query strings. Path length is limited
|
|
1332
1350
|
to 2,048 characters and 32 segments. `/_urlcode` is reserved.
|
|
1333
1351
|
|
|
@@ -1466,7 +1484,9 @@ Unknown query keys are ignored unless explicitly passed by a redirect.
|
|
|
1466
1484
|
### Redirects
|
|
1467
1485
|
|
|
1468
1486
|
`redirect.url` is an absolute HTTP(S) URL with literal scheme/host and no embedded
|
|
1469
|
-
credentials or whitespace/control characters
|
|
1487
|
+
credentials or whitespace/control characters, or a root-relative path (one leading `/`, never
|
|
1488
|
+
`//`, no dot segments) that answers a path-only `Location`. On a `/**` route `{**}` is the captured
|
|
1489
|
+
suffix, once, each segment encoded. `{pathInput}` placeholders are
|
|
1470
1490
|
allowed only in the destination pathname and encoded as single components.
|
|
1471
1491
|
No environment/secret interpolation. Status defaults to 302; allowed values are
|
|
1472
1492
|
301, 302, 303, 307 and 308.
|
|
@@ -1590,12 +1610,17 @@ parity for a `sandbox: true` route; a trusted route has no such restriction.
|
|
|
1590
1610
|
|
|
1591
1611
|
Context contains `inputs.path/query/header`, `args`, `env`, `secrets`. Arguments
|
|
1592
1612
|
may be scalar literals, input references, `{env: alias}` or `{secret: alias}`.
|
|
1593
|
-
Bindings use `{value: "literal"}`, `{env: EXTERNAL_NAME}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
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.
|
|
1599
1624
|
|
|
1600
1625
|
Development may read `.env.local`; process values win. Serving never reads it.
|
|
1601
1626
|
Dotenv supports single-line NAME=value, paired single/double quotes, blank lines
|
|
@@ -1709,6 +1734,7 @@ not part of the route key.
|
|
|
1709
1734
|
| `/go` | `/go`, `/go?campaign=spring` | `/Go`, `/go/`, `/go/extra` |
|
|
1710
1735
|
| `/r/{code}` | `/r/abc`, `/r/123` | `/r/`, `/r/abc/extra` |
|
|
1711
1736
|
| `/r/{code}/details` | `/r/abc/details` | `/r/abc/other/details` |
|
|
1737
|
+
| `/legacy/**` with a `redirect` handler | `/legacy/a`, `/legacy/a/b/c` | `/legacy`, `/legacy/`, `/legacy/a//b` |
|
|
1712
1738
|
| `/assets/*` with a `static` handler | Files under `/assets/`, including `/assets/css/site.css` | `/assets`, `/assets-other/site.css` |
|
|
1713
1739
|
|
|
1714
1740
|
A `{parameter}` captures exactly one nonempty path segment. It is **not greedy**:
|
|
@@ -1721,8 +1747,16 @@ path. It covers the remaining nested file path; it is not a named capture or a
|
|
|
1721
1747
|
regex operator. Matching a mount does not guarantee a response file exists:
|
|
1722
1748
|
missing files return 404. It is not a catch-all for functions or redirects.
|
|
1723
1749
|
|
|
1750
|
+
A `redirect` handler alone supports a terminal `/**` after a literal prefix (never bare `/**`, never
|
|
1751
|
+
with a `{parameter}`). It matches one or more remaining segments, and `{**}` in `redirect.url` is
|
|
1752
|
+
those segments, each percent-encoded and joined by `/`, usable once and only in the destination
|
|
1753
|
+
path. Empty segments, `.`/`..`, encoded slashes and captures over 1,024 characters do not match.
|
|
1754
|
+
Exact and `{parameter}` routes always win over it, so `/legacy/keep/{id}` can carve an exception out
|
|
1755
|
+
of `/legacy/**`. It is refused on static hosting (S3 redirects match one path) and on Cloudflare
|
|
1756
|
+
until the Worker table supports suffix matching, and it cannot share a prefix with a `static` mount.
|
|
1757
|
+
|
|
1724
1758
|
No regex routes, greedy parameters, optional segments, partial-segment parameters,
|
|
1725
|
-
`**` globs, or regex constraints inside `{code}` are implemented. Characters such
|
|
1759
|
+
other `**` globs, or regex constraints inside `{code}` are implemented. Characters such
|
|
1726
1760
|
as `.` and `+` have no regex meaning in a literal path. Do not paste a regex into
|
|
1727
1761
|
a route key: some regex-looking text is legal literal text, while unsupported
|
|
1728
1762
|
syntax may fail validation. Parameter-schema `pattern` is also unsupported.
|
|
@@ -2821,7 +2855,7 @@ Extensions are trusted operator modules, separate from a project's own
|
|
|
2821
2855
|
`function`/`middleware` code. Auth
|
|
2822
2856
|
and admin implementations live in `urlcode-auth` and `urlcode-admin`; the runtime
|
|
2823
2857
|
supplies only the generic integration contract. No project file can import a host
|
|
2824
|
-
extension or choose
|
|
2858
|
+
extension, choose a bundle release, or choose an npm package.
|
|
2825
2859
|
|
|
2826
2860
|
Stored short links moved out of core this way too: a `urlcode-dynamic-link`
|
|
2827
2861
|
package (mount-based, like `auth`/`admin`) owned the durable link store, its CLI
|
|
@@ -2833,6 +2867,13 @@ The `store` extension is the data-owning counterpart: it serves declared,
|
|
|
2833
2867
|
bounded collections as a CRUD API from an operator-owned directory. See
|
|
2834
2868
|
[data store](https://github.com/jimhoyd-com/urlcode/blob/main/docs/STORE.md).
|
|
2835
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
|
+
|
|
2836
2877
|
A project declares versioned configuration and exclusive route mounts:
|
|
2837
2878
|
|
|
2838
2879
|
```yaml
|
|
@@ -3217,29 +3258,24 @@ Assembly rules, in the resolved order:
|
|
|
3217
3258
|
|
|
3218
3259
|
#### Recorded versions
|
|
3219
3260
|
|
|
3220
|
-
`init --with
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
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.
|
|
3227
3267
|
|
|
3228
3268
|
Nothing is installed. The generated site has no `node_modules` and no
|
|
3229
|
-
`package-lock.json` until you run `npm install` in it yourself, which
|
|
3230
|
-
command and
|
|
3231
|
-
comes from
|
|
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.
|
|
3232
3272
|
|
|
3233
3273
|
- `--no-manifest` generates the site without a `package.json`, for a site whose
|
|
3234
3274
|
dependencies are managed elsewhere. Plain `urlcode init` is unchanged and
|
|
3235
3275
|
still writes no manifest; add `--manifest` to pin the runtime for a
|
|
3236
3276
|
route-only project too.
|
|
3237
|
-
- `--pin <package>=<specifier>`
|
|
3238
|
-
|
|
3239
|
-
(`--pin @jimhoyd/urlcode-auth=file:/abs/urlcode-auth-0.1.0-alpha.6.tgz`). A
|
|
3240
|
-
package installed from a local path or tarball is detected from npm's own
|
|
3241
|
-
install record and pinned by that path without any flag; the README says so,
|
|
3242
|
-
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.
|
|
3243
3279
|
|
|
3244
3280
|
There is no upgrade command. Moving a generated project to newer versions today
|
|
3245
3281
|
means editing its `package.json` and re-running `npm install` yourself; nothing
|
|
@@ -3260,9 +3296,8 @@ extension artifact for tooling that understands its declared format. It is not
|
|
|
3260
3296
|
a Node module and cannot activate an extension, run a hook, replace a trusted
|
|
3261
3297
|
operator host, or grant a route any authority.
|
|
3262
3298
|
|
|
3263
|
-
Install an artifact only from its immutable `extensions@v…` GitHub Release.
|
|
3264
|
-
|
|
3265
|
-
schema snapshot can be installed with:
|
|
3299
|
+
Install an artifact only from its immutable `extensions@v…` GitHub Release. The
|
|
3300
|
+
published inert store configuration schema snapshot can be installed with:
|
|
3266
3301
|
|
|
3267
3302
|
```sh
|
|
3268
3303
|
urlcode extension-artifacts install store-schema --artifact-release extensions@v1.0.0 --project app
|
|
@@ -3310,6 +3345,71 @@ not the `@jimhoyd/urlcode-store` implementation. Installing it does not install
|
|
|
3310
3345
|
or activate that package. Its README names the separate executable and operator
|
|
3311
3346
|
requirements.
|
|
3312
3347
|
|
|
3348
|
+
### Signed executable extension bundles
|
|
3349
|
+
|
|
3350
|
+
Official executable extensions are delivered through a separate, immutable
|
|
3351
|
+
`extension-bundles@v…` GitHub Release namespace;
|
|
3352
|
+
it is intentionally disjoint from the permanently data-only `extensions@v…`
|
|
3353
|
+
artifact channel above. A bundle is a bounded, frozen Node module tree produced
|
|
3354
|
+
from reviewed first-party source, not a general extension marketplace and not
|
|
3355
|
+
a project dependency resolver.
|
|
3356
|
+
|
|
3357
|
+
An operator explicitly installs one named bundle from an immutable release:
|
|
3358
|
+
|
|
3359
|
+
```sh
|
|
3360
|
+
urlcode extension-bundles install store \
|
|
3361
|
+
--bundle-release extension-bundles@v1.0.0 --project app
|
|
3362
|
+
```
|
|
3363
|
+
|
|
3364
|
+
For a new composed site, `init --with` can perform that verified installation
|
|
3365
|
+
before it writes the route project. This is the npm-free extension path: the
|
|
3366
|
+
generated `package.json`, when requested, pins URLCode core only; the generated
|
|
3367
|
+
host loads only the names recorded in the bundle lockfile.
|
|
3368
|
+
|
|
3369
|
+
```sh
|
|
3370
|
+
urlcode init site --with ui,auth,admin \
|
|
3371
|
+
--bundle-release extension-bundles@v1.0.0
|
|
3372
|
+
```
|
|
3373
|
+
|
|
3374
|
+
`init` verifies each requested bundle in a temporary operator staging root,
|
|
3375
|
+
obtains each scaffold from that verified module tree, then writes the cache and
|
|
3376
|
+
`urlcode.extension-bundles.lock.json` into the new site. It never resolves an
|
|
3377
|
+
extension package from npm in this mode. A failed verification or scaffold
|
|
3378
|
+
refusal leaves no site directory behind. The release tag is still an explicit
|
|
3379
|
+
operator choice; YAML cannot supply it.
|
|
3380
|
+
|
|
3381
|
+
The command verifies attestations for both the catalog and selected archive
|
|
3382
|
+
against the requested tag and dedicated workflow, rejects self-hosted runners,
|
|
3383
|
+
checks the catalog's commit, filename and SHA-256, and extracts only regular
|
|
3384
|
+
files in the signed module tree. It writes
|
|
3385
|
+
`urlcode.extension-bundles.lock.json` and keeps the frozen bytes under
|
|
3386
|
+
`app/.urlcode/extension-bundles/<sha256>/`. There is no automatic discovery,
|
|
3387
|
+
installation, update, or fallback to npm. `inspect` reads the committed lock;
|
|
3388
|
+
a modified cache or an incompatible core version refuses before import.
|
|
3389
|
+
|
|
3390
|
+
Executable bundles are **trusted operator code**, exactly like a hand-written
|
|
3391
|
+
operator host module. Project YAML cannot choose a bundle, name a release,
|
|
3392
|
+
trigger a download, or grant a bundle authority. An operator host explicitly
|
|
3393
|
+
loads a locked entry by name, then chooses which returned registration to pass
|
|
3394
|
+
to `createRuntime`:
|
|
3395
|
+
|
|
3396
|
+
```js
|
|
3397
|
+
import { loadExtensionBundle } from '@jimhoyd/urlcode/extension-bundles';
|
|
3398
|
+
|
|
3399
|
+
const { storeExtension } = await loadExtensionBundle('/absolute/site/app', 'store');
|
|
3400
|
+
export default { extensions: [storeExtension({ directory: '/srv/site-data', projectSha256: process.env.PROJECT_SHA256 })] };
|
|
3401
|
+
```
|
|
3402
|
+
|
|
3403
|
+
This does not make bundle code sandboxed and does not alter a route that
|
|
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.
|
|
3412
|
+
|
|
3313
3413
|
---
|
|
3314
3414
|
|
|
3315
3415
|
# Composing a site from ui, auth and admin
|
|
@@ -3320,10 +3420,16 @@ One command produces a site that already has accounts, an administration
|
|
|
3320
3420
|
console and a presentation kit wired together:
|
|
3321
3421
|
|
|
3322
3422
|
```sh
|
|
3323
|
-
npm install @jimhoyd/urlcode
|
|
3324
|
-
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…
|
|
3325
3425
|
```
|
|
3326
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
|
+
|
|
3327
3433
|
This page is the map of what you may then change, and with which tool. It
|
|
3328
3434
|
covers three different activities that are easy to confuse:
|
|
3329
3435
|
|
|
@@ -3501,9 +3607,10 @@ not run it. With the packages named:
|
|
|
3501
3607
|
and those translations do not currently reach the console
|
|
3502
3608
|
([#227](https://github.com/jimhoyd-com/urlcode/issues/227)).
|
|
3503
3609
|
|
|
3504
|
-
`urlcode init <directory> --with ui,auth,admin
|
|
3505
|
-
|
|
3506
|
-
|
|
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.
|
|
3507
3614
|
|
|
3508
3615
|
Run the extension's published `fastChecks` while editing. Theme and copy changes
|
|
3509
3616
|
need no framework build. Template and CSS checks load only the UI kit and named
|