create-astroid 0.3.0 → 0.3.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/index.mjs CHANGED
@@ -34,9 +34,15 @@ import {
34
34
 
35
35
  const TEMPLATE_DIR = join(dirname(fileURLToPath(import.meta.url)), "template");
36
36
 
37
- // Files whose leading `_` is stripped on copy (npm strips real dotfiles from a
38
- // published package, so they ship as `_gitignore` / `_env.example`).
39
- const DOTFILE_RENAMES = { _gitignore: ".gitignore", "_env.example": ".env.example" };
37
+ // Files (and dirs) whose leading `_` is stripped on copy (npm strips real
38
+ // dotfiles from a published package, so they ship as `_gitignore` /
39
+ // `_env.example` / `_github/…`). Applied per entry in `copyTemplate`, so a
40
+ // directory renames too — `_github` becomes `.github` and its contents follow.
41
+ const DOTFILE_RENAMES = {
42
+ _gitignore: ".gitignore",
43
+ "_env.example": ".env.example",
44
+ _github: ".github",
45
+ };
40
46
 
41
47
  // Archetype → default editable home sections. Imported from astroidjs rather
42
48
  // than duplicated here: as a literal in this file it could name a section that
@@ -173,8 +179,23 @@ function astroidConfigSource(config) {
173
179
  ...(config.modules?.length ? [` modules: ${JSON.stringify(config.modules)},`] : []),
174
180
  // Must be emitted: `astroid generate` rebuilds the middleware from THIS
175
181
  // file, so a config that omitted the portal would regenerate a middleware
176
- // with no guard while src/portal-auth.ts sat there unused.
177
- ...(config.portal?.enabled ? [" portal: { enabled: true },"] : []),
182
+ // with no guard while src/portal-auth.ts sat there unused. And the FULL shape
183
+ // (tablePrefix, signUp) — not a bare `{ enabled: true }` — so a regenerate
184
+ // reproduces the SAME unprefixed `user`/`session` seam the 0002_portal_auth
185
+ // migration created, rather than defaulting the prefix back to `portal_`.
186
+ ...(config.portal?.enabled
187
+ ? [
188
+ " portal: {",
189
+ " enabled: true,",
190
+ ...(config.portal.tablePrefix !== undefined
191
+ ? [` tablePrefix: ${JSON.stringify(config.portal.tablePrefix)},`]
192
+ : []),
193
+ ...(config.portal.signUp !== undefined
194
+ ? [` signUp: ${JSON.stringify(config.portal.signUp)},`]
195
+ : []),
196
+ " },",
197
+ ]
198
+ : []),
178
199
  ' deploy: { platform: "cloudflare" },',
179
200
  "});",
180
201
  "",
@@ -243,9 +264,11 @@ async function main() {
243
264
  const archetype = ARCHETYPES.includes(archetypeRaw) ? archetypeRaw : "marketing";
244
265
  const color = flags.color || (await prompt("Brand color (hex)", "#5b4bff"));
245
266
  const host = flags.host && flags.host !== true ? flags.host : undefined;
246
- // Portal + commerce are opt-in and unprompted: each pulls in real
247
- // infrastructure a plain marketing site should not carry.
248
- const portal = flags.portal === true || flags.portal === "true";
267
+ // The customer PORTAL is opt-in via --portal, but a storefront IMPLIES one — a
268
+ // shop has customers who sign in, reorder, and track orders — so enable it there
269
+ // by default. (Commerce below stays opt-in: infra a marketing site shouldn't carry.)
270
+ const portal =
271
+ flags.portal === true || flags.portal === "true" || archetype === "storefront";
249
272
  // The map module is opt-in and pulls real weight (maplibre-gl is ~1 MB), so
250
273
  // it is never on by default.
251
274
  const map = flags.map === true || flags.map === "true";
@@ -284,7 +307,10 @@ async function main() {
284
307
  theme: { name, colors: { brand: color } },
285
308
  sections: ARCHETYPE_SECTIONS[archetype],
286
309
  ...(commerce ? { commerce: { provider: commerce } } : {}),
287
- ...(portal ? { portal: { enabled: true } } : {}),
310
+ // Unprefixed `user`/`session` (customers — email + password), so the studio's
311
+ // `louise_`-prefixed tables and the portal's never collide (mirrors the
312
+ // reference storefront). `signUp: true` because a shop lets customers register.
313
+ ...(portal ? { portal: { enabled: true, tablePrefix: "", signUp: true } } : {}),
288
314
  // ONE array, built from every enabled flag. Two separate `...(x ? {modules}
289
315
  // : {})` spreads would let the later one overwrite the earlier, silently
290
316
  // dropping a module whenever both were passed.
@@ -394,15 +420,17 @@ async function main() {
394
420
  // instance. Must match the `tablePrefix` in src/auth.ts and the `louise_user`
395
421
  // table the generated `editorsRoute` reads.
396
422
  write(dir, "migrations/0001_auth.sql", generateAuthSchemaSql({ tablePrefix: "louise_" }));
397
- // The portal's own auth tables. A SECOND set, prefixed — the two instances
398
- // share one D1 but never a row, so a portal account can't sign into the
399
- // studio and an editor doesn't appear in the portal. Without this migration
400
- // the portal builds fine and fails on the first sign-in.
423
+ // The portal's own auth tables — a SECOND Better Auth instance sharing one D1
424
+ // but never a row, so a portal account can't sign into the studio and an
425
+ // editor doesn't appear in the portal. `customers: true` (email + password)
426
+ // and the config's tablePrefix, so this schema matches the scaffolded
427
+ // portal-auth.ts exactly. Without this migration the portal builds fine and
428
+ // fails on the first sign-in with a missing table.
401
429
  if (config.portal?.enabled) {
402
430
  write(
403
431
  dir,
404
432
  "migrations/0002_portal_auth.sql",
405
- generateAuthSchemaSql({ tablePrefix: "portal_" }),
433
+ generateAuthSchemaSql({ customers: true, tablePrefix: config.portal.tablePrefix ?? "" }),
406
434
  );
407
435
  }
408
436
  authMigrationOk = true;
@@ -419,8 +447,8 @@ async function main() {
419
447
  write(
420
448
  dir,
421
449
  "migrations/0002_portal_auth.sql",
422
- "-- Portal Better Auth tables (prefixed) — generate after install:\n" +
423
- "-- pnpm exec louise gen-auth-schema --table-prefix portal_ --out migrations/0002_portal_auth.sql\n",
450
+ "-- Portal Better Auth tables (customers, unprefixed) — generate after install:\n" +
451
+ "-- pnpm exec louise gen-auth-schema --out migrations/0002_portal_auth.sql\n",
424
452
  );
425
453
  }
426
454
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-astroid",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Scaffold a new Astroid site — an editable, multi-editor Astro app on Cloudflare Workers — in one command.",
5
5
  "keywords": [
6
6
  "astro",
@@ -32,8 +32,8 @@
32
32
  "dependencies": {
33
33
  "@better-auth/passkey": "^1.6.23",
34
34
  "better-auth": "^1.6.23",
35
- "louise-toolkit": "0.17.0",
36
- "astroidjs": "0.3.0"
35
+ "louise-toolkit": "0.18.0",
36
+ "astroidjs": "0.3.2"
37
37
  },
38
38
  "engines": {
39
39
  "node": ">=24.0.0"
@@ -103,6 +103,20 @@ pnpm doctor # validate config, bindings, and generated-file freshness
103
103
  wrangler deploy # or: pnpm astroid deploy
104
104
  ```
105
105
 
106
+ ## Taking payments
107
+
108
+ Storefront sites (`--commerce square`) are scaffolded with a server-authoritative
109
+ payment route at `src/pages/api/checkout.ts` and a `<SquareCard>` input. **Keep
110
+ its sequence** — extend it (shipping, tax, an order row, a receipt), don't replace
111
+ it. It re-prices every line from the D1 catalog mirror (the client's price is a
112
+ _staleness check_, never an input to the charge — accept a `unitPrice` from the
113
+ request body and anyone buys anything for a penny) and derives the Square
114
+ idempotency key from the verified cart **and** the cart id. Hand-rolling
115
+ `createPayment` without a stable, cart-scoped idempotency key is the failure this
116
+ route exists to prevent: a double-clicked Pay button charges twice, and a
117
+ constant (or omitted) key lets two customers' identical carts collide into a
118
+ single charge.
119
+
106
120
  ## Layout
107
121
 
108
122
  | Path | What |
@@ -0,0 +1,54 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ # Cancel superseded runs on the same ref (a force-push mid-run).
9
+ concurrency:
10
+ group: ci-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ build:
15
+ runs-on: ubuntu-latest
16
+ env:
17
+ # corepack pins pnpm from `packageManager` in package.json; in CI it must
18
+ # fetch that version without an interactive prompt.
19
+ COREPACK_ENABLE_DOWNLOAD_PROMPT: 0
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - uses: actions/setup-node@v4
24
+ with:
25
+ node-version: 24
26
+
27
+ # pnpm via corepack, pinned by `packageManager` — the same version you
28
+ # develop with, so `--frozen-lockfile` below always matches the committed
29
+ # pnpm-lock.yaml. (Commit that lockfile; this step relies on it.)
30
+ - name: Enable pnpm (corepack)
31
+ run: corepack enable
32
+
33
+ - name: Install
34
+ run: pnpm install --frozen-lockfile
35
+
36
+ # Validate the config, bindings, and that the generated
37
+ # src/schema.ts · src/worker.ts · src/middleware.ts trio is still in sync
38
+ # with astroid.config.ts. A fast fail before the heavier steps.
39
+ - name: Doctor
40
+ run: pnpm doctor
41
+
42
+ # No-ops on a fresh scaffold; runs automatically once you add a `lint`
43
+ # script (oxlint/biome) — same for `test` (vitest) further down.
44
+ - name: Lint
45
+ run: pnpm run --if-present lint
46
+
47
+ - name: Type-check
48
+ run: pnpm check
49
+
50
+ - name: Test
51
+ run: pnpm run --if-present test
52
+
53
+ - name: Build
54
+ run: pnpm build
@@ -39,5 +39,6 @@
39
39
  },
40
40
  "engines": {
41
41
  "node": ">=24.0.0"
42
- }
42
+ },
43
+ "packageManager": "pnpm@11.13.0"
43
44
  }