@jimhoyd/urlcode 0.4.8 → 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/README.md +6 -3
- package/dist/BUILD-MANIFEST.json +20 -19
- package/dist/agents-guide.js +8 -4
- package/dist/authoring.js +36 -3
- package/dist/build-cloudflare.js +1 -0
- package/dist/build-static.js +1 -0
- package/dist/cli.js +21 -9
- package/dist/config.js +7 -1
- package/dist/context.js +32 -4
- package/dist/extension-artifacts.js +18 -11
- package/dist/extension-bundles.js +70 -0
- package/dist/extensions.js +4 -0
- package/dist/functions.js +2 -1
- package/dist/index.js +2 -0
- package/dist/init-with.js +55 -23
- package/dist/interchange.js +1 -1
- package/dist/match.js +23 -5
- package/dist/mcp.js +1 -1
- package/dist/readiness.js +2 -2
- package/dist/router.js +20 -8
- package/dist/runtime.js +1 -0
- package/dist/types/agents-guide.d.ts +6 -1
- package/dist/types/authoring.d.ts +1 -1
- package/dist/types/context.d.ts +12 -0
- package/dist/types/extension-artifacts.d.ts +13 -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 +2 -0
- package/dist/types/init-with.d.ts +6 -1
- package/dist/types/match.d.ts +1 -0
- package/dist/types/types.d.ts +1 -0
- package/dist/types.js +1 -1
- package/llms-full.txt +94 -22
- package/llms.txt +62 -126
- package/package.json +7 -2
- package/recipes/redirect/README.md +2 -2
- package/starters/default/AGENTS.md +2 -2
package/dist/types.js
CHANGED
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 60,557 tokens (estimate: characters / 4). -->
|
|
3
3
|
|
|
4
4
|
<!-- urlcode-current-version:start -->
|
|
5
5
|
# URLCode
|
|
@@ -7,7 +7,7 @@
|
|
|
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.0`. `function`/`middleware`
|
|
11
11
|
> routes are trusted by default with `sandbox: true` as the per-route opt-in. The
|
|
12
12
|
> auth, admin and ui extension packages in this repository are versioned at the same
|
|
13
13
|
> revision; confirm what is published with `npm run release:status`.
|
|
@@ -18,9 +18,6 @@ or fetch inside a `sandbox: true` function, regex routes, database access,
|
|
|
18
18
|
global middleware, YAML interpolation, or packages named in YAML. Secrets need external revision-pinned
|
|
19
19
|
grants. Unsupported features fail with the route named; nothing degrades silently.
|
|
20
20
|
|
|
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
21
|
## Contents
|
|
25
22
|
|
|
26
23
|
1. [The URLCode framework](#the-urlcode-framework)
|
|
@@ -209,11 +206,13 @@ executable extension path. See [signed declarative artifacts](https://github.com
|
|
|
209
206
|
urlcode serve --project /absolute/site --host-file /absolute/operator/host.mjs --origin https://site.example
|
|
210
207
|
```
|
|
211
208
|
|
|
212
|
-
`urlcode init <dir> --with ui,auth,admin` writes this layout in one step:
|
|
213
|
-
resolves each installed `@jimhoyd/urlcode-<name>` from the current
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
|
214
|
+
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
|
|
217
216
|
[scaffolding](https://github.com/jimhoyd-com/urlcode/blob/main/docs/EXTENSIONS.md#scaffolding-with-init---with)). `urlcode-auth init`
|
|
218
217
|
and `urlcode-admin init` write the same layout for a single package; `urlcode-auth bootstrap` creates the first
|
|
219
218
|
administrator from JSON on stdin. `inspectExtensionRevision(project)` prints
|
|
@@ -508,12 +507,12 @@ this project's own redirects — cheaper than this table or the recipe catalog.
|
|
|
508
507
|
|---|---|---|
|
|
509
508
|
| 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
509
|
| 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
|
-
|
|
|
510
|
+
| 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) |
|
|
511
|
+
| 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
512
|
| 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
513
|
| 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
514
|
| 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) |
|
|
515
|
+
| 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
516
|
| 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
517
|
| 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
518
|
| A cache strategy on any route | `policies.cache` | [cache](https://github.com/jimhoyd-com/urlcode/blob/main/docs/policies/cache.md) |
|
|
@@ -537,8 +536,9 @@ Which handler serves the response:
|
|
|
537
536
|
Data persistence has no native handler. The operator-installed `store` extension
|
|
538
537
|
serves declared collections as a CRUD API, and `urlcode recipes search "crud store
|
|
539
538
|
persist"` finds the `store-crud` recipe. It needs the operator to install
|
|
540
|
-
`@jimhoyd/urlcode-store`
|
|
541
|
-
ui,auth,store
|
|
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
|
|
542
542
|
`--with store` needs `--ack store:public-write`, which only a core release after the
|
|
543
543
|
store's first publication has, so say so rather than promising it. Report anything beyond that recipe (filtering, sorting, per-record
|
|
544
544
|
ownership, a database) as a gap. `urlcode context` lists the same built-ins so
|
|
@@ -757,7 +757,7 @@ programmatic compatibility analysis and provider verification limits.
|
|
|
757
757
|
Source: https://github.com/jimhoyd-com/urlcode/blob/main/docs/YAML-GUIDE.md
|
|
758
758
|
|
|
759
759
|
<!-- urlcode-current-version:start -->
|
|
760
|
-
This guide targets URLCode 0.
|
|
760
|
+
This guide targets URLCode 0.5.0. Start with the function example below,
|
|
761
761
|
then add only the fields your route needs. The authoritative machine-readable
|
|
762
762
|
shape is [JSON Schema](https://github.com/jimhoyd-com/urlcode/blob/main/schemas/urlcode.schema.json); semantic rules are in the
|
|
763
763
|
[specification](https://github.com/jimhoyd-com/urlcode/blob/main/docs/SPECIFICATION.md). Unsupported fields fail validation.
|
|
@@ -1327,7 +1327,8 @@ Keys are absolute case-sensitive paths. Trailing slashes are significant.
|
|
|
1327
1327
|
Parameters occupy whole segments, e.g. `/p/{id}`, with distinct identifier names.
|
|
1328
1328
|
Each parameter matches exactly one nonempty segment, never across `/`; it is not
|
|
1329
1329
|
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
|
|
1330
|
+
support a terminal `/*` wildcard with an otherwise literal path; a `redirect` alone supports a
|
|
1331
|
+
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
1332
|
percent encoding, spaces, backslashes or query strings. Path length is limited
|
|
1332
1333
|
to 2,048 characters and 32 segments. `/_urlcode` is reserved.
|
|
1333
1334
|
|
|
@@ -1466,7 +1467,9 @@ Unknown query keys are ignored unless explicitly passed by a redirect.
|
|
|
1466
1467
|
### Redirects
|
|
1467
1468
|
|
|
1468
1469
|
`redirect.url` is an absolute HTTP(S) URL with literal scheme/host and no embedded
|
|
1469
|
-
credentials or whitespace/control characters
|
|
1470
|
+
credentials or whitespace/control characters, or a root-relative path (one leading `/`, never
|
|
1471
|
+
`//`, no dot segments) that answers a path-only `Location`. On a `/**` route `{**}` is the captured
|
|
1472
|
+
suffix, once, each segment encoded. `{pathInput}` placeholders are
|
|
1470
1473
|
allowed only in the destination pathname and encoded as single components.
|
|
1471
1474
|
No environment/secret interpolation. Status defaults to 302; allowed values are
|
|
1472
1475
|
301, 302, 303, 307 and 308.
|
|
@@ -1709,6 +1712,7 @@ not part of the route key.
|
|
|
1709
1712
|
| `/go` | `/go`, `/go?campaign=spring` | `/Go`, `/go/`, `/go/extra` |
|
|
1710
1713
|
| `/r/{code}` | `/r/abc`, `/r/123` | `/r/`, `/r/abc/extra` |
|
|
1711
1714
|
| `/r/{code}/details` | `/r/abc/details` | `/r/abc/other/details` |
|
|
1715
|
+
| `/legacy/**` with a `redirect` handler | `/legacy/a`, `/legacy/a/b/c` | `/legacy`, `/legacy/`, `/legacy/a//b` |
|
|
1712
1716
|
| `/assets/*` with a `static` handler | Files under `/assets/`, including `/assets/css/site.css` | `/assets`, `/assets-other/site.css` |
|
|
1713
1717
|
|
|
1714
1718
|
A `{parameter}` captures exactly one nonempty path segment. It is **not greedy**:
|
|
@@ -1721,8 +1725,16 @@ path. It covers the remaining nested file path; it is not a named capture or a
|
|
|
1721
1725
|
regex operator. Matching a mount does not guarantee a response file exists:
|
|
1722
1726
|
missing files return 404. It is not a catch-all for functions or redirects.
|
|
1723
1727
|
|
|
1728
|
+
A `redirect` handler alone supports a terminal `/**` after a literal prefix (never bare `/**`, never
|
|
1729
|
+
with a `{parameter}`). It matches one or more remaining segments, and `{**}` in `redirect.url` is
|
|
1730
|
+
those segments, each percent-encoded and joined by `/`, usable once and only in the destination
|
|
1731
|
+
path. Empty segments, `.`/`..`, encoded slashes and captures over 1,024 characters do not match.
|
|
1732
|
+
Exact and `{parameter}` routes always win over it, so `/legacy/keep/{id}` can carve an exception out
|
|
1733
|
+
of `/legacy/**`. It is refused on static hosting (S3 redirects match one path) and on Cloudflare
|
|
1734
|
+
until the Worker table supports suffix matching, and it cannot share a prefix with a `static` mount.
|
|
1735
|
+
|
|
1724
1736
|
No regex routes, greedy parameters, optional segments, partial-segment parameters,
|
|
1725
|
-
`**` globs, or regex constraints inside `{code}` are implemented. Characters such
|
|
1737
|
+
other `**` globs, or regex constraints inside `{code}` are implemented. Characters such
|
|
1726
1738
|
as `.` and `+` have no regex meaning in a literal path. Do not paste a regex into
|
|
1727
1739
|
a route key: some regex-looking text is legal literal text, while unsupported
|
|
1728
1740
|
syntax may fail validation. Parameter-schema `pattern` is also unsupported.
|
|
@@ -3260,9 +3272,8 @@ extension artifact for tooling that understands its declared format. It is not
|
|
|
3260
3272
|
a Node module and cannot activate an extension, run a hook, replace a trusted
|
|
3261
3273
|
operator host, or grant a route any authority.
|
|
3262
3274
|
|
|
3263
|
-
Install an artifact only from its immutable `extensions@v…` GitHub Release.
|
|
3264
|
-
|
|
3265
|
-
schema snapshot can be installed with:
|
|
3275
|
+
Install an artifact only from its immutable `extensions@v…` GitHub Release. The
|
|
3276
|
+
published inert store configuration schema snapshot can be installed with:
|
|
3266
3277
|
|
|
3267
3278
|
```sh
|
|
3268
3279
|
urlcode extension-artifacts install store-schema --artifact-release extensions@v1.0.0 --project app
|
|
@@ -3310,6 +3321,67 @@ not the `@jimhoyd/urlcode-store` implementation. Installing it does not install
|
|
|
3310
3321
|
or activate that package. Its README names the separate executable and operator
|
|
3311
3322
|
requirements.
|
|
3312
3323
|
|
|
3324
|
+
### Signed executable extension bundles
|
|
3325
|
+
|
|
3326
|
+
Official executable extensions are migrating away from consumer npm installs.
|
|
3327
|
+
They use a separate, immutable `extension-bundles@v…` GitHub Release namespace;
|
|
3328
|
+
it is intentionally disjoint from the permanently data-only `extensions@v…`
|
|
3329
|
+
artifact channel above. A bundle is a bounded, frozen Node module tree produced
|
|
3330
|
+
from reviewed first-party source, not a general extension marketplace and not
|
|
3331
|
+
a project dependency resolver.
|
|
3332
|
+
|
|
3333
|
+
An operator explicitly installs one named bundle from an immutable release:
|
|
3334
|
+
|
|
3335
|
+
```sh
|
|
3336
|
+
urlcode extension-bundles install store \
|
|
3337
|
+
--bundle-release extension-bundles@v1.0.0 --project app
|
|
3338
|
+
```
|
|
3339
|
+
|
|
3340
|
+
For a new composed site, `init --with` can perform that verified installation
|
|
3341
|
+
before it writes the route project. This is the npm-free extension path: the
|
|
3342
|
+
generated `package.json`, when requested, pins URLCode core only; the generated
|
|
3343
|
+
host loads only the names recorded in the bundle lockfile.
|
|
3344
|
+
|
|
3345
|
+
```sh
|
|
3346
|
+
urlcode init site --with ui,auth,admin \
|
|
3347
|
+
--bundle-release extension-bundles@v1.0.0
|
|
3348
|
+
```
|
|
3349
|
+
|
|
3350
|
+
`init` verifies each requested bundle in a temporary operator staging root,
|
|
3351
|
+
obtains each scaffold from that verified module tree, then writes the cache and
|
|
3352
|
+
`urlcode.extension-bundles.lock.json` into the new site. It never resolves an
|
|
3353
|
+
extension package from npm in this mode. A failed verification or scaffold
|
|
3354
|
+
refusal leaves no site directory behind. The release tag is still an explicit
|
|
3355
|
+
operator choice; YAML cannot supply it.
|
|
3356
|
+
|
|
3357
|
+
The command verifies attestations for both the catalog and selected archive
|
|
3358
|
+
against the requested tag and dedicated workflow, rejects self-hosted runners,
|
|
3359
|
+
checks the catalog's commit, filename and SHA-256, and extracts only regular
|
|
3360
|
+
files in the signed module tree. It writes
|
|
3361
|
+
`urlcode.extension-bundles.lock.json` and keeps the frozen bytes under
|
|
3362
|
+
`app/.urlcode/extension-bundles/<sha256>/`. There is no automatic discovery,
|
|
3363
|
+
installation, update, or fallback to npm. `inspect` reads the committed lock;
|
|
3364
|
+
a modified cache or an incompatible core version refuses before import.
|
|
3365
|
+
|
|
3366
|
+
Executable bundles are **trusted operator code**, exactly like a hand-written
|
|
3367
|
+
operator host module. Project YAML cannot choose a bundle, name a release,
|
|
3368
|
+
trigger a download, or grant a bundle authority. An operator host explicitly
|
|
3369
|
+
loads a locked entry by name, then chooses which returned registration to pass
|
|
3370
|
+
to `createRuntime`:
|
|
3371
|
+
|
|
3372
|
+
```js
|
|
3373
|
+
import { loadExtensionBundle } from '@jimhoyd/urlcode/extension-bundles';
|
|
3374
|
+
|
|
3375
|
+
const { storeExtension } = await loadExtensionBundle('/absolute/site/app', 'store');
|
|
3376
|
+
export default { extensions: [storeExtension({ directory: '/srv/site-data', projectSha256: process.env.PROJECT_SHA256 })] };
|
|
3377
|
+
```
|
|
3378
|
+
|
|
3379
|
+
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.
|
|
3384
|
+
|
|
3313
3385
|
---
|
|
3314
3386
|
|
|
3315
3387
|
# Composing a site from ui, auth and admin
|
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,132 +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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
`urlcode recipes show redirect` give a runnable starting project.
|
|
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.
|
|
28
26
|
|
|
29
27
|
| Shape | Supported? | Use |
|
|
30
28
|
|---|---|---|
|
|
31
|
-
| Fixed redirect,
|
|
32
|
-
| Parameterized path (`/users/{id}` to `/profiles/{id}`) | Yes | `{name}`
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
| Host
|
|
39
|
-
|
|
40
|
-
## Declarative-first
|
|
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
|
-
## Authoring
|
|
87
|
-
- [AI authoring contract](docs/AI-AUTHORING.md): workflow, capability matrix, checks.
|
|
88
|
-
- [Authoring skill](.claude/skills/urlcode-authoring/SKILL.md): loadable authoring skill shipped with this revision; see [distribution](docs/AI-AUTHORING.md#agent-skills).
|
|
89
|
-
- [Operations skill](.claude/skills/urlcode-operations/SKILL.md): loadable deployment/verification/resilience skill shipped with this revision.
|
|
90
|
-
- [YAML guide](docs/YAML-GUIDE.md): recipes for all handlers and common options.
|
|
91
|
-
- [JSON Schema](schemas/urlcode.schema.json): accepted fields and types.
|
|
92
|
-
- [Field reference](docs/YAML-REFERENCE.md): generated exhaustive field inventory.
|
|
93
|
-
- [Semantics](docs/SPECIFICATION.md): validation, defaults and sandbox API.
|
|
94
|
-
- [Runnable cookbook](examples/cookbook/README.md): 25 routes with HTTP fixtures.
|
|
95
|
-
- [Route matching](docs/ROUTING.md): precedence, non-greedy parameters, updates.
|
|
96
|
-
- [Middleware](docs/MIDDLEWARE.md): next(), state, ordering and native body limits.
|
|
97
|
-
- [Policies](docs/POLICIES.md): optional host-enforced `policies`/`profiles` keys, all off by default: `throttle`, `agents`, `security`, `compression`, `cache`; merge rules and per-target support.
|
|
98
|
-
- [Plugins](docs/PLUGINS.md): host hook API operators pass in code; never named in YAML.
|
|
99
|
-
- [TypeScript](docs/TYPESCRIPT.md): the package ships declarations for every export (`urlcode`, `@jimhoyd/urlcode/plugins`, `@jimhoyd/urlcode/policies`, `@jimhoyd/urlcode/observability`, `@jimhoyd/urlcode/compliance`, `@jimhoyd/urlcode/prerender`, `@jimhoyd/urlcode/sandbox`, `@jimhoyd/urlcode/aws`, `@jimhoyd/urlcode/vercel`, `@jimhoyd/urlcode/cloudflare`); the runtime source is TypeScript, `dist/` is its stripped JavaScript.
|
|
100
|
-
- [HTTP](docs/HTTP.md): methods, request bodies and response headers.
|
|
101
|
-
- [Assets](docs/ASSETS.md): pages, MIME, downloads, cache and ranges.
|
|
102
|
-
- [Site conventions](docs/SITE.md): optional top-level `site` key, all off by default: `robots`, `sitemap`, `favicon`, `securityTxt`, `llms` generate native routes; declared routes win; absolute URLs need `--origin`.
|
|
103
|
-
- [Prerendering](docs/PRERENDER.md): `@jimhoyd/urlcode/prerender` build helper and recipe; render function/middleware routes once into native page routes, no request-time guest code.
|
|
104
|
-
- [Organization](docs/ORGANIZATION.md): entry point and included files.
|
|
105
|
-
|
|
106
|
-
## Start here
|
|
107
|
-
- [The framework](docs/FRAMEWORK.md): four packages, the ladder from redirects to a full app, the composition contract, the rules an agent must follow.
|
|
108
|
-
- [AI authoring contract](docs/AI-AUTHORING.md): workflow, capability matrix, copyable task prompt, checks.
|
|
109
|
-
- [JSON Schema](schemas/urlcode.schema.json) and [field reference](docs/YAML-REFERENCE.md): every accepted field.
|
|
110
|
-
- [YAML guide](docs/YAML-GUIDE.md) and [runnable cookbook](examples/cookbook/README.md): recipes with HTTP fixtures.
|
|
111
|
-
- [Semantics](docs/SPECIFICATION.md): validation, defaults, sandbox API.
|
|
112
|
-
|
|
113
|
-
## Routes and handlers
|
|
114
|
-
- [Routing](docs/ROUTING.md): exact and `{param}` paths, precedence, `/*` only on static and extension mounts.
|
|
115
|
-
- [HTTP](docs/HTTP.md): methods, validated inputs, bodies, response headers, cookies.
|
|
116
|
-
- [Function security](docs/FUNCTION-SECURITY.md): trusted and unsandboxed by default, `sandbox: true` QuickJS/WASM opt-in, Request/Response subset, operator grants.
|
|
117
|
-
- [Middleware](docs/MIDDLEWARE.md) and [examples](docs/MIDDLEWARE-EXAMPLES.md): `next()`, state, ordering; also `urlcode recipes add middleware`.
|
|
118
|
-
- [Assets](docs/ASSETS.md): pages, static, downloads, MIME, ranges. [Prerender](docs/PRERENDER.md): render once, no request-time code.
|
|
119
|
-
- [Conditions](docs/CONDITIONS.md): exact predicates, disjoint redirect/respond cases, no-store.
|
|
120
|
-
- [Egress](docs/EGRESS.md): bounded HTTPS proxy and best-effort signals behind operator grants; self-hosted only.
|
|
121
|
-
- [Policies](docs/POLICIES.md): `throttle`, `agents`, `security`, `compression`, `cache`; all off unless declared. [Site](docs/SITE.md): robots, sitemap, favicon, security.txt, llms.txt.
|
|
122
|
-
- [Organization](docs/ORGANIZATION.md), [best practices](docs/BEST-PRACTICES.md), [scaffolding](docs/SCAFFOLDING.md).
|
|
123
|
-
- [Interchange](docs/INTERCHANGE.md), [bulk import](docs/BULK.md), [recipes](docs/RECIPES.md) (`urlcode recipes search`, `examples search`), [TypeScript guests](docs/TYPESCRIPT-AUTHORING.md).
|
|
124
|
-
|
|
125
|
-
## Extensions (accounts, administration, presentation)
|
|
126
|
-
- [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`.
|
|
127
|
-
- [Signed declarative artifacts](docs/EXTENSIONS.md#signed-declarative-artifacts): project-pinned, attested JSON/Markdown data for offline tooling. MCP `get_extension_artifacts` verifies/inventories a committed lock and `get_extension_artifact` reads one bounded member; neither installs or activates an npm extension.
|
|
128
|
-
- [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.
|
|
129
|
-
- [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.
|
|
130
|
-
- [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.
|
|
131
|
-
- [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.
|
|
132
|
-
- 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.
|
|
133
|
-
- 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.
|
|
134
|
-
- [Plugins](docs/PLUGINS.md): host hook API in operator code, never named in YAML.
|
|
135
|
-
|
|
136
|
-
## Tooling and API
|
|
137
|
-
- [Tooling SDK and MCP](docs/TOOLING.md): `urlcode mcp`, read-only inspection, validation, conversion previews.
|
|
138
|
-
- [TypeScript](docs/TYPESCRIPT.md): declarations for `@jimhoyd/urlcode` and its `/plugins`, `/policies`, `/observability`, `/compliance`, `/prerender`, `/extensions`, `/sandbox`, `/aws`, `/vercel`, `/cloudflare` entries.
|
|
139
|
-
- [Capabilities](docs/CAPABILITIES.md): per-target support; `urlcode capabilities --target NAME`.
|
|
140
|
-
|
|
141
|
-
## Operations
|
|
142
|
-
- [Operations](docs/OPERATIONS.md), [install](docs/INSTALL.md), [deployment checks](docs/DEPLOYMENT-CHECKS.md), [CI action](docs/CI.md).
|
|
143
|
-
- [Readiness](docs/READINESS.md), [capacity](docs/CAPACITY.md), [resilience](docs/RESILIENCE.md), [observability](docs/OBSERVABILITY.md).
|
|
144
|
-
- [Vercel](docs/VERCEL.md), [AWS](docs/AWS.md), [Cloudflare](docs/CLOUDFLARE.md): adapters with local conformance tests; no provider deployment verified yet.
|
|
145
|
-
- [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.
|
|
146
|
-
- [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",
|
|
@@ -110,6 +114,7 @@
|
|
|
110
114
|
"docs:llms": "node scripts/build-llms-full.ts",
|
|
111
115
|
"docs:cookbook-index": "node scripts/build-cookbook-index.ts",
|
|
112
116
|
"artifacts:prepare": "node --disable-warning=ExperimentalWarning scripts/prepare-extension-artifacts.ts",
|
|
117
|
+
"bundles:prepare": "node --disable-warning=ExperimentalWarning scripts/prepare-extension-bundles.ts",
|
|
113
118
|
"check:downstream-skills": "node scripts/check-downstream-skill-drift.ts",
|
|
114
119
|
"sync:agents": "node scripts/sync-agent-lists.ts",
|
|
115
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",
|
|
@@ -14,8 +14,8 @@ Run `urlcode validate --local --project .` and `urlcode serve --project .`.
|
|
|
14
14
|
default). Add top-level `site: {notFound: 404.html}` for a custom 404 page
|
|
15
15
|
instead of the plain default.
|
|
16
16
|
|
|
17
|
-
For the shapes this recipe does not cover — a
|
|
18
|
-
|
|
17
|
+
For the shapes this recipe does not cover — a root-relative or `/**` suffix
|
|
18
|
+
redirect, or a host/scheme-based redirect — run
|
|
19
19
|
`urlcode context --project . --task redirects` (MCP `get_context
|
|
20
20
|
{"task":"redirects"}`) for the exact supported alternative and the exact
|
|
21
21
|
validation error, or see [docs/OPEN-DECISIONS.md](../../docs/OPEN-DECISIONS.md).
|
|
@@ -15,7 +15,7 @@ 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
|
|
@@ -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
|
|