@cparkerwebm/webmonterey 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/LICENSE +21 -0
- package/README.md +104 -0
- package/agents/.gitkeep +0 -0
- package/dist/webm.mjs +2381 -0
- package/hooks/.gitkeep +0 -0
- package/package.json +101 -0
- package/schema/design.json +118 -0
- package/skills/launch/SKILL.md +183 -0
- package/skills/new-component/SKILL.md +85 -0
- package/skills/start/SKILL.md +117 -0
- package/skills/traps/SKILL.md +333 -0
- package/skills/upgrade/SKILL.md +42 -0
- package/src/actions/index.ts +348 -0
- package/src/cli/checks.test.ts +711 -0
- package/src/cli/checks.ts +822 -0
- package/src/cli/codemods.test.ts +42 -0
- package/src/cli/codemods.ts +51 -0
- package/src/cli/compare.test.ts +144 -0
- package/src/cli/compare.ts +222 -0
- package/src/cli/design-extract.test.ts +96 -0
- package/src/cli/design-extract.ts +229 -0
- package/src/cli/doctor.ts +187 -0
- package/src/cli/mcp.test.ts +57 -0
- package/src/cli/mcp.ts +110 -0
- package/src/cli/new.ts +148 -0
- package/src/cli/package-root.ts +58 -0
- package/src/cli/scaffold.test.ts +230 -0
- package/src/cli/scaffold.ts +424 -0
- package/src/cli/seed.ts +133 -0
- package/src/cli/slug.test.ts +70 -0
- package/src/cli/slug.ts +109 -0
- package/src/cli/sync.test.ts +137 -0
- package/src/cli/sync.ts +266 -0
- package/src/cli/upgrade.ts +93 -0
- package/src/design/__fixtures__/tokens-v1.4.1.css +227 -0
- package/src/design/brand.ts +49 -0
- package/src/design/compile.test.ts +98 -0
- package/src/design/compile.ts +155 -0
- package/src/design/defaults.ts +315 -0
- package/src/design/index.ts +18 -0
- package/src/design/resolve.test.ts +80 -0
- package/src/design/resolve.ts +108 -0
- package/src/design/types.ts +96 -0
- package/src/emails/autoresponse.test.ts +82 -0
- package/src/emails/autoresponse.ts +83 -0
- package/src/emails/footer.test.ts +102 -0
- package/src/emails/footer.ts +91 -0
- package/src/emails/index.ts +24 -0
- package/src/emails/subject.test.ts +66 -0
- package/src/emails/subject.ts +80 -0
- package/src/emails/submission-notification.ts +76 -0
- package/src/env.d.ts +12 -0
- package/src/includes/cloudflare/d1/client.ts +86 -0
- package/src/includes/cloudflare/r2/README.md +68 -0
- package/src/includes/cloudflare/r2/media.ts +41 -0
- package/src/includes/cloudflare/r2/url.test.ts +44 -0
- package/src/includes/cloudflare/r2/url.ts +34 -0
- package/src/includes/cloudflare/turnstile/Turnstile.astro +161 -0
- package/src/includes/cloudflare/turnstile/verify.ts +157 -0
- package/src/includes/cloudflare/workers/env.ts +56 -0
- package/src/includes/google/tag-manager/TagManager.astro +73 -0
- package/src/includes/sinch/mailgun/redirect.test.ts +96 -0
- package/src/includes/sinch/mailgun/redirect.ts +96 -0
- package/src/includes/sinch/mailgun/send.ts +186 -0
- package/src/includes/webmonterey/animations/animations.css +347 -0
- package/src/includes/webmonterey/animations/observe.test.ts +88 -0
- package/src/includes/webmonterey/animations/observe.ts +209 -0
- package/src/includes/webmonterey/compliance/ConsentInit.astro +74 -0
- package/src/includes/webmonterey/compliance/CookieConsent.astro +558 -0
- package/src/includes/webmonterey/compliance/consent-styles.test.ts +83 -0
- package/src/includes/webmonterey/compliance/consent.ts +142 -0
- package/src/includes/webmonterey/config.test.ts +94 -0
- package/src/includes/webmonterey/config.ts +346 -0
- package/src/includes/webmonterey/copy-defaults.ts +148 -0
- package/src/includes/webmonterey/copy.ts +13 -0
- package/src/includes/webmonterey/credits/Credit.astro +80 -0
- package/src/includes/webmonterey/credits/credit.test.ts +111 -0
- package/src/includes/webmonterey/credits/credit.ts +59 -0
- package/src/includes/webmonterey/forms/honeypot.test.ts +40 -0
- package/src/includes/webmonterey/forms/honeypot.ts +66 -0
- package/src/includes/webmonterey/prose/inline.test.ts +94 -0
- package/src/includes/webmonterey/prose/inline.ts +71 -0
- package/src/includes/webmonterey/scroll-top/ScrollTop.astro +209 -0
- package/src/includes/webmonterey/site.ts +136 -0
- package/src/includes/webmonterey/structured-data/nodes.ts +315 -0
- package/src/includes/webmonterey/zoned-hour.test.ts +49 -0
- package/src/integration/adapter.ts +53 -0
- package/src/integration/app-middleware.ts +43 -0
- package/src/integration/config.ts +96 -0
- package/src/integration/content.ts +66 -0
- package/src/integration/image-size.test.ts +83 -0
- package/src/integration/image-size.ts +100 -0
- package/src/integration/index.ts +386 -0
- package/src/integration/virtual.d.ts +101 -0
- package/src/layouts/base.astro +402 -0
- package/src/package.test.ts +121 -0
- package/src/pages/404.astro +33 -0
- package/src/pages/[...slug].astro +126 -0
- package/src/pages/robots.txt.ts +41 -0
- package/src/pages/webm.astro +23 -0
- package/src/scripts/.gitkeep +0 -0
- package/src/styles/base.css +185 -0
- package/src/styles/global.css +47 -0
- package/src/styles/layers.test.ts +32 -0
- package/src/styles/layers.ts +21 -0
- package/src/styles/layout.css +235 -0
- package/src/styles/reset.css +135 -0
- package/src/styles/utilities.css +90 -0
- package/src/worker.ts +76 -0
- package/template/assets/logo.svg +14 -0
- package/template/assets/open-graph.png +0 -0
- package/template/migrations/0001_create_submissions.sql +40 -0
- package/template/migrations/README.md +41 -0
- package/template/public/android-chrome-192x192.png +0 -0
- package/template/public/android-chrome-512x512.png +0 -0
- package/template/public/apple-touch-icon.png +0 -0
- package/template/public/favicon-16x16.png +0 -0
- package/template/public/favicon-32x32.png +0 -0
- package/template/public/favicon.ico +0 -0
- package/template/public/favicon.svg +4 -0
- package/template/public/open-graph.png +0 -0
- package/template/scripts/check-node.mjs +72 -0
- package/template/scripts/test-hooks.mjs +96 -0
- package/template/site/.editorconfig +22 -0
- package/template/site/.prettierignore +15 -0
- package/template/site/.prettierrc.json +17 -0
- package/template/site/CLAUDE.md +245 -0
- package/template/site/CONTENT.md +135 -0
- package/template/site/PRIVACY-POLICY-DRAFT.md +81 -0
- package/template/site/public/_headers +70 -0
- package/template/site/src/forms/contact.json +45 -0
- package/template/workflows/ci.yml +52 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: traps
|
|
3
|
+
description: Known traps in Astro on Cloudflare Workers that contradict what a model recalls. Load before touching styles or the cascade, a route's prerender flag, wrangler.jsonc, images, D1, Turnstile, or a deploy. Each entry was found on a real client site, not read in docs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Traps
|
|
7
|
+
|
|
8
|
+
Every entry here was found on a live client site. Each contradicts what a model is likely to
|
|
9
|
+
recall, and most produce **no error** — a wrong render, a dead URL, a form that silently rejects.
|
|
10
|
+
|
|
11
|
+
**The through-line for the cascade and router entries: neither `curl` nor `astro dev` can detect
|
|
12
|
+
any of them.** "It works locally" proves nothing about a server-rendered route, an image on that
|
|
13
|
+
route, or how a browser treats a hostname. What does work:
|
|
14
|
+
|
|
15
|
+
- `npm run preview` — a real build on real `workerd`, with the asset router in play
|
|
16
|
+
- `curl -H "Sec-Fetch-Dest: document" -H "Sec-Fetch-Mode: navigate" <url>` — makes curl look like
|
|
17
|
+
a navigation, which is what the asset router branches on
|
|
18
|
+
- **a real browser with the console open**, for anything hostname-scoped. A status-code check
|
|
19
|
+
cannot see a widget that failed to render.
|
|
20
|
+
|
|
21
|
+
Four of these are now `webm doctor` checks. Run it first.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Cascade and CSS
|
|
26
|
+
|
|
27
|
+
**A bare `@layer` order statement does not survive bundling.** It is not attached to any rule, so
|
|
28
|
+
nothing carries it into the output chunk, and layer order falls back to first appearance in the
|
|
29
|
+
bundle. On a real client site `webm.components.core` landed before `webm.reset`, so
|
|
30
|
+
`img { max-inline-size: 100% }` beat a component's `max-inline-size: 11.25rem` and rendered a
|
|
31
|
+
180px logo at **883px**.
|
|
32
|
+
|
|
33
|
+
The trigger is **import order in page frontmatter**. Astro emits CSS chunks in module-graph
|
|
34
|
+
order, so a component imported above the layout puts its `components.core` chunk ahead of the
|
|
35
|
+
`reset` chunk. Reordering two import lines — which no reviewer would flag — is enough.
|
|
36
|
+
|
|
37
|
+
`base.astro` emits the statement as an inline `<style is:inline>` ahead of every stylesheet.
|
|
38
|
+
In this package it comes from `styles/layers.ts`, so the two lists cannot drift.
|
|
39
|
+
|
|
40
|
+
**An empty `@layer` block is dropped by the minifier**, which is why `components.custom` and
|
|
41
|
+
`overrides` are absent from built CSS until a client writes a rule.
|
|
42
|
+
|
|
43
|
+
**`[hidden]` does not hide anything on its own.** The UA stylesheet's `[hidden] { display: none }`
|
|
44
|
+
is in the UA origin, so any author `display` beats it — a grid or flex child simply ignores the
|
|
45
|
+
attribute and `el.hidden = true` appears to do nothing, with no error. `reset.css` sets
|
|
46
|
+
`[hidden] { display: none !important }`. The `!important` is load-bearing: `webm.reset` is the
|
|
47
|
+
first layer, so a plain declaration there loses on layer order alone, and for `!important`
|
|
48
|
+
declarations that order inverts.
|
|
49
|
+
|
|
50
|
+
**Scoped styles are scoped. A class is not reusable across pages.** A class written in one page's
|
|
51
|
+
`<style>` and used in another renders **completely unstyled** — no error, no warning, a bare
|
|
52
|
+
element. This bit one client build five separate times. Shared classes go in `src/styles/` or
|
|
53
|
+
`styles/custom/`.
|
|
54
|
+
|
|
55
|
+
**`reset.css` restores `dialog { margin: auto }`.** The blanket `* { margin: 0 }` otherwise
|
|
56
|
+
overrides the UA stylesheet and every modal pins itself to the top-left corner. This has already
|
|
57
|
+
been deleted once by accident while editing the adjacent rule — the comment survived and the rule
|
|
58
|
+
did not, so the file still read as correct.
|
|
59
|
+
|
|
60
|
+
**Do not add `inlineStylesheets: 'always'`.** CSS attribution to on-demand routes works: a
|
|
61
|
+
`prerender = false` route gets its own chunk containing the full layer stack in the right order.
|
|
62
|
+
The workaround costs ~20KB inlined into every page in exchange for nothing.
|
|
63
|
+
|
|
64
|
+
**Restyling a base class in `webm.overrides` orphans every modifier of it.** Layer order beats
|
|
65
|
+
specificity outright, so a rule on `.webm-consent-btn` in the last layer also beats the package's
|
|
66
|
+
own `.webm-consent-btn--primary` — the modifier's declarations are in an earlier layer and lose
|
|
67
|
+
even though the selector is more specific. The primary button keeps the text color meant for the
|
|
68
|
+
old background and ships as pale-on-pale. **This is the failure mode of the last layer**: it wins
|
|
69
|
+
against the package, and the package's variants are part of what it wins against. Restyle the
|
|
70
|
+
modifier alongside the base, or move the rule into `webm.components.custom`, where it competes on
|
|
71
|
+
specificity like normal CSS.
|
|
72
|
+
|
|
73
|
+
**A custom property whose value contains `var()` resolves where it is DECLARED, not where it is
|
|
74
|
+
used.** `--webm-focus-ring: var(--webm-focus-width) solid var(--webm-focus-color)` on `:root`
|
|
75
|
+
inherits down **already resolved**, so a component setting `--webm-focus-color` on itself changes
|
|
76
|
+
nothing — the ring keeps the site-wide color, and any component on a colored surface is stuck
|
|
77
|
+
with an invisible focus indicator. There is no error and nothing looks wrong in review. Compose
|
|
78
|
+
from the parts at the point of use (`outline: var(--webm-focus-width) solid var(--webm-focus-color)`)
|
|
79
|
+
and the overrides apply. The same trap applies to any composed token — shadows, transitions,
|
|
80
|
+
gradients.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## The asset router and on-demand routes
|
|
85
|
+
|
|
86
|
+
**`not_found_handling: "404-page"` intercepts navigations before the Worker sees them.** A
|
|
87
|
+
navigation request matching no static asset is served `404.html` and never falls through — and
|
|
88
|
+
every `prerender = false` route is such a path.
|
|
89
|
+
|
|
90
|
+
The trigger is `Sec-Fetch-Dest: document`, which browsers send and curl does not. **The same URL
|
|
91
|
+
in the same second returns the real page to curl and the 404 page to Chrome.** A no-JS form POST
|
|
92
|
+
is a navigation too, so it takes out progressive enhancement specifically.
|
|
93
|
+
|
|
94
|
+
Fix: `assets.run_worker_first` in wrangler.jsonc. **Every route given `prerender = false` must be
|
|
95
|
+
listed, including its trailing-slash form** — they are separate paths to the asset router.
|
|
96
|
+
`webm doctor` checks this.
|
|
97
|
+
|
|
98
|
+
**`<Image>` and `getImage` return a dead URL on an on-demand route.** `imageService: 'compile'`
|
|
99
|
+
optimizes at build and ships no runtime image endpoint, but `<Image>` on a `prerender = false`
|
|
100
|
+
route still emits `/_image?href=…`, which is never deployed. It 404s in production and renders
|
|
101
|
+
broken. Invisible locally — `astro dev` serves `/_image` happily.
|
|
102
|
+
|
|
103
|
+
Branch on `Astro.isPrerendered` and fall back to a plain `<img>`. Do **not** switch to
|
|
104
|
+
`passthrough` (kills optimization site-wide) or `cloudflare-binding` (a paid product). `webm
|
|
105
|
+
doctor` checks this.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Hostnames, deploys and platform
|
|
110
|
+
|
|
111
|
+
**A Workers URL is `<worker>.<account-subdomain>.workers.dev`.** Dropping the account subdomain
|
|
112
|
+
produces something that looks plausible and resolves to nothing. Configured into a Turnstile
|
|
113
|
+
widget it yields `TurnstileError: 110200`, and the failure is nearly invisible: the widget never
|
|
114
|
+
renders, never mints a token, every submission is rejected 403, and the form looks normal. The
|
|
115
|
+
only DOM symptom is a missing iframe.
|
|
116
|
+
|
|
117
|
+
**Branch previews are a SIBLING label, not a subdomain** —
|
|
118
|
+
`<branch>-<worker>.<account-subdomain>.workers.dev`. Listing the exact worker hostname in
|
|
119
|
+
Turnstile does not cover them, so the form breaks on precisely the links clients are sent. List
|
|
120
|
+
`<account-subdomain>.workers.dev`; a hostname covers its subdomains. Turnstile does not support
|
|
121
|
+
wildcards, and the free tier allows 10 hostnames per widget.
|
|
122
|
+
|
|
123
|
+
**Chrome's "This site looks fake" warning on preview links comes from the Worker's NAME.** Its
|
|
124
|
+
lookalike-domain check flags "domains that embed other domain names within their own hostname",
|
|
125
|
+
and a Worker called `webm-acme-com` puts `acme-com` into every preview hostname. That is why
|
|
126
|
+
Workers are named `webm-<slug>` with the TLD dropped — `webm-acme` embeds nothing that reads as a
|
|
127
|
+
domain. `webm new` does this; do not name a Worker after the full domain.
|
|
128
|
+
|
|
129
|
+
If a warning still appears on a correctly named Worker it is a URL-shape false positive, not a
|
|
130
|
+
Safe Browsing verdict — **Ignore is safe**. There is no other lever: a custom domain points at a
|
|
131
|
+
_Worker_, not a _version_, so it can only serve production, and per-version preview URLs are
|
|
132
|
+
`workers.dev`-only.
|
|
133
|
+
|
|
134
|
+
**`workers_dev: false` does NOT disable preview URLs.** Separate switches, which is why
|
|
135
|
+
wrangler.jsonc sets both explicitly.
|
|
136
|
+
|
|
137
|
+
**`wrangler deployments list` misleads twice.** `Source: Unknown (deployment)` appears even for
|
|
138
|
+
Workers Builds deployments — it describes the author, not the origin. And the list pages at 10
|
|
139
|
+
entries, so "has the count gone up?" can never become a way of waiting for a deploy.
|
|
140
|
+
|
|
141
|
+
**Verification immediately after a deploy gives false negatives.** Some edges serve the previous
|
|
142
|
+
version for a minute or two. Wait for a known marker before asserting anything.
|
|
143
|
+
|
|
144
|
+
**A brand-new Worker can return `error code: 1042` on valid paths for about a minute.** Retry
|
|
145
|
+
once after ~60s before diagnosing. The documented meaning of 1042 does not apply to a freshly
|
|
146
|
+
deployed static site.
|
|
147
|
+
|
|
148
|
+
**After a repo is connected to Workers Builds, `npx wrangler deploy` from a laptop is wrong.** It
|
|
149
|
+
succeeds, which is the trap: it creates a version no dashboard build produced, so build history no
|
|
150
|
+
longer describes what is live, and the next `git push` reverts it.
|
|
151
|
+
|
|
152
|
+
**Local and remote D1/R2 are separate stores.** Rows and objects never move between them, in
|
|
153
|
+
either direction, and nothing warns you. Code moves on push; schema moves via
|
|
154
|
+
`wrangler d1 migrations apply --remote`; **data does not move at all.**
|
|
155
|
+
|
|
156
|
+
**Astro's dev server daemonizes.** It detaches and the launching command exits, which reads as a
|
|
157
|
+
crash. Manage it with `astro dev status` / `logs` / `stop`.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## TypeScript and APIs
|
|
162
|
+
|
|
163
|
+
**`querySelector<HTMLSelectElement>` does not compile — and it is the only element type that
|
|
164
|
+
does not.** `worker-configuration.d.ts` declares HTMLRewriter's `interface Element`, whose
|
|
165
|
+
`remove()` returns `Element` so calls can chain. Being an interface it _merges_ with `lib.dom`'s
|
|
166
|
+
`Element`, so the global now carries a `remove(): Element` overload, and
|
|
167
|
+
`querySelector<T extends Element>` demands it of `T`. Every element type inherits that — except
|
|
168
|
+
`HTMLSelectElement`, which declares its own `remove(index: number): void`, and an own declaration
|
|
169
|
+
shadows the inherited one.
|
|
170
|
+
|
|
171
|
+
Cast instead: `document.querySelector('#t') as HTMLSelectElement | null`. Do not "fix" it by
|
|
172
|
+
removing `worker-configuration.d.ts` from tsconfig — that is what types the bindings.
|
|
173
|
+
`webm doctor` checks this.
|
|
174
|
+
|
|
175
|
+
**`Astro.locals.runtime` does not exist.** Removed in `@astrojs/cloudflare` v13. Any snippet using
|
|
176
|
+
`Astro.locals.runtime.env.DB` is Pages-era. Use `import { env } from 'cloudflare:workers'`.
|
|
177
|
+
|
|
178
|
+
**Bindings are unavailable on prerendered routes.** Any route touching `env.DB` needs
|
|
179
|
+
`export const prerender = false`.
|
|
180
|
+
|
|
181
|
+
**`import.meta.env` is NOT the workaround for that.** Both `.env` and `.dev.vars` populate it
|
|
182
|
+
server-side at build time, so on a prerendered route the secret is written **into
|
|
183
|
+
`dist/client/*.html`**, which Cloudflare serves publicly. A local `npm run build` before
|
|
184
|
+
`wrangler deploy` is exactly the machine that has `.dev.vars` present. Never
|
|
185
|
+
`JSON.stringify(import.meta.env)` into a page.
|
|
186
|
+
|
|
187
|
+
**`astro/zod` is zod 4.** The discriminated-union option constraint is
|
|
188
|
+
`core.$ZodTypeDiscriminable` — internal, not re-exported. Zod 3's `ZodDiscriminatedUnionOption`
|
|
189
|
+
no longer exists, so any snippet using it is pre-v4. Derive from the function signature instead.
|
|
190
|
+
`astro:schema`, and `z` from `astro:content`, are deprecated.
|
|
191
|
+
|
|
192
|
+
**The collections config is `src/content.config.ts`.** `src/content/config.ts` was removed in v6.
|
|
193
|
+
|
|
194
|
+
**`src/fetch.ts` is reserved in Astro 7.** Do not create it.
|
|
195
|
+
|
|
196
|
+
**Never put a non-ASCII character in a `throw new Error` message.** Astro puts the message into an
|
|
197
|
+
`x-astro-prerender-error` header during prerendering; an em-dash triggers a warning and renders
|
|
198
|
+
mangled. Fine in comments and UI copy — just not there.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Cron Triggers
|
|
203
|
+
|
|
204
|
+
**A `triggers.crons` entry on its own deploys cleanly and never runs anything.**
|
|
205
|
+
|
|
206
|
+
The adapter generates the Worker entrypoint, and that generated file exports `fetch` and nothing
|
|
207
|
+
else. There is no `scheduled` handler in it. But the config half works perfectly: a `triggers`
|
|
208
|
+
block in `wrangler.jsonc` merges straight into the generated `dist/server/wrangler.json` and
|
|
209
|
+
deploys without a warning. Cloudflare then invokes a handler the Worker does not have, on
|
|
210
|
+
schedule, forever, and nothing in the repo says so.
|
|
211
|
+
|
|
212
|
+
Verified rather than assumed — with a cron in `wrangler.jsonc` and no `main`:
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
npx astro build
|
|
216
|
+
grep scheduled dist/server/entry.mjs # nothing
|
|
217
|
+
npx wrangler dev --local --test-scheduled
|
|
218
|
+
curl 'http://127.0.0.1:8787/__scheduled?cron=0+*+*+*+*'
|
|
219
|
+
# -> 404, served by the asset router
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### The fix, which is supported and already in production
|
|
223
|
+
|
|
224
|
+
Point `main` at a **source** entrypoint that re-exports the adapter's own handler. This is the one
|
|
225
|
+
legitimate reason to set that key.
|
|
226
|
+
|
|
227
|
+
```jsonc
|
|
228
|
+
// wrangler.jsonc
|
|
229
|
+
"main": "./src/worker.ts",
|
|
230
|
+
"triggers": { "crons": ["0 * * * *"] }
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
// src/worker.ts
|
|
235
|
+
import { defineWorker } from '@cparkerwebm/webmonterey/worker';
|
|
236
|
+
import { runSweep } from './includes/sweep.ts';
|
|
237
|
+
|
|
238
|
+
export default defineWorker({
|
|
239
|
+
scheduled: (controller, env, ctx) => ctx.waitUntil(runSweep(env)),
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`defineWorker` supplies the adapter's `fetch` and does not let you replace it — every page, action
|
|
244
|
+
and API route arrives through that function, so a site that meant to add a cron and accidentally
|
|
245
|
+
replaced the request path takes itself down while the cron works perfectly. Site-wide request
|
|
246
|
+
logic goes in Astro middleware instead.
|
|
247
|
+
|
|
248
|
+
`webm doctor` fails on a cron with no `main`, on a `main` that names a build artefact, on an
|
|
249
|
+
entrypoint with no `scheduled`, and on one that forgets the adapter handler.
|
|
250
|
+
|
|
251
|
+
**`./dist/_worker.js/index.js` is the value NOT to use.** It is the Pages-era form, it names a
|
|
252
|
+
build artefact rather than a source file, and it fails the build with "main field doesn't point to
|
|
253
|
+
an existing file".
|
|
254
|
+
|
|
255
|
+
**Cron runs in UTC, always** — there is no timezone setting anywhere in Cloudflare. A job that has
|
|
256
|
+
to land at a local hour should fire hourly and let the handler check the clock in the site's own
|
|
257
|
+
zone with `hourNow()`; pinning it to a fixed UTC hour drifts by one twice a year.
|
|
258
|
+
|
|
259
|
+
**Cloudflare Queues: a producer is two lines; a consumer is not supported by the package.** A
|
|
260
|
+
producer is a binding plus `env.QUEUE.send()`. A consumer needs a `queue()` handler on the Worker
|
|
261
|
+
export, and `defineWorker` accepts one — but nothing in the package has exercised it. Spike it on a
|
|
262
|
+
throwaway Worker before a client depends on it.
|
|
263
|
+
|
|
264
|
+
## Build and platform defaults
|
|
265
|
+
|
|
266
|
+
**Static assets build to `dist/client/`, not `dist/`.** Astro's own docs and most tutorials say
|
|
267
|
+
`./dist`.
|
|
268
|
+
|
|
269
|
+
**`npx wrangler deploy` from the repo root is correct — do not add `--config`.** The build writes
|
|
270
|
+
`.wrangler/deploy/config.json`, which redirects Wrangler to the generated
|
|
271
|
+
`dist/client/wrangler.json`. The root file is still the one you edit.
|
|
272
|
+
|
|
273
|
+
**Do not hand-write `public/.assetsignore`.** Adapter v14 generates
|
|
274
|
+
`dist/client/.assetsignore` itself, and the worker is emitted to `dist/_worker.js` — a sibling of
|
|
275
|
+
the assets directory, never inside it.
|
|
276
|
+
|
|
277
|
+
**The adapter binds Cloudflare Images by default.** `imageService` defaults to
|
|
278
|
+
`'cloudflare-binding'`, a separate paid product transforming at request time. The integration sets
|
|
279
|
+
`'compile'`.
|
|
280
|
+
|
|
281
|
+
**An `env.SESSION` KV binding is added automatically** and auto-provisioned on deploy. It backs
|
|
282
|
+
`Astro.session`. Leave it alone.
|
|
283
|
+
|
|
284
|
+
**Turnstile has no official Astro or Workers integration.** `@cloudflare/pages-plugin-turnstile`
|
|
285
|
+
is Pages-only. Verification is a manual POST to
|
|
286
|
+
`https://challenges.cloudflare.com/turnstile/v0/siteverify`.
|
|
287
|
+
|
|
288
|
+
**Astro deleted every `llms.txt` / `llms-full.txt`.** Those URLs 404. The MCP server is the only
|
|
289
|
+
official AI-docs endpoint.
|
|
290
|
+
|
|
291
|
+
**Cloudflare's own Astro framework guide is stale** — it still says "Astro 6 beta" and points at
|
|
292
|
+
`locals` for bindings. Where it disagrees with docs.astro.build on adapter APIs, **docs.astro.build
|
|
293
|
+
wins.** Cloudflare docs remain authoritative for wrangler and the platform.
|
|
294
|
+
|
|
295
|
+
**`git` does not track empty directories.** Every empty folder needs a `.gitkeep`.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Email and DNS
|
|
300
|
+
|
|
301
|
+
**A staging site's email is redirected, and both directions of getting that wrong are silent.**
|
|
302
|
+
`environment` in webmonterey.json decides it: on `staging`, every recipient is rewritten to
|
|
303
|
+
`stagingEmail` and the subject is prefixed `[staging → who-it-was-for]`. Left on `staging` after
|
|
304
|
+
launch, the client's enquiries go to the agency and their own inbox stays empty — the form still
|
|
305
|
+
says thank you. Left on `production` before launch, testing a preview mails the client's real
|
|
306
|
+
contacts for real. `webm doctor` checks both directions against `launched`; `/webm:launch` is
|
|
307
|
+
where it gets flipped.
|
|
308
|
+
|
|
309
|
+
**A staging site with no `stagingEmail` refuses to send at all.** The package carries no default
|
|
310
|
+
inbox, so there is nowhere safe to redirect to. `sendEmail` throws rather than mailing the real
|
|
311
|
+
recipients; `webm doctor` fails the site before anyone submits a form.
|
|
312
|
+
|
|
313
|
+
**Anything on `workers.dev` redirects regardless of what `environment` says.** webmonterey.json is
|
|
314
|
+
committed, so a branch preview of a live site inherits `production` from main — the hostname test
|
|
315
|
+
is what stops that preview mailing real people. It matches the `workers.dev` label specifically,
|
|
316
|
+
not "any hostname that is not the canonical domain": `domain` is stored bare, so the latter would
|
|
317
|
+
divert every enquiry the day a site answers on `www.`.
|
|
318
|
+
|
|
319
|
+
**A scheduled handler has no request, so it has no hostname.** Only `environment` protects the
|
|
320
|
+
nightly sweep — the check that reads a hostname cannot see a cron at all. This is why the switch
|
|
321
|
+
is config rather than something derived from the URL.
|
|
322
|
+
|
|
323
|
+
**Check the client's existing DMARC before adding a sending subdomain.** A DMARC record on
|
|
324
|
+
`example.com` applies to its subdomains by default. If the client publishes `p=reject` and DKIM on
|
|
325
|
+
the new subdomain is not right, **every message vanishes** — no bounce, no error, nothing in the
|
|
326
|
+
logs.
|
|
327
|
+
|
|
328
|
+
**R2 has no object versioning.** Its equivalent is **bucket locks**, which prevent deletion and
|
|
329
|
+
overwriting for a retention period. Any snippet enabling "R2 versioning" is wrong — the command
|
|
330
|
+
does not exist.
|
|
331
|
+
|
|
332
|
+
**`rclone sync` is not a backup.** It makes the destination match the source, so a file deleted at
|
|
333
|
+
the source disappears from the backup within 24 hours. Use `rclone copy`.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upgrade
|
|
3
|
+
description: Move a WebMonterey site to a newer version of the framework package. Use for "upgrade the package", "update webmonterey", "there's a new version", "bump the framework".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Upgrade
|
|
7
|
+
|
|
8
|
+
## Run the CLI, do not follow steps from memory
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npx webm upgrade
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**This skill is deliberately thin, and that is the point.** An invoked skill's text enters the
|
|
15
|
+
conversation once and is not re-read on later turns. So this skill runs `npm install`, which
|
|
16
|
+
overwrites its own `SKILL.md` with the new version's — and then keeps executing the _old_
|
|
17
|
+
instructions to completion.
|
|
18
|
+
|
|
19
|
+
The binary on disk is the new version immediately. The markdown is frozen for the session. So the
|
|
20
|
+
steps live in the CLI, where they update the moment the install finishes.
|
|
21
|
+
|
|
22
|
+
## What it does
|
|
23
|
+
|
|
24
|
+
1. Branch — never upgrade on `main`
|
|
25
|
+
2. Bump `@cparkerwebm/webmonterey`
|
|
26
|
+
3. Run any codemods the new version ships
|
|
27
|
+
4. `webm sync` — re-materialize the fleet skills
|
|
28
|
+
5. `webm doctor` — the traps that fail silently
|
|
29
|
+
6. `npm run check` and `npm run build`
|
|
30
|
+
|
|
31
|
+
## After it finishes
|
|
32
|
+
|
|
33
|
+
**Run `/reload-plugins`** if the version changed anything outside `skills/`. New and edited
|
|
34
|
+
`SKILL.md` files are picked up live; the plugin's other components are not.
|
|
35
|
+
|
|
36
|
+
Push the branch and check the preview URL before merging. A framework upgrade is exactly the kind
|
|
37
|
+
of change where `preview` catches what `dev` cannot.
|
|
38
|
+
|
|
39
|
+
## If a site should not upgrade
|
|
40
|
+
|
|
41
|
+
**Pinning is a legitimate answer.** A site that is fine can sit on an old major indefinitely; the
|
|
42
|
+
fleet dashboard shows who is where. Do not upgrade a site with no reason to change.
|