@forumone/create-throughline 0.3.0 → 0.4.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/generator.d.ts.map +1 -1
  3. package/dist/generator.js +7 -1
  4. package/dist/generator.js.map +1 -1
  5. package/dist/post-install.d.ts.map +1 -1
  6. package/dist/post-install.js +10 -3
  7. package/dist/post-install.js.map +1 -1
  8. package/dist/templates/base/.env.example +9 -13
  9. package/dist/templates/base/README.md +63 -11
  10. package/dist/templates/base/apps/web/package.json.template +21 -12
  11. package/dist/templates/base/apps/web/scripts/check-migrations.ts +147 -0
  12. package/dist/templates/base/apps/web/scripts/lib/target.ts +95 -0
  13. package/dist/templates/base/apps/web/scripts/write-migration-snapshot.ts +119 -0
  14. package/dist/templates/base/apps/web/src/app/api/inngest/route.ts.template +40 -4
  15. package/dist/templates/base/apps/web/src/lib/database.ts +158 -0
  16. package/dist/templates/base/apps/web/src/payload.config.ts.template +90 -20
  17. package/dist/templates/base/github/scripts/check-generated.sh +44 -0
  18. package/dist/templates/base/github/workflows/ci.yml +131 -0
  19. package/dist/templates/base/package.json.template +6 -1
  20. package/dist/templates/base/scripts/check-audit.ts +117 -0
  21. package/dist/templates/base/scripts/check-single-instance.ts +113 -0
  22. package/dist/templates/base/scripts/check-tested-packages.ts +165 -0
  23. package/dist/templates/base/turbo.json +16 -1
  24. package/dist/templates/with-reference-ds/design-system/package.json.template +3 -3
  25. package/dist/templates/with-reference-ds/design-system/scripts/build-manifest.ts +59 -29
  26. package/dist/templates/with-reference-ds/design-system/scripts/validate.ts +26 -1
  27. package/dist/utils/versions.d.ts +20 -0
  28. package/dist/utils/versions.d.ts.map +1 -0
  29. package/dist/utils/versions.js +59 -0
  30. package/dist/utils/versions.js.map +1 -0
  31. package/dist/versions.json +14 -0
  32. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # @forumone/create-throughline
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8b7d698: A scaffolded project ships a two-tier CI workflow:
8
+
9
+ - `fast` runs on every pull request with no database. It installs from the lockfile, then runs typecheck, lint, the workspace and migration gates, manifest validation, a generated-files check and tests, with a non-blocking audit.
10
+ - `verify` runs on pushes to main. It migrates a fresh Postgres and runs a production build.
11
+
12
+ `check-generated.sh` regenerates `payload-types.ts` and the admin import map and fails on any difference, including an uncommitted file. `apps/web` gains `generate:types` and `generate:importmap`.
13
+
14
+ - 8b7d698: A scaffolded project manages its schema through migrations from the first table on:
15
+
16
+ - **Config.** `payload.config.ts` sets `push: false` and an explicit `migrationDir`. It connects through a resolver that knows the Vercel–Neon integration's variables, keeps a migration off Neon's pooled endpoint, and accepts an optional migration-only credential.
17
+ - **Scripts.** `apps/web` gains `migrate`, `migrate:create`, `migrate:status`, `migrate:snapshot` and `check:migrations`. The check asserts that every migration has its schema snapshot, that no snapshot is orphaned, and that no drop follows a `CASCADE` that has already carried it out.
18
+ - **Write guard.** `scripts/lib/target.ts` gives scripts that write to a database a `requireTarget` guard.
19
+
20
+ - 8b7d698: A scaffolded project ships three workspace gates as root scripts:
21
+
22
+ - `check:instances`: `payload`, `@payloadcms/ui`, `react` and `react-dom` each resolve to exactly one copy
23
+ - `check:tested`: every package that ships code has at least one test
24
+ - `check:audit`: production advisories are checked against an allowlist that carries a reason for each entry and fails when an entry goes stale
25
+
26
+ ### Patch Changes
27
+
28
+ - e9077b3: The reference design system's manifest build and validation, which every scaffolded project vendors, now:
29
+
30
+ - refuse a component directory with no contract, instead of silently leaving it out of the manifest
31
+ - read the directories that hold components from a `CONTRACT_LAYERS` list
32
+ - name the manifest from the project's own `package.json`, instead of claiming to be `@forumone/throughline-reference-ds`
33
+ - treat `antiExamples.empty` and `intent.brevity` as errors
34
+
35
+ - 6800052: The scaffold compiles against the current Throughline releases, and serves its tools over MCP:
36
+
37
+ - `payload.config.ts` no longer imports `createApiKeysCollection`, which core removed.
38
+ - One `createMcpToolCollector()` is passed to every tool-bearing plugin, and `@payloadcms/plugin-mcp` is registered after them, with its key collection narrowed to admins.
39
+ - The Inngest route types the integration registry, so `serve()` can type its function list.
40
+ - `@payloadcms/plugin-form-builder` is a caret range like the rest of `@payloadcms/*`, so it no longer installs a second `@payloadcms/ui`.
41
+ - `.env.example` and the printed next steps drop the per-server MCP keys that no longer exist.
42
+
43
+ ## 0.3.1
44
+
45
+ ### Patch Changes
46
+
47
+ - eec328e: A scaffolded site depends on the current release of every Throughline package. The ranges were typed into the templates as `^0.2.0`, which admits patches only for a 0.x version, so a new site installed the earliest releases. They are now read from the workspace when the scaffolder is built, and baked into `dist/versions.json`.
48
+ - 70223ef: The scaffolded `payload.config.ts` signs approval links with `generateActionToken` and points them at `/api/approvals/action`, the route `approvalsPlugin` serves. It used to build an unsigned query string against `/api/approvals/decision`, which nothing serves, so every button in an approval email 404'd.
49
+
3
50
  ## 0.3.0
