@getvitops/create 0.8.0-alpha.0 → 0.8.2
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 +306 -5
- package/package.json +2 -2
- package/templates/emdash/.agents/skills/promote/SKILL.md +24 -7
- package/templates/emdash/.claude/hooks/session-start.sh +0 -0
- package/templates/emdash/.github/workflows/backmerge.yml +18 -0
- package/templates/emdash/.github/workflows/refresh-dev.yml +25 -0
- package/templates/emdash/.node-version +1 -0
- package/templates/emdash/AGENTS.md +51 -9
- package/templates/emdash/README.md +10 -4
- package/templates/emdash/SETUP.html +53 -0
- package/templates/emdash/astro.config.mjs +22 -3
- package/templates/emdash/package.json +10 -9
- package/templates/emdash/pnpm-lock.yaml +183 -0
- package/templates/emdash/pnpm-workspace.yaml +1 -1
- package/templates/emdash/public/_headers +10 -0
- package/templates/emdash/scripts/build-dev-wrangler.mjs +6 -0
- package/templates/emdash/scripts/refresh-dev.mjs +182 -0
- package/templates/emdash/seed/seed.json +104 -5
- package/templates/emdash/site.json +2 -2
- package/templates/emdash/src/pages/[...slug].astro +8 -2
- package/templates/emdash/src/pages/index.astro +8 -2
- package/templates/emdash/wrangler.jsonc +4 -0
- package/templates/static/.github/workflows/backmerge.yml +18 -0
- package/templates/static/.node-version +1 -0
- package/templates/static/AGENTS.md +9 -9
- package/templates/static/SETUP.html +53 -0
- package/templates/static/package.json +4 -4
- package/templates/static/pnpm-workspace.yaml +21 -0
- package/templates/static/public/_headers +10 -0
- package/templates/static/site.json +2 -2
|
@@ -36,16 +36,32 @@
|
|
|
36
36
|
"name": "primary",
|
|
37
37
|
"label": "Primary Navigation",
|
|
38
38
|
"items": [
|
|
39
|
-
{
|
|
40
|
-
|
|
39
|
+
{
|
|
40
|
+
"type": "custom",
|
|
41
|
+
"label": "Home",
|
|
42
|
+
"url": "/"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"type": "custom",
|
|
46
|
+
"label": "About",
|
|
47
|
+
"url": "/about"
|
|
48
|
+
}
|
|
41
49
|
]
|
|
42
50
|
},
|
|
43
51
|
{
|
|
44
52
|
"name": "footer",
|
|
45
53
|
"label": "Footer",
|
|
46
54
|
"items": [
|
|
47
|
-
{
|
|
48
|
-
|
|
55
|
+
{
|
|
56
|
+
"type": "custom",
|
|
57
|
+
"label": "Terms & Conditions",
|
|
58
|
+
"url": "/terms"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"type": "custom",
|
|
62
|
+
"label": "Privacy Policy",
|
|
63
|
+
"url": "/privacy"
|
|
64
|
+
}
|
|
49
65
|
]
|
|
50
66
|
}
|
|
51
67
|
],
|
|
@@ -86,6 +102,84 @@
|
|
|
86
102
|
}
|
|
87
103
|
]
|
|
88
104
|
},
|
|
105
|
+
{
|
|
106
|
+
"_type": "vitops.hero",
|
|
107
|
+
"_key": "demo-hero",
|
|
108
|
+
"eyebrow": "Vitops blocks",
|
|
109
|
+
"heading": "Build pages from design-system patterns",
|
|
110
|
+
"body": "Every block below renders a framework pattern, so it follows your tokens and works without JavaScript.",
|
|
111
|
+
"primaryLabel": "Open the admin",
|
|
112
|
+
"primaryHref": "/_emdash/admin",
|
|
113
|
+
"secondaryLabel": "Docs",
|
|
114
|
+
"secondaryHref": "https://docs.vitops.ca",
|
|
115
|
+
"layout": "centered"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"_type": "vitops.cards",
|
|
119
|
+
"_key": "demo-cards",
|
|
120
|
+
"columns": "3",
|
|
121
|
+
"cards": [
|
|
122
|
+
{
|
|
123
|
+
"_key": "c1",
|
|
124
|
+
"title": "Tokens",
|
|
125
|
+
"body": "Colour, type and spacing come from site.json.",
|
|
126
|
+
"href": "https://docs.vitops.ca",
|
|
127
|
+
"linkLabel": "Read more"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"_key": "c2",
|
|
131
|
+
"title": "Patterns",
|
|
132
|
+
"body": "Cards, carousels and dialogs share one vocabulary."
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"_key": "c3",
|
|
136
|
+
"title": "Accessible",
|
|
137
|
+
"body": "Semantic HTML first, enhanced when JavaScript loads."
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"_type": "vitops.accordion",
|
|
143
|
+
"_key": "demo-accordion",
|
|
144
|
+
"exclusive": true,
|
|
145
|
+
"items": [
|
|
146
|
+
{
|
|
147
|
+
"_key": "a1",
|
|
148
|
+
"summary": "What is a block?",
|
|
149
|
+
"body": "A reusable section an editor inserts from the slash menu.",
|
|
150
|
+
"open": true
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"_key": "a2",
|
|
154
|
+
"summary": "Can I add my own?",
|
|
155
|
+
"body": "Yes. Pass blocks.extra to the plugin and map a component for it."
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"_type": "vitops.marquee",
|
|
161
|
+
"_key": "demo-marquee",
|
|
162
|
+
"pauseOnHover": true,
|
|
163
|
+
"reverse": false,
|
|
164
|
+
"items": [
|
|
165
|
+
{
|
|
166
|
+
"_key": "m1",
|
|
167
|
+
"text": "Fast"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"_key": "m2",
|
|
171
|
+
"text": "Accessible"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"_key": "m3",
|
|
175
|
+
"text": "Editable"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"_key": "m4",
|
|
179
|
+
"text": "Yours"
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
},
|
|
89
183
|
{
|
|
90
184
|
"_type": "vitops.banner",
|
|
91
185
|
"_key": "welcome-banner",
|
|
@@ -161,7 +255,12 @@
|
|
|
161
255
|
"style": "h2",
|
|
162
256
|
"markDefs": [],
|
|
163
257
|
"children": [
|
|
164
|
-
{
|
|
258
|
+
{
|
|
259
|
+
"_type": "span",
|
|
260
|
+
"_key": "terms-use-h-1",
|
|
261
|
+
"text": "Use of the site",
|
|
262
|
+
"marks": []
|
|
263
|
+
}
|
|
165
264
|
]
|
|
166
265
|
},
|
|
167
266
|
{
|
|
@@ -17,11 +17,17 @@ const { entry: page } = slug ? await getEmDashEntry('pages', slug) : { entry: nu
|
|
|
17
17
|
if (!page) {
|
|
18
18
|
return Astro.rewrite('/404');
|
|
19
19
|
}
|
|
20
|
+
|
|
21
|
+
// `page.data` is typed `unknown` until `.emdash/types.ts` is generated from a
|
|
22
|
+
// running site (`pnpm dev`, then `emdash types`), so a fresh scaffold would fail
|
|
23
|
+
// `astro check`; the cast keeps it passing both before and after, where the
|
|
24
|
+
// generated type is already `string`.
|
|
25
|
+
const title = page.data.title as string | undefined;
|
|
20
26
|
---
|
|
21
27
|
|
|
22
|
-
<Layout title={
|
|
28
|
+
<Layout title={title}>
|
|
23
29
|
<article class="rhythm">
|
|
24
|
-
<h1>{
|
|
30
|
+
<h1>{title}</h1>
|
|
25
31
|
{page.data.content && <PortableText value={page.data.content} />}
|
|
26
32
|
</article>
|
|
27
33
|
</Layout>
|
|
@@ -14,11 +14,17 @@ import Layout from '../layouts/Layout.astro';
|
|
|
14
14
|
// shows up without a redeploy. Add `getStaticPaths()` + `prerender = true`
|
|
15
15
|
// here only if this site's home page genuinely doesn't need that.
|
|
16
16
|
const { entry: page } = await getEmDashEntry('pages', 'home');
|
|
17
|
+
|
|
18
|
+
// `page.data` is typed `unknown` until `.emdash/types.ts` is generated from a
|
|
19
|
+
// running site (`pnpm dev`, then `emdash types`), so a fresh scaffold would fail
|
|
20
|
+
// `astro check`; the cast keeps it passing both before and after, where the
|
|
21
|
+
// generated type is already `string`.
|
|
22
|
+
const title = page?.data.title as string | undefined;
|
|
17
23
|
---
|
|
18
24
|
|
|
19
|
-
<Layout description={
|
|
25
|
+
<Layout description={title}>
|
|
20
26
|
<section class="rhythm">
|
|
21
|
-
<h1>{
|
|
27
|
+
<h1>{title ?? 'Welcome'}</h1>
|
|
22
28
|
{
|
|
23
29
|
page?.data.content ? (
|
|
24
30
|
<PortableText value={page.data.content} />
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"bucket_name": "my-emdash-site-media",
|
|
31
31
|
},
|
|
32
32
|
],
|
|
33
|
+
// Outbound mail for EmDash's magic-link sign-in, invites and recovery
|
|
34
|
+
// (cloudflareEmail() in astro.config.mjs). The binding does nothing until the
|
|
35
|
+
// sender domain is onboarded: `npx vitops email setup send.<your-domain>`.
|
|
36
|
+
"send_email": [{ "name": "EMAIL" }],
|
|
33
37
|
// Drives scheduled publishing, plugin cron, and maintenance (see
|
|
34
38
|
// src/worker.ts). Without it, content scheduled in the admin never
|
|
35
39
|
// publishes on Cloudflare Workers. Local `astro dev` doesn't need it.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Carry changes made directly on `main` (a hotfix) back into `dev`, so the
|
|
2
|
+
# integration branch doesn't silently drift from what is live. Runs on every
|
|
3
|
+
# push to `main`, including each promotion, and opens a PR only when merging
|
|
4
|
+
# main into dev would actually change dev.
|
|
5
|
+
#
|
|
6
|
+
# Deploys via the vitops toolchain's reusable workflow — see
|
|
7
|
+
# https://github.com/getvitops/design-system/blob/main/.github/workflows/site-backmerge.yml
|
|
8
|
+
# for why the PR is left for a human unless BACKMERGE_TOKEN is set.
|
|
9
|
+
name: Back-merge main → dev
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
branches: [main]
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
backmerge:
|
|
17
|
+
uses: getvitops/design-system/.github/workflows/site-backmerge.yml@v1
|
|
18
|
+
secrets: inherit
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24
|
|
@@ -18,15 +18,15 @@ as Cloudflare Workers static assets (no server runtime, no database).
|
|
|
18
18
|
full generated class-vocabulary reference to this file.
|
|
19
19
|
- **Layout foundations — these are the answer, not a starting point.** Six
|
|
20
20
|
substitutions carry the structure of nearly every page:
|
|
21
|
-
| About to write…
|
|
22
|
-
|
|
|
23
|
-
| a `wrap`/`wrapper`/`container`/`inner` class
|
|
24
|
-
| `max-width` + `margin-inline: auto`
|
|
25
|
-
| `padding-block` on a section
|
|
26
|
-
| margins between headings/paragraphs/lists
|
|
27
|
-
| `display: grid` + `grid-template-columns: repeat(n, …)` | **`subgrid`**
|
|
28
|
-
| `repeat(auto-fit, minmax(…))`
|
|
29
|
-
| `display: flex` + `gap` for a row of controls
|
|
21
|
+
| About to write… | Write instead |
|
|
22
|
+
| ------------------------------------------------------- | --------------- |
|
|
23
|
+
| a `wrap`/`wrapper`/`container`/`inner` class | **`centered`** |
|
|
24
|
+
| `max-width` + `margin-inline: auto` | **`centered`** |
|
|
25
|
+
| `padding-block` on a section | **`region`** |
|
|
26
|
+
| margins between headings/paragraphs/lists | **`rhythm`** |
|
|
27
|
+
| `display: grid` + `grid-template-columns: repeat(n, …)` | **`subgrid`** |
|
|
28
|
+
| `repeat(auto-fit, minmax(…))` | **`grid-auto`** |
|
|
29
|
+
| `display: flex` + `gap` for a row of controls | **`cluster`** |
|
|
30
30
|
- Every page is prerendered by default (`output: 'static'`) — there is no
|
|
31
31
|
server to fall back to, so nothing here can read request-time data
|
|
32
32
|
(cookies, headers, query params) at render time. If a page genuinely needs
|
|
@@ -194,6 +194,59 @@
|
|
|
194
194
|
so.
|
|
195
195
|
</span>
|
|
196
196
|
</label>
|
|
197
|
+
<label class="custom-check">
|
|
198
|
+
<input type="checkbox" data-sop="hosting.content-in-prod" />
|
|
199
|
+
<span class="custom-check__box"
|
|
200
|
+
><span class="custom-check__indicator custom-check__indicator--check"></span
|
|
201
|
+
></span>
|
|
202
|
+
<span>
|
|
203
|
+
<strong>Know where content is edited (EmDash template).</strong> Editors work on the
|
|
204
|
+
<em>production</em> site — drafts, "Publish changes" and preview links are the
|
|
205
|
+
staging step. The dev site holds a disposable copy of production's database; it is
|
|
206
|
+
refreshed from prod and never promoted back. Code, pages and
|
|
207
|
+
<code>site.json</code> still go dev → main.
|
|
208
|
+
</span>
|
|
209
|
+
</label>
|
|
210
|
+
<label class="custom-check">
|
|
211
|
+
<input type="checkbox" data-sop="hosting.refresh-dev" />
|
|
212
|
+
<span class="custom-check__box"
|
|
213
|
+
><span class="custom-check__indicator custom-check__indicator--check"></span
|
|
214
|
+
></span>
|
|
215
|
+
<span>
|
|
216
|
+
EmDash template: run <strong>Refresh dev from prod</strong> (Actions tab) once the
|
|
217
|
+
prod site has content, and again before rehearsing a schema change. It runs weekly
|
|
218
|
+
on its own. To copy media as well, set <code>R2_ACCESS_KEY_ID</code> and
|
|
219
|
+
<code>R2_SECRET_ACCESS_KEY</code> (an R2 API token's S3 credentials) as repo
|
|
220
|
+
secrets; without them only the database is refreshed.
|
|
221
|
+
</span>
|
|
222
|
+
</label>
|
|
223
|
+
<label class="custom-check">
|
|
224
|
+
<input type="checkbox" data-sop="hosting.magic-link" />
|
|
225
|
+
<span class="custom-check__box"
|
|
226
|
+
><span class="custom-check__indicator custom-check__indicator--check"></span
|
|
227
|
+
></span>
|
|
228
|
+
<span>
|
|
229
|
+
EmDash template: onboard the sender domain for sign-in email —
|
|
230
|
+
<code>npx vitops email setup send.<your-domain></code>, then put that address
|
|
231
|
+
in <code>cloudflareEmail({ from })</code> in <code>astro.config.mjs</code> (it ships
|
|
232
|
+
a placeholder). Editors sign in with a magic link ("Sign in with email" on the login
|
|
233
|
+
page) because a passkey only works at the origin it was registered on, so a
|
|
234
|
+
refreshed dev site has no other way in. Check <strong>Settings → Email</strong> and
|
|
235
|
+
send the test email on both prod and dev.
|
|
236
|
+
</span>
|
|
237
|
+
</label>
|
|
238
|
+
<label class="custom-check">
|
|
239
|
+
<input type="checkbox" data-sop="hosting.preview-secret" />
|
|
240
|
+
<span class="custom-check__box"
|
|
241
|
+
><span class="custom-check__indicator custom-check__indicator--check"></span
|
|
242
|
+
></span>
|
|
243
|
+
<span>
|
|
244
|
+
EmDash template: set a different <code>EMDASH_PREVIEW_SECRET</code> on the dev
|
|
245
|
+
worker (<code>wrangler secret put EMDASH_PREVIEW_SECRET</code>). The secret lives in
|
|
246
|
+
the database, so a refresh copies prod's — and prod preview links would then open
|
|
247
|
+
drafts on dev.
|
|
248
|
+
</span>
|
|
249
|
+
</label>
|
|
197
250
|
</div>
|
|
198
251
|
</details>
|
|
199
252
|
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@getvitops/astro": "latest",
|
|
23
|
-
"astro": "^7.
|
|
23
|
+
"astro": "^7.4.4"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@astrojs/check": "^0.9.
|
|
26
|
+
"@astrojs/check": "^0.9.12",
|
|
27
27
|
"@getvitops/cli": "latest",
|
|
28
28
|
"typescript": "^6.0.3",
|
|
29
29
|
"vite": "latest",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"devEngines": {
|
|
34
34
|
"runtime": {
|
|
35
35
|
"name": "node",
|
|
36
|
-
"version": "24
|
|
36
|
+
"version": "24",
|
|
37
37
|
"onFail": "download"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
|
-
"packageManager": "pnpm@
|
|
40
|
+
"packageManager": "pnpm@12.16.1"
|
|
41
41
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Build scripts: allow only what each runtime needs; rest false so
|
|
2
|
+
# strictDepBuilds (pnpm >=10.26 / 11) passes.
|
|
3
|
+
allowBuilds:
|
|
4
|
+
esbuild: true
|
|
5
|
+
workerd: true
|
|
6
|
+
sharp: false
|
|
7
|
+
|
|
8
|
+
# Supply-chain hardening: nothing installs within 3 days of its publish
|
|
9
|
+
# (toolchain exempt), and no non-registry transitive sources.
|
|
10
|
+
minimumReleaseAge: 4320
|
|
11
|
+
minimumReleaseAgeExclude:
|
|
12
|
+
- '@getvitops/*'
|
|
13
|
+
- vite-plus
|
|
14
|
+
- '@voidzero-dev/*'
|
|
15
|
+
- oxlint
|
|
16
|
+
- '@oxlint/*'
|
|
17
|
+
- oxlint-tsgolint
|
|
18
|
+
- '@oxlint-tsgolint/*'
|
|
19
|
+
- oxfmt
|
|
20
|
+
- '@oxfmt/*'
|
|
21
|
+
blockExoticSubdeps: true
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Cloudflare Workers static assets apply this file to every asset response.
|
|
2
|
+
#
|
|
3
|
+
# A baseline that cannot break a page. No Content-Security-Policy on purpose: <Head /> emits
|
|
4
|
+
# inline scripts (the consent stub, the colour-scheme bootstrap), so a strict policy needs
|
|
5
|
+
# per-site nonces or hashes — add one deliberately once you know which third-party tags you load.
|
|
6
|
+
/*
|
|
7
|
+
X-Content-Type-Options: nosniff
|
|
8
|
+
Referrer-Policy: strict-origin-when-cross-origin
|
|
9
|
+
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
|
|
10
|
+
X-Frame-Options: SAMEORIGIN
|