@jimhoyd/urlcode 0.4.2 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/urlcode-authoring/SKILL.md +57 -14
- package/.claude/skills/urlcode-operations/SKILL.md +4 -0
- package/README.md +42 -18
- package/SECURITY.md +5 -3
- package/dist/BUILD-MANIFEST.json +30 -27
- package/dist/agent-context.js +82 -0
- package/dist/agents-guide.js +42 -42
- package/dist/authoring.js +12 -2
- package/dist/body-schema.js +159 -0
- package/dist/build-cloudflare.js +2 -0
- package/dist/capabilities.js +1 -1
- package/dist/cli.js +28 -12
- package/dist/config.js +74 -6
- package/dist/context.js +4 -6
- package/dist/errors.js +3 -1
- package/dist/examples.js +1 -1
- package/dist/extensions.js +60 -2
- package/dist/http-policy.js +19 -4
- package/dist/http-response.js +2 -2
- package/dist/init-with.js +71 -9
- package/dist/mcp.js +15 -2
- package/dist/pattern-guard.js +32 -0
- package/dist/policies/security.js +0 -0
- package/dist/project-tests.js +35 -11
- package/dist/readiness.js +184 -32
- package/dist/recipes.js +1 -1
- package/dist/router.js +17 -0
- package/dist/runtime.js +26 -3
- package/dist/scaffold.js +0 -0
- package/dist/server.js +26 -3
- package/dist/site.js +0 -0
- package/dist/tooling.js +2 -1
- package/dist/types/agent-context.d.ts +44 -0
- package/dist/types/authoring.d.ts +3 -1
- package/dist/types/body-schema.d.ts +58 -0
- package/dist/types/config.d.ts +10 -2
- package/dist/types/context.d.ts +1 -1
- package/dist/types/errors.d.ts +9 -1
- package/dist/types/examples.d.ts +1 -1
- package/dist/types/extensions.d.ts +47 -2
- package/dist/types/http-policy.d.ts +3 -0
- package/dist/types/init-with.d.ts +10 -1
- package/dist/types/pattern-guard.d.ts +10 -0
- package/dist/types/project-tests.d.ts +9 -0
- package/dist/types/readiness.d.ts +68 -0
- package/dist/types/recipes.d.ts +1 -1
- package/dist/types/runtime.d.ts +4 -0
- package/dist/types/server.d.ts +9 -1
- package/dist/types/tooling.d.ts +1 -0
- package/dist/types/types.d.ts +13 -0
- package/dist/types.js +10 -2
- package/dist/typescript-authoring.js +5 -3
- package/dist/verify-deployment.js +16 -10
- package/examples/body-validation/README.md +16 -0
- package/examples/body-validation/example.yaml +17 -0
- package/examples/body-validation/tests/requests.json +15 -0
- package/examples/body-validation/urlcode.yaml +34 -0
- package/examples/coverage-waiver/README.md +8 -0
- package/examples/coverage-waiver/example.yaml +16 -0
- package/examples/coverage-waiver/functions/notes.mjs +2 -0
- package/examples/coverage-waiver/tests/requests.json +3 -0
- package/examples/coverage-waiver/urlcode.yaml +10 -0
- package/examples/data-dir/README.md +39 -0
- package/examples/data-dir/data/welcome.txt +1 -0
- package/examples/data-dir/example.yaml +22 -0
- package/examples/data-dir/functions/note.mjs +18 -0
- package/examples/data-dir/tests/requests.json +6 -0
- package/examples/data-dir/urlcode.yaml +6 -0
- package/examples/lifecycle/README.md +15 -0
- package/examples/lifecycle/example.yaml +19 -0
- package/examples/lifecycle/functions/notes.mjs +29 -0
- package/examples/lifecycle/tests/requests.json +12 -0
- package/examples/lifecycle/urlcode.yaml +29 -0
- package/examples/not-found/README.md +10 -0
- package/examples/not-found/example.yaml +17 -0
- package/examples/not-found/public/404.html +3 -0
- package/examples/not-found/public/index.html +3 -0
- package/examples/not-found/tests/requests.json +7 -0
- package/examples/not-found/urlcode.yaml +7 -0
- package/examples/shared-blocks/README.md +11 -0
- package/examples/shared-blocks/example.yaml +17 -0
- package/examples/shared-blocks/tests/requests.json +7 -0
- package/examples/shared-blocks/urlcode.yaml +37 -0
- package/llms-full.txt +364 -49
- package/llms.txt +44 -6
- package/package.json +15 -26
- package/recipes/static-page/README.md +9 -0
- package/recipes/static-page/public/index.html +11 -0
- package/recipes/static-page/recipe.yaml +21 -0
- package/recipes/static-page/tests/requests.json +22 -0
- package/recipes/static-page/urlcode.yaml +7 -0
- package/recipes/static-plus-api/README.md +6 -0
- package/recipes/static-plus-api/urlcode.yaml +4 -0
- package/recipes/store-crud/README.md +53 -0
- package/recipes/store-crud/recipe.yaml +31 -0
- package/recipes/store-crud/tests/requests.json +18 -0
- package/recipes/store-crud/urlcode.yaml +18 -0
- package/schemas/urlcode.schema.json +125 -60
- package/skills/urlcode/SKILL.md +47 -27
- package/starters/default/AGENTS.md +43 -43
- package/starters/page/README.md +14 -0
- package/starters/page/public/index.html +12 -0
- package/starters/page/tests/requests.json +17 -0
- package/starters/page/urlcode.yaml +6 -0
- package/.claude-plugin/marketplace.json +0 -18
- package/CONTRIBUTING.md +0 -148
- package/ROADMAP.md +0 -70
- package/docs/AI-AUTHORING.md +0 -339
- package/docs/ASSETS.md +0 -107
- package/docs/AUTH-BACKUP.md +0 -32
- package/docs/AWS.md +0 -86
- package/docs/BEST-PRACTICES.md +0 -276
- package/docs/BULK.md +0 -79
- package/docs/CAPABILITIES.md +0 -192
- package/docs/CAPACITY.md +0 -305
- package/docs/CI-FOLLOWUP-2026-09-19.md +0 -97
- package/docs/CI-RELEASE-AUDIT-2026-09-19.md +0 -322
- package/docs/CI.md +0 -147
- package/docs/CLOUDFLARE.md +0 -109
- package/docs/CODEBASE-AUDIT-2026-09-20.md +0 -284
- package/docs/COMPLIANCE.md +0 -239
- package/docs/COMPOSING-A-SITE.md +0 -287
- package/docs/CONDITIONS.md +0 -74
- package/docs/CONTAINER-PROMOTION.md +0 -74
- package/docs/DEPLOYMENT-CHECKS.md +0 -108
- package/docs/DEVELOPMENT-PIPELINE.md +0 -304
- package/docs/EGRESS.md +0 -125
- package/docs/EXTENSIONS.md +0 -403
- package/docs/FRAMEWORK.md +0 -217
- package/docs/FUNCTION-SECURITY.md +0 -251
- package/docs/HTTP.md +0 -129
- package/docs/INSTALL.md +0 -128
- package/docs/INTERCHANGE.md +0 -134
- package/docs/LOAD-TESTING.md +0 -91
- package/docs/LOCAL-DEVELOPMENT.md +0 -102
- package/docs/MIDDLEWARE-EXAMPLES.md +0 -75
- package/docs/MIDDLEWARE.md +0 -102
- package/docs/MONITORING.md +0 -115
- package/docs/OBSERVABILITY.md +0 -222
- package/docs/OPEN-DECISIONS.md +0 -149
- package/docs/OPERATIONAL-PROOF.md +0 -41
- package/docs/OPERATIONS.md +0 -201
- package/docs/ORGANIZATION.md +0 -135
- package/docs/PERFORMANCE.md +0 -72
- package/docs/PLUGINS.md +0 -271
- package/docs/POLICIES.md +0 -211
- package/docs/PRERENDER.md +0 -245
- package/docs/PROJECT-DIRECTION.md +0 -118
- package/docs/PROVIDER-VERIFICATION.md +0 -84
- package/docs/READINESS.md +0 -153
- package/docs/README.md +0 -93
- package/docs/RECIPES.md +0 -99
- package/docs/RELEASE-0.4.0-alpha.3.md +0 -50
- package/docs/RELEASE-0.4.1.md +0 -73
- package/docs/RELEASE-0.4.2.md +0 -30
- package/docs/RELEASE-READINESS.md +0 -146
- package/docs/RELEASE-SECURITY.md +0 -100
- package/docs/RESILIENCE.md +0 -161
- package/docs/ROUTING.md +0 -92
- package/docs/SANDBOX-REVIEW.md +0 -72
- package/docs/SCAFFOLDING.md +0 -70
- package/docs/SECURITY-AUDIT.md +0 -164
- package/docs/SITE.md +0 -150
- package/docs/SPECIFICATION.md +0 -359
- package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +0 -288
- package/docs/SPIKE-BUSINESS-SUITE.md +0 -1029
- package/docs/SPIKE-CORE-LAYERING.md +0 -368
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +0 -207
- package/docs/STANDARDS.md +0 -311
- package/docs/STARTERS.md +0 -83
- package/docs/STATIC.md +0 -105
- package/docs/TOOLING.md +0 -298
- package/docs/TUNNELS.md +0 -72
- package/docs/TYPESCRIPT-AUTHORING.md +0 -87
- package/docs/TYPESCRIPT.md +0 -123
- package/docs/VERCEL.md +0 -114
- package/docs/VERSION-ALIGNMENT.md +0 -88
- package/docs/YAML-GUIDE.md +0 -57
- package/docs/YAML-REFERENCE.md +0 -449
- package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +0 -68
- package/docs/archive/2026-09-19/MANAGEMENT-SECURITY.md +0 -102
- package/docs/archive/2026-09-19/NEXT-PHASE-PLAN.md +0 -108
- package/docs/archive/2026-09-19/NEXT-STEPS.md +0 -646
- package/docs/archive/2026-09-19/OPEN-DECISIONS.md +0 -277
- package/docs/archive/2026-09-19/RELEASE-SECURITY.md +0 -186
- package/docs/archive/2026-09-19/ROADMAP.md +0 -387
- package/docs/archive/2026-09-19/SPIKE-EXTENSION-MODEL.md +0 -430
- package/docs/archive/2026-09-19/SPIKE-EXTENSIONS.md +0 -492
- package/docs/archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md +0 -365
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +0 -778
- package/docs/archive/2026-09-19/USABILITY-REVIEW.md +0 -139
- package/docs/archive/2026-09-20/OPEN-DECISIONS-COMPLETED.md +0 -116
- package/docs/archive/README.md +0 -29
- package/docs/policies/agents.md +0 -182
- package/docs/policies/cache.md +0 -152
- package/docs/policies/compression.md +0 -169
- package/docs/policies/contract.md +0 -52
- package/docs/policies/hardened.md +0 -56
- package/docs/policies/interoperability.md +0 -169
- package/docs/policies/operations.md +0 -45
- package/docs/policies/security.md +0 -161
- package/docs/policies/throttle.md +0 -103
- package/docs/yaml/assets.md +0 -36
- package/docs/yaml/conditions.md +0 -20
- package/docs/yaml/functions.md +0 -168
- package/docs/yaml/middleware.md +0 -31
- package/docs/yaml/organization.md +0 -74
- package/docs/yaml/policies.md +0 -37
- package/docs/yaml/redirects.md +0 -64
- package/docs/yaml/responses.md +0 -57
- package/docs/yaml/site.md +0 -24
- package/packaging/claude-plugin/.claude-plugin/plugin.json +0 -19
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +0 -130
- package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +0 -108
package/llms.txt
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
<!-- urlcode-current-version:start -->
|
|
1
2
|
# URLCode
|
|
2
3
|
|
|
3
4
|
> A portable runtime for programmable URL behavior, and the framework that grows
|
|
4
5
|
> from it: routes in YAML, functions and middleware, then accounts, administration and
|
|
5
6
|
> stored links as operator-installed extensions. Stable project format
|
|
6
|
-
> `version: "1"`. Core is Apache-2.0; this revision is `0.4.
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
|
|
7
|
+
> `version: "1"`. Core is Apache-2.0; this revision is `0.4.7`. `function`/`middleware`
|
|
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`.
|
|
11
|
+
<!-- urlcode-current-version:end -->
|
|
11
12
|
|
|
12
13
|
Use the schema and docs from the runtime revision you run. Do not assume Node
|
|
13
14
|
or fetch inside a `sandbox: true` function, regex routes, database access,
|
|
@@ -26,6 +27,43 @@ extensions and recipes/templates before writing a custom function or middleware.
|
|
|
26
27
|
Keep necessary custom code focused and report the capability gap; never invent
|
|
27
28
|
fields or bypass target limits or operator grants. See [the design principle](docs/PROJECT-DIRECTION.md#design-principle-declarative-first).
|
|
28
29
|
|
|
30
|
+
## Need X? Use this built-in first
|
|
31
|
+
|
|
32
|
+
Route a task to the feature before writing code. Every row is off unless declared.
|
|
33
|
+
|
|
34
|
+
| I need | Use | Where |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| Security headers (CSP, HSTS, nosniff, frame and referrer policy) | `policies.security` (`headers: oshp`, or `profile: hardened`) | [security](docs/policies/security.md), [policies](docs/POLICIES.md) |
|
|
37
|
+
| Cache headers for a page, download or static folder | `cacheControl`, exactly one of `no-cache` (default), `no-store`, `public, max-age=3600`, `public, max-age=31536000, immutable` | [assets](docs/yaml/assets.md) |
|
|
38
|
+
| Cache strategy or origin cache for any route | `policies.cache` | [cache](docs/policies/cache.md) |
|
|
39
|
+
| Request body size, required body, content type, JSON syntax check | `request.body` (`maxBytes`, `required`, `contentTypes`, `format`); 413/415/400 | [HTTP](docs/HTTP.md) |
|
|
40
|
+
| Restrict or allow HTTP methods | `methods` (default GET/HEAD); 405 with `Allow` | [HTTP](docs/HTTP.md) |
|
|
41
|
+
| Rate limiting | `policies.throttle` | [throttle](docs/policies/throttle.md) |
|
|
42
|
+
| Block bots or AI crawlers | `policies.agents`; `robots` via `site` | [agents](docs/policies/agents.md), [site](docs/SITE.md) |
|
|
43
|
+
| Compression | `policies.compression` | [compression](docs/policies/compression.md) |
|
|
44
|
+
| Fixed JSON, text or headers with no code | `respond` and `response.headers` | [HTTP](docs/HTTP.md) |
|
|
45
|
+
| robots.txt, sitemap, favicon, security.txt | top-level `site` | [site](docs/SITE.md) |
|
|
46
|
+
| Sign-in, roles, protected routes | `extensions.auth`, then `auth: true` on the route | [extensions](docs/EXTENSIONS.md) |
|
|
47
|
+
|
|
48
|
+
Which handler serves the response:
|
|
49
|
+
|
|
50
|
+
| The response is | Handler | Recipe |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| Fixed text or JSON | `respond` | `health-page` |
|
|
53
|
+
| A short HTML snippet | `respond` `text` plus `response.headers` `Content-Type: text/html; charset=utf-8` | `docs/HTTP.md` |
|
|
54
|
+
| One HTML file | `page` | `static-page` |
|
|
55
|
+
| A directory of files | `static` | `static-plus-api` |
|
|
56
|
+
| An attachment | `download` | `protected-download` |
|
|
57
|
+
|
|
58
|
+
Persistence: core has no storage handler, but the operator-installed store extension serves declared collections as a CRUD API; `urlcode recipes search "crud store persist"` finds the `store-crud` recipe (operator installs the package; `init --with store` is not yet available from npm, #323). Gaps to report rather than invent: a database, filtering or per-record ownership beyond that recipe, CORS, and a native link store. `urlcode context`
|
|
59
|
+
prints the same pointer for the project in front of you.
|
|
60
|
+
|
|
61
|
+
After a real authoring attempt, draft feedback only for a capability gap,
|
|
62
|
+
repeated workaround, documentation/discovery gap or suspected defect. Include
|
|
63
|
+
the runtime/target, sanitized YAML, observed validation/test evidence, expected
|
|
64
|
+
behavior and a proposed fixture. Search existing URLCode issues first. Never
|
|
65
|
+
publish or comment on an issue without the user's explicit approval.
|
|
66
|
+
|
|
29
67
|
## Authoring
|
|
30
68
|
- [AI authoring contract](docs/AI-AUTHORING.md): workflow, capability matrix, checks.
|
|
31
69
|
- [Authoring skill](.claude/skills/urlcode-authoring/SKILL.md): loadable authoring skill shipped with this revision; see [distribution](docs/AI-AUTHORING.md#agent-skills).
|
|
@@ -82,7 +120,7 @@ fields or bypass target limits or operator grants. See [the design principle](do
|
|
|
82
120
|
|
|
83
121
|
## Operations
|
|
84
122
|
- [Operations](docs/OPERATIONS.md), [install](docs/INSTALL.md), [deployment checks](docs/DEPLOYMENT-CHECKS.md), [CI action](docs/CI.md).
|
|
85
|
-
- [Readiness](docs/READINESS.md), [capacity](docs/CAPACITY.md), [
|
|
123
|
+
- [Readiness](docs/READINESS.md), [capacity](docs/CAPACITY.md), [resilience](docs/RESILIENCE.md), [observability](docs/OBSERVABILITY.md).
|
|
86
124
|
- [Vercel](docs/VERCEL.md), [AWS](docs/AWS.md), [Cloudflare](docs/CLOUDFLARE.md): adapters with local conformance tests; no provider deployment verified yet.
|
|
87
125
|
- [Static hosting](docs/STATIC.md): `urlcode build --target static` compiles redirects/pages/static/downloads to S3 + CloudFront objects and redirect metadata; no server, so function/middleware/extension/proxy/signals/conditions/parameters/bindings/policies are all refused; GitHub Pages is explicitly out of scope.
|
|
88
126
|
- [Release readiness](docs/RELEASE-READINESS.md), [roadmap](ROADMAP.md): what is proven, what is planned.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jimhoyd/urlcode",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "Portable runtime for programmable URL behavior",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -23,75 +23,59 @@
|
|
|
23
23
|
"exports": {
|
|
24
24
|
".": {
|
|
25
25
|
"types": "./dist/types/index.d.ts",
|
|
26
|
-
"development": "./src/index.ts",
|
|
27
26
|
"default": "./dist/index.js"
|
|
28
27
|
},
|
|
29
28
|
"./aws": {
|
|
30
29
|
"types": "./dist/types/aws.d.ts",
|
|
31
|
-
"development": "./src/aws.ts",
|
|
32
30
|
"default": "./dist/aws.js"
|
|
33
31
|
},
|
|
34
32
|
"./cloudflare": {
|
|
35
33
|
"types": "./dist/types/cloudflare.d.ts",
|
|
36
|
-
"development": "./src/cloudflare.ts",
|
|
37
34
|
"default": "./dist/cloudflare.js"
|
|
38
35
|
},
|
|
39
36
|
"./prerender": {
|
|
40
37
|
"types": "./dist/types/prerender.d.ts",
|
|
41
|
-
"development": "./src/prerender.ts",
|
|
42
38
|
"default": "./dist/prerender.js"
|
|
43
39
|
},
|
|
44
40
|
"./schema": "./schemas/urlcode.schema.json",
|
|
45
41
|
"./vercel": {
|
|
46
42
|
"types": "./dist/types/vercel.d.ts",
|
|
47
|
-
"development": "./src/vercel.ts",
|
|
48
43
|
"default": "./dist/vercel.js"
|
|
49
44
|
},
|
|
50
45
|
"./plugins": {
|
|
51
46
|
"types": "./dist/types/plugins.d.ts",
|
|
52
|
-
"development": "./src/plugins.ts",
|
|
53
47
|
"default": "./dist/plugins.js"
|
|
54
48
|
},
|
|
55
49
|
"./policies": {
|
|
56
50
|
"types": "./dist/types/policies.d.ts",
|
|
57
|
-
"development": "./src/policies.ts",
|
|
58
51
|
"default": "./dist/policies.js"
|
|
59
52
|
},
|
|
60
53
|
"./compliance": {
|
|
61
54
|
"types": "./dist/types/compliance.d.ts",
|
|
62
|
-
"development": "./src/compliance.ts",
|
|
63
55
|
"default": "./dist/compliance.js"
|
|
64
56
|
},
|
|
65
57
|
"./observability": {
|
|
66
58
|
"types": "./dist/types/observability.d.ts",
|
|
67
|
-
"development": "./src/observability.ts",
|
|
68
59
|
"default": "./dist/observability.js"
|
|
69
60
|
},
|
|
70
61
|
"./extensions": {
|
|
71
62
|
"types": "./dist/types/extensions.d.ts",
|
|
72
|
-
"development": "./src/extensions.ts",
|
|
73
63
|
"default": "./dist/extensions.js"
|
|
74
64
|
},
|
|
75
65
|
"./sandbox": {
|
|
76
66
|
"types": "./dist/types/sandbox.d.ts",
|
|
77
|
-
"development": "./src/sandbox.ts",
|
|
78
67
|
"default": "./dist/sandbox.js"
|
|
79
68
|
},
|
|
80
69
|
"./package.json": "./package.json"
|
|
81
70
|
},
|
|
82
71
|
"files": [
|
|
83
72
|
".claude",
|
|
84
|
-
".claude-plugin",
|
|
85
|
-
"packaging/claude-plugin",
|
|
86
73
|
"dist",
|
|
87
74
|
"schemas",
|
|
88
75
|
"data",
|
|
89
76
|
"starters",
|
|
90
77
|
"README.md",
|
|
91
|
-
"docs",
|
|
92
78
|
"SECURITY.md",
|
|
93
|
-
"ROADMAP.md",
|
|
94
|
-
"CONTRIBUTING.md",
|
|
95
79
|
"examples",
|
|
96
80
|
"llms.txt",
|
|
97
81
|
"llms-full.txt",
|
|
@@ -108,9 +92,9 @@
|
|
|
108
92
|
"check:code": "node scripts/check.ts && node scripts/check-release-tags.ts && node scripts/check-workspace-links.ts && npm run release:check",
|
|
109
93
|
"verify": "npm run lint && npm run typecheck && npm run check && npm run build && npm test && npm run verify:workspaces",
|
|
110
94
|
"workspace:styles": "npm run styles --workspace @jimhoyd/urlcode-ui",
|
|
111
|
-
"verify:workspaces": "npm run verify --workspace @jimhoyd/urlcode-ui && npm run verify --workspace @jimhoyd/urlcode-auth && npm run verify --workspace @jimhoyd/urlcode-admin && npm run test:workspace-integration",
|
|
112
|
-
"
|
|
113
|
-
"test:package": "npm run build &&
|
|
95
|
+
"verify:workspaces": "npm run verify --workspace @jimhoyd/urlcode-ui && npm run verify --workspace @jimhoyd/urlcode-auth && npm run verify --workspace @jimhoyd/urlcode-admin && npm run verify --workspace @jimhoyd/urlcode-store && npm run audit:packages && npm run test:workspace-integration",
|
|
96
|
+
"audit:packages": "node scripts/package-audit.ts --all",
|
|
97
|
+
"test:package": "npm run build && npm run test:package:built",
|
|
114
98
|
"dev": "node src/cli.ts dev --project starters/default",
|
|
115
99
|
"serve": "node src/cli.ts serve --project starters/default",
|
|
116
100
|
"validate": "node src/cli.ts validate --local --project starters/default",
|
|
@@ -125,13 +109,10 @@
|
|
|
125
109
|
"docs:llms": "node scripts/build-llms-full.ts",
|
|
126
110
|
"docs:cookbook-index": "node scripts/build-cookbook-index.ts",
|
|
127
111
|
"check:downstream-skills": "node scripts/check-downstream-skill-drift.ts",
|
|
128
|
-
"benchmark:bulk": "node benchmarks/bulk.ts",
|
|
129
|
-
"benchmark:sandbox-vs-trusted": "node benchmarks/sandbox-vs-trusted.ts",
|
|
130
|
-
"benchmark:agent": "node benchmarks/agent/run.ts",
|
|
131
112
|
"sync:agents": "node scripts/sync-agent-lists.ts",
|
|
132
|
-
"check:docs": "node scripts/check-trust-model-prose.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",
|
|
113
|
+
"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",
|
|
133
114
|
"ci:plan": "node scripts/ci-plan.ts",
|
|
134
|
-
"test:package:built": "node scripts/package-smoke.ts",
|
|
115
|
+
"test:package:built": "node scripts/package-audit.ts . && node scripts/package-smoke.ts",
|
|
135
116
|
"release:status": "node scripts/release.ts status",
|
|
136
117
|
"release:plan": "node scripts/release.ts plan",
|
|
137
118
|
"release:publish": "node scripts/release.ts publish",
|
|
@@ -154,7 +135,6 @@
|
|
|
154
135
|
"es-module-lexer": "3.0.2",
|
|
155
136
|
"mime-types": "3.0.2",
|
|
156
137
|
"quickjs-emscripten": "0.32.0",
|
|
157
|
-
"typescript": "6.0.3",
|
|
158
138
|
"yaml": "2.9.1"
|
|
159
139
|
},
|
|
160
140
|
"devDependencies": {
|
|
@@ -166,8 +146,17 @@
|
|
|
166
146
|
"eslint": "10.10.0",
|
|
167
147
|
"globals": "17.12.0",
|
|
168
148
|
"semver": "7.8.5",
|
|
149
|
+
"typescript": "6.0.3",
|
|
169
150
|
"typescript-eslint": "8.70.0"
|
|
170
151
|
},
|
|
152
|
+
"peerDependencies": {
|
|
153
|
+
"typescript": ">=6.0.3 <7.0.0"
|
|
154
|
+
},
|
|
155
|
+
"peerDependenciesMeta": {
|
|
156
|
+
"typescript": {
|
|
157
|
+
"optional": true
|
|
158
|
+
}
|
|
159
|
+
},
|
|
171
160
|
"homepage": "https://github.com/jimhoyd-com/urlcode#readme",
|
|
172
161
|
"bugs": {
|
|
173
162
|
"url": "https://github.com/jimhoyd-com/urlcode/issues"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Static page
|
|
2
|
+
|
|
3
|
+
Run `urlcode validate --local --project .`, `urlcode test --project .` and
|
|
4
|
+
`urlcode audit --project . --expect-routes 1`.
|
|
5
|
+
|
|
6
|
+
`/` serves `public/index.html` with the native `page` handler, so no project
|
|
7
|
+
code runs. Edit the file to change the page. For a whole directory of assets use
|
|
8
|
+
`static`; for fixed text or JSON use `respond`; for an attachment use
|
|
9
|
+
`download`. The `static-plus-api` recipe combines a page with a directory.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
id: static-page
|
|
2
|
+
description: One HTML file served natively as a page, with no code and no directory mount.
|
|
3
|
+
tags: [page, html, hello, world,static, site, landing, native, cache-control]
|
|
4
|
+
complexity: starter
|
|
5
|
+
capabilities: [enabled, methods, page]
|
|
6
|
+
targets: {self-hosted: compatible, aws: compatible, vercel: compatible, cloudflare: refused, static: compatible}
|
|
7
|
+
routes: 1
|
|
8
|
+
inputs:
|
|
9
|
+
- name: page
|
|
10
|
+
file: public/index.html
|
|
11
|
+
description: The HTML document served at /.
|
|
12
|
+
files: [urlcode.yaml, public/index.html, tests/requests.json, README.md]
|
|
13
|
+
tests:
|
|
14
|
+
fixtures: tests/requests.json
|
|
15
|
+
commands:
|
|
16
|
+
- urlcode validate --local --project .
|
|
17
|
+
- urlcode test --project .
|
|
18
|
+
- urlcode audit --project . --expect-routes 1
|
|
19
|
+
behavior:
|
|
20
|
+
- GET / answers 200 with the file as text/html and Cache-Control no-cache
|
|
21
|
+
- HEAD answers an empty body; POST answers 405
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"path": "/",
|
|
4
|
+
"status": 200,
|
|
5
|
+
"expectBody": "<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>Hello, World</title>\n</head>\n<body>\n <h1>Hello, World</h1>\n</body>\n</html>\n",
|
|
6
|
+
"expectHeaders": {
|
|
7
|
+
"content-type": "text/html; charset=utf-8",
|
|
8
|
+
"cache-control": "no-cache"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"path": "/",
|
|
13
|
+
"method": "HEAD",
|
|
14
|
+
"status": 200,
|
|
15
|
+
"expectBody": ""
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "/",
|
|
19
|
+
"method": "POST",
|
|
20
|
+
"status": 405
|
|
21
|
+
}
|
|
22
|
+
]
|
|
@@ -13,3 +13,9 @@ activation, so new files need a reload. Edit the HTML, add files under
|
|
|
13
13
|
The function needs the self-hosted runtime; drop `/api/info` to deploy the
|
|
14
14
|
static part on a serverless target. Static mounts cannot escape their
|
|
15
15
|
directory, and the recipe sets explicit caching so nothing is guessed.
|
|
16
|
+
|
|
17
|
+
Adding a function that writes files or accepts a POST body? Filesystem access
|
|
18
|
+
only exists in trusted (default) routes, since `sandbox: true` has no
|
|
19
|
+
filesystem, so keep the route trusted and state why with `sandboxReason`, as
|
|
20
|
+
`/api/info` does. `audit` advises when a POST route with a body policy has
|
|
21
|
+
neither `sandbox: true` nor `sandboxReason`.
|
|
@@ -18,6 +18,10 @@ routes:
|
|
|
18
18
|
args:
|
|
19
19
|
name: Example site
|
|
20
20
|
version: "1"
|
|
21
|
+
# Why this route runs trusted. Keep it when you add a function that
|
|
22
|
+
# touches the filesystem: that needs a trusted route, and a body policy
|
|
23
|
+
# makes `audit` ask for this line.
|
|
24
|
+
sandboxReason: Reviewed first-party code; trusted deliberately.
|
|
21
25
|
response:
|
|
22
26
|
headers:
|
|
23
27
|
Cache-Control: no-store
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Store CRUD
|
|
2
|
+
|
|
3
|
+
`/api/todos/*` is a persistent JSON CRUD API. The project declares one
|
|
4
|
+
collection and its mount; the operator-installed `store` extension
|
|
5
|
+
(`@jimhoyd/urlcode-store`) serves it and keeps the data in the operator's
|
|
6
|
+
directory. There is no handler code. Full contract, limits and guarantees:
|
|
7
|
+
[docs/STORE.md](../../docs/STORE.md).
|
|
8
|
+
|
|
9
|
+
## Operator prerequisites
|
|
10
|
+
|
|
11
|
+
The store is not core and does not activate on its own.
|
|
12
|
+
|
|
13
|
+
- The operator installs `@jimhoyd/urlcode-store` next to `@jimhoyd/urlcode`
|
|
14
|
+
and registers it in a host file kept outside the project.
|
|
15
|
+
- The package is on npm (`@jimhoyd/urlcode-store`).
|
|
16
|
+
`urlcode init --with ui,auth,store` scaffolds a protected site from the
|
|
17
|
+
published packages. A no-auth `init --with store` needs `--ack store:public-write`,
|
|
18
|
+
which the core published at the store's first release does not have, so write the host file below by hand until a
|
|
19
|
+
newer core is released.
|
|
20
|
+
- The data directory must be outside the project. It is single-writer: one
|
|
21
|
+
server process per directory.
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
// /operator/host.mjs -- trusted operator code, never part of the project
|
|
25
|
+
import {inspectExtensionRevision} from '@jimhoyd/urlcode/extensions';
|
|
26
|
+
import {storeExtension} from '@jimhoyd/urlcode-store';
|
|
27
|
+
const projectSha256 = await inspectExtensionRevision(process.env.URLCODE_PROJECT);
|
|
28
|
+
export default {extensions: [storeExtension({directory: '/operator/data/store', projectSha256})]};
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
urlcode validate --local --project . --host-file /operator/host.mjs --origin https://api.example.com
|
|
33
|
+
urlcode test --project . --host-file /operator/host.mjs --origin https://api.example.com
|
|
34
|
+
urlcode audit --project . --expect-routes 1 --host-file /operator/host.mjs --origin https://api.example.com
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The bundled fixtures run one ordered create, read, update, delete lifecycle
|
|
38
|
+
that captures the new record's id and deletes it again, so they need an empty
|
|
39
|
+
collection and leave it empty. Point them at a scratch data directory, not one
|
|
40
|
+
holding real records. Try the write path by hand after serving:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
curl -X POST -H 'Content-Type: application/json' -d '{"title":"first"}' https://api.example.com/api/todos
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Before exposing it
|
|
47
|
+
|
|
48
|
+
The mount is public unless you protect it: add `auth: true` (with the auth
|
|
49
|
+
extension) or another policy before any writable collection is reachable. Every
|
|
50
|
+
caller sees the whole collection; there is no per-user ownership. Changing
|
|
51
|
+
`urlcode.yaml` changes the revision and needs a new pin. Cloudflare and static
|
|
52
|
+
targets refuse extensions, and the store writes local files, so run it on the
|
|
53
|
+
self-hosted runtime with a persistent disk.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
id: store-crud
|
|
2
|
+
description: Persistent JSON CRUD API for a declared collection (create, read, update, delete, list) served by the operator-installed store extension, with no handler code.
|
|
3
|
+
tags: [crud, store, persist, persistence, database, storage, todo, records, collection, json, api, rest, extension, "201", "404"]
|
|
4
|
+
complexity: advanced
|
|
5
|
+
capabilities: [enabled, extension, methods]
|
|
6
|
+
targets: {self-hosted: conditional, cloudflare: refused, aws: conditional, vercel: conditional, static: refused}
|
|
7
|
+
routes: 1
|
|
8
|
+
services:
|
|
9
|
+
- name: store extension
|
|
10
|
+
description: An operator host file registering storeExtension from @jimhoyd/urlcode-store, with a data directory outside the project. The operator installs the package; `urlcode init --with ui,auth,store` scaffolds a protected host, and the README shows a hand-written one.
|
|
11
|
+
grants:
|
|
12
|
+
- kind: extension
|
|
13
|
+
description: The registration pins projectSha256 to this exact revision; editing a collection or the mount needs operator review and a new pin.
|
|
14
|
+
- kind: origin
|
|
15
|
+
description: The canonical origin passed as --origin at every command that activates the project; cross-origin writes are refused.
|
|
16
|
+
inputs:
|
|
17
|
+
- name: collection
|
|
18
|
+
file: urlcode.yaml
|
|
19
|
+
description: The collection name, mount path, typed fields, defaults and record limits.
|
|
20
|
+
files: [urlcode.yaml, tests/requests.json, README.md]
|
|
21
|
+
tests:
|
|
22
|
+
fixtures: tests/requests.json
|
|
23
|
+
commands:
|
|
24
|
+
- urlcode validate --local --project . --host-file /operator/host.mjs --origin https://api.example.com
|
|
25
|
+
- urlcode test --project . --host-file /operator/host.mjs --origin https://api.example.com
|
|
26
|
+
- urlcode audit --project . --expect-routes 1 --host-file /operator/host.mjs --origin https://api.example.com
|
|
27
|
+
behavior:
|
|
28
|
+
- GET /api/todos on an empty collection answers 200 {"items":[],"total":0} with Cache-Control no-store
|
|
29
|
+
- POST /api/todos with a valid JSON record answers 201, the record with a server-assigned id, and a Location header
|
|
30
|
+
- invalid or unknown fields answer 400, a non-JSON Content-Type answers 415, a missing id answers 404, an unsupported method answers 405
|
|
31
|
+
- PUT replaces the record, PATCH updates supplied fields, DELETE answers 204; records survive a restart
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[
|
|
2
|
+
{"path": "/api/todos", "status": 200, "expectBody": "{\"items\":[],\"total\":0}", "expectHeaders": {"cache-control": "no-store"}},
|
|
3
|
+
{"steps": [
|
|
4
|
+
{"path": "/api/todos", "method": "POST", "headers": {"content-type": "application/json"}, "body": "{\"title\":\"first\"}", "status": 201, "expectHeaders": {"cache-control": "no-store"}, "capture": {"id": {"json": "id"}}},
|
|
5
|
+
{"path": "/api/todos/{{id}}", "status": 200, "expectHeaders": {"cache-control": "no-store"}},
|
|
6
|
+
{"path": "/api/todos/{{id}}", "method": "HEAD", "status": 200, "expectBody": ""},
|
|
7
|
+
{"path": "/api/todos/{{id}}", "method": "PATCH", "headers": {"content-type": "application/json"}, "body": "{\"done\":true}", "status": 200, "expectHeaders": {"cache-control": "no-store"}},
|
|
8
|
+
{"path": "/api/todos/{{id}}", "method": "PUT", "headers": {"content-type": "application/json"}, "body": "{\"title\":\"second\"}", "status": 200, "expectHeaders": {"cache-control": "no-store"}},
|
|
9
|
+
{"path": "/api/todos", "status": 200, "expectHeaders": {"cache-control": "no-store"}},
|
|
10
|
+
{"path": "/api/todos/{{id}}", "method": "DELETE", "status": 204, "expectBody": ""},
|
|
11
|
+
{"path": "/api/todos/{{id}}", "status": 404}
|
|
12
|
+
]},
|
|
13
|
+
{"path": "/api/todos", "method": "POST", "headers": {"content-type": "application/json"}, "body": "{\"done\":true}", "status": 400},
|
|
14
|
+
{"path": "/api/todos", "method": "POST", "headers": {"content-type": "application/json"}, "body": "{\"title\":\"x\",\"owner\":\"me\"}", "status": 400},
|
|
15
|
+
{"path": "/api/todos", "method": "POST", "headers": {"content-type": "text/plain"}, "body": "title=x", "status": 415},
|
|
16
|
+
{"path": "/api/todos/00000000-0000-4000-8000-000000000000", "status": 404},
|
|
17
|
+
{"path": "/api/todos", "method": "DELETE", "status": 405}
|
|
18
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
version: "1"
|
|
2
|
+
extensions:
|
|
3
|
+
store:
|
|
4
|
+
version: "1"
|
|
5
|
+
config:
|
|
6
|
+
collections:
|
|
7
|
+
todos:
|
|
8
|
+
mount: /api/todos
|
|
9
|
+
fields:
|
|
10
|
+
title: {type: string, required: true, minLength: 1, maxLength: 200}
|
|
11
|
+
done: {type: boolean, default: false}
|
|
12
|
+
maxRecords: 1000
|
|
13
|
+
maxRecordBytes: 4096
|
|
14
|
+
routes:
|
|
15
|
+
/api/todos/*:
|
|
16
|
+
description: Create, read, update, delete and list todo records persisted by the operator-installed store extension.
|
|
17
|
+
extension: store
|
|
18
|
+
methods: [GET, HEAD, POST, PUT, PATCH, DELETE]
|