4
51
 
5
52
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAgC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAa,MAAM,kBAAkB,CAAA;AAI5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAK3C,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC7F;AAED,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC9B;AAED;;;;;;;;;GASG;AACH,iBAAe,eAAe,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmClF;AAiBD,iBAAS,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAI3C;AAUD,iBAAS,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AA6BD,eAAO,MAAM,SAAS;;;;;;;CAAsE,CAAA"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAgC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAa,MAAM,kBAAkB,CAAA;AAK5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAK3C,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsC7F;AAED,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC9B;AAED;;;;;;;;;GASG;AACH,iBAAe,eAAe,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmClF;AAiBD,iBAAS,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAI3C;AAcD,iBAAS,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AA6BD,eAAO,MAAM,SAAS;;;;;;;CAAsE,CAAA"}
package/dist/generator.js CHANGED
@@ -3,6 +3,7 @@ import { cp, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { renderTemplate } from './utils/templates.js';
6
+ import { loadPackageVersions } from './utils/versions.js';
6
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
8
  const DEFAULT_TEMPLATES_DIR = resolve(__dirname, 'templates');
8
9
  /**
@@ -25,7 +26,8 @@ export async function generate(answers, options = {}) {
25
26
  const designSystemName = answers.packageScope
26
27
  ? `@${answers.packageScope}/design-system`
27
28
  : `${answers.projectName}-design-system`;
28
- const data = { ...answers, designSystemName };
29
+ const versions = await loadPackageVersions(templatesDir);
30
+ const data = { ...answers, designSystemName, ...versions };
29
31
  onProgress('Creating project structure');
30
32
  await mkdir(answers.targetDir, { recursive: true });
31
33
  await renderDirectory({
@@ -115,9 +117,13 @@ function isBinary(filename) {
115
117
  /**
116
118
  * npm strips files literally named `.gitignore` from a published tarball, so
117
119
  * we author them as `gitignore` in templates and restore the dot on output.
120
+ * `.github` goes the same way: a dot-directory in a tarball is at the mercy of
121
+ * whichever ignore rules the publishing npm applies, and the scaffold's CI is
122
+ * not something to lose silently.
118
123
  */
119
124
  const DOTFILE_RENAMES = {
120
125
  gitignore: '.gitignore',
126
+ github: '.github',
121
127
  };
122
128
  function applyDotfileRename(name) {
123
129
  return DOTFILE_RENAMES[name] ?? name;
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"post-install.d.ts","sourceRoot":"","sources":["../src/post-install.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkFrD"}
1
+ {"version":3,"file":"post-install.d.ts","sourceRoot":"","sources":["../src/post-install.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwFrD"}
@@ -28,11 +28,18 @@ export function printNextSteps(answers) {
28
28
  lines.push(` ${pc.dim('openssl rand -base64 48 # PAYLOAD_SECRET')}`);
29
29
  lines.push(` ${pc.dim('openssl rand -base64 48 # APPROVAL_TOKEN_SECRET')}`);
30
30
  lines.push(` ${pc.dim('openssl rand -base64 48 # FORMS_IP_HASH_SECRET')}`);
31
+ // `push: false`: the schema exists only once a migration has created it.
32
+ lines.push(formatStep(step++, 'Create and apply the first migration:'));
33
+ lines.push(` ${pc.dim('pnpm --dir apps/web migrate:create initial')}`);
34
+ lines.push(` ${pc.dim('pnpm --dir apps/web migrate')}`);
35
+ // Committed and checked by CI, so the first push is green only with them.
36
+ lines.push(formatStep(step++, 'Generate the Payload types and import map, and commit them:'));
37
+ lines.push(` ${pc.dim('pnpm --dir apps/web generate:types')}`);
38
+ lines.push(` ${pc.dim('pnpm --dir apps/web generate:importmap')}`);
31
39
  lines.push(formatStep(step++, `${pc.bold('pnpm dev')} to run locally`));
32
40
  lines.push(formatStep(step++, 'Open http://localhost:3000/admin and create your first admin user'));
33
- lines.push(formatStep(step++, 'In the admin, go to API Keys and create one key per MCP server'));
34
- lines.push(formatStep(step++, 'Paste those keys into .env.local'));
35
- lines.push(formatStep(step++, 'Wire Claude (or your MCP client) to your MCP endpoints — see the generated README.md'));
41
+ lines.push(formatStep(step++, 'In the admin, create one key under MCP → Payload MCP API Keys'));
42
+ lines.push(formatStep(step, 'Give that key to Claude (or your MCP client) for /api/mcp — see the generated README.md'));
36
43
  // eslint-disable-next-line no-console
37
44
  console.log();
38
45
  // eslint-disable-next-line no-console
@@ -1 +1 @@
1
- {"version":3,"file":"post-install.js","sourceRoot":"","sources":["../src/post-install.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"post-install.js","sourceRoot":"","sources":["../src/post-install.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,9 @@
1
1
  # --- Required ---
2
2
  DATABASE_URI=postgres://user:pass@host/dbname
3
+ # Optional: a schema-owner credential used only by `pnpm migrate`, so the app
4
+ # itself can run as a role that reads and writes rows and no more. See
5
+ # apps/web/src/lib/database.ts.
6
+ # MIGRATION_DATABASE_URL=
3
7
  PAYLOAD_SECRET=generate-with-openssl-rand-base64-48
4
8
  NEXT_PUBLIC_SERVER_URL=http://localhost:3000
5
9
 
@@ -23,19 +27,11 @@ APPROVAL_TOKEN_SECRET=
23
27
  # Generate: openssl rand -base64 48
24
28
  FORMS_IP_HASH_SECRET=
25
29
 
26
- # --- MCP server keys ---
27
- # Each key is generated in the Payload admin (API Keys collection) after
28
- # first deploy and pasted back here for local dev.
29
- COMPONENT_SERVER_API_KEY=
30
- PUBLISHING_SERVER_API_KEY=
31
- APPROVALS_SERVER_API_KEY=
32
- AUDIT_SERVER_API_KEY=
33
- FORMS_SERVER_API_KEY=
34
- INTEGRATIONS_SERVER_API_KEY=
35
-
36
- # System API key used by scheduled workflows that talk back to the
37
- # Publishing MCP server (executes scheduled publishes).
38
- PUBLISHING_SYSTEM_API_KEY=
30
+ # --- MCP ---
31
+ # No keys here. Every Throughline tool is served on one /api/mcp by
32
+ # @payloadcms/plugin-mcp; create a key in the admin under MCP -> Payload MCP
33
+ # API Keys and give it to your MCP client, not to this file. Scheduled
34
+ # publishes run in process and need no key either.
39
35
 
40
36
  # --- Optional: storage ---
41
37
  # Vercel Blob token if deploying to Vercel; remove this and the storage
@@ -32,28 +32,37 @@ openssl rand -base64 48
32
32
  # - RESEND_API_KEY — from Resend dashboard
33
33
  # - INNGEST_EVENT_KEY/SIGNING — from Inngest (optional in local dev)
34
34
 
35
- # 5. Start the dev server
35
+ # 5. Create and apply the first migration. The config sets `push: false`, so
36
+ # migrations are the only way the schema changes, from the first table on.
37
+ pnpm --dir apps/web migrate:create initial
38
+ pnpm --dir apps/web migrate
39
+
40
+ # 6. Generate the Payload types and admin import map, and commit both along
41
+ # with the migration. CI fails until they match the config.
42
+ pnpm --dir apps/web generate:types
43
+ pnpm --dir apps/web generate:importmap
44
+
45
+ # 7. Start the dev server
36
46
  pnpm dev
37
47
 
38
- # 6. Visit http://localhost:3000/admin and create your first user.
48
+ # 8. Visit http://localhost:3000/admin and create your first user.
39
49
  ```
40
50
 
41
51
  ## After admin signup
42
52
 
43
- 1. In the Payload admin, open the **API Keys** collection and create one key per MCP server (Component, Publishing, Approvals, Audit, Forms, Integrations). Paste each key into `.env.local` (`COMPONENT_SERVER_API_KEY`, etc.) and restart `pnpm dev`.
53
+ 1. In the Payload admin, open **MCP → Payload MCP API Keys** and create one key. Pick a **User** — required; the key inherits that user's access control — tick **Enable API Key**, save, and copy the key. It is shown once, and it does not go in `.env.local`.
44
54
 
45
- 2. **Plug the MCP servers into Claude (or your MCP client).** The endpoints are:
55
+ 2. **Plug it into Claude (or your MCP client).** One endpoint carries every server's tools:
46
56
 
47
57
  ```
48
- /api/components/mcp
49
- /api/publishing/mcp
50
- /api/approvals/mcp
51
- /api/audit/mcp
52
- /api/forms/mcp
53
- /api/integrations/mcp
58
+ POST /api/mcp
59
+ Authorization: Bearer <your key>
54
60
  ```
55
61
 
56
- Authenticate with the corresponding API key from `.env.local`.
62
+ ```bash
63
+ claude mcp add throughline http://localhost:3000/api/mcp \
64
+ --header "Authorization: Bearer $THROUGHLINE_API_KEY"
65
+ ```
57
66
 
58
67
  3. **Replace the example `pages` collection** in `apps/web/src/payload.config.ts` with your real content model. Keep the `policy` group on collections that should require approval — that field shape is what the approvals plugin reads.
59
68
 
@@ -67,6 +76,49 @@ pnpm dev
67
76
  - `pnpm lint` — ESLint
68
77
  - `pnpm test` — run all package tests
69
78
 
79
+ ## Migrations
80
+
81
+ The schema changes only through `apps/web/src/migrations` — `push` is off, in
82
+ development too. From `apps/web`:
83
+
84
+ - `pnpm migrate:create <name>` — generate a migration from the config. It diffs against the `.json` snapshot beside the newest migration, not against the database, and needs no database.
85
+ - `pnpm migrate` — apply pending migrations. Connects through `MIGRATION_DATABASE_URL` when it is set.
86
+ - `pnpm migrate:status` — what has been applied.
87
+ - `pnpm migrate:snapshot` — after **hand-writing** a migration, write its snapshot, or the generator falls a step behind and re-proposes changes that already shipped.
88
+ - `pnpm check:migrations` — every migration has a snapshot, no snapshot is orphaned, and no drop follows a `CASCADE` that already carried it out. Also runs from the root.
89
+
90
+ Scripts that write to a database can call `requireTarget` from `apps/web/scripts/lib/target.ts`, which prints the database, host and variable it is about to write through, and demands `--confirm` before overwriting anything.
91
+
92
+ ## Generated files
93
+
94
+ `apps/web/src/payload-types.ts` and `apps/web/src/app/(payload)/admin/importMap.js`
95
+ are generated from `payload.config.ts` and **committed**. Regenerate both after
96
+ any change to the config, a collection or a plugin version, and commit the
97
+ result — CI regenerates them and fails on a difference:
98
+
99
+ ```bash
100
+ pnpm --dir apps/web generate:types
101
+ pnpm --dir apps/web generate:importmap
102
+ ```
103
+
104
+ A stale import map is a 500 on any admin screen that renders a component the
105
+ map has never heard of.
106
+
107
+ ## CI
108
+
109
+ `.github/workflows/ci.yml` runs two tiers. `fast` runs on every pull request
110
+ and needs no database: install from the lockfile, typecheck, lint, the gates
111
+ below, manifest validation, the generated-files check and tests. `verify` runs
112
+ on pushes to main: it migrates a fresh Postgres and runs a production build.
113
+
114
+ ## Workspace gates
115
+
116
+ Each is explained at the top of its script in `scripts/`:
117
+
118
+ - `pnpm check:instances` — `payload`, `@payloadcms/ui`, `react` and `react-dom` each resolve to exactly one copy. Two byte-identical copies are two React contexts, and a runtime failure nothing else can see.
119
+ - `pnpm check:tested` — every workspace package that ships code has at least one test. Not a coverage number. `apps/web` starts on its exception list; delete the entry with your first test.
120
+ - `pnpm check:audit` — high and critical production advisories against an allowlist that carries a reason for each entry, and that fails when an entry stops applying.
121
+
70
122
  ## Project layout
71
123
 
72
124
  ```
@@ -9,23 +9,31 @@
9
9
  "start": "next start",
10
10
  "clean": "rm -rf .next .turbo",
11
11
  "typecheck": "tsc --noEmit",
12
- "payload": "payload"
12
+ "payload": "payload",
13
+ "generate:types": "payload generate:types",
14
+ "generate:importmap": "payload generate:importmap",
15
+ "migrate": "PAYLOAD_MIGRATING=1 payload migrate",
16
+ "migrate:create": "payload migrate:create",
17
+ "migrate:status": "payload migrate:status",
18
+ "migrate:snapshot": "tsx scripts/write-migration-snapshot.ts",
19
+ "check:migrations": "tsx scripts/check-migrations.ts"
13
20
  },
14
21
  "dependencies": {
15
- "@forumone/throughline-approvals": "^0.2.0",
16
- "@forumone/throughline-audit": "^0.2.0",
17
- "@forumone/throughline-components": "^0.2.0",
18
- "@forumone/throughline-core": "^0.2.0",
19
- "@forumone/throughline-email": "^0.2.0",
20
- "@forumone/throughline-forms": "^0.2.0",
21
- "@forumone/throughline-integrations": "^0.2.0",
22
- "@forumone/throughline-publishing": "^0.2.0",
23
- "@forumone/throughline-workflows": "^0.2.0",
24
- {{#if useReferenceDs}}"@forumone/throughline-design-contract": "^0.2.0",
22
+ "@forumone/throughline-approvals": "^{{version_approvals}}",
23
+ "@forumone/throughline-audit": "^{{version_audit}}",
24
+ "@forumone/throughline-components": "^{{version_components}}",
25
+ "@forumone/throughline-core": "^{{version_core}}",
26
+ "@forumone/throughline-email": "^{{version_email}}",
27
+ "@forumone/throughline-forms": "^{{version_forms}}",
28
+ "@forumone/throughline-integrations": "^{{version_integrations}}",
29
+ "@forumone/throughline-publishing": "^{{version_publishing}}",
30
+ "@forumone/throughline-workflows": "^{{version_workflows}}",
31
+ {{#if useReferenceDs}}"@forumone/throughline-design-contract": "^{{version_design_contract}}",
25
32
  "{{designSystemName}}": "workspace:*",
26
33
  {{/if}}"@payloadcms/db-postgres": "^3.83.0",
27
34
  "@payloadcms/next": "^3.83.0",
28
- "@payloadcms/plugin-form-builder": "3.83.0",
35
+ "@payloadcms/plugin-form-builder": "^3.83.0",
36
+ "@payloadcms/plugin-mcp": "^3.83.0",
29
37
  "@payloadcms/richtext-lexical": "^3.83.0",
30
38
  "graphql": "^16.8.1",
31
39
  "inngest": "^4.2.0",
@@ -38,6 +46,7 @@
38
46
  "@types/node": "^20.17.0",
39
47
  "@types/react": "^19.2.0",
40
48
  "@types/react-dom": "^19.2.0",
49
+ "tsx": "^4.21.0",
41
50
  "typescript": "^5.6.0"
42
51
  }
43
52
  }
@@ -0,0 +1,147 @@
1
+ import { readdir, readFile } from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ /*
6
+ Three checks on `src/migrations`, each about a failure nobody sees until the
7
+ moment it has to work.
8
+
9
+ pnpm check:migrations
10
+
11
+ With `push: false` a migration is the only way this schema ever changes, so the
12
+ migration chain has to stay usable.
13
+
14
+ ## 1. A migration with no schema snapshot
15
+
16
+ `migrate:create` does not diff the config against the database. It diffs it
17
+ against the `.json` snapshot beside the **most recent** migration, and writes one
18
+ only when it generated the SQL itself. So a migration written by hand leaves the
19
+ generator a step behind, and every run after it re-proposes a change that
20
+ already shipped — often as drizzle's interactive rename prompt, which cannot be
21
+ answered non-interactively, so the command exits having written nothing.
22
+
23
+ Every migration is checked, not only the newest: a gap in the middle is inert
24
+ today and was a blocked generator on the day it merged, which is the day this
25
+ needs to fail. Write one with `pnpm migrate:snapshot`.
26
+
27
+ ## 2. An orphaned snapshot
28
+
29
+ A `.json` with no `.ts` beside it. Delete a migration and leave its snapshot,
30
+ and that snapshot sorts last and becomes what the generator diffs against — a
31
+ schema no database will ever be in.
32
+
33
+ ## 3. A drop that a CASCADE has already carried out
34
+
35
+ Payload's generator can emit, in this order:
36
+
37
+ DROP TABLE "posts" CASCADE;
38
+ ALTER TABLE "search_rels" DROP CONSTRAINT "search_rels_posts_fk";
39
+
40
+ The CASCADE already removed that constraint, so the second statement errors,
41
+ and because a migration runs in a transaction it takes the whole migration — or
42
+ the whole rollback — with it. What is flagged is a bare `DROP CONSTRAINT` or
43
+ `DROP INDEX` that follows a cascading `DROP` in the same function. A bare drop
44
+ with no CASCADE above it is dropping something that is still there, and
45
+ demanding `IF EXISTS` of it would be the kind of blanket rule people learn to
46
+ suppress.
47
+ */
48
+
49
+ const here = path.dirname(fileURLToPath(import.meta.url))
50
+ const MIGRATIONS = path.resolve(here, '../src/migrations')
51
+
52
+ const BARE_DROP = /\bDROP\s+(CONSTRAINT|INDEX)\s+"/gi
53
+
54
+ // A `DROP` that cascaded, kept inside one statement by `[^;]*`. Not the word
55
+ // on its own, which also matches the `ON DELETE cascade` of every foreign key.
56
+ const CASCADE = /\bDROP\b[^;]*\bCASCADE\b/i
57
+
58
+ const stem = (name: string) => name.replace(/\.(json|ts)$/, '')
59
+
60
+ const entries = await readdir(MIGRATIONS).catch((error: NodeJS.ErrnoException) => {
61
+ if (error.code === 'ENOENT') return [] as string[]
62
+ throw error
63
+ })
64
+
65
+ const files = entries.filter((name) => name.endsWith('.ts') && name !== 'index.ts').sort()
66
+
67
+ if (files.length === 0) {
68
+ console.log('No migrations yet. Create the first with:\n\n' + ' pnpm migrate:create initial\n')
69
+ process.exit(0)
70
+ }
71
+
72
+ // Basenames on both sides, because that is what `createMigration` sorts: it
73
+ // reads the directory, keeps the `.json`, sorts, and takes the last.
74
+ const snapshots = new Set(entries.filter((name) => name.endsWith('.json')).map(stem))
75
+
76
+ const missing = files.filter((file) => !snapshots.has(stem(file)))
77
+
78
+ if (missing.length > 0) {
79
+ console.error(
80
+ `${missing.length} migration(s) with no schema snapshot:\n\n` +
81
+ missing.map((file) => ` ${file}\n ${stem(file)}.json <- missing`).join('\n\n') +
82
+ '\n\nmigrate:create diffs the config against the snapshot beside the most\n' +
83
+ 'recent migration. Without one it diffs against a schema that is a step\n' +
84
+ 'behind, and re-proposes a change that has already shipped. Write it with:\n\n' +
85
+ ' pnpm migrate:snapshot\n\n' +
86
+ 'Run it only once the migration is complete: the snapshot claims the\n' +
87
+ 'database matches the config.\n',
88
+ )
89
+ process.exit(1)
90
+ }
91
+
92
+ const migrationNames = new Set(files.map(stem))
93
+ const orphans = [...snapshots].filter((name) => !migrationNames.has(name)).sort()
94
+
95
+ if (orphans.length > 0) {
96
+ console.error(
97
+ `${orphans.length} schema snapshot(s) with no migration beside them:\n\n` +
98
+ orphans.map((name) => ` ${name}.json`).join('\n') +
99
+ '\n\nA snapshot sorting last is what migrate:create diffs against, so one\n' +
100
+ 'left behind by a deleted migration describes a schema no database will\n' +
101
+ 'reach. Delete it, or restore the migration it belongs to.\n',
102
+ )
103
+ process.exit(1)
104
+ }
105
+
106
+ const problems: Array<{ file: string; statement: string }> = []
107
+
108
+ for (const file of files) {
109
+ const source = await readFile(path.join(MIGRATIONS, file), 'utf8')
110
+
111
+ for (const name of ['up', 'down'] as const) {
112
+ const start = source.indexOf(`export async function ${name}`)
113
+ if (start === -1) continue
114
+ const other = source.indexOf('export async function', start + 1)
115
+ const body = source.slice(start, other === -1 ? undefined : other)
116
+
117
+ for (const match of body.matchAll(BARE_DROP)) {
118
+ const at = match.index ?? 0
119
+ if (!CASCADE.test(body.slice(0, at))) continue
120
+
121
+ const absolute = start + at
122
+ const lineStart = source.lastIndexOf('\n', absolute) + 1
123
+ const lineEnd = source.indexOf('\n', absolute)
124
+ problems.push({
125
+ file,
126
+ statement: source.slice(lineStart, lineEnd === -1 ? undefined : lineEnd).trim(),
127
+ })
128
+ }
129
+ }
130
+ }
131
+
132
+ if (problems.length > 0) {
133
+ console.error(
134
+ `${problems.length} drop(s) after a CASCADE without IF EXISTS:\n\n` +
135
+ problems.map((p) => ` ${p.file}\n ${p.statement}`).join('\n\n') +
136
+ '\n\nA preceding DROP ... CASCADE has already removed these, so the\n' +
137
+ 'migration will abort. Add IF EXISTS:\n\n' +
138
+ ' DROP CONSTRAINT IF EXISTS "..."\n' +
139
+ ' DROP INDEX IF EXISTS "..."\n',
140
+ )
141
+ process.exit(1)
142
+ }
143
+
144
+ console.log(
145
+ `Checked ${files.length} migration(s): every drop is guarded, every migration has\n` +
146
+ `its schema snapshot, and the generator will diff against ${stem(files.at(-1)!)}.`,
147
+ )
@@ -0,0 +1,95 @@
1
+ import { resolveDatabaseConnection } from '../../src/lib/database'
2
+
3
+ /*
4
+ Which database a script is about to write to, and an explicit confirmation of
5
+ it. For seed, import and repair scripts:
6
+
7
+ const where = requireTarget(process.argv, overwriting, 'Replaces every page')
8
+
9
+ A script reads its connection string from the environment like everything else,
10
+ so a shell that happens to have production's connection string loaded points it
11
+ at production and nothing asks. This prints the target before anything is
12
+ written, and demands `--confirm <database>@<host>` when the run would overwrite.
13
+
14
+ It is not authentication — anyone who can run the script can type the token. It
15
+ is the difference between a mistake and a decision, and the print alone is worth
16
+ it: a run aimed at the wrong host is visible before it writes rather than after.
17
+
18
+ Sized to the risk: creating what is missing is recoverable by deleting it;
19
+ replacing what an editor has worked on is not.
20
+ */
21
+
22
+ export interface Target {
23
+ host: string
24
+ database: string
25
+ /** The environment variable the connection string came from, or `(none)`. */
26
+ source: string
27
+ }
28
+
29
+ /**
30
+ * The database the config will actually connect to — through the same resolver
31
+ * `payload.config.ts` uses, not by reading `DATABASE_URI` directly. A guard that
32
+ * reports a different database than the code connects to is not a weaker guard;
33
+ * it is a false one.
34
+ */
35
+ export function target(): Target {
36
+ let connection = ''
37
+ let source = '(none)'
38
+ try {
39
+ const resolved = resolveDatabaseConnection()
40
+ connection = resolved.value
41
+ source = resolved.name
42
+ } catch {
43
+ // Nothing set at all: a target of "none", which every destructive run refuses.
44
+ }
45
+
46
+ try {
47
+ const parsed = new URL(connection)
48
+ return { host: parsed.hostname, database: parsed.pathname.replace(/^\//, ''), source }
49
+ } catch {
50
+ return { host: '(unparseable)', database: '', source }
51
+ }
52
+ }
53
+
54
+ /*
55
+ The token that has to be typed back. Not the database name on its own: Neon
56
+ names every branch's database `neondb`, so `--confirm neondb` would match
57
+ production and a preview alike. The host distinguishes them, so the host is in
58
+ the token — long enough that it cannot be typed from memory while thinking about
59
+ a different environment.
60
+ */
61
+ export const confirmToken = (where: Target): string => `${where.database}@${where.host}`
62
+
63
+ /**
64
+ * Print the target, and exit unless a destructive run has named it back.
65
+ *
66
+ * @param argv The script's arguments, for `--confirm <database>@<host>`.
67
+ * @param destructive Whether this run would overwrite existing documents.
68
+ * @param consequence One line naming what would be overwritten, shown in the refusal.
69
+ */
70
+ export function requireTarget(argv: string[], destructive: boolean, consequence: string): Target {
71
+ const where = target()
72
+ console.log(
73
+ `\n target ${where.database || '(none)'} on ${where.host}\n from ${where.source}`,
74
+ )
75
+
76
+ if (!destructive) return where
77
+
78
+ const index = argv.indexOf('--confirm')
79
+ const confirmed = index === -1 ? undefined : argv[index + 1]
80
+ const expected = confirmToken(where)
81
+
82
+ if (where.database === '' || confirmed !== expected) {
83
+ console.error(
84
+ `\n Refusing to write.\n\n` +
85
+ ` ${consequence}\n` +
86
+ ` in the database "${where.database}" on ${where.host},\n` +
87
+ ` read from ${where.source}.\n\n` +
88
+ ` Re-run with --confirm ${where.database ? expected : '<database>@<host>'}\n` +
89
+ ` if that is what you meant.\n`,
90
+ )
91
+ process.exit(2)
92
+ }
93
+
94
+ return where
95
+ }