@jimhoyd/urlcode 0.4.0-alpha.3 → 0.4.1
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-plugin/marketplace.json +1 -1
- package/README.md +20 -15
- package/ROADMAP.md +16 -11
- package/dist/BUILD-MANIFEST.json +16 -15
- package/dist/authoring.js +15 -1
- package/dist/capability-query.js +0 -1
- package/dist/catalog.js +0 -1
- package/dist/cli.js +23 -7
- package/dist/config.js +1 -1
- package/dist/explain.js +1 -1
- package/dist/http-response.js +1 -1
- package/dist/index.js +1 -0
- package/dist/init-with.js +36 -11
- package/dist/manifest.js +1 -1
- package/dist/mcp-authoring.js +2 -2
- package/dist/mcp.js +1 -1
- package/dist/policies/cache.js +2 -2
- package/dist/project-dependencies.js +305 -0
- package/dist/runtime.js +1 -1
- package/dist/trusted-functions.js +4 -5
- package/dist/types/authoring.d.ts +9 -1
- package/dist/types/capability-query.d.ts +0 -1
- package/dist/types/catalog.d.ts +0 -4
- package/dist/types/config.d.ts +1 -9
- package/dist/types/explain.d.ts +0 -1
- package/dist/types/http-response.d.ts +0 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/init-with.d.ts +7 -13
- package/dist/types/manifest.d.ts +0 -1
- package/dist/types/project-dependencies.d.ts +78 -0
- package/dist/types/trusted-functions.d.ts +1 -4
- package/docs/AI-AUTHORING.md +5 -1
- package/docs/AWS.md +9 -0
- package/docs/CI-FOLLOWUP-2026-09-19.md +1 -1
- package/docs/CODEBASE-AUDIT-2026-09-20.md +6 -0
- package/docs/COMPOSING-A-SITE.md +278 -0
- package/docs/DEVELOPMENT-PIPELINE.md +208 -119
- package/docs/EXTENSIONS.md +36 -6
- package/docs/FRAMEWORK.md +45 -30
- package/docs/INSTALL.md +13 -8
- package/docs/MIDDLEWARE.md +10 -4
- package/docs/OPEN-DECISIONS.md +46 -6
- package/docs/READINESS.md +4 -3
- package/docs/README.md +3 -4
- package/docs/RELEASE-0.4.1.md +73 -0
- package/docs/RELEASE-SECURITY.md +27 -12
- package/docs/SPECIFICATION.md +5 -1
- package/docs/SPIKE-CORE-LAYERING.md +1 -1
- package/docs/STARTERS.md +17 -5
- package/docs/TOOLING.md +6 -4
- package/docs/VERCEL.md +10 -2
- package/docs/VERSION-ALIGNMENT.md +42 -8
- package/docs/archive/2026-09-19/ROADMAP.md +1 -0
- package/docs/archive/2026-09-19/SPIKE-EXTENSION-MODEL.md +1 -0
- package/docs/{SPIKE-LAMBDA-COMPILE.md → archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md} +168 -12
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +2 -0
- package/docs/archive/README.md +1 -0
- package/docs/yaml/functions.md +10 -2
- package/docs/yaml/middleware.md +5 -3
- package/examples/cookbook/middleware/envelope.mjs +4 -2
- package/llms-full.txt +387 -44
- package/llms.txt +1 -0
- package/package.json +8 -5
- package/packaging/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/recipes/middleware/middleware/envelope.mjs +4 -2
package/README.md
CHANGED
|
@@ -32,8 +32,10 @@ compiles it once, and refuses anything a target cannot enforce with the route
|
|
|
32
32
|
named. Functions and middleware run trusted, in-process, with full Node
|
|
33
33
|
access by default; a route opts into an isolated QuickJS/WebAssembly sandbox
|
|
34
34
|
with a fresh heap per call and no Node, filesystem or network by declaring
|
|
35
|
-
`sandbox: true`. Either way, secrets
|
|
36
|
-
pinned to the project revision
|
|
35
|
+
`sandbox: true`. Either way, the `env`/`secrets` the runtime *hands* a route
|
|
36
|
+
come only from operator grants pinned to the project revision; grants govern
|
|
37
|
+
that injected context, not the ambient Node environment a trusted, in-process
|
|
38
|
+
module can reach on its own like any other code in the host.
|
|
37
39
|
|
|
38
40
|
URLCode is not a URL shortener: stored short links are an operator-installed
|
|
39
41
|
extension, not core's job. It is not a
|
|
@@ -53,16 +55,17 @@ an AI agent must follow are in [the framework](docs/FRAMEWORK.md).
|
|
|
53
55
|
|
|
54
56
|
| Package | Adds | Status |
|
|
55
57
|
|---|---|---|
|
|
56
|
-
| [urlcode](https://github.com/jimhoyd-com/urlcode) (this repository) | Runtime, CLI, policies, provider adapters, extension contract | `0.4.
|
|
57
|
-
| [urlcode-ui](packages/ui) (in this repository) | Shared presentation: escaped templates, shadcn/ui partials, themes, translations | `0.1
|
|
58
|
-
| [urlcode-auth](packages/auth) (in this repository) | Accounts: password, passkeys, OIDC, email codes, TOTP, sessions, roles, account page | `0.1
|
|
59
|
-
| [urlcode-admin](packages/admin) (in this repository) | Administration: users, sessions, roles, audit, approvals, cases, impersonation | `0.1
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
| [urlcode](https://github.com/jimhoyd-com/urlcode) (this repository) | Runtime, CLI, policies, provider adapters, extension contract | `0.4.1` stable release target, Apache-2.0 |
|
|
59
|
+
| [urlcode-ui](packages/ui) (in this repository) | Shared presentation: escaped templates, shadcn/ui partials, themes, translations | `0.4.1` stable release target; review pending |
|
|
60
|
+
| [urlcode-auth](packages/auth) (in this repository) | Accounts: password, passkeys, OIDC, email codes, TOTP, sessions, roles, account page | `0.4.1` stable release target; review pending |
|
|
61
|
+
| [urlcode-admin](packages/admin) (in this repository) | Administration: users, sessions, roles, audit, approvals, cases, impersonation | `0.4.1` stable release target; review pending |
|
|
62
|
+
|
|
63
|
+
This release aligns all four packages at `0.4.1`, with publication to npm's
|
|
64
|
+
`latest` channel in the order core → ui → auth → admin. The table describes
|
|
65
|
+
the release target; confirm publication with `npm run release:status` before
|
|
66
|
+
installing it. Matching numbers do not establish a permanent fixed-version
|
|
67
|
+
policy. Peer ranges, channel rules and generated application pins are documented
|
|
68
|
+
in [package and channel alignment](docs/VERSION-ALIGNMENT.md).
|
|
66
69
|
|
|
67
70
|
`urlcode-dynamic-link` and `urlcode-short` were published once as
|
|
68
71
|
`0.1.0-alpha.1` and have since been retired: both were unpublished from npm and
|
|
@@ -103,8 +106,9 @@ it. Cross-repository acceptance is tracked in
|
|
|
103
106
|
|
|
104
107
|
## Status
|
|
105
108
|
|
|
106
|
-
`0.4.
|
|
107
|
-
|
|
109
|
+
The `0.4.1` release line brings core, UI, auth and admin to matching stable
|
|
110
|
+
versions. A stable version selects the npm `latest` channel; it does not close
|
|
111
|
+
the review and deployment evidence gaps below. `0.4.0-alpha.1`
|
|
108
112
|
added the extension contract, capabilities and provider conformance, strict
|
|
109
113
|
redirect interchange, bulk import, recipes and search, TypeScript guest
|
|
110
114
|
authoring, conditions, bounded proxy and signals, and the MCP read and
|
|
@@ -174,7 +178,8 @@ SQLite build requirement, unverified from this repository.
|
|
|
174
178
|
## Try it
|
|
175
179
|
|
|
176
180
|
```sh
|
|
177
|
-
|
|
181
|
+
# After the 0.4.1 release has been published:
|
|
182
|
+
npm install --global @jimhoyd/urlcode@0.4.1
|
|
178
183
|
urlcode init my-urls && cd my-urls
|
|
179
184
|
urlcode dev
|
|
180
185
|
```
|
package/ROADMAP.md
CHANGED
|
@@ -12,7 +12,9 @@ conditions, proxy/signals, trusted Node functions and middleware, and opt-in
|
|
|
12
12
|
`sandbox: true` isolation. Target support differs: use `urlcode capabilities`
|
|
13
13
|
before promising a deployment. Stored short links have no supported package.
|
|
14
14
|
|
|
15
|
-
Auth, admin
|
|
15
|
+
Auth, admin and UI are optional packages, developed in this repository under
|
|
16
|
+
`packages/` and released independently. Middleware is not a package: per-route
|
|
17
|
+
`middleware:` is native to core. Auth/admin already
|
|
16
18
|
render through the shared UI kit when configured. Core includes scaffolding,
|
|
17
19
|
searchable recipes/examples, compact context, schema queries, a semantic manifest,
|
|
18
20
|
and MCP inspection with separately enabled authoring. These are implemented,
|
|
@@ -20,8 +22,13 @@ not future phases. See [the framework](docs/FRAMEWORK.md).
|
|
|
20
22
|
|
|
21
23
|
## Next work
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
Auth, admin and UI are workspace packages here, and all three have been released
|
|
26
|
+
from this repository; that migration is
|
|
27
|
+
[done](docs/OPEN-DECISIONS.md#done-the-monorepo-migration-is-complete) and its
|
|
28
|
+
plan is [archived](docs/archive/2026-09-19/SPIKE-MONOREPO.md). The separate
|
|
29
|
+
middleware package was withdrawn rather than migrated —
|
|
30
|
+
`@jimhoyd/urlcode-middleware` is unpublished and its repository deleted — so
|
|
31
|
+
there is nothing to move in and nothing to fold into core afterward.
|
|
25
32
|
|
|
26
33
|
1. **Make the existing product coherent.** Keep docs, examples, generated LLM
|
|
27
34
|
resources, installed skills and the standalone template consistent with their
|
|
@@ -42,14 +49,12 @@ folding it into core; coordinate the guidance cleanup with that migration.
|
|
|
42
49
|
[Issue 58](https://github.com/jimhoyd-com/urlcode/issues/58) and
|
|
43
50
|
[release readiness](docs/RELEASE-READINESS.md) retain those gates. Live
|
|
44
51
|
Google/Apple/SES checks remain explicitly deferred.
|
|
45
|
-
4. **
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
and per-route Lambda compilation are proposals,
|
|
52
|
-
not available features. Decide scope before implementing them.
|
|
52
|
+
4. **Choose expansion from evidence.** Collections and a business application
|
|
53
|
+
suite are proposals, not available features. Decide scope before implementing
|
|
54
|
+
them. Per-route Lambda compilation is **decided against**: projects using
|
|
55
|
+
`function` or `middleware` deploy as one trusted Node process, and serverless
|
|
56
|
+
targets keep refusing those routes at activation
|
|
57
|
+
([the decision](docs/OPEN-DECISIONS.md#accepted-one-node-deployment-per-project)).
|
|
53
58
|
|
|
54
59
|
## Records and ownership
|
|
55
60
|
|
package/dist/BUILD-MANIFEST.json
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
"dist/agents-guide.js": "0b19909aee3f8a1039c34c43ff58116ec1bc3f1875e3dbc34b3b01655273201c",
|
|
7
7
|
"dist/assets.js": "0833b093d2fe457ce68281110dc07d47914992753607741c13b339d92c76cf5c",
|
|
8
8
|
"dist/authoring-files.js": "ad32814f9ae9c549c6247396f982aa700d13a293cc0a589609eccc68f8965550",
|
|
9
|
-
"dist/authoring.js": "
|
|
9
|
+
"dist/authoring.js": "5fe1e871ff216ef1f7d2f490f74397b81b1de4edfe31cdb6115907296aa1c652",
|
|
10
10
|
"dist/aws.js": "7d8e3a97b4f6dbefbd68da058f05192469d1efcaed5458b284863ebe462e060e",
|
|
11
11
|
"dist/build-cloudflare.js": "af4f49ad45287d7c4105bb84b1592b5b8e76c930f7159505338298f7c5fd5651",
|
|
12
12
|
"dist/build-static.js": "fe01ef4fcc66d83d8be7a787538b55974e84fd3d7c89e26f0a2f1a152762d565",
|
|
13
13
|
"dist/bulk.js": "aac88d422bd9a7a341a421fb4e29ff5580b28d39250b7f1f5315ba20ae92da8a",
|
|
14
14
|
"dist/capabilities.js": "28dba05b3851b8b12b3c1b809af87b732f84d601c737afe9225abe429886e691",
|
|
15
|
-
"dist/capability-query.js": "
|
|
16
|
-
"dist/catalog.js": "
|
|
17
|
-
"dist/cli.js": "
|
|
15
|
+
"dist/capability-query.js": "9f68bc94901451d7b9ecdd3aee8c3376dca7f0b890e31d8205306cf8120dea86",
|
|
16
|
+
"dist/catalog.js": "c378ef9bfd63b940790f6975f7db54557c2459722b65a7c170b97df24184bc52",
|
|
17
|
+
"dist/cli.js": "45b8adaae66a46e7b05067f1ced488b9dd7ccff8d15ba71e858459d76f5ddbb8",
|
|
18
18
|
"dist/client-address.js": "9d0d01466aab23124330605c5a0e0f981f87c897858acfad909f546a63df165b",
|
|
19
19
|
"dist/cloudflare.js": "17b43b0a6b6a0ef8a893415c989e4ae3976d97cc966adbf897b785f9adc472e3",
|
|
20
20
|
"dist/compliance-rules/baseline.js": "6296dea6bcb9f9bb80ed6ed5ab2f456e7f74e6de2be5d2d98ca8c983fb4df3ab",
|
|
@@ -24,14 +24,14 @@
|
|
|
24
24
|
"dist/compliance.js": "29a18da3cefea94802ac7d817d1ab4e3674abd5c3ac4ef893cb8b01c6a23fb61",
|
|
25
25
|
"dist/conditions.js": "ff25e97da550a3bdd80af55522045669438aa31a77d2bc83ad602bcefdee0475",
|
|
26
26
|
"dist/config-worker.js": "bc2170d60c35f8d5de227cbfc4c5067dc61d7cbc5b98aed88e68a11797072da5",
|
|
27
|
-
"dist/config.js": "
|
|
27
|
+
"dist/config.js": "8a0fde242b4658c522031a15d89daed25f8b77761f5c4204f295d5e311ac8ff7",
|
|
28
28
|
"dist/context.js": "549a53e4738b9c20a1ec55614027f758026fb5860bf4ac962ce22d7029e48df0",
|
|
29
29
|
"dist/ecosystem-cli.js": "1e71bacd53d6fa8c7857cfcce3c3012ce054771a434b78d1f39e892e32642edd",
|
|
30
30
|
"dist/egress.js": "2ae29fe2cb4590fd2f715abe6817db47fed2f50dd31f37946ecf42eafbb0dd9e",
|
|
31
31
|
"dist/errors.js": "a908e66496afebbdd632a8c159a6e14d23f8f38798a0b4342ca122f2511c6cde",
|
|
32
32
|
"dist/examples.js": "89ae2b764a688fe52acfb9b94d3d262d7742401ebd0ef10c9ebfc76edfa5631c",
|
|
33
33
|
"dist/explain-cli.js": "f0f8f3b5046430d03c6abb629b38651c9c734b03456040735ce9d7e582b2bcd5",
|
|
34
|
-
"dist/explain.js": "
|
|
34
|
+
"dist/explain.js": "7fb632036cc68280701dd512c74d27d6e0f97b4848e9024b284d7c5393b35eb5",
|
|
35
35
|
"dist/extensions.js": "0506ec6289fa0582399d8c6d27727ffeb629bd442264248c921c63cc5b46ff01",
|
|
36
36
|
"dist/function-sources.js": "16fad4abc81c7ee07b6cbcef2d23a9fc50e97a17cb4dde1db53469bbb555c96f",
|
|
37
37
|
"dist/function-worker.js": "35771790ada4e1b36d447d4967a5e6cf0b543f32944e4c904f5fbf8998e824a5",
|
|
@@ -39,27 +39,28 @@
|
|
|
39
39
|
"dist/guest-api.js": "49aec9ed72733ad119e63bcfcee18158dcc8ceb951e1823b8947f6407ff24afc",
|
|
40
40
|
"dist/header-validation.js": "465181dbb08ff05f52defdd29fda025c0c64589bf319fa87fa6d3ab4b68216d5",
|
|
41
41
|
"dist/http-policy.js": "4cb187642269078ddf3460a3f1813dc57878a616a6f8132ef8f8d3b4b91ebb56",
|
|
42
|
-
"dist/http-response.js": "
|
|
43
|
-
"dist/index.js": "
|
|
44
|
-
"dist/init-with.js": "
|
|
42
|
+
"dist/http-response.js": "62f5da2e08bfe7ecff07464a49aaf910aa55770308a59118be9372390c2df9a1",
|
|
43
|
+
"dist/index.js": "8d7532ae0a31f9439ebb62e7b01a2748aee00f350262ff55d1226121d206c1ed",
|
|
44
|
+
"dist/init-with.js": "bf43b3aa4b421e9a31c5227e6afdf4956aba70d836ffddde96176a772d79e09b",
|
|
45
45
|
"dist/interchange-cli.js": "35bd70ba8077af5c3e39404ff0e5d28707632090a59a90645141252449fd3f04",
|
|
46
46
|
"dist/interchange.js": "26789420af33344d9a08c00fe6b2708aa71994224df527b3075295be2151611e",
|
|
47
47
|
"dist/logging.js": "8ec57fea26983aa071b05fe15d680d6550b03488101163ed9666da674118cf31",
|
|
48
|
-
"dist/manifest.js": "
|
|
48
|
+
"dist/manifest.js": "83eed0d59621039ef364cf6e7b54c5bd47e0d8cf0a5abe7b07344bfc1db56ed8",
|
|
49
49
|
"dist/match.js": "53ebcc2cda529a8d07fb83f8cb69a1446036ffc1641af3c4bd45a55851bded28",
|
|
50
|
-
"dist/mcp-authoring.js": "
|
|
51
|
-
"dist/mcp.js": "
|
|
50
|
+
"dist/mcp-authoring.js": "1183a56c5decb99ecb67a5da7bd91d1b494bc17a2965051aa63c4a7e291462b5",
|
|
51
|
+
"dist/mcp.js": "484c03cdf81f94116ec973feb70754298f8b1d5b3b42561969f8cce26b3c97c2",
|
|
52
52
|
"dist/observability.js": "f4b1ab496f051fe3ef2ed36b2b2e469c63a8940700e2df852e6ff2d9cccdd5ae",
|
|
53
53
|
"dist/operator-host.js": "e3dac9d43a83beb775202b4ab9eeeba7ac63cebfc5dcc669be38d825be587bd0",
|
|
54
54
|
"dist/plugins.js": "a61aa933cd9e1b41020b86fae78aa3a7f45f5313124aa11b50cb57898722385e",
|
|
55
55
|
"dist/policies/agents.js": "c1dbc1e90339f472eeeb2372f7f4637f59a5d210797ea680ac8120a02aa20c10",
|
|
56
|
-
"dist/policies/cache.js": "
|
|
56
|
+
"dist/policies/cache.js": "d08184e50b0ecb7331b29e4145aaefacb23b64ddaf3555d579743762f90c1629",
|
|
57
57
|
"dist/policies/compression.js": "800f3ae3b1a4c1fe58c3d9908364ea9d78f655ff50c7f7589f584378e7f194b0",
|
|
58
58
|
"dist/policies/security.js": "2b3bfdf82140156fea4b162adc28ee43ccd85c402fb200680dbb8d8706417420",
|
|
59
59
|
"dist/policies/throttle.js": "bbb0cb1c24cf70d0a1ac32fc0618efb6c44e5b5d42564842f9c585ff7e501603",
|
|
60
60
|
"dist/policies.js": "3750a7032ec7326aa544f4808683ad9eb91d4f2c5afe79904420cd8a355710a9",
|
|
61
61
|
"dist/policy.js": "33c527008fafe4af18243ba668d069517c20659150f9f1d1a5a1d076e8724f89",
|
|
62
62
|
"dist/prerender.js": "6210999e848ab86c87f265943960ef63716896da7a40e248221a6a30425c56b8",
|
|
63
|
+
"dist/project-dependencies.js": "b43a8f3125f62d38f0e71237b7813a785ef0613a83fcdec3fa8314f33758a534",
|
|
63
64
|
"dist/project-tests.js": "445eaa139d2f1a2534369cfdeef081ef518de03218a467e7c5c2059caf840270",
|
|
64
65
|
"dist/provider-verification.js": "ffcf9f05e8cb5f71ea46a97e279e8a613fdfdf9435ab02bd57af11e2680a9107",
|
|
65
66
|
"dist/proxy.js": "34107e59ac0dbbea090efa3cf5badf3226c5302ba25e8f8ff9b2b5596ed80e50",
|
|
@@ -67,7 +68,7 @@
|
|
|
67
68
|
"dist/recipes.js": "11853f23e63c3c65c5bed03a278cde0349d28c9fa23266c9907a2df318bb5fc3",
|
|
68
69
|
"dist/route-diff.js": "8d71d50d332ffaaed588656bd32aa36763260d1ad87bb62d7ea1c8fb23383969",
|
|
69
70
|
"dist/router.js": "cf831025d5ed6778d1d6a0e541177799794e76766c82084ded81d3e87317ca1e",
|
|
70
|
-
"dist/runtime.js": "
|
|
71
|
+
"dist/runtime.js": "2346d4630d178ee52caa7a94d4289f9b16f4b8ee467070b39fdd05742ed4513f",
|
|
71
72
|
"dist/sandbox.js": "c88c5aa5dd95b4f55f1ba62da7969ad66dbd97c30f72e5b5f2817a2098ac1c65",
|
|
72
73
|
"dist/scaffold.js": "3ef243aceca7ddf2780aa2c8c04ae17edd07682d3a418fd5d65ba00f5c762f14",
|
|
73
74
|
"dist/schema-query.js": "bfd1844acd8d54ac361115191fcfbaf828a67e223b9ec6e9c22f158761af149b",
|
|
@@ -75,7 +76,7 @@
|
|
|
75
76
|
"dist/signals.js": "b55e54efc8fb6703e08f2a1e808ec8e011bda67738db8ceadf1b248f695795be",
|
|
76
77
|
"dist/site.js": "c40a06a6f9e2d6d3bea94727783c115fed4309bb0190787f58434eed64cbde84",
|
|
77
78
|
"dist/tooling.js": "7409db98422325cd4181d073d645ea57debca0e54cb151123474b85f4d4ed7dc",
|
|
78
|
-
"dist/trusted-functions.js": "
|
|
79
|
+
"dist/trusted-functions.js": "f3800c75f45ce90faf85ba6664398ad9031c5a9ac51d5ce42bdcff08a497d48e",
|
|
79
80
|
"dist/types.js": "2fe7664dbb5695ec72114197abde72de4031df9c3b211740eacefa68e9b21844",
|
|
80
81
|
"dist/typescript-authoring.js": "2e98498a15bdbb76f36fbee84afb7c42ca95bbf80a2e6dd4040147f3b1642a29",
|
|
81
82
|
"dist/vercel.js": "a0da20c095d0c7fa8f579ed4dd5951e7a34191f8a72a57c5e782c9eab39f6f08",
|
package/dist/authoring.js
CHANGED
|
@@ -8,9 +8,18 @@ import { renderAgentsGuide, renderMcpConfig, mcpConfigFile } from './agents-guid
|
|
|
8
8
|
import { compileRoutes } from './router.js';
|
|
9
9
|
import { prepareFunctionSnapshot, requestedPermissions } from './policy.js';
|
|
10
10
|
import { assert } from './errors.js';
|
|
11
|
+
import { renderPackageManifest } from './project-dependencies.js';
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
export async function initProject(destination , { manifest } = {}) {
|
|
14
23
|
const target = resolve(destination);
|
|
15
24
|
await mkdir(dirname(target), { recursive: true });
|
|
16
25
|
// Reserve destination before copying; never merge into existing user files.
|
|
@@ -30,6 +39,11 @@ export async function initProject(destination ) {
|
|
|
30
39
|
// .mcp.json registers the read-only server for repository-aware agents; the starter carries the same bytes.
|
|
31
40
|
const mcp = await open(join(target,mcpConfigFile), 'wx', 0o644);
|
|
32
41
|
try { await mcp.writeFile(renderMcpConfig('.')); } finally { await mcp.close(); }
|
|
42
|
+
if (manifest) {
|
|
43
|
+
// Exclusive create: the starter ships no package.json, so this never merges into or overwrites one.
|
|
44
|
+
const pkg = await open(join(target,'package.json'), 'wx', 0o644);
|
|
45
|
+
try { await pkg.writeFile(renderPackageManifest(target, manifest)); } finally { await pkg.close(); }
|
|
46
|
+
}
|
|
33
47
|
} catch (error) { await rm(target, { recursive: true, force: true }); throw error; }
|
|
34
48
|
return target;
|
|
35
49
|
}
|
package/dist/capability-query.js
CHANGED
|
@@ -44,7 +44,6 @@ function usage(base ,files ,name )
|
|
|
44
44
|
}
|
|
45
45
|
return result;
|
|
46
46
|
}
|
|
47
|
-
export function capabilityNameList() {return capabilityNames;}
|
|
48
47
|
/** One catalog entry with its schema fragments and bundled usage. No project, credentials or network are read. */
|
|
49
48
|
export function getCapability(name ) {
|
|
50
49
|
if(typeof name!=='string'||!(capabilityNames ).includes(name))throw new ConfigError('Unknown capability; valid names: '+capabilityNames.join(', '));
|
package/dist/catalog.js
CHANGED
|
@@ -36,7 +36,6 @@ async function validate() {
|
|
|
36
36
|
}
|
|
37
37
|
return validator;
|
|
38
38
|
}
|
|
39
|
-
export const metadataFiles={recipe:'recipe.yaml',example:'example.yaml'} ;
|
|
40
39
|
/** Reads and schema-validates one metadata file; the id must equal the directory name, and file lists stay authoring-safe paths. */
|
|
41
40
|
export async function readMetadata(root ,id ,file ) {
|
|
42
41
|
const text=(await readAuthoringFile(root,file,65536)).toString('utf8');
|
package/dist/cli.js
CHANGED
|
@@ -14,6 +14,7 @@ import { startServer } from './server.js';
|
|
|
14
14
|
import {scaffoldProject} from './scaffold.js';
|
|
15
15
|
import { initProject, addRedirect } from './authoring.js';
|
|
16
16
|
import { initProjectWith, parseWithNames } from './init-with.js';
|
|
17
|
+
import { collectDependencySet, installSteps, parsePin } from './project-dependencies.js';
|
|
17
18
|
import { runProjectTests } from './project-tests.js';
|
|
18
19
|
import { verifyDeployment, failLevels } from './verify-deployment.js';
|
|
19
20
|
|
|
@@ -27,8 +28,11 @@ import { loadComplianceRules, profileNames as complianceProfiles } from './compl
|
|
|
27
28
|
import { parseRouteSnapshot, diffRoutes, renderRouteDiff } from './route-diff.js';
|
|
28
29
|
import { readFile } from 'node:fs/promises';
|
|
29
30
|
|
|
30
|
-
const usage = `URLCode 0.4.
|
|
31
|
-
urlcode init <directory> [--with auth,admin]
|
|
31
|
+
const usage = `URLCode 0.4.1 — local/self-hosted runtime
|
|
32
|
+
urlcode init <directory> [--with ui,auth,admin] [--manifest|--no-manifest] [--pin @scope/pkg=specifier]
|
|
33
|
+
# --with: layered site from installed @jimhoyd/urlcode-<name> packages, with a package.json pinning them exactly; name ui first
|
|
34
|
+
# --manifest: also pin the runtime for a route-only project; --no-manifest: --with without a package.json
|
|
35
|
+
# --pin: record a local path or tarball instead of the registry version; repeatable. No install is ever run for you.
|
|
32
36
|
urlcode scaffold [--project directory] [--dry-run]
|
|
33
37
|
urlcode validate [--project directory] [--local] [--origin https://links.example] # origin: absolute URLs in site.* files
|
|
34
38
|
urlcode dev [--project directory] [--port 3000] [--host 127.0.0.1]
|
|
@@ -80,6 +84,7 @@ const print = (value ) => process.stdout.write(typeof value ===
|
|
|
80
84
|
const options = {
|
|
81
85
|
json:{ type:'boolean' }, yaml:{ type:'boolean' }, report:{type:'string'}, 'accept-provider-differences':{type:'boolean'},
|
|
82
86
|
project:{ type:'string', default:'.' }, 'host-file':{type:'string'}, with:{type:'string'},
|
|
87
|
+
manifest:{type:'boolean'}, 'no-manifest':{type:'boolean'}, pin:{type:'string', multiple:true},
|
|
83
88
|
port:{ type:'string' }, host:{ type:'string', default:'127.0.0.1' },
|
|
84
89
|
'expect-routes':{type:'string'}, requests:{type:'string'}, concurrency:{type:'string'}, seconds:{type:'string'}, 'max-p95-ms':{type:'string'}, warmup:{type:'string'}, target:{type:'string'},
|
|
85
90
|
workers:{type:'string'}, 'function-timeout-ms':{type:'string'}, 'max-response-bytes':{type:'string'}, 'max-body-bytes':{type:'string'},
|
|
@@ -149,6 +154,8 @@ try {
|
|
|
149
154
|
if (command !== 'mcp' && command !== 'context') operatorHost = await loadOperatorHost(values['host-file'], values.project);
|
|
150
155
|
}
|
|
151
156
|
if (values.with !== undefined && command !== 'init') throw new ConfigError('--with is only supported by init');
|
|
157
|
+
if ((values.manifest || values['no-manifest'] || values.pin !== undefined) && command !== 'init') throw new ConfigError('--manifest/--no-manifest/--pin are only supported by init');
|
|
158
|
+
if (values.manifest && values['no-manifest']) throw new ConfigError('Use either --manifest or --no-manifest');
|
|
152
159
|
if (values['allow-authoring'] && command !== 'mcp') throw new ConfigError('--allow-authoring is only supported by mcp');
|
|
153
160
|
const hostOptions = { extensions: operatorHost.extensions, plugins: operatorHost.plugins };
|
|
154
161
|
if ((!['import','recipes','recipe','examples','example','bulk-import'].includes(command) && extra.length) || (!['init','add','import','recipes','recipe','examples','example','bulk-import','explain','capabilities','schema'].includes(command) && arg)) throw new ConfigError('Unexpected positional arguments');
|
|
@@ -266,14 +273,23 @@ try {
|
|
|
266
273
|
const loaded = await loadDocument(values.project);
|
|
267
274
|
print(requestedPermissions(loaded,await prepareFunctionSnapshot(loaded))); break;
|
|
268
275
|
}
|
|
269
|
-
case 'init':
|
|
276
|
+
case 'init': {
|
|
270
277
|
if (!arg) throw new ConfigError('Provide a new project directory');
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
278
|
+
// Pins are opt-in for a route-only project (its runtime may be managed elsewhere) and the default for
|
|
279
|
+
// --with, which has just resolved the very packages the generated site depends on.
|
|
280
|
+
const wanted = values.with === undefined ? values.manifest === true : !values['no-manifest'];
|
|
281
|
+
const pins = new Map((values.pin ?? []).map(parsePin));
|
|
282
|
+
if (pins.size && !wanted) throw new ConfigError('--pin needs a manifest; drop --no-manifest or add --manifest');
|
|
283
|
+
if (values.with === undefined) {
|
|
284
|
+
const set = wanted ? await collectDependencySet([], [], { overrides: pins }) : undefined;
|
|
285
|
+
const created = await initProject(arg, { manifest: set });
|
|
286
|
+
print(set ? { event:'created', dependencies:set.pins, nextSteps:installSteps(created, set) } : { event:'created' });
|
|
287
|
+
break;
|
|
275
288
|
}
|
|
289
|
+
const created = await initProjectWith(arg, parseWithNames(values.with), { manifest: wanted, pins });
|
|
290
|
+
print({ event:'created', ...created, review:`Review ${created.project}/urlcode.yaml and pin its revision explicitly (for example PROJECT_SHA256=${created.projectSha256}); re-review after any project change` });
|
|
276
291
|
break;
|
|
292
|
+
}
|
|
277
293
|
case 'validate': {
|
|
278
294
|
const runtime = await createRuntime(values.project, { ...hostOptions, local:values.local, permissions, origin:values.origin });
|
|
279
295
|
print({ event:'valid', routes:runtime.count, version:runtime.version }); await runtime.close(); break;
|
package/dist/config.js
CHANGED
|
@@ -74,7 +74,7 @@ function modulePath(pattern , kind , file
|
|
|
74
74
|
* a route-level `cache` becomes `policies.cache` (refused alongside a direct `policies.cache`).
|
|
75
75
|
* Everything downstream (routes, audit, the compiled table) sees only the long form.
|
|
76
76
|
*/
|
|
77
|
-
|
|
77
|
+
function normalizeRoute(pattern , route ) {
|
|
78
78
|
const authored = route ;
|
|
79
79
|
const needsFunction = typeof authored.function === 'string';
|
|
80
80
|
const needsMiddleware = authored.middleware?.some(entry => typeof entry === 'string') ?? false;
|
package/dist/explain.js
CHANGED
|
@@ -94,7 +94,7 @@ function targetsOf(loaded ,route ,options
|
|
|
94
94
|
}
|
|
95
95
|
return result;
|
|
96
96
|
}
|
|
97
|
-
|
|
97
|
+
function routeState(route ,now ) {return route.enabled===false?'disabled':route.expiresAt&&now>=route.expiresAt?'expired':'active';}
|
|
98
98
|
/** Describe one compiled route. `chain` is the policy chain compiled for it, when the project declares policies. */
|
|
99
99
|
export function explainCompiledRoute(loaded ,route ,chain ,options ={}) {
|
|
100
100
|
const root=loaded.root,declared=loaded.routes[route.pattern];
|
package/dist/http-response.js
CHANGED
|
@@ -16,7 +16,7 @@ import { HttpError } from './errors.js';
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
// Hop-by-hop and runtime-owned headers a handler must never set on the wire.
|
|
19
|
-
|
|
19
|
+
const forbiddenHeaders = new Set(['connection','keep-alive','transfer-encoding','content-length','upgrade','trailer','proxy-authenticate','proxy-authorization','te']);
|
|
20
20
|
|
|
21
21
|
// One place decides what a URLCode response *is*, independent of how a host
|
|
22
22
|
// delivers it. A Node server writes it to a socket; a Lambda returns it as
|
package/dist/index.js
CHANGED
package/dist/init-with.js
CHANGED
|
@@ -8,13 +8,21 @@ import { mcpConfigFile, renderMcpConfig } from './agents-guide.js';
|
|
|
8
8
|
import { loadDocument, parseYaml, validateDocument } from './config.js';
|
|
9
9
|
import { inspectExtensionRevision } from './extensions.js';
|
|
10
10
|
|
|
11
|
+
import { collectDependencySet, installSteps, renderPackageManifest } from './project-dependencies.js';
|
|
12
|
+
|
|
11
13
|
import { ConfigError, assert } from './errors.js';
|
|
12
14
|
|
|
13
15
|
/** Directory names inside the generated site. The route project lives under `app/`; everything else is operator-owned. */
|
|
14
|
-
|
|
16
|
+
const PROJECT_DIRECTORY = 'app', HOST_FILE = 'host.mjs', ROUTES_FILE = 'routes/extensions.yaml';
|
|
15
17
|
const namePattern = /^[a-z][a-z0-9-]{0,63}$/;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
18
26
|
|
|
19
27
|
export function parseWithNames(value ) {
|
|
20
28
|
const names = value.split(',').map(name => name.trim());
|
|
@@ -22,7 +30,7 @@ export function parseWithNames(value ) {
|
|
|
22
30
|
assert(new Set(names).size === names.length, 'Duplicate --with names');
|
|
23
31
|
return names;
|
|
24
32
|
}
|
|
25
|
-
|
|
33
|
+
const packageName = (name ) => `@jimhoyd/urlcode-${name}`;
|
|
26
34
|
const isCode = (error , code ) => error instanceof Error && 'code' in error && error.code === code;
|
|
27
35
|
const strings = (value ) => Array.isArray(value) && value.every(item => typeof item === 'string');
|
|
28
36
|
const record = (value ) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -32,7 +40,7 @@ const record = (value ) => value !== n
|
|
|
32
40
|
* conditions), imports it, and calls its `scaffold` export. Nothing is bundled; core never imports these packages
|
|
33
41
|
* at build time. Refuses a missing package or a package without `scaffold` before anything is written.
|
|
34
42
|
*/
|
|
35
|
-
|
|
43
|
+
async function loadScaffold(name , request , cwd ) {
|
|
36
44
|
const pkg = packageName(name);
|
|
37
45
|
let entry ;
|
|
38
46
|
try { entry = createRequire(join(cwd, 'package.json')).resolve(pkg); }
|
|
@@ -68,7 +76,7 @@ async function write(target , content , mode = 0o644)
|
|
|
68
76
|
const file = await open(target, 'wx', mode);
|
|
69
77
|
try { await file.writeFile(content); await file.sync(); } finally { await file.close(); }
|
|
70
78
|
}
|
|
71
|
-
|
|
79
|
+
function renderHost(names , results ) {
|
|
72
80
|
const lines = [`// Generated by urlcode init --with ${names.join(',')}. Trusted operator code: keep it outside ${PROJECT_DIRECTORY}/ and review before serving.`];
|
|
73
81
|
for (const result of results) lines.push(...result.hostImports);
|
|
74
82
|
lines.push('');
|
|
@@ -85,12 +93,23 @@ function demote(markdown ) {
|
|
|
85
93
|
let fence = false;
|
|
86
94
|
return markdown.split('\n').map(line => { if (/^\s*(?:```|~~~)/.test(line)) fence = !fence; return !fence && /^#{1,5} /.test(line) ? `#${line}` : line; }).join('\n');
|
|
87
95
|
}
|
|
88
|
-
|
|
89
|
-
const
|
|
96
|
+
function renderDependencySection(directory , set ) {
|
|
97
|
+
const rows = set.pins.map(pin => `- \`${pin.name}\` ${pin.version} (${pin.role})${pin.specifier === pin.version ? '' : ` installed from \`${pin.specifier}\``}`);
|
|
98
|
+
const lines = ['## Dependencies', '',
|
|
99
|
+
'`package.json` pins the runtime, every extension named in `--with` and their declared peers to the exact versions that were installed when this site was generated. Those versions were checked against each package\'s own `peerDependencies` as one set.', '',
|
|
100
|
+
...rows, '',
|
|
101
|
+
...installSteps(directory, set).flatMap(step => [step, '']),
|
|
102
|
+
set.local ? 'At least one pin is a local path or tarball rather than a registry version: reproducing this install needs that path to exist, so keep it under your control or replace the specifier before publishing the site.' : 'The pins are registry versions; `npm install` resolves them without the network only if your cache or mirror already holds them.', '',
|
|
103
|
+
'There is no upgrade command. Changing a pinned version today means editing `package.json` yourself and re-running `npm install`; review the extension changelogs first.', ''];
|
|
104
|
+
return lines.join('\n');
|
|
105
|
+
}
|
|
106
|
+
function renderReadme(directory , names , results , starter , env , projectSha256 , set ) {
|
|
107
|
+
const steps = [...(set ? installSteps(directory, set) : []), ...results.flatMap(result => result.nextSteps)];
|
|
90
108
|
const parts = [`# ${basename(directory)}`, '',
|
|
91
109
|
`Created with \`urlcode init ${basename(directory)} --with ${names.join(',')}\`. \`${PROJECT_DIRECTORY}/\` is the route project (\`urlcode.yaml\`, functions, tests); \`${HOST_FILE}\` is the trusted operator host that wires the installed extension packages; operator modules and private data stay outside the project. Run every command with \`--project ${PROJECT_DIRECTORY} --host-file "$PWD/${HOST_FILE}"\`.`, '',
|
|
92
110
|
'## Starter', '', `The starter files live in \`${PROJECT_DIRECTORY}/\`; add \`--project ${PROJECT_DIRECTORY}\` and the host file to the commands below.`, '', demote(starter).trim(), ''];
|
|
93
111
|
for (const result of results) parts.push(`## Extension: ${result.name}`, '', result.readme.trim(), '');
|
|
112
|
+
if (set) parts.push(renderDependencySection(directory, set));
|
|
94
113
|
parts.push('## Next steps', '', ...steps.map((step, index) => `${index + 1}. ${step}`), '');
|
|
95
114
|
if (Object.keys(env).length) parts.push('## Environment', '', ...Object.entries(env).map(([key, text]) => `- \`${key}\`: ${text}`), '');
|
|
96
115
|
parts.push('## Project revision', '', `\`${PROJECT_DIRECTORY}/urlcode.yaml\` currently has revision \`${projectSha256}\` (\`inspectExtensionRevision\`). Review the project, then pin exactly that value where the host expects it; any change to extension YAML, policies or mounts changes it and needs a new explicit review.`, '');
|
|
@@ -102,7 +121,7 @@ export function renderReadme(directory , names , result
|
|
|
102
121
|
* `urlcode.yaml`, one `host.mjs`, one `README.md` and the extensions' own files. All packages are resolved and
|
|
103
122
|
* their scaffolds computed before anything is written, so a refusal leaves no directory behind.
|
|
104
123
|
*/
|
|
105
|
-
export async function initProjectWith(destination , names , { cwd = process.cwd() } = {}) {
|
|
124
|
+
export async function initProjectWith(destination , names , { cwd = process.cwd(), manifest = true, pins } = {}) {
|
|
106
125
|
assert(names.length > 0, 'Provide at least one --with name');
|
|
107
126
|
const directory = resolve(destination), project = join(directory, PROJECT_DIRECTORY), hostFile = join(directory, HOST_FILE);
|
|
108
127
|
const request = { directory, project, hostFile, names };
|
|
@@ -120,6 +139,9 @@ export async function initProjectWith(destination , names
|
|
|
120
139
|
const seen = new Set ();
|
|
121
140
|
for (const file of result.files) { const path = filePath(directory, file.path); assert(!seen.has(path), `${result.name} scaffolds ${file.path} twice`); seen.add(path); }
|
|
122
141
|
}
|
|
142
|
+
// Also resolved before the destination exists: an incompatible or incompletely installed set refuses with
|
|
143
|
+
// nothing written. It runs after the scaffold conflicts so a composition error is still reported as one.
|
|
144
|
+
const dependencies = manifest ? await collectDependencySet(names, names.map(packageName), { cwd, ...(pins === undefined ? {} : { overrides: pins }) }) : undefined;
|
|
123
145
|
await mkdir(dirname(directory), { recursive: true });
|
|
124
146
|
await mkdir(directory, { mode: 0o700 }); // refuses an existing destination
|
|
125
147
|
try {
|
|
@@ -153,13 +175,16 @@ export async function initProjectWith(destination , names
|
|
|
153
175
|
await write(target, file.content, file.mode ?? 0o644); written.add(target);
|
|
154
176
|
}
|
|
155
177
|
await write(hostFile, renderHost(names, results), 0o600);
|
|
156
|
-
await write(join(directory, '
|
|
178
|
+
if (dependencies) await write(join(directory, 'package.json'), renderPackageManifest(directory, dependencies));
|
|
179
|
+
await write(join(directory, 'README.md'), renderReadme(directory, names, results, starter, env, projectSha256, dependencies));
|
|
157
180
|
await write(join(directory, '.gitignore'), 'node_modules/\ndata/\n.env\n.env.*\n');
|
|
158
181
|
// The read-only MCP server for agents opened at the site root; --host-file and --allow-authoring stay operator choices.
|
|
159
182
|
await write(join(directory, mcpConfigFile), renderMcpConfig(PROJECT_DIRECTORY));
|
|
160
183
|
// AGENTS.md: initProject writes the application-level file into app/ once it produces one (NEXT-STEPS 1.1);
|
|
161
184
|
// nothing here overrides it. A site-level agent note would be assembled beside README.md at this point.
|
|
162
|
-
return { directory, project, hostFile, extensions: [...names], projectSha256,
|
|
185
|
+
return { directory, project, hostFile, extensions: [...names], projectSha256,
|
|
186
|
+
nextSteps: [...(dependencies ? installSteps(directory, dependencies) : []), ...results.flatMap(result => result.nextSteps)],
|
|
187
|
+
dependencies: dependencies?.pins ?? [] };
|
|
163
188
|
} catch (error) { await rm(directory, { recursive: true, force: true }); throw error; }
|
|
164
189
|
} finally { wipe(); }
|
|
165
190
|
}
|
package/dist/manifest.js
CHANGED
|
@@ -111,5 +111,5 @@ export async function buildManifest(project ,options ={})
|
|
|
111
111
|
}
|
|
112
112
|
/** The manifest as `build` writes it: two-space JSON with a trailing newline. */
|
|
113
113
|
export function renderManifest(manifest ) {return JSON.stringify(manifest,null,2)+'\n';}
|
|
114
|
-
|
|
114
|
+
const manifestFileName='manifest.json';
|
|
115
115
|
export function manifestPath(out ) {return join(out,manifestFileName);}
|
package/dist/mcp-authoring.js
CHANGED
|
@@ -54,7 +54,7 @@ async function verdict(root ,origin ) {
|
|
|
54
54
|
catch{return {valid:false ,note:'Project does not validate; call run_validate for the CLI report.'};}
|
|
55
55
|
}
|
|
56
56
|
function object(value ) {return value!==null&&typeof value==='object'&&!Array.isArray(value);}
|
|
57
|
-
function expandHandler(
|
|
57
|
+
function expandHandler(handler ) {
|
|
58
58
|
if(object(handler))return handler;
|
|
59
59
|
assert(typeof handler==='string','Handler must be a route object or a short form');
|
|
60
60
|
if(/^https?:\/\//.test(handler))return {redirect:{url:handler}};
|
|
@@ -83,7 +83,7 @@ async function createRoute(root ,args ,origin
|
|
|
83
83
|
assert(file==='urlcode.yaml'||(loaded.document.includes??[]).includes(file),'file must be urlcode.yaml or an include listed in it');
|
|
84
84
|
const target=await confinedPath(root,file);
|
|
85
85
|
assert(!Object.hasOwn(loaded.routes,path),'Route already exists');
|
|
86
|
-
const route={...expandHandler(
|
|
86
|
+
const route={...expandHandler(args.handler)};
|
|
87
87
|
const middleware=expandMiddleware(args.middleware);if(middleware)route.middleware=middleware;
|
|
88
88
|
const lockPath=join(root,'urlcode.yaml.lock'),lock=await open(lockPath,'wx',0o600);
|
|
89
89
|
let temp ;
|
package/dist/mcp.js
CHANGED
|
@@ -78,7 +78,7 @@ export async function serveMcp(options ) {
|
|
|
78
78
|
if(message.method==='initialize') {
|
|
79
79
|
if(initialized){await error(id,-32600,'Already initialized');return;}
|
|
80
80
|
if(typeof params.protocolVersion!=='string'||!object(params.capabilities)||!object(params.clientInfo)||typeof params.clientInfo.name!=='string'||typeof params.clientInfo.version!=='string'){await error(id,-32602,'Invalid initialize params');return;}
|
|
81
|
-
initialized=true;await send({jsonrpc:'2.0',id,result:{protocolVersion,capabilities:{tools:{}},serverInfo:{name:'urlcode',version:'0.4.
|
|
81
|
+
initialized=true;await send({jsonrpc:'2.0',id,result:{protocolVersion,capabilities:{tools:{}},serverInfo:{name:'urlcode',version:'0.4.1'}}});return;
|
|
82
82
|
}
|
|
83
83
|
if(message.method==='ping'){await send({jsonrpc:'2.0',id,result:{}});return;}
|
|
84
84
|
if(!ready){await error(id,-32002,'Initialize first');return;}
|
package/dist/policies/cache.js
CHANGED
|
@@ -198,7 +198,7 @@ function bodyOf(result ) { return result.body ? (Buffer.is
|
|
|
198
198
|
|
|
199
199
|
// Conditional requests for results the handler did not validate itself:
|
|
200
200
|
// assets answer 304 before this phase, so only 200 results are examined.
|
|
201
|
-
function revalidate(
|
|
201
|
+
function revalidate(req , result ) {
|
|
202
202
|
if (result.status !== 200 || (req.method !== 'GET' && req.method !== 'HEAD')) return result;
|
|
203
203
|
let headers = result.headers, etag = header(headers, 'etag');
|
|
204
204
|
if (!etag) {
|
|
@@ -249,7 +249,7 @@ export function onResponse(state , req , result
|
|
|
249
249
|
// refusal produced ahead of the handler keeps its own headers.
|
|
250
250
|
if (flight || state.statuses.has(result.status)) headers = mergeVary(headers, state.vary);
|
|
251
251
|
let out = { ...result, headers };
|
|
252
|
-
if (state.strategy === 'revalidate') out = revalidate(
|
|
252
|
+
if (state.strategy === 'revalidate') out = revalidate(req, out);
|
|
253
253
|
if (!flight) return out;
|
|
254
254
|
// Store decision for the request that reached the handler; waiters are
|
|
255
255
|
// released either way, with the entry or with nothing.
|