@jimhoyd/urlcode 0.4.2 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/urlcode-authoring/SKILL.md +57 -14
- package/.claude/skills/urlcode-operations/SKILL.md +4 -0
- package/README.md +19 -15
- 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 +43 -5
- package/package.json +15 -22
- 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
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
# CI and monorepo release audit — 2026-09-19
|
|
2
|
-
|
|
3
|
-
Status: historical findings and recommendation. The accompanying implementation
|
|
4
|
-
and current commands are documented in [the development pipeline](DEVELOPMENT-PIPELINE.md).
|
|
5
|
-
The proposed helper table below records the audit design, not the installed command list. Source inspected:
|
|
6
|
-
`3a3d9adf6f0ec53faa078c93dfb977aa5ac416f7`; GitHub settings, runs, tags,
|
|
7
|
-
releases and npm dist-tags were read on September 19. Concurrent PRs may change
|
|
8
|
-
this snapshot. This review does not authorize publishing or changing protections.
|
|
9
|
-
|
|
10
|
-
## Recommendation
|
|
11
|
-
|
|
12
|
-
Keep npm workspaces and Changesets, independent package versions, and the
|
|
13
|
-
existing public package names. Add an explicit change classifier for CI and one
|
|
14
|
-
release coordinator for all four packages. Preserve the existing required gate
|
|
15
|
-
and CodeQL policy. Stop using tag pushes as the place to discover whether a
|
|
16
|
-
release is buildable. Prepare and validate an immutable release plan first;
|
|
17
|
-
create tags from that plan once; resume partial publication without moving them.
|
|
18
|
-
|
|
19
|
-
Do this incrementally. A repository layout migration, Nx, Turborepo, remote
|
|
20
|
-
caching, or fewer security assertions is not a prerequisite for faster PRs.
|
|
21
|
-
|
|
22
|
-
## Measured bottleneck
|
|
23
|
-
|
|
24
|
-
[PR #181](https://github.com/jimhoyd-com/urlcode/pull/181) changed only
|
|
25
|
-
`AGENTS.md` (9 additions, 4 deletions). Its
|
|
26
|
-
[verify run](https://github.com/jimhoyd-com/urlcode/actions/runs/35475753912)
|
|
27
|
-
took **9m08s** from creation to completion.
|
|
28
|
-
|
|
29
|
-
| Job | Execution time |
|
|
30
|
-
| --- | ---: |
|
|
31
|
-
| verify, Linux / Node 22 | 8m24s, after 39s from workflow creation to job start |
|
|
32
|
-
| verify, Linux / Node 24 | 5m49s |
|
|
33
|
-
| verify, Linux / Node 26 | 5m36s |
|
|
34
|
-
| build-fidelity | 27s |
|
|
35
|
-
| action | 25s |
|
|
36
|
-
| container | 22s |
|
|
37
|
-
| audit | 19s |
|
|
38
|
-
| verify-complete | 2s |
|
|
39
|
-
|
|
40
|
-
Within Node 22, `npm run verify` took 7m33s. Its core tests took 281.7s,
|
|
41
|
-
auth tests 92.9s, admin tests 23.6s, and UI tests 2.7s. These suites run
|
|
42
|
-
**serially**. Lint, typecheck and source checks took roughly 29s combined.
|
|
43
|
-
Package verification subsequently took 28s; drills took 7s. This is chiefly
|
|
44
|
-
execution time, not queueing, in this example.
|
|
45
|
-
|
|
46
|
-
The same pattern appears in
|
|
47
|
-
[PR #180's run](https://github.com/jimhoyd-com/urlcode/actions/runs/35475563183):
|
|
48
|
-
8m38s overall, with Node 22 the longest job. These are sampled runs, not a
|
|
49
|
-
long-term percentile study or a demonstrated performance gain.
|
|
50
|
-
|
|
51
|
-
Conversely, the
|
|
52
|
-
[UI alpha.6 release](https://github.com/jimhoyd-com/urlcode/actions/runs/35475220590)
|
|
53
|
-
took 19m23s: approximately 11m24s before the job started, then 7m37s in the
|
|
54
|
-
whole-repository verify step. Both queue pressure and unnecessarily broad
|
|
55
|
-
release verification matter there; the API does not establish the queue's cause.
|
|
56
|
-
|
|
57
|
-
The workflow already avoids branch-push plus PR duplication and cancels stale
|
|
58
|
-
PR runs. Its three PR matrix legs repeat lint, types, generated-file checks,
|
|
59
|
-
builds, every package's tests, package smoke tests and drills. Main expands this
|
|
60
|
-
to nine OS/Node combinations. `test:package` builds again after `verify` has
|
|
61
|
-
built; candidate/core release explicitly build before calling `verify`, which
|
|
62
|
-
builds again, and then `test:package`, which builds a third time.
|
|
63
|
-
|
|
64
|
-
## What the checks buy
|
|
65
|
-
|
|
66
|
-
| Check | Purpose | Proposed placement |
|
|
67
|
-
| --- | --- | --- |
|
|
68
|
-
| Documentation and generated resources | Prevent stale references, guidance and authoring resources | Every PR; standalone fast lane for prose changes |
|
|
69
|
-
| Lint and typecheck | Source and contract errors | Once per relevant change on a canonical Node version |
|
|
70
|
-
| Core and extension regressions | Behavioral, integration and isolation invariants | Affected packages and downstream consumers on PRs; full validation for releases |
|
|
71
|
-
| Supported Node versions | Detect runtime compatibility failures | Retain all three versions for runtime changes initially; avoid repeating static checks |
|
|
72
|
-
| Windows/macOS | Path, process, filesystem and platform differences | Targeted PR coverage plus full main coverage initially |
|
|
73
|
-
| Package install smoke | Prove shipped archives and declarations work | Relevant package/CLI/starter changes and releases; lightweight pack inventory for shipped-doc changes |
|
|
74
|
-
| Container | Validate the shipped execution environment | Keep existing required job; early optimization can leave this cheap job alone |
|
|
75
|
-
| Action smoke | Exercise the consumer-facing composite action | Runtime/action/starter/build changes |
|
|
76
|
-
| Build fidelity | Detect nondeterministic emitted files and packs | Build/package/toolchain changes and releases |
|
|
77
|
-
| Runtime dependency audit | Detect known advisories | Keep initially: measured cost is small; also run on a schedule and before release |
|
|
78
|
-
| CodeQL | Static security analysis | Preserve current enforced policy; not the observed critical path |
|
|
79
|
-
| Operational drills | Local lifecycle/recovery regression signal | Relevant runtime changes, main and release validation |
|
|
80
|
-
| Real-model evals | Authoring quality regression | Existing weekly/manual workflow; no need to put it on ordinary PRs |
|
|
81
|
-
|
|
82
|
-
Do not remove timeout, worker replacement, sandbox or authentication tests to
|
|
83
|
-
save time. Profile and change their scheduling or fixtures while preserving the
|
|
84
|
-
behavior they establish. The longest sampled core subtests included explain
|
|
85
|
-
versus runtime agreement (~35s), a slim-image installer test (~21s), and sitemap
|
|
86
|
-
limits (~21s). Investigate repeated CLI startups and repeated project loading;
|
|
87
|
-
these measurements do not yet establish which internal operation dominates.
|
|
88
|
-
Auth's ~93s warrants separate profiling, including password hashing, without
|
|
89
|
-
changing production security parameters to speed up tests.
|
|
90
|
-
|
|
91
|
-
## PR workflow shape
|
|
92
|
-
|
|
93
|
-
Always start the workflow. A small, tested classifier compares the complete PR
|
|
94
|
-
diff against its base, including renamed/deleted paths, and emits an explicit
|
|
95
|
-
job plan. Missing history, unknown paths, classifier errors and workflow/shared
|
|
96
|
-
toolchain changes select full verification. Do not infer safety just from a
|
|
97
|
-
`.md` extension: starter, recipe and executable authoring inputs need their
|
|
98
|
-
own categories. Core changes affect all consumers; UI affects auth/admin; auth
|
|
99
|
-
affects admin; admin-only changes need not retest all of core.
|
|
100
|
-
|
|
101
|
-
For a prose-only PR, run guidance and generated-resource checks, local link and
|
|
102
|
-
reference checks, and applicable package file-inclusion assertions. Extract this
|
|
103
|
-
from today's `check` rather than calling the full source/test syntax walk.
|
|
104
|
-
Changes to generator code, manifests, lockfiles, schemas, executable examples or
|
|
105
|
-
CI configuration must leave the prose-only lane.
|
|
106
|
-
|
|
107
|
-
For code PRs, run static checks once, build required outputs/styles in dependency
|
|
108
|
-
order, and schedule core/UI/auth/admin tests independently. Start by preserving
|
|
109
|
-
the current Node coverage. Pilot two balanced core-test shards on the slowest
|
|
110
|
-
leg, measure runner-minutes and wall time, and increase only if the gains justify
|
|
111
|
-
the extra jobs. Keep the full local `npm run verify` entry point.
|
|
112
|
-
|
|
113
|
-
The `verify-complete` gate must know which jobs the classifier required. It must
|
|
114
|
-
reject failure, cancellation, missing results and unexpected skips; accept a
|
|
115
|
-
skip only when the validated plan explicitly marks that job unnecessary. Test
|
|
116
|
-
the gate's failure paths. The present gate rejects every skipped dependency, so
|
|
117
|
-
adding `if:` conditions without updating its contract will break merging.
|
|
118
|
-
|
|
119
|
-
Do not add workflow-level `paths-ignore` to a required workflow: GitHub documents
|
|
120
|
-
that such skipped workflows can leave required checks pending. Use job selection
|
|
121
|
-
inside an always-triggered workflow instead.
|
|
122
|
-
[GitHub documentation](https://docs.github.com/en/actions/how-tos/manage-workflow-runs/skip-workflow-runs)
|
|
123
|
-
|
|
124
|
-
The live main ruleset requires `container` and `verify-complete`, and separately
|
|
125
|
-
enforces CodeQL findings. It has no bypass actors. Its strict up-to-date setting
|
|
126
|
-
is **false**, contrary to CONTRIBUTING's claim that an up-to-date branch is
|
|
127
|
-
required. Preserve protections in the first implementation; reconcile that
|
|
128
|
-
documentation. A later policy change needs a deliberate review, not an implicit
|
|
129
|
-
side effect of renaming jobs.
|
|
130
|
-
|
|
131
|
-
Windows failures are real: the
|
|
132
|
-
[main run at cc582f2](https://github.com/jimhoyd-com/urlcode/actions/runs/35475923267)
|
|
133
|
-
failed on `spawnSync npm.cmd EINVAL` in UI packaging. PR
|
|
134
|
-
[#182](https://github.com/jimhoyd-com/urlcode/pull/182) already addresses this.
|
|
135
|
-
Linux-only PR tests let this reach main. Add a focused Windows packaging/process
|
|
136
|
-
smoke for relevant changes before considering a smaller main matrix. Keep the
|
|
137
|
-
existing full main matrix during rollout; moving exhaustive coverage to nightly
|
|
138
|
-
is a later tradeoff, and release candidates must still pass full validation.
|
|
139
|
-
|
|
140
|
-
Initial goals: prose PRs under 90 seconds excluding runner queueing; ordinary
|
|
141
|
-
code PRs under 5 minutes. These are targets, not measured promises. Report queue
|
|
142
|
-
time separately from execution, and compare p50/p95 over at least 20 runs per
|
|
143
|
-
change class before deciding the optimization succeeded.
|
|
144
|
-
|
|
145
|
-
## Release and tag findings
|
|
146
|
-
|
|
147
|
-
1. **Tags have actually moved across source revisions.** Release history for
|
|
148
|
-
`v0.4.0-alpha.1` records five distinct commits: `b5cd619`, `871dd87`,
|
|
149
|
-
`bb0f9e6`, `84e45ea`, then successful `8dabc7e`. The sampled history also
|
|
150
|
-
shows multiple commits for `v0.3.0`, `v0.2.0`, and `v0.1.0`. This establishes
|
|
151
|
-
reuse of tag names across commits, not who changed them or whether every
|
|
152
|
-
failed attempt published an artifact. The live ruleset inventory contained
|
|
153
|
-
only a branch ruleset, not tag protection.
|
|
154
|
-
2. **An ancestor of main is not necessarily a validated release commit.** All
|
|
155
|
-
four release workflows check main ancestry, but do not require the exact
|
|
156
|
-
commit's main CI result. UI alpha.6 published from `8fa7f8b` while that
|
|
157
|
-
commit's main verify run failed on Windows. Release validation was Linux-only.
|
|
158
|
-
3. **Core alphas are classified as normal GitHub releases.** Both alpha.1 and
|
|
159
|
-
alpha.2 had `prerelease: false`; GitHub's latest-release endpoint returned
|
|
160
|
-
alpha.2. Core's release creation omits the prerelease flag. npm correctly
|
|
161
|
-
separates its `alpha` channel; this is a distinct GitHub-channel defect.
|
|
162
|
-
4. **Core container publication always updates `latest`.** `release.yml` tags
|
|
163
|
-
and pushes `latest` even for prereleases when `PUBLISH_CONTAINER` is enabled.
|
|
164
|
-
The code path is confirmed; this audit did not establish whether an alpha
|
|
165
|
-
actually overwrote the live GHCR tag.
|
|
166
|
-
5. **Candidate and release have already diverged.** Candidate reads Dockerfile
|
|
167
|
-
into two shell variables, swallowing `AS build` into the image variable and
|
|
168
|
-
failing the digest regex. The same parse was fixed only in release. Running
|
|
169
|
-
candidate's parser locally against the current Dockerfile reproduced failure.
|
|
170
|
-
The last listed candidate successes predate this snapshot; no new candidate
|
|
171
|
-
was dispatched during this audit. Existing release tests check release's parser,
|
|
172
|
-
not both paths.
|
|
173
|
-
6. **Existence is treated as sufficient for retry.** Publish steps skip an
|
|
174
|
-
existing npm version without checking its integrity against the candidate;
|
|
175
|
-
GitHub release uploads use `--clobber`. The fidelity job tests repeated packing
|
|
176
|
-
in one checkout, not equality to a prior publication. Extension builds use
|
|
177
|
-
floating Node 22 and registry-installed peers, so same-commit reproducibility
|
|
178
|
-
across days is not established. Preserve and compare the original artifact.
|
|
179
|
-
7. **Concurrency is scoped to a tag.** Two different versions of one package can
|
|
180
|
-
publish concurrently; there is no shared release-train order or monotonic
|
|
181
|
-
channel guard. A late old release could update a channel after a newer one.
|
|
182
|
-
8. **Changesets is only partially in charge.** Version 3.0.3 is installed and
|
|
183
|
-
pre-mode is enabled, with independent versions. Core is outside its workspace
|
|
184
|
-
package set. The tag checker checks trigger disjointness, not remote tag SHA,
|
|
185
|
-
registry integrity, missing changesets, peer compatibility or channel state.
|
|
186
|
-
PR [#183](https://github.com/jimhoyd-com/urlcode/pull/183) already tackles a
|
|
187
|
-
Changesets peer-range rewrite and stale channel documentation; build on it.
|
|
188
|
-
9. **Release documentation contains competing snapshots.** VERSION-ALIGNMENT
|
|
189
|
-
still describes old repository pins, and the Changesets README says only UI
|
|
190
|
-
is covered and publishing is not wired up, despite a successful monorepo UI
|
|
191
|
-
release. RELEASE-SECURITY describes candidate/release as sharing a path that
|
|
192
|
-
has demonstrably diverged. Generate the live inventory; retain policy in prose.
|
|
193
|
-
|
|
194
|
-
The actual npm channels at audit time were:
|
|
195
|
-
|
|
196
|
-
| Package | `latest` | `alpha` |
|
|
197
|
-
| --- | --- | --- |
|
|
198
|
-
| core | 0.3.0 | 0.4.0-alpha.2 |
|
|
199
|
-
| ui | 0.1.0-alpha.5 | 0.1.0-alpha.6 |
|
|
200
|
-
| auth | 0.1.0-alpha.3 | 0.1.0-alpha.3 |
|
|
201
|
-
| admin | 0.1.0-alpha.3 | 0.1.0-alpha.3 |
|
|
202
|
-
|
|
203
|
-
Different channel values are not inherently drift. Historical extension `latest`
|
|
204
|
-
values are already alphas; do not silently move them. Define channel intent,
|
|
205
|
-
test the documented install combinations against peer ranges, and report
|
|
206
|
-
deviations. An old core release tag also should not follow current main:
|
|
207
|
-
unreleased development is normal; silently relabeling a published version is not.
|
|
208
|
-
|
|
209
|
-
## One release process for four independently versioned packages
|
|
210
|
-
|
|
211
|
-
Use Changesets for reviewed release intent and changelogs, with `fixed` and
|
|
212
|
-
`linked` remaining empty. Require a changeset or a reviewed no-release reason
|
|
213
|
-
for changes to publishable behavior. A routine feature PR need not manually
|
|
214
|
-
bump versions: an accumulated release PR owns version changes, lockfile updates,
|
|
215
|
-
peer-range changes and changelogs together. This replaces VERSION-ALIGNMENT's
|
|
216
|
-
instruction to bump on every source-changing PR once the new flow is implemented.
|
|
217
|
-
[Changesets configuration](https://github.com/changesets/changesets/blob/main/docs/config-file-options.md)
|
|
218
|
-
|
|
219
|
-
Near term, keep root core where it is and include it explicitly in the common
|
|
220
|
-
release inventory and plan, with its existing version update reviewed in the
|
|
221
|
-
same release PR. Do not pretend `changeset version` covers it. Longer term,
|
|
222
|
-
evaluate moving core to `packages/core` with a private tooling root so Changesets
|
|
223
|
-
can own all four uniformly. That is a separate migration with CLI/container/
|
|
224
|
-
starter/package tests, not part of the urgent CI fix. Preserve existing tag
|
|
225
|
-
formats during this transition; any future new core tag format needs an explicit
|
|
226
|
-
compatibility decision for install.sh, Homebrew, action consumers and old links.
|
|
227
|
-
|
|
228
|
-
Proposed lifecycle:
|
|
229
|
-
|
|
230
|
-
1. A protected release PR produces versions and a plan listing package name,
|
|
231
|
-
directory, version, tag, channel and dependency order. Manifests supply the
|
|
232
|
-
versions; the plan does not become a second hand-maintained version database.
|
|
233
|
-
2. After merge, select and freeze its exact main SHA. Require that SHA's main
|
|
234
|
-
checks and release-specific validation; never substitute the newest passing
|
|
235
|
-
run from another commit. A canceled/missing full check must be run for the
|
|
236
|
-
selected SHA. Run without publication credentials during preparation.
|
|
237
|
-
3. Build once with locked tooling, pack the proposed packages, and install the
|
|
238
|
-
actual tarballs together in a clean consumer project without development
|
|
239
|
-
export conditions. Separately test declared published peer floors. Where a
|
|
240
|
-
new floor belongs to this release, test its candidate tarball before publishing
|
|
241
|
-
and confirm registry resolution after its predecessor publishes.
|
|
242
|
-
4. Sign and retain artifacts and a manifest binding source SHA, package versions,
|
|
243
|
-
dependency versions, lock hash and artifact hashes. Exercise this same prepare
|
|
244
|
-
path for a manual candidate; candidate must not be a second copied implementation.
|
|
245
|
-
5. Create each expected tag once at that SHA. Refuse a remote tag at a different
|
|
246
|
-
SHA. Publish the prepared bytes in dependency order: core/UI before auth, auth
|
|
247
|
-
before admin, including only packages that need a release. Serialize publication
|
|
248
|
-
across the release train. Keep write/OIDC permissions confined to publication.
|
|
249
|
-
6. On retry, reconcile each artifact with npm integrity, tags, GitHub assets and
|
|
250
|
-
container digests. Identical means complete; a mismatch stops. A registry
|
|
251
|
-
timeout is not proof a version is absent. Never clobber a different asset or
|
|
252
|
-
move a version tag. Reuse retained bytes; if unavailable, require a rebuild
|
|
253
|
-
that matches recorded hashes. A source fix needs a new version and release PR.
|
|
254
|
-
7. Mark prereleases consistently on GitHub/npm/GHCR, and update mutable channels
|
|
255
|
-
only according to explicit policy with a guard against regressions. Summarize
|
|
256
|
-
partial success clearly; separate registries cannot form an atomic transaction.
|
|
257
|
-
|
|
258
|
-
Protect release tag namespaces against update/deletion, with a narrowly defined
|
|
259
|
-
creation path. This is a proposed strengthening, not a settings change made by
|
|
260
|
-
the audit. Avoid a token-created-tag event chain: coordinate preparation and
|
|
261
|
-
publication explicitly in workflow jobs/dispatch, rather than depending on
|
|
262
|
-
another push workflow being triggered by a workflow-created tag.
|
|
263
|
-
|
|
264
|
-
Initially retain the existing four trusted-publisher workflow filenames as thin
|
|
265
|
-
wrappers around shared checked-in helpers. Consolidating to a new filename is a
|
|
266
|
-
registry trust migration: verify each package's npm publisher identity and direct
|
|
267
|
-
publish permission first. A dry run cannot prove the OIDC exchange works.
|
|
268
|
-
[npm trusted publishing](https://docs.npmjs.com/trusted-publishers/)
|
|
269
|
-
|
|
270
|
-
## Small helper surface
|
|
271
|
-
|
|
272
|
-
These names describe proposed commands, not commands implemented by this audit.
|
|
273
|
-
|
|
274
|
-
| Command | Contract |
|
|
275
|
-
| --- | --- |
|
|
276
|
-
| `npm run ci:plan -- --base SHA --head SHA` | Print change class, affected package closure, required jobs and why; deterministic and fail closed |
|
|
277
|
-
| `npm run ci:report -- --run ID` | Read GitHub job/step timings, separate queue and execution, show failed checks and run links |
|
|
278
|
-
| `npm run release:status` | Read manifests, Changesets state, Git tags, GitHub releases, npm versions/channels/integrity and peer compatibility; no mutations |
|
|
279
|
-
| `npm run release:plan -- --sha SHA` | Produce the exact package/version/tag/channel/order plan, including root core |
|
|
280
|
-
| `npm run release:prepare -- --plan FILE` | Validate, build, pack, smoke-test and write immutable artifact manifest; no publication |
|
|
281
|
-
| `npm run release:publish -- --plan FILE` | CI-only mutation path; validate commit/checks/tags/artifacts, publish or resume idempotently |
|
|
282
|
-
|
|
283
|
-
Use structured JSON outputs plus a short human summary. Share the version/tag/
|
|
284
|
-
channel parser, manifest inventory, Docker image parser and integrity comparison
|
|
285
|
-
between candidate and release. Test those contracts by execution, not only by
|
|
286
|
-
regex checks that particular shell snippets exist in YAML. Add workflow syntax
|
|
287
|
-
validation and fixtures for wrong tag, wrong SHA, mismatched version, prerelease,
|
|
288
|
-
partial publish, existing unequal artifact, registry outage and peer-floor failure.
|
|
289
|
-
|
|
290
|
-
## Rollout order and acceptance
|
|
291
|
-
|
|
292
|
-
1. **Release correctness:** shared candidate parser, correct GitHub/GHCR alpha
|
|
293
|
-
handling, read-only status/preflight, immutable retry checks and exact-SHA
|
|
294
|
-
main-CI gate. Coordinate with #183; preserve the current publisher identities.
|
|
295
|
-
2. **Immediate PR speed:** prose lane and tested classifier/gate. Keep existing
|
|
296
|
-
required check names, audit, CodeQL and container. Demonstrate a docs-only PR
|
|
297
|
-
finishing quickly and a deliberately failed required job blocking the gate.
|
|
298
|
-
3. **Code throughput:** split static/core/workspace checks, remove redundant
|
|
299
|
-
builds, profile and shard the slow suites, add targeted Windows coverage.
|
|
300
|
-
Verify equivalent test coverage and measure queue/cost as well as latency.
|
|
301
|
-
4. **Release coordination:** one Changesets release PR and package inventory,
|
|
302
|
-
one preparation path, serialized dependency-aware publication and immutable
|
|
303
|
-
reconciliation. Prove partial-failure recovery on a future authorized release.
|
|
304
|
-
5. **Optional structural work:** core workspace migration and reconsideration of
|
|
305
|
-
the nine-leg main matrix after measured coverage/performance evidence.
|
|
306
|
-
|
|
307
|
-
This audit made no release, tag, registry-channel or branch-protection changes.
|
|
308
|
-
It inspected live evidence and reproduced the candidate parser failure locally;
|
|
309
|
-
it did not execute a new full matrix, deploy containers, change npm trust, or
|
|
310
|
-
prove end-to-end recovery. Passing CI is not an independent security assessment.
|
|
311
|
-
|
|
312
|
-
## Implementation follow-through
|
|
313
|
-
|
|
314
|
-
The user subsequently authorized implementation. The accompanying PR installs
|
|
315
|
-
the conservative prose lane, separate core/workspace jobs, shared release
|
|
316
|
-
helpers, live inventory and an explicit sequential tag coordinator. Root core
|
|
317
|
-
remains outside Changesets; whole-train preparation before any tag and finer
|
|
318
|
-
package selection are tracked in [#185](https://github.com/jimhoyd-com/urlcode/issues/185).
|
|
319
|
-
The user explicitly approved the immutable-tag rule, now active as
|
|
320
|
-
[23712319](https://github.com/jimhoyd-com/urlcode/rules/23712319), without bypass.
|
|
321
|
-
The two historical core alpha release flags were corrected and GitHub latest
|
|
322
|
-
restored to stable v0.3.0. No tags or artifact bytes were rewritten.
|
package/docs/CI.md
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
# Checking a project on GitHub
|
|
2
|
-
|
|
3
|
-
For this repository's own PR checks and releases, see the
|
|
4
|
-
[development pipeline](DEVELOPMENT-PIPELINE.md). This page describes the action
|
|
5
|
-
used by applications built with URLCode.
|
|
6
|
-
|
|
7
|
-
`jimhoyd-com/urlcode/action` is a composite GitHub Action for a URLCode
|
|
8
|
-
*project*: a repository with a `urlcode.yaml`. It runs the same local checks
|
|
9
|
-
you run by hand and, on pull requests, keeps one comment up to date with the
|
|
10
|
-
route-inventory diff against the base branch. It needs no cloud credentials;
|
|
11
|
-
the only token it touches is the workflow's own `GITHUB_TOKEN`.
|
|
12
|
-
|
|
13
|
-
The starter ships it as `.github/workflows/urlcode.yml` (`urlcode init` copies
|
|
14
|
-
it; the [template repository](https://github.com/jimhoyd-com/urlcode-template)
|
|
15
|
-
carries the same file):
|
|
16
|
-
|
|
17
|
-
```yaml
|
|
18
|
-
name: urlcode
|
|
19
|
-
on:
|
|
20
|
-
push:
|
|
21
|
-
pull_request:
|
|
22
|
-
permissions:
|
|
23
|
-
contents: read
|
|
24
|
-
pull-requests: write # the sticky route-diff comment; drop it to only log the diff
|
|
25
|
-
jobs:
|
|
26
|
-
check:
|
|
27
|
-
runs-on: ubuntu-latest
|
|
28
|
-
steps:
|
|
29
|
-
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
30
|
-
with:
|
|
31
|
-
persist-credentials: false
|
|
32
|
-
- uses: jimhoyd-com/urlcode/action@main # pin a release tag or commit
|
|
33
|
-
with:
|
|
34
|
-
expect-routes: 2
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
The action lives at `action/action.yml` in the runtime repository, so the
|
|
38
|
-
reference is `jimhoyd-com/urlcode/action@<ref>`. Pin `<ref>` the way you pin
|
|
39
|
-
the runtime: a release tag or a commit SHA, not `main`, once the project is
|
|
40
|
-
past its first commit.
|
|
41
|
-
|
|
42
|
-
## What it runs
|
|
43
|
-
|
|
44
|
-
| Step | Command | Fails the job when |
|
|
45
|
-
|---|---|---|
|
|
46
|
-
| Install | `npm ci` in the project (see below) | Dependencies do not install |
|
|
47
|
-
| Validate | `urlcode validate --project <project>` | The YAML, includes, functions or bindings do not load |
|
|
48
|
-
| Test | `urlcode test --project <project>` | A `tests/requests.json` fixture fails |
|
|
49
|
-
| Audit | `urlcode audit --project <project> --expect-routes N --compliance <profile>` | Count mismatch, failed generated check, uncovered active route/method, or a `high` compliance finding without `compliance-warn` |
|
|
50
|
-
| Route diff | `urlcode routes --compare base.json --format markdown` | Never; it reports |
|
|
51
|
-
|
|
52
|
-
Every command is the CLI documented in [readiness](READINESS.md) and
|
|
53
|
-
[compliance](COMPLIANCE.md); the action adds no check of its own. `--origin`
|
|
54
|
-
is passed to validate, test and audit when set. Steps run with `bash`, so the
|
|
55
|
-
action works on the Linux, macOS and Windows runners.
|
|
56
|
-
|
|
57
|
-
The project's runtime comes from the project. With a `package.json` the action
|
|
58
|
-
runs `npm ci` (or `npm install` without a lockfile) and uses the
|
|
59
|
-
`@jimhoyd/urlcode` that resolves from there, hoisted or not. A project without
|
|
60
|
-
`package.json`, such as a fresh `urlcode init`, gets the `runtime` input
|
|
61
|
-
installed into a private prefix under the runner's temp directory:
|
|
62
|
-
`@jimhoyd/urlcode` (the latest release) by default, or a version
|
|
63
|
-
(`@jimhoyd/urlcode@0.3.0`) or an absolute tarball path.
|
|
64
|
-
|
|
65
|
-
## Inputs
|
|
66
|
-
|
|
67
|
-
| Input | Default | Meaning |
|
|
68
|
-
|---|---|---|
|
|
69
|
-
| `project` | `.` | Directory containing `urlcode.yaml`, relative to the workspace |
|
|
70
|
-
| `node-version` | `26` | Passed to `actions/setup-node` |
|
|
71
|
-
| `runtime` | `@jimhoyd/urlcode` | npm spec installed when the project has no `package.json` |
|
|
72
|
-
| `expect-routes` | empty | `audit --expect-routes N`; empty skips the count check |
|
|
73
|
-
| `compliance` | `baseline` | `baseline`, `strict`, `privacy` or `none` |
|
|
74
|
-
| `compliance-rules` | empty | Absolute path to an operator rules module outside the project |
|
|
75
|
-
| `compliance-warn` | `false` | Report findings without failing (`--compliance-warn`) |
|
|
76
|
-
| `origin` | empty | Public origin of the deployment under review |
|
|
77
|
-
| `route-diff` | `true` | Post the route diff on pull requests |
|
|
78
|
-
|
|
79
|
-
Output `route-diff` is the path of the Markdown diff on a pull request, empty
|
|
80
|
-
otherwise, for a later step that wants to upload or reuse it.
|
|
81
|
-
|
|
82
|
-
## The sticky comment
|
|
83
|
-
|
|
84
|
-
On `pull_request` events the action fetches the base commit, checks it out into
|
|
85
|
-
a temporary git worktree, runs `urlcode routes` there and on the head with the
|
|
86
|
-
head's runtime, and renders `routes --compare` as Markdown: one table each for
|
|
87
|
-
added, removed and changed routes, or "No route changes". A changed route lists
|
|
88
|
-
each differing field (handler, methods, state, `sandbox`, `sandboxReason`,
|
|
89
|
-
middleware count, policies, generated marker or policy description) with its
|
|
90
|
-
before and after value.
|
|
91
|
-
|
|
92
|
-
`action/comment.mjs` then finds the pull request's comments for the marker
|
|
93
|
-
`<!-- urlcode-route-diff project="<project>" -->` and updates that comment,
|
|
94
|
-
or creates it on the first run. The key is the project directory, so a
|
|
95
|
-
repository with several projects gets one comment per project and never a
|
|
96
|
-
pile of stale ones. Updating comments needs `pull-requests: write`; on a
|
|
97
|
-
fork's pull request or without that permission the API answers 403 or 404 and
|
|
98
|
-
the step logs a notice and exits 0, leaving the diff in the job log. The same
|
|
99
|
-
happens when the base commit is not reachable or its YAML does not load with
|
|
100
|
-
the head runtime. The diff is generic: only the action knows about GitHub.
|
|
101
|
-
|
|
102
|
-
## Custom compliance rules
|
|
103
|
-
|
|
104
|
-
Write a rules module as [compliance](COMPLIANCE.md#writing-custom-rules)
|
|
105
|
-
describes and keep it outside the audited project, because it runs as trusted
|
|
106
|
-
host code. In a repository with the project at the root, put it in a sibling
|
|
107
|
-
directory and pass the absolute path:
|
|
108
|
-
|
|
109
|
-
```yaml
|
|
110
|
-
- uses: jimhoyd-com/urlcode/action@main
|
|
111
|
-
with:
|
|
112
|
-
expect-routes: 25
|
|
113
|
-
compliance: strict
|
|
114
|
-
compliance-rules: ${{ github.workspace }}/ci/rules.mjs
|
|
115
|
-
compliance-warn: true
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
`ci/rules.mjs` is inside the checkout but not inside the project only when
|
|
119
|
-
`project` is a subdirectory; with `project: .` keep the rules in a second
|
|
120
|
-
checkout or under `${{ runner.temp }}`. `compliance-rules` alone implies
|
|
121
|
-
`compliance: baseline`; `compliance: none` without rules skips the compliance
|
|
122
|
-
section entirely.
|
|
123
|
-
|
|
124
|
-
## Exit codes
|
|
125
|
-
|
|
126
|
-
The job fails when any of validate, test or audit exits nonzero; the
|
|
127
|
-
[audit exit codes](COMPLIANCE.md#exit-codes) apply unchanged. The route diff
|
|
128
|
-
and the comment never fail the job. A failing install (missing `@jimhoyd/urlcode`
|
|
129
|
-
dependency, unavailable `runtime` spec) fails the job before any check runs.
|
|
130
|
-
|
|
131
|
-
## The same checks locally
|
|
132
|
-
|
|
133
|
-
```sh
|
|
134
|
-
urlcode validate --project .
|
|
135
|
-
urlcode test --project .
|
|
136
|
-
urlcode audit --project . --expect-routes 2 --compliance baseline
|
|
137
|
-
git stash && urlcode routes --project . > /tmp/base.json && git stash pop
|
|
138
|
-
urlcode routes --project . --compare /tmp/base.json --format markdown
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
Or `make validate`, `make test` and `make audit ARGS='--expect-routes 2'`
|
|
142
|
-
from the starter Makefile. The runtime repository exercises the action on
|
|
143
|
-
full-lane pull requests against `examples/cookbook` (`.github/workflows/ci.yml`,
|
|
144
|
-
job `action`) with the packed tarball as `runtime`, and `test/action.test.ts`
|
|
145
|
-
checks that `action.yml` is a composite action with the inputs above and that
|
|
146
|
-
every third-party action it or the starter workflow uses is pinned to a
|
|
147
|
-
commit.
|
package/docs/CLOUDFLARE.md
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# Deploying to Cloudflare Workers
|
|
2
|
-
|
|
3
|
-
Cloudflare Workers is not a Node host. There is no `worker_threads`, no
|
|
4
|
-
filesystem, and code generation at runtime is forbidden. So this target does not
|
|
5
|
-
adapt the runtime the way [Vercel](VERCEL.md) and [AWS](AWS.md) do — it
|
|
6
|
-
**compiles the project ahead of time** and ships a Worker that reads the result.
|
|
7
|
-
|
|
8
|
-
```sh
|
|
9
|
-
urlcode build --target cloudflare --project . --out dist
|
|
10
|
-
npx wrangler deploy
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
A working project is in [`examples/cloudflare/`](../examples/cloudflare/).
|
|
14
|
-
|
|
15
|
-
## Declarative routes only
|
|
16
|
-
|
|
17
|
-
This target serves **redirects** and **declared responses** (`respond:`), with
|
|
18
|
-
path, query and header parameters, defaults, validation, response headers,
|
|
19
|
-
`enabled` and `expires`. Everything else is refused **at build time**, with the
|
|
20
|
-
route pattern and the reason named:
|
|
21
|
-
|
|
22
|
-
| Handler | Why it is refused |
|
|
23
|
-
| --- | --- |
|
|
24
|
-
| `function`, `middleware` | need the self-hosted Node lifecycle, and worker threads and the QuickJS WASM engine when a route declares `sandbox: true` |
|
|
25
|
-
| `page`, `static`, `download` | need a platform static-asset binding, not an inline copy |
|
|
26
|
-
| `env`, `secrets` | would have to be baked into the artifact |
|
|
27
|
-
|
|
28
|
-
Generated [site conventions](SITE.md) follow the same table: `robots`,
|
|
29
|
-
`sitemap` and `securityTxt` are `respond` routes and compile into the artifact
|
|
30
|
-
(pass `--origin` to `build` for the absolute URLs they contain); `favicon` and
|
|
31
|
-
`llms` are `page` routes and are refused.
|
|
32
|
-
|
|
33
|
-
A build artifact is a file that gets copied, cached and committed by mistake, so
|
|
34
|
-
it never carries a secret. That is why `env` and `secrets` are refused even when
|
|
35
|
-
the value is a literal in the YAML.
|
|
36
|
-
|
|
37
|
-
Refusing at build time rather than at runtime is the point: a project that
|
|
38
|
-
cannot be served fails `urlcode build`, so it never reaches a deployment.
|
|
39
|
-
|
|
40
|
-
## What the build emits
|
|
41
|
-
|
|
42
|
-
`--out` receives three generated files. None of them are edited by hand, and
|
|
43
|
-
`dist/` belongs in `.gitignore`:
|
|
44
|
-
|
|
45
|
-
- `artifact.js` — the compiled routes. **This is an internal build output, not a
|
|
46
|
-
published contract.** Its `format` may change in any release, and the runtime
|
|
47
|
-
refuses a format it does not recognise rather than guessing. Always rebuild
|
|
48
|
-
with the same version of URLCode that the Worker imports.
|
|
49
|
-
- `validators.js` — the parameter schemas, precompiled by Ajv into standalone ES
|
|
50
|
-
modules. The platform forbids runtime code generation, so a validator cannot
|
|
51
|
-
be compiled on the Worker; it has to be compiled by the build.
|
|
52
|
-
- `index.js` — the Worker entry, which is three lines over
|
|
53
|
-
`createFetchHandler` from `@jimhoyd/urlcode/cloudflare`. That import resolves to the
|
|
54
|
-
package's built `dist/cloudflare.js` (and its declarations, for a TypeScript
|
|
55
|
-
Worker); the artifact never depends on the TypeScript sources or on type
|
|
56
|
-
stripping.
|
|
57
|
-
|
|
58
|
-
Ajv's standalone output hardcodes a CommonJS `require` for its runtime helpers
|
|
59
|
-
even in ESM mode, which an ES module cannot evaluate. The build inlines each
|
|
60
|
-
helper from the installed Ajv — the real function, not a copy that could drift —
|
|
61
|
-
and fails if it meets a `require` it does not recognise, rather than emitting a
|
|
62
|
-
Worker that cannot start.
|
|
63
|
-
|
|
64
|
-
`wrangler.toml` needs no `nodejs_compat` flag. The runtime and the generated
|
|
65
|
-
validators use Web standards only.
|
|
66
|
-
|
|
67
|
-
## Portability, and where it stops
|
|
68
|
-
|
|
69
|
-
The Worker shares its route matching, request policy and response policy with
|
|
70
|
-
the self-hosted server: `src/match.ts`, `src/http-policy.ts` and
|
|
71
|
-
`src/http-response.ts` are the same modules, with no Node imports. Two checks
|
|
72
|
-
keep it that way: an ESLint rule forbids `node:` imports in the modules that
|
|
73
|
-
ship to the Worker, and `scripts/check.ts` (part of `npm run verify`) walks the
|
|
74
|
-
import closure of `src/cloudflare.ts` and fails on any `node:` specifier that
|
|
75
|
-
is not an `import type`. `test/cloudflare.test.ts` builds a project, runs the same project on the
|
|
76
|
-
self-hosted server, and asserts both return the same status, body and headers
|
|
77
|
-
(everything but the per-request identifier) — including the example in this
|
|
78
|
-
repository, replayed through the compiled Worker.
|
|
79
|
-
|
|
80
|
-
[Policies](POLICIES.md) follow the same rule: `agents` and `security` are
|
|
81
|
-
compiled into the artifact with project list files embedded as entries,
|
|
82
|
-
`compression` is delegated to the edge, and `throttle` and `cache` are refused
|
|
83
|
-
at build time with the route named. The artifact also carries the project-level
|
|
84
|
-
`security` policy, so the Worker's own 404 and thrown-error responses get the
|
|
85
|
-
same security headers the self-hosted server gives them.
|
|
86
|
-
|
|
87
|
-
Two differences are real and deliberate:
|
|
88
|
-
|
|
89
|
-
- **Duplicate request headers.** The platform joins repeated headers into one
|
|
90
|
-
value before the Worker runs, so per-header counts do not exist. The
|
|
91
|
-
self-hosted server rejects a duplicated scalar header parameter with 400. Here
|
|
92
|
-
that check cannot fire: the parameter sees the joined value (`a, b`) and is
|
|
93
|
-
validated against its schema like any other. A constrained schema still
|
|
94
|
-
rejects it; an unconstrained `type: string` accepts it where the self-hosted
|
|
95
|
-
server would not. Constrain header parameters you care about. The duplicate
|
|
96
|
-
`Content-Type` check on a declared request body is unavailable for the same
|
|
97
|
-
reason; a joined value fails the media-type check instead.
|
|
98
|
-
- **The request target.** The self-hosted server inspects the request line
|
|
99
|
-
verbatim. The Worker only ever sees a parsed `Request`, so the target is
|
|
100
|
-
reconstructed from `URL`, and a malformed target the self-hosted server would
|
|
101
|
-
refuse may have been normalised or rejected by the platform before this code
|
|
102
|
-
runs. Path traversal, control characters, over-long targets and ambiguous `%`
|
|
103
|
-
sequences that do survive are still refused by the shared `parseTarget`.
|
|
104
|
-
|
|
105
|
-
**This has never been deployed to Cloudflare.** Everything above is verified
|
|
106
|
-
against the runtime's own test suite and a local build, not against the
|
|
107
|
-
platform. A first real deployment is the next thing that would change that, and
|
|
108
|
-
until it happens, treat compatibility with a specific `compatibility_date` and
|
|
109
|
-
with Wrangler's bundler as unproven.
|