@forumone/create-throughline 0.5.0 → 1.0.0-next.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 +8 -0
- package/dist/post-install.d.ts.map +1 -1
- package/dist/post-install.js +6 -2
- package/dist/post-install.js.map +1 -1
- package/dist/templates/base/.env.example +1 -5
- package/dist/templates/base/CLAUDE.md +1 -1
- package/dist/templates/base/README.md +2 -3
- package/dist/templates/base/apps/web/package.json.template +6 -12
- package/dist/templates/base/apps/web/src/access/anonymousAccess.test.ts +1 -4
- package/dist/templates/base/apps/web/src/app/api/inngest/route.ts.template +15 -109
- package/dist/templates/base/apps/web/src/instrumentation.ts +1 -1
- package/dist/templates/base/apps/web/src/lib/cache-tags.ts +1 -1
- package/dist/templates/base/apps/web/src/lib/inngest.ts.template +7 -0
- package/dist/templates/base/apps/web/src/payload.config.ts.template +81 -126
- package/dist/templates/base/apps/web/vitest.config.ts +0 -1
- package/dist/templates/base/github/workflows/ci.yml +14 -1
- package/dist/templates/base/gitignore +1 -1
- package/dist/templates/base/turbo.json +0 -1
- package/dist/templates/with-reference-ds/design-system/package.json.template +1 -1
- package/dist/templates/with-reference-ds/design-system/scripts/build-manifest.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/scripts/validate.ts +3 -3
- package/dist/templates/with-reference-ds/design-system/src/components/CTASection/CTASection.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/Card/Card.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/CardGrid/CardGrid.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/Divider/Divider.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/FAQ/FAQ.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/Hero/Hero.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/MediaBlock/MediaBlock.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/Prose/Prose.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/Quote/Quote.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/SectionIntro/SectionIntro.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/Spacer/Spacer.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/components/Stats/Stats.contract.ts +1 -1
- package/dist/templates/with-reference-ds/design-system/src/tokens/index.ts +1 -1
- package/dist/utils/versions.d.ts +3 -3
- package/dist/utils/versions.d.ts.map +1 -1
- package/dist/utils/versions.js +14 -8
- package/dist/utils/versions.js.map +1 -1
- package/dist/versions.json +2 -12
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @forumone/create-throughline
|
|
2
2
|
|
|
3
|
+
## 1.0.0-next.2
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 1595db1: The scaffold builds a 1.0 site. It depends on `@forumone/throughline` and, with the reference design system, `@forumone/throughline-design-system`, rather than on the nine 0.x packages. `payload.config.ts` registers the suite with one `throughline()` call, and `app/api/inngest/route.ts` serves `suite.jobs`, with the one Inngest client in `lib/inngest.ts`. Forms is no longer scaffolded, nor is `FORMS_IP_HASH_SECRET` asked for. The template's stub approval resolver, which overrode the approvals plugin's own and so blocked every publish that required approval, is gone.
|
|
8
|
+
|
|
9
|
+
From 1.0 the scaffolder is versioned with `@forumone/throughline` and `@forumone/throughline-design-system`: one version across the three.
|
|
10
|
+
|
|
3
11
|
## 0.5.0
|
|
4
12
|
|
|
5
13
|
### Minor Changes
|
|
@@ -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,
|
|
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,CA6FrD"}
|
package/dist/post-install.js
CHANGED
|
@@ -27,7 +27,12 @@ export function printNextSteps(answers) {
|
|
|
27
27
|
lines.push(formatStep(step++, 'Generate required secrets:'));
|
|
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
|
+
// The config imports the design system's built manifest, and every Payload
|
|
31
|
+
// command below loads the config. `pnpm dev` builds it too, but runs last.
|
|
32
|
+
if (answers.useReferenceDs) {
|
|
33
|
+
lines.push(formatStep(step++, 'Build the design system, which the Payload config reads:'));
|
|
34
|
+
lines.push(` ${pc.dim('pnpm --filter ./design-system build')}`);
|
|
35
|
+
}
|
|
31
36
|
// `push: false`: the schema exists only once a migration has created it.
|
|
32
37
|
lines.push(formatStep(step++, 'Create and apply the first migration:'));
|
|
33
38
|
lines.push(` ${pc.dim('pnpm --dir apps/web migrate:create initial')}`);
|
|
@@ -57,7 +62,6 @@ export function printNextSteps(answers) {
|
|
|
57
62
|
const customizations = [
|
|
58
63
|
'Replace the example Pages collection with your content model',
|
|
59
64
|
'Implement the groupResolver and user resolvers in payload.config.ts',
|
|
60
|
-
'Add your destinations to the formsPlugin allowlist',
|
|
61
65
|
];
|
|
62
66
|
if (!answers.useReferenceDs) {
|
|
63
67
|
customizations.push('Point componentsPlugin at your design system manifest URL');
|
package/dist/post-install.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-install.js","sourceRoot":"","sources":["../src/post-install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,MAAM,YAAY,CAAA;AAG3B;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,IAAI,GAAG,CAAC,CAAA;IAEZ,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC3C,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC,CAAA;IAE7D,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAA;IACzD,CAAC;IAED,KAAK,CAAC,IAAI,CACR,UAAU,CACR,IAAI,EAAE,EACN,QAAQ,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,qBAAqB,CACjF,CACF,CAAA;IAED,IAAI,OAAO,CAAC,gBAAgB,KAAK,MAAM,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,qEAAqE,CAAC,EAAE,CAAC,CAAA;IACvG,CAAC;SAAM,IAAI,OAAO,CAAC,gBAAgB,KAAK,UAAU,EAAE,CAAC;QACnD,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,gFAAgF,CAAC,EAAE,CAAC,CAAA;IAClH,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,+EAA+E,CAAC,EAAE,CAAC,CAAA;IACjH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,4BAA4B,CAAC,CAAC,CAAA;IAC5D,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,2CAA2C,CAAC,EAAE,CAAC,CAAA;IAC3E,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,kDAAkD,CAAC,EAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"post-install.js","sourceRoot":"","sources":["../src/post-install.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,7 +21,7 @@ NEXT_PUBLIC_SERVER_URL=http://localhost:3000
|
|
|
21
21
|
INNGEST_EVENT_KEY=
|
|
22
22
|
INNGEST_SIGNING_KEY=
|
|
23
23
|
|
|
24
|
-
# --- Resend (for approval
|
|
24
|
+
# --- Resend (for approval notification emails) ---
|
|
25
25
|
# RESEND_API_KEY and EMAIL_FROM_ADDRESS are checked at startup (emailEnv).
|
|
26
26
|
RESEND_API_KEY=
|
|
27
27
|
EMAIL_FROM_ADDRESS=notifications@example.com
|
|
@@ -33,10 +33,6 @@ EMAIL_REPLY_TO=
|
|
|
33
33
|
# 32+ characters. Generate: openssl rand -base64 48
|
|
34
34
|
APPROVAL_TOKEN_SECRET=
|
|
35
35
|
|
|
36
|
-
# --- Forms (checked at startup: formsEnv) ---
|
|
37
|
-
# 32+ characters. Generate: openssl rand -base64 48
|
|
38
|
-
FORMS_IP_HASH_SECRET=
|
|
39
|
-
|
|
40
36
|
# --- MCP ---
|
|
41
37
|
# No keys here. Every Throughline tool is served on one /api/mcp by
|
|
42
38
|
# @payloadcms/plugin-mcp; create a key in the admin under MCP -> Payload MCP
|
|
@@ -87,7 +87,7 @@ be composed. It is built from a `<Name>.contract.ts` beside every component.
|
|
|
87
87
|
{{else}}
|
|
88
88
|
The design system here is a placeholder. When you build it, give every
|
|
89
89
|
component a contract, publish a manifest, and point `componentsPlugin` at it —
|
|
90
|
-
see
|
|
90
|
+
see `@forumone/throughline-design-system` in the Throughline repository.
|
|
91
91
|
{{/if}}
|
|
92
92
|
## Tests
|
|
93
93
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Generated by [`@forumone/create-throughline`](https://github.com/forumone/throughline/tree/main/packages/create-throughline).
|
|
4
4
|
|
|
5
|
-
This is a Throughline project: a Payload CMS application wired with the Throughline plugin suite (audit, components, publishing, approvals, audit-query, integrations, email
|
|
5
|
+
This is a Throughline project: a Payload CMS application wired with the Throughline plugin suite (audit, components, publishing, approvals, audit-query, integrations, email) plus an Inngest endpoint that runs the workflow functions.
|
|
6
6
|
|
|
7
7
|
## Prerequisites
|
|
8
8
|
|
|
@@ -28,7 +28,6 @@ openssl rand -base64 48
|
|
|
28
28
|
# - DATABASE_URI — your Postgres connection string
|
|
29
29
|
# - PAYLOAD_SECRET — generated above
|
|
30
30
|
# - APPROVAL_TOKEN_SECRET — generated above
|
|
31
|
-
# - FORMS_IP_HASH_SECRET — generated above
|
|
32
31
|
# - RESEND_API_KEY — from Resend dashboard
|
|
33
32
|
# - EMAIL_FROM_ADDRESS — a verified sender on your Resend domain
|
|
34
33
|
# - INNGEST_EVENT_KEY/SIGNING — from Inngest (optional in local dev)
|
|
@@ -84,7 +83,7 @@ pnpm dev
|
|
|
84
83
|
- `pnpm payload:reap` — kill any Payload CLI run a killed shell left behind (the next Payload command does this too)
|
|
85
84
|
|
|
86
85
|
Every script that runs the Payload CLI — `payload`, `generate:*`, `migrate*` —
|
|
87
|
-
goes through `throughline-payload` from `@forumone/throughline
|
|
86
|
+
goes through `throughline-payload` from `@forumone/throughline`. pnpm does
|
|
88
87
|
not forward signals to the node process it starts, so a bare `payload` whose
|
|
89
88
|
shell is killed keeps running, and a hung one spins on a core indefinitely. The
|
|
90
89
|
runner gives Payload its own process group, forwards Ctrl-C and friends to it,
|
|
@@ -24,28 +24,22 @@
|
|
|
24
24
|
"test:smoke:install": "playwright install --with-deps chromium"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@forumone/throughline
|
|
28
|
-
"@forumone/throughline-
|
|
29
|
-
"@forumone/throughline-components": "^{{version_components}}",
|
|
30
|
-
"@forumone/throughline-core": "^{{version_core}}",
|
|
31
|
-
"@forumone/throughline-email": "^{{version_email}}",
|
|
32
|
-
"@forumone/throughline-forms": "^{{version_forms}}",
|
|
33
|
-
"@forumone/throughline-integrations": "^{{version_integrations}}",
|
|
34
|
-
"@forumone/throughline-publishing": "^{{version_publishing}}",
|
|
35
|
-
"@forumone/throughline-workflows": "^{{version_workflows}}",
|
|
36
|
-
{{#if useReferenceDs}}"@forumone/throughline-design-contract": "^{{version_design_contract}}",
|
|
27
|
+
"@forumone/throughline": "^{{version_throughline}}",
|
|
28
|
+
{{#if useReferenceDs}}"@forumone/throughline-design-system": "^{{version_design_system}}",
|
|
37
29
|
"{{designSystemName}}": "workspace:*",
|
|
38
30
|
{{/if}}"@payloadcms/db-postgres": "^3.89.0",
|
|
39
31
|
"@payloadcms/next": "^3.89.0",
|
|
40
|
-
"@payloadcms/plugin-form-builder": "^3.89.0",
|
|
41
32
|
"@payloadcms/plugin-mcp": "^3.89.0",
|
|
42
33
|
"@payloadcms/richtext-lexical": "^3.89.0",
|
|
34
|
+
"@react-email/components": "^0.5.4",
|
|
35
|
+
"@react-email/render": "^1.3.2",
|
|
43
36
|
"graphql": "^16.8.1",
|
|
44
37
|
"inngest": "^4.2.0",
|
|
45
38
|
"next": "^16.2.4",
|
|
46
39
|
"payload": "^3.89.0",
|
|
47
40
|
"react": "^19.2.0",
|
|
48
|
-
"react-dom": "^19.2.0"
|
|
41
|
+
"react-dom": "^19.2.0",
|
|
42
|
+
"resend": "^6.1.4"
|
|
49
43
|
},
|
|
50
44
|
"devDependencies": {
|
|
51
45
|
"@playwright/test": "^1.63.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describeAnonymousAccess } from '@forumone/throughline
|
|
1
|
+
import { describeAnonymousAccess } from '@forumone/throughline/testing'
|
|
2
2
|
import config from '../payload.config'
|
|
3
3
|
|
|
4
4
|
/*
|
|
@@ -17,15 +17,12 @@ describeAnonymousAccess(config, {
|
|
|
17
17
|
renderPath: {
|
|
18
18
|
// Anonymous reads see published pages only; signed-in users see drafts.
|
|
19
19
|
pages: 'the page routes, once the frontend renders them',
|
|
20
|
-
// formsPlugin opens form definitions so a page can render the fields.
|
|
21
|
-
forms: 'any page that embeds a form',
|
|
22
20
|
},
|
|
23
21
|
private: {
|
|
24
22
|
users: 'accounts',
|
|
25
23
|
'payload-mcp-api-keys': 'MCP keys are credentials; narrowed to admins in payload.config.ts',
|
|
26
24
|
'audit-events': 'who did what',
|
|
27
25
|
approvals: 'who signed off on what',
|
|
28
|
-
'form-submissions': 'what readers typed into a form',
|
|
29
26
|
integrations: 'connection settings and sync state',
|
|
30
27
|
'job-failures': 'background jobs that ran out of retries, with their errors',
|
|
31
28
|
'payload-kv': 'the key-value store Payload keeps',
|
|
@@ -1,122 +1,28 @@
|
|
|
1
|
-
import type { InngestFunction } from 'inngest'
|
|
2
1
|
import { serve } from 'inngest/next'
|
|
3
2
|
import { getPayload } from 'payload'
|
|
4
|
-
import config from '@/payload.config'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
createAuditEventEchoFunction,
|
|
9
|
-
createExecuteScheduledPublishesFunction,
|
|
10
|
-
createExpireStaleApprovalsFunction,
|
|
11
|
-
createHealthcheckFailureHandler,
|
|
12
|
-
createHealthcheckFunction,
|
|
13
|
-
createPayloadReachableCheck,
|
|
14
|
-
createPublishAtScheduledTimeFunction,
|
|
15
|
-
createRevalidateOnPublishFunction,
|
|
16
|
-
createTerminalFailureHandler,
|
|
17
|
-
type ScheduledPublishRequest,
|
|
18
|
-
} from '@forumone/throughline-workflows'
|
|
19
|
-
import { getPublishingService } from '@forumone/throughline-publishing'
|
|
20
|
-
import { getEmailFunctions } from '@forumone/throughline-email'
|
|
21
|
-
import { getFormsFunctions } from '@forumone/throughline-forms'
|
|
22
|
-
import {
|
|
23
|
-
getIntegrationContext,
|
|
24
|
-
getIntegrationRegistry,
|
|
25
|
-
} from '@forumone/throughline-integrations'
|
|
3
|
+
import config, { suite } from '@/payload.config'
|
|
4
|
+
import { inngest } from '@/lib/inngest'
|
|
5
|
+
import { inngestJobs } from '@forumone/throughline/jobs/inngest'
|
|
6
|
+
import { createTerminalFailureHandler } from '@forumone/throughline/jobs'
|
|
26
7
|
|
|
27
|
-
const inngest = createInngestClient({ id: '{{projectName}}' })
|
|
28
8
|
const payload = await getPayload({ config })
|
|
29
9
|
|
|
30
10
|
/*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const emailFunctions = getEmailFunctions(payload) ?? []
|
|
38
|
-
const formsFunctions = getFormsFunctions(payload) ?? []
|
|
39
|
-
/*
|
|
40
|
-
Through the publishing service, in process — so a scheduled publish passes the
|
|
41
|
-
same composition, accessibility and approval gates as an interactive one. Not
|
|
42
|
-
`payload.update`, which skips all of them.
|
|
43
|
-
*/
|
|
44
|
-
async function publishScheduled({ collection, id, reasoning }: ScheduledPublishRequest) {
|
|
45
|
-
const outcome = await getPublishingService(payload).publish({
|
|
46
|
-
collection,
|
|
47
|
-
id,
|
|
48
|
-
actor: { apiKeyName: 'scheduled-publish', channel: 'mcp' },
|
|
49
|
-
meta: { reasoning },
|
|
50
|
-
})
|
|
51
|
-
return { published: outcome.published, reason: outcome.reason }
|
|
52
|
-
}
|
|
11
|
+
Every job the Throughline suite runs, as `throughline()` in payload.config.ts
|
|
12
|
+
listed them: publish revalidation, scheduled publishing and its backstop,
|
|
13
|
+
approval expiry, the audit echo, the healthcheck, the approval emails and each
|
|
14
|
+
integration's jobs. Each keeps the function id it has always had, so a run in
|
|
15
|
+
flight survives an upgrade.
|
|
53
16
|
|
|
54
|
-
|
|
17
|
+
A job that runs out of retries is logged, recorded in the `job-failures`
|
|
18
|
+
collection, and posted to ERROR_WEBHOOK_URL if that is set: given once, here,
|
|
19
|
+
and handed to every job.
|
|
55
20
|
|
|
56
|
-
|
|
57
|
-
Where each publishable collection's documents are served, so a publish
|
|
58
|
-
revalidates the right path. There are no defaults: a collection missing here
|
|
59
|
-
has its cache tags dropped but no path revalidated, and the run logs a warning.
|
|
60
|
-
Add an entry for every collection you give publishingPlugin.
|
|
61
|
-
*/
|
|
62
|
-
const urlBuilders = {
|
|
63
|
-
pages: (slug: string) => (slug === 'home' ? '/' : `/${slug}`),
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/*
|
|
67
|
-
Typed with this app's own `InngestFunction.Any`. The registry is generic in what
|
|
68
|
-
`createFunctions` returns and defaults to `unknown`, because the platform never
|
|
69
|
-
touches those functions — and one `unknown` element leaves `serve()` unable to
|
|
70
|
-
type the whole array below.
|
|
21
|
+
Add this site's own functions to the list beside them.
|
|
71
22
|
*/
|
|
72
|
-
const
|
|
73
|
-
const integrationContext = getIntegrationContext(payload)
|
|
74
|
-
const integrationFunctions =
|
|
75
|
-
integrationRegistry && integrationContext
|
|
76
|
-
? integrationRegistry
|
|
77
|
-
.list()
|
|
78
|
-
.flatMap((integration) => integration.createFunctions(integrationContext))
|
|
79
|
-
: []
|
|
23
|
+
const jobs = inngestJobs(inngest, { onFailure: createTerminalFailureHandler({ payload }), payload })
|
|
80
24
|
|
|
81
25
|
export const { GET, POST, PUT } = serve({
|
|
82
26
|
client: inngest,
|
|
83
|
-
functions: [
|
|
84
|
-
createRevalidateOnPublishFunction({
|
|
85
|
-
inngest,
|
|
86
|
-
payload,
|
|
87
|
-
urlBuilders,
|
|
88
|
-
cacheTags,
|
|
89
|
-
onTerminalFailure,
|
|
90
|
-
}),
|
|
91
|
-
// On time: sleeps until each document's scheduled time.
|
|
92
|
-
createPublishAtScheduledTimeFunction({
|
|
93
|
-
inngest,
|
|
94
|
-
payload,
|
|
95
|
-
collections: scheduledCollections,
|
|
96
|
-
publish: publishScheduled,
|
|
97
|
-
onTerminalFailure,
|
|
98
|
-
}),
|
|
99
|
-
// The backstop, for a schedule whose wake-up event never arrived.
|
|
100
|
-
createExecuteScheduledPublishesFunction({
|
|
101
|
-
inngest,
|
|
102
|
-
payload,
|
|
103
|
-
collections: scheduledCollections,
|
|
104
|
-
publish: publishScheduled,
|
|
105
|
-
schedule: '17 3 * * *',
|
|
106
|
-
overdueByMs: 60 * 60 * 1000,
|
|
107
|
-
onTerminalFailure,
|
|
108
|
-
}),
|
|
109
|
-
createExpireStaleApprovalsFunction({ inngest, payload, onTerminalFailure }),
|
|
110
|
-
createAuditEventEchoFunction({ inngest, onTerminalFailure }),
|
|
111
|
-
createHealthcheckFunction({
|
|
112
|
-
inngest,
|
|
113
|
-
payload,
|
|
114
|
-
checks: [createPayloadReachableCheck()],
|
|
115
|
-
onFailure: createHealthcheckFailureHandler({ payload }),
|
|
116
|
-
onTerminalFailure,
|
|
117
|
-
}),
|
|
118
|
-
...emailFunctions,
|
|
119
|
-
...formsFunctions,
|
|
120
|
-
...integrationFunctions,
|
|
121
|
-
],
|
|
27
|
+
functions: [...jobs.functions(suite.jobs)],
|
|
122
28
|
})
|
|
@@ -18,6 +18,6 @@ Change the scheme here and every end moves with it. Imported from the
|
|
|
18
18
|
package's `/cache-tags` subpath, which imports nothing, so frontend code that
|
|
19
19
|
uses it does not pull in Payload.
|
|
20
20
|
*/
|
|
21
|
-
import { createCacheTags } from '@forumone/throughline
|
|
21
|
+
import { createCacheTags } from '@forumone/throughline/cache-tags'
|
|
22
22
|
|
|
23
23
|
export const cacheTags = createCacheTags()
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createInngestClient } from '@forumone/throughline/jobs/inngest'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
The one Inngest client: payload.config.ts sends events through it, and the
|
|
5
|
+
endpoint at app/api/inngest serves this site's jobs with it.
|
|
6
|
+
*/
|
|
7
|
+
export const inngest = createInngestClient({ id: '{{projectName}}' })
|
|
@@ -4,31 +4,17 @@ import { buildConfig } from 'payload'
|
|
|
4
4
|
import type { Access, CollectionConfig } from 'payload'
|
|
5
5
|
import { postgresAdapter } from '@payloadcms/db-postgres'
|
|
6
6
|
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
auditPlugin,
|
|
10
|
-
createInngestClient,
|
|
11
|
-
createMcpToolCollector,
|
|
12
|
-
mcpApiKeyAccess,
|
|
13
|
-
} from '@forumone/throughline-core'
|
|
7
|
+
import { assertEnvironment, mcpApiKeyAccess, throughline } from '@forumone/throughline'
|
|
8
|
+
import { inngestJobs } from '@forumone/throughline/jobs/inngest'
|
|
14
9
|
import { mcpPlugin } from '@payloadcms/plugin-mcp'
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
approvalsPlugin,
|
|
20
|
-
buildActionUrl,
|
|
21
|
-
generateActionToken,
|
|
22
|
-
} from '@forumone/throughline-approvals'
|
|
23
|
-
import { auditQueryPlugin } from '@forumone/throughline-audit'
|
|
24
|
-
import { integrationsPlugin } from '@forumone/throughline-integrations'
|
|
25
|
-
import { emailEnv, emailPlugin } from '@forumone/throughline-email'
|
|
26
|
-
import { formsEnv, formsPlugin } from '@forumone/throughline-forms'
|
|
27
|
-
import { createTagRevalidationHooks } from '@forumone/throughline-workflows'
|
|
10
|
+
import { approvalsEnv } from '@forumone/throughline/approvals'
|
|
11
|
+
import { emailEnv } from '@forumone/throughline/email'
|
|
12
|
+
import { createPayloadReachableCheck } from '@forumone/throughline/integrations'
|
|
13
|
+
import { createTagRevalidationHooks } from '@forumone/throughline/publishing'
|
|
28
14
|
import { cacheTags } from './lib/cache-tags'
|
|
29
|
-
import {
|
|
15
|
+
import { inngest } from './lib/inngest'
|
|
30
16
|
import { databaseConnectionString } from './lib/database'
|
|
31
|
-
{{#if useReferenceDs}}import type { Manifest } from '@forumone/throughline-design-contract'
|
|
17
|
+
{{#if useReferenceDs}}import type { Manifest } from '@forumone/throughline-design-system/contract'
|
|
32
18
|
import designSystemManifest from '{{designSystemName}}/manifest' with { type: 'json' }
|
|
33
19
|
{{/if}}
|
|
34
20
|
|
|
@@ -51,7 +37,6 @@ and type generation), placeholders of the right length do; see
|
|
|
51
37
|
assertEnvironment(
|
|
52
38
|
approvalsEnv,
|
|
53
39
|
emailEnv,
|
|
54
|
-
formsEnv,
|
|
55
40
|
{
|
|
56
41
|
name: 'PAYLOAD_SECRET',
|
|
57
42
|
minLength: 32,
|
|
@@ -70,16 +55,74 @@ assertEnvironment(
|
|
|
70
55
|
const __filename = fileURLToPath(import.meta.url)
|
|
71
56
|
const __dirname = path.dirname(__filename)
|
|
72
57
|
|
|
73
|
-
const inngest = createInngestClient({ id: '{{projectName}}' })
|
|
74
|
-
|
|
75
58
|
/*
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
59
|
+
The Throughline suite, in one call. It registers every plugin in the order they
|
|
60
|
+
need, gives each the one MCP tool collector, sends their events through the jobs
|
|
61
|
+
adapter, and lists the jobs these options call for, which app/api/inngest
|
|
62
|
+
serves. One value for each thing two plugins must agree on: the approvals
|
|
63
|
+
collection's slug reaches approvals, the emails and the expiry job alike.
|
|
64
|
+
|
|
65
|
+
A plugin is on when its key is here. Delete a key to turn one off.
|
|
81
66
|
*/
|
|
82
|
-
const
|
|
67
|
+
export const suite = throughline({
|
|
68
|
+
jobs: inngestJobs(inngest),
|
|
69
|
+
// The collections publishing gates, "Your work" lists and scheduling publishes.
|
|
70
|
+
collections: ['pages'],
|
|
71
|
+
|
|
72
|
+
{{#if useReferenceDs}}components: {
|
|
73
|
+
manifest: { type: 'object', manifest: designSystemManifest as unknown as Manifest },
|
|
74
|
+
matching: { strategy: 'tfidf' },
|
|
75
|
+
},
|
|
76
|
+
{{else}}components: {
|
|
77
|
+
// TODO: point at your design system manifest
|
|
78
|
+
manifest: { type: 'url', url: 'https://your-design-system.example.com/manifest.json' },
|
|
79
|
+
matching: { strategy: 'tfidf' },
|
|
80
|
+
},
|
|
81
|
+
{{/if}}publishing: {
|
|
82
|
+
/*
|
|
83
|
+
Where each publishable collection's documents are served, so a publish
|
|
84
|
+
revalidates the right path. There are no defaults: a collection missing here
|
|
85
|
+
has its cache tags dropped but no path revalidated, and the run logs a
|
|
86
|
+
warning. Add an entry for every collection in `collections`.
|
|
87
|
+
*/
|
|
88
|
+
urls: { pages: (slug: string) => (slug === 'home' ? '/' : `/${slug}`) },
|
|
89
|
+
revalidation: { cacheTags },
|
|
90
|
+
// Scheduled publishes go through the publishing pipeline, as an editor's
|
|
91
|
+
// would. The backstop catches a schedule whose wake-up event never arrived.
|
|
92
|
+
scheduling: { backstop: { schedule: '17 3 * * *', overdueByMs: 60 * 60 * 1000 } },
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
approvals: {
|
|
96
|
+
groups: [
|
|
97
|
+
{ slug: 'editorial', name: 'Editorial' },
|
|
98
|
+
{ slug: 'legal', name: 'Legal' },
|
|
99
|
+
{ slug: 'communications', name: 'Communications' },
|
|
100
|
+
{ slug: 'senior', name: 'Senior leadership' },
|
|
101
|
+
],
|
|
102
|
+
groupResolver: {
|
|
103
|
+
// TODO: replace with your actual user/group lookup (DB query, SSO, etc.)
|
|
104
|
+
async resolveUsers(_groupSlugs) {
|
|
105
|
+
return []
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
auditQuery: {},
|
|
111
|
+
|
|
112
|
+
// Approval links are signed with APPROVAL_TOKEN_SECRET and point at
|
|
113
|
+
// NEXT_PUBLIC_SERVER_URL, both checked above.
|
|
114
|
+
email: {
|
|
115
|
+
// TODO: replace with your real user lookups
|
|
116
|
+
resolveApprover: async (_userId) => null,
|
|
117
|
+
resolveRequester: async (_userId) => null,
|
|
118
|
+
tokens: { brandName: '{{projectName}}' },
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
integrations: {},
|
|
122
|
+
|
|
123
|
+
// A failing check is recorded in `job-failures` and posted to ERROR_WEBHOOK_URL.
|
|
124
|
+
healthcheck: { checks: [createPayloadReachableCheck()] },
|
|
125
|
+
})
|
|
83
126
|
|
|
84
127
|
/*
|
|
85
128
|
Drops a Next cache tag when content changes, named by the same scheme your
|
|
@@ -109,7 +152,7 @@ const Users: CollectionConfig = {
|
|
|
109
152
|
type: 'select',
|
|
110
153
|
hasMany: true,
|
|
111
154
|
defaultValue: ['admin'],
|
|
112
|
-
options: ['admin', 'editor', 'approver'
|
|
155
|
+
options: ['admin', 'editor', 'approver'],
|
|
113
156
|
},
|
|
114
157
|
{
|
|
115
158
|
name: 'groups',
|
|
@@ -194,102 +237,14 @@ export default buildConfig({
|
|
|
194
237
|
outputFile: path.resolve(__dirname, 'payload-types.ts'),
|
|
195
238
|
},
|
|
196
239
|
plugins: [
|
|
197
|
-
|
|
198
|
-
// requires the audit log at init. And every tool-bearing plugin before
|
|
199
|
-
// `mcpPlugin`, which reads the declared tools when the config is built — a
|
|
200
|
-
// plugin registered after it gets no per-key checkbox, and its tools are
|
|
201
|
-
// denied to every key with no error anywhere.
|
|
202
|
-
auditPlugin({ inngest }),
|
|
203
|
-
|
|
204
|
-
// Where a workflow that ran out of retries, or a failing healthcheck, leaves
|
|
205
|
-
// a row — see the failure handlers in app/api/inngest/route.ts. Not the
|
|
206
|
-
// audit log: that records who did what through an MCP tool.
|
|
207
|
-
jobFailuresPlugin({}),
|
|
208
|
-
|
|
209
|
-
componentsPlugin({
|
|
210
|
-
{{#if useReferenceDs}}manifest: { type: 'object', manifest: designSystemManifest as unknown as Manifest },
|
|
211
|
-
{{else}}// TODO: point at your design system manifest
|
|
212
|
-
manifest: { type: 'url', url: 'https://your-design-system.example.com/manifest.json' },
|
|
213
|
-
{{/if}}matching: { strategy: 'tfidf' },
|
|
214
|
-
mcpTools,
|
|
215
|
-
}),
|
|
216
|
-
|
|
217
|
-
publishingPlugin({
|
|
218
|
-
collections: [{ slug: 'pages' }],
|
|
219
|
-
inngest,
|
|
220
|
-
// Lazy-resolve approvals at call time so plugin order doesn't matter.
|
|
221
|
-
approvalResolver: {
|
|
222
|
-
getActiveApproval: async () => null, // TODO: wire to approvals plugin
|
|
223
|
-
},
|
|
224
|
-
mcpTools,
|
|
225
|
-
}),
|
|
226
|
-
|
|
227
|
-
approvalsPlugin({
|
|
228
|
-
inngest,
|
|
229
|
-
groups: [
|
|
230
|
-
{ slug: 'editorial', name: 'Editorial' },
|
|
231
|
-
{ slug: 'legal', name: 'Legal' },
|
|
232
|
-
{ slug: 'communications', name: 'Communications' },
|
|
233
|
-
{ slug: 'senior', name: 'Senior leadership' },
|
|
234
|
-
],
|
|
235
|
-
groupResolver: {
|
|
236
|
-
// TODO: replace with your actual user/group lookup (DB query, SSO, etc.)
|
|
237
|
-
async resolveUsers(_groupSlugs) {
|
|
238
|
-
return []
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
mcpTools,
|
|
242
|
-
}),
|
|
243
|
-
|
|
244
|
-
auditQueryPlugin({ mcpTools }),
|
|
245
|
-
|
|
246
|
-
emailPlugin({
|
|
247
|
-
inngest,
|
|
248
|
-
// TODO: replace with your real user lookups
|
|
249
|
-
resolveApprover: async (_userId) => null,
|
|
250
|
-
resolveRequester: async (_userId) => null,
|
|
251
|
-
// Signed, and pointed at the route approvalsPlugin serves
|
|
252
|
-
// (`/api/approvals/action`). The token binds one approval, one action
|
|
253
|
-
// and one approver, so a link cannot be edited into a different decision.
|
|
254
|
-
buildActionUrl: async ({ approvalId, action, approverId }) => {
|
|
255
|
-
const secret = process.env.APPROVAL_TOKEN_SECRET
|
|
256
|
-
if (!secret) throw new Error('APPROVAL_TOKEN_SECRET is required to sign approval links.')
|
|
257
|
-
const token = await generateActionToken(
|
|
258
|
-
{ approvalId, action, approverId, issuedAt: Date.now() },
|
|
259
|
-
secret,
|
|
260
|
-
)
|
|
261
|
-
return buildActionUrl(process.env.NEXT_PUBLIC_SERVER_URL!, token)
|
|
262
|
-
},
|
|
263
|
-
tokens: { brandName: '{{projectName}}' },
|
|
264
|
-
}),
|
|
265
|
-
|
|
266
|
-
formsPlugin({
|
|
267
|
-
inngest,
|
|
268
|
-
// ipHashSecret: read from FORMS_IP_HASH_SECRET, checked above.
|
|
269
|
-
// TODO: list every place a form is allowed to send to, and delete the
|
|
270
|
-
// placeholder. The plugin refuses to start with an empty list — the
|
|
271
|
-
// allowlist is the security perimeter — and refuses to deliver anywhere
|
|
272
|
-
// not on it. example.com is reserved and accepts no mail, so the
|
|
273
|
-
// placeholder routes nothing anywhere while it stands.
|
|
274
|
-
allowedDestinations: [
|
|
275
|
-
{
|
|
276
|
-
type: 'email',
|
|
277
|
-
value: 'forms@example.com',
|
|
278
|
-
label: 'Placeholder',
|
|
279
|
-
description: 'Replace before launch: example.com accepts no mail.',
|
|
280
|
-
},
|
|
281
|
-
],
|
|
282
|
-
mcpTools,
|
|
283
|
-
}),
|
|
284
|
-
|
|
285
|
-
integrationsPlugin({ inngest, mcpTools }),
|
|
240
|
+
suite.plugin,
|
|
286
241
|
|
|
287
|
-
//
|
|
288
|
-
// every tool as the person it is bound to, so who may
|
|
289
|
-
// revoke one is an admin's call: `mcpApiKeyAccess` narrows
|
|
290
|
-
// collection to `isAdmin`, and refuses a key principal outright.
|
|
242
|
+
// After the suite, whose plugins fill the tools it serves. An MCP key is a
|
|
243
|
+
// credential that runs every tool as the person it is bound to, so who may
|
|
244
|
+
// mint, read and revoke one is an admin's call: `mcpApiKeyAccess` narrows
|
|
245
|
+
// the key collection to `isAdmin`, and refuses a key principal outright.
|
|
291
246
|
mcpPlugin({
|
|
292
|
-
mcp: { tools: mcpTools
|
|
247
|
+
mcp: { tools: suite.mcpTools },
|
|
293
248
|
overrideApiKeyCollection: mcpApiKeyAccess(isAdmin),
|
|
294
249
|
}),
|
|
295
250
|
],
|
|
@@ -48,7 +48,6 @@ export default defineConfig({
|
|
|
48
48
|
DATABASE_URI: 'postgres://test:test@localhost:5432/does-not-exist',
|
|
49
49
|
PAYLOAD_SECRET: 'test-only-not-a-real-secret-000000000000',
|
|
50
50
|
APPROVAL_TOKEN_SECRET: 'test-only-not-a-real-secret-000000000000',
|
|
51
|
-
FORMS_IP_HASH_SECRET: 'test-only-not-a-real-secret-000000000000',
|
|
52
51
|
RESEND_API_KEY: 're_test_only_not_a_real_key',
|
|
53
52
|
EMAIL_FROM_ADDRESS: 'test-only@example.invalid',
|
|
54
53
|
NEXT_PUBLIC_SERVER_URL: 'http://localhost:3000',
|
|
@@ -31,7 +31,6 @@ env:
|
|
|
31
31
|
DATABASE_URI: postgres://ci:ci@localhost:5432/ci
|
|
32
32
|
PAYLOAD_SECRET: ci-placeholder-not-a-secret-000000000000
|
|
33
33
|
APPROVAL_TOKEN_SECRET: ci-placeholder-not-a-secret-000000000000
|
|
34
|
-
FORMS_IP_HASH_SECRET: ci-placeholder-not-a-secret-000000000000
|
|
35
34
|
RESEND_API_KEY: re_ci_placeholder
|
|
36
35
|
EMAIL_FROM_ADDRESS: ci@example.com
|
|
37
36
|
NEXT_PUBLIC_SERVER_URL: http://localhost:3000
|
|
@@ -76,6 +75,13 @@ jobs:
|
|
|
76
75
|
- name: Validate the design-system manifest
|
|
77
76
|
run: pnpm validate
|
|
78
77
|
|
|
78
|
+
# payload.config.ts imports the design system's built manifest, and the
|
|
79
|
+
# Payload commands below load the config outside turbo, which would
|
|
80
|
+
# otherwise have built it first. A placeholder design system has nothing
|
|
81
|
+
# to build.
|
|
82
|
+
- name: Build the design system
|
|
83
|
+
run: pnpm --filter ./design-system run --if-present build
|
|
84
|
+
|
|
79
85
|
- name: Check generated Payload artifacts are current
|
|
80
86
|
run: bash .github/scripts/check-generated.sh
|
|
81
87
|
|
|
@@ -122,6 +128,13 @@ jobs:
|
|
|
122
128
|
- name: Install
|
|
123
129
|
run: pnpm install --frozen-lockfile
|
|
124
130
|
|
|
131
|
+
# payload.config.ts imports the design system's built manifest, and the
|
|
132
|
+
# Payload commands below load the config outside turbo, which would
|
|
133
|
+
# otherwise have built it first. A placeholder design system has nothing
|
|
134
|
+
# to build.
|
|
135
|
+
- name: Build the design system
|
|
136
|
+
run: pnpm --filter ./design-system run --if-present build
|
|
137
|
+
|
|
125
138
|
# A fresh database, migrated exactly as a deploy would migrate it. A
|
|
126
139
|
# migration that does not apply cleanly from empty fails here rather than
|
|
127
140
|
# on the first environment somebody creates.
|
|
@@ -14,7 +14,7 @@ dist/
|
|
|
14
14
|
|
|
15
15
|
coverage/
|
|
16
16
|
|
|
17
|
-
# Written by throughline-payload (@forumone/throughline
|
|
17
|
+
# Written by throughline-payload (@forumone/throughline): the process
|
|
18
18
|
# group of each in-flight Payload CLI run, so the next run can reap one whose
|
|
19
19
|
# runner was killed before it could clean up.
|
|
20
20
|
.payload-cli-pids
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"clean": "rm -rf dist .turbo storybook-static *.tsbuildinfo"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@forumone/throughline-design-
|
|
37
|
+
"@forumone/throughline-design-system": "^{{version_design_system}}",
|
|
38
38
|
"clsx": "^2.1.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
CONTRACT_VERSION,
|
|
6
6
|
ManifestSchema,
|
|
7
7
|
type ComponentContract,
|
|
8
|
-
} from '@forumone/throughline-design-contract'
|
|
8
|
+
} from '@forumone/throughline-design-system/contract'
|
|
9
9
|
import { getTokenList } from '../src/tokens/index.ts'
|
|
10
10
|
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFile, stat } from 'node:fs/promises'
|
|
2
2
|
import { dirname, resolve } from 'node:path'
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import { loadManifest } from '@forumone/throughline-design-contract'
|
|
5
|
-
import { formatLintIssues, lintManifest } from '@forumone/throughline-design-
|
|
4
|
+
import { loadManifest } from '@forumone/throughline-design-system/contract'
|
|
5
|
+
import { formatLintIssues, lintManifest } from '@forumone/throughline-design-system/lint'
|
|
6
6
|
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
8
8
|
const packageRoot = resolve(__dirname, '..')
|
|
@@ -36,7 +36,7 @@ async function collectStoryIds(): Promise<Set<string> | undefined> {
|
|
|
36
36
|
Lint rules this design system treats as failures, though the shared linter
|
|
37
37
|
reports them as warnings.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
The suite's linter sets both to `warning` because it serves every project, and a
|
|
40
40
|
project migrating an existing design system onto the contract should not be
|
|
41
41
|
blocked by prose. A design system written against the contract from the start is
|
|
42
42
|
never mid-migration, so it starts strict. Relax a rule here, deliberately, if
|
|
@@ -21,7 +21,7 @@ export const allTokens = {
|
|
|
21
21
|
|
|
22
22
|
export type TokenName = keyof typeof allTokens
|
|
23
23
|
|
|
24
|
-
/** Returns every token in the shape the design-contract
|
|
24
|
+
/** Returns every token in the shape the manifest schema (`@forumone/throughline-design-system/contract`) expects. */
|
|
25
25
|
export function getTokenList() {
|
|
26
26
|
const categorize = (name: string) => name.split('.')[0] ?? 'other'
|
|
27
27
|
return Object.entries(allTokens).map(([name, value]) => ({
|
package/dist/utils/versions.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The version of every Throughline package the scaffold depends on, keyed for
|
|
3
|
-
* the template renderer: `@forumone/throughline-design-
|
|
4
|
-
* `
|
|
3
|
+
* the template renderer: `@forumone/throughline-design-system` becomes
|
|
4
|
+
* `version_design_system`, rendered as `"^{{version_design_system}}"`.
|
|
5
5
|
*
|
|
6
6
|
* The ranges used to be typed into the templates by hand, and nothing moved
|
|
7
7
|
* them: they sat at `^0.2.0` — which for a 0.x version admits patches only —
|
|
@@ -15,6 +15,6 @@
|
|
|
15
15
|
* no such file and the workspace's own `package.json` files are the answer.
|
|
16
16
|
*/
|
|
17
17
|
export declare function loadPackageVersions(templatesDir: string): Promise<Record<string, string>>;
|
|
18
|
-
/** `{ '@forumone/throughline
|
|
18
|
+
/** `{ '@forumone/throughline': '1.0.0', … }` from a `packages/` directory. */
|
|
19
19
|
export declare function readWorkspaceVersions(packagesDir: string): Promise<Record<string, string>>;
|
|
20
20
|
//# sourceMappingURL=versions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versions.d.ts","sourceRoot":"","sources":["../../src/utils/versions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"versions.d.ts","sourceRoot":"","sources":["../../src/utils/versions.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAO/F;AAED,8EAA8E;AAC9E,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA0BhG"}
|
package/dist/utils/versions.js
CHANGED
|
@@ -2,10 +2,12 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { readFile, readdir } from 'node:fs/promises';
|
|
3
3
|
import { join, resolve } from 'node:path';
|
|
4
4
|
const SCOPE_PREFIX = '@forumone/throughline-';
|
|
5
|
+
/** The one package whose name has no suffix: `version_throughline`. */
|
|
6
|
+
const BARE_NAME = '@forumone/throughline';
|
|
5
7
|
/**
|
|
6
8
|
* The version of every Throughline package the scaffold depends on, keyed for
|
|
7
|
-
* the template renderer: `@forumone/throughline-design-
|
|
8
|
-
* `
|
|
9
|
+
* the template renderer: `@forumone/throughline-design-system` becomes
|
|
10
|
+
* `version_design_system`, rendered as `"^{{version_design_system}}"`.
|
|
9
11
|
*
|
|
10
12
|
* The ranges used to be typed into the templates by hand, and nothing moved
|
|
11
13
|
* them: they sat at `^0.2.0` — which for a 0.x version admits patches only —
|
|
@@ -26,7 +28,7 @@ export async function loadPackageVersions(templatesDir) {
|
|
|
26
28
|
// src/templates or dist/templates -> the package -> packages/
|
|
27
29
|
return templateKeys(await readWorkspaceVersions(resolve(templatesDir, '..', '..', '..')));
|
|
28
30
|
}
|
|
29
|
-
/** `{ '@forumone/throughline
|
|
31
|
+
/** `{ '@forumone/throughline': '1.0.0', … }` from a `packages/` directory. */
|
|
30
32
|
export async function readWorkspaceVersions(packagesDir) {
|
|
31
33
|
const versions = {};
|
|
32
34
|
const entries = await readdir(packagesDir, { withFileTypes: true }).catch(() => []);
|
|
@@ -37,22 +39,26 @@ export async function readWorkspaceVersions(packagesDir) {
|
|
|
37
39
|
if (!existsSync(manifest))
|
|
38
40
|
continue;
|
|
39
41
|
const { name, version, private: isPrivate, } = JSON.parse(await readFile(manifest, 'utf-8'));
|
|
40
|
-
if (!name
|
|
42
|
+
if (!name || !isThroughline(name) || !version || isPrivate)
|
|
41
43
|
continue;
|
|
42
44
|
versions[name] = version;
|
|
43
45
|
}
|
|
44
46
|
if (Object.keys(versions).length === 0) {
|
|
45
47
|
throw new Error(`create-throughline: no package versions found. Expected versions.json beside the ` +
|
|
46
|
-
`templates, or @forumone/throughline
|
|
48
|
+
`templates, or @forumone/throughline packages in ${packagesDir}.`);
|
|
47
49
|
}
|
|
48
50
|
return versions;
|
|
49
51
|
}
|
|
52
|
+
function isThroughline(name) {
|
|
53
|
+
return name === BARE_NAME || name.startsWith(SCOPE_PREFIX);
|
|
54
|
+
}
|
|
50
55
|
function templateKeys(versions) {
|
|
51
56
|
const keys = {};
|
|
52
57
|
for (const [name, version] of Object.entries(versions)) {
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
if (name === BARE_NAME)
|
|
59
|
+
keys['version_throughline'] = version;
|
|
60
|
+
else if (name.startsWith(SCOPE_PREFIX))
|
|
61
|
+
keys[`version_${name.slice(SCOPE_PREFIX.length).replace(/-/g, '_')}`] = version;
|
|
56
62
|
}
|
|
57
63
|
return keys;
|
|
58
64
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versions.js","sourceRoot":"","sources":["../../src/utils/versions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEzC,MAAM,YAAY,GAAG,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"versions.js","sourceRoot":"","sources":["../../src/utils/versions.ts"],"names":[],"mappings":"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"}
|
package/dist/versions.json
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"@forumone/throughline-
|
|
3
|
-
"@forumone/throughline
|
|
4
|
-
"@forumone/throughline-components": "0.6.0",
|
|
5
|
-
"@forumone/throughline-core": "0.10.0",
|
|
6
|
-
"@forumone/throughline-design-contract": "0.6.0",
|
|
7
|
-
"@forumone/throughline-email": "0.3.0",
|
|
8
|
-
"@forumone/throughline-forms": "0.7.0",
|
|
9
|
-
"@forumone/throughline-integrations": "0.9.0",
|
|
10
|
-
"@forumone/throughline-plugin-contract": "0.5.0",
|
|
11
|
-
"@forumone/throughline-publishing": "0.11.0",
|
|
12
|
-
"@forumone/throughline-reference-ds": "0.3.7",
|
|
13
|
-
"@forumone/throughline-workflows": "0.5.0"
|
|
2
|
+
"@forumone/throughline-design-system": "1.0.0-next.2",
|
|
3
|
+
"@forumone/throughline": "1.0.0-next.2"
|
|
14
4
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forumone/create-throughline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-next.2",
|
|
4
4
|
"description": "Scaffolder for new Throughline projects (Payload CMS + MCP servers + Inngest workflows).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"tsx": "^4.21.0",
|
|
51
51
|
"typescript": "^5.6.0",
|
|
52
52
|
"vitest": "^4.1.10",
|
|
53
|
-
"@forumone/throughline-
|
|
54
|
-
"@forumone/throughline-
|
|
53
|
+
"@forumone/throughline-tsconfig": "0.0.0",
|
|
54
|
+
"@forumone/throughline-eslint-config": "0.0.0"
|
|
55
55
|
},
|
|
56
56
|
"engines": {
|
|
57
57
|
"node": ">=20.9.0"
|