@forumone/throughline-workflows 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +135 -40
- package/dist/audit-event-echo.d.ts +14 -0
- package/dist/audit-event-echo.d.ts.map +1 -0
- package/dist/audit-event-echo.js +53 -0
- package/dist/audit-event-echo.js.map +1 -0
- package/dist/execute-scheduled-publishes.d.ts +23 -0
- package/dist/execute-scheduled-publishes.d.ts.map +1 -0
- package/dist/execute-scheduled-publishes.js +119 -0
- package/dist/execute-scheduled-publishes.js.map +1 -0
- package/dist/expire-stale-approvals.d.ts +11 -0
- package/dist/expire-stale-approvals.d.ts.map +1 -0
- package/dist/expire-stale-approvals.js +83 -0
- package/dist/expire-stale-approvals.js.map +1 -0
- package/dist/healthcheck.d.ts +23 -0
- package/dist/healthcheck.d.ts.map +1 -0
- package/dist/healthcheck.js +117 -0
- package/dist/healthcheck.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/revalidate-on-publish.d.ts +13 -0
- package/dist/revalidate-on-publish.d.ts.map +1 -0
- package/dist/revalidate-on-publish.js +59 -0
- package/dist/revalidate-on-publish.js.map +1 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -7
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @forumone/throughline-workflows
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 28f5af4: Initial release of the workflows package. Five composable Inngest function factories for the common async work in the framework: `createRevalidateOnPublishFunction` (Next.js cache invalidation on publish), `createExecuteScheduledPublishesFunction` (cron-driven scheduled publishes that go through the Publishing Server's MCP for full pipeline coverage), `createExpireStaleApprovalsFunction` (daily approval expiration with audit + `approval/expired` event), `createAuditEventEchoFunction` (fan-out for approval lifecycle plus pluggable custom handlers), `createHealthcheckFunction` (configurable checks with `onFailure` routing and a `system/healthcheck` heartbeat). Plus reusable `createPayloadReachableCheck` and `createManifestReachableCheck` helpers. No Payload plugin — factories only; client apps merge the functions into their Inngest endpoint. `next` is an optional peer dependency.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Forum One Communications Corporation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,45 +1,140 @@
|
|
|
1
1
|
# @forumone/throughline-workflows
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Composable Inngest function factories for the Throughline framework. Client apps import the factories they need and merge the functions into their Inngest endpoint.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package has **no Payload plugin** — it exports factories only. Workflows subscribe to events the server packages emit; they don't modify Payload configuration.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## What this package provides
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
9
|
+
| Factory | What it does |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `createRevalidateOnPublishFunction` | Invalidates Next.js caches for the page, listings, and sitemap when content publishes |
|
|
12
|
+
| `createExecuteScheduledPublishesFunction` | Cron-driven scheduled publish executor that calls through the Publishing Server's MCP |
|
|
13
|
+
| `createExpireStaleApprovalsFunction` | Daily cron that flips pending approvals past `expiresAt` to `expired` and fires an `approval/expired` event |
|
|
14
|
+
| `createAuditEventEchoFunction` | Fan-out point: turns `audit/event.recorded` into `notification/send-approval-*` events plus custom handlers |
|
|
15
|
+
| `createHealthcheckFunction` | Periodic health monitoring with configurable checks |
|
|
16
|
+
|
|
17
|
+
Plus two reusable check helpers used with the healthcheck factory:
|
|
18
|
+
|
|
19
|
+
- `createPayloadReachableCheck(slug?)` — verifies Payload can `find` from a collection
|
|
20
|
+
- `createManifestReachableCheck(url)` — verifies an HTTPS manifest URL responds 2xx
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add @forumone/throughline-workflows
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Peers: `payload@^3.0.0`, `inngest@^4.0.0`. `next` is an **optional** peer — install it only if you use `createRevalidateOnPublishFunction` with the default revalidator.
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
In your Next.js app's Inngest endpoint:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// src/app/api/inngest/route.ts
|
|
36
|
+
import { serve } from 'inngest/next'
|
|
37
|
+
import { getPayload } from 'payload'
|
|
38
|
+
import config from '@payload-config'
|
|
39
|
+
import { inngest } from '@/lib/inngest'
|
|
40
|
+
import {
|
|
41
|
+
createRevalidateOnPublishFunction,
|
|
42
|
+
createExecuteScheduledPublishesFunction,
|
|
43
|
+
createExpireStaleApprovalsFunction,
|
|
44
|
+
createAuditEventEchoFunction,
|
|
45
|
+
createHealthcheckFunction,
|
|
46
|
+
createPayloadReachableCheck,
|
|
47
|
+
} from '@forumone/throughline-workflows'
|
|
48
|
+
|
|
49
|
+
const payload = await getPayload({ config })
|
|
50
|
+
|
|
51
|
+
export const { GET, POST, PUT } = serve({
|
|
52
|
+
client: inngest,
|
|
53
|
+
functions: [
|
|
54
|
+
createRevalidateOnPublishFunction({ inngest, payload }),
|
|
55
|
+
createExecuteScheduledPublishesFunction({
|
|
56
|
+
inngest,
|
|
57
|
+
payload,
|
|
58
|
+
collections: [{ slug: 'pages' }, { slug: 'posts' }],
|
|
59
|
+
publishingServerUrl: process.env.NEXT_PUBLIC_SERVER_URL!,
|
|
60
|
+
// PUBLISHING_SYSTEM_API_KEY env var carries the api key
|
|
61
|
+
}),
|
|
62
|
+
createExpireStaleApprovalsFunction({ inngest, payload }),
|
|
63
|
+
createAuditEventEchoFunction({ inngest }),
|
|
64
|
+
createHealthcheckFunction({
|
|
65
|
+
inngest,
|
|
66
|
+
payload,
|
|
67
|
+
checks: [createPayloadReachableCheck()],
|
|
68
|
+
}),
|
|
69
|
+
],
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Why this lives outside the server packages
|
|
74
|
+
|
|
75
|
+
By C10, every server package fires Inngest events when consequential things happen. This package is the **subscriber side** — the functions that react to those events. Splitting the subscriber side from the publisher packages keeps the server packages from accumulating "and also runs this revalidation logic" appendices, and it lets clients pick which workflows they want without dragging in everything.
|
|
76
|
+
|
|
77
|
+
The factories-only shape (no Payload plugin) is the same reasoning. A workflow that wants to revalidate Next.js pages doesn't need to register a Payload collection. A scheduled-publish executor doesn't need a Payload hook. They're cron / event handlers that happen to read from Payload.
|
|
78
|
+
|
|
79
|
+
## Non-Next.js frontends
|
|
80
|
+
|
|
81
|
+
The default `revalidate` function dynamically imports `next/cache` and is a no-op anywhere `revalidatePath` / `revalidateTag` aren't available. For other frameworks supply your own:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
createRevalidateOnPublishFunction({
|
|
85
|
+
inngest,
|
|
86
|
+
payload,
|
|
87
|
+
revalidate: async ({ path, tags }) => {
|
|
88
|
+
if (path) await myCdn.purge(path)
|
|
89
|
+
for (const tag of tags) await myCdn.purgeTag(tag)
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`next` is declared as an optional peer (`peerDependenciesMeta.next.optional = true`) so non-Next consumers don't see a missing-peer warning.
|
|
95
|
+
|
|
96
|
+
## Why scheduled publishes call through the MCP
|
|
97
|
+
|
|
98
|
+
`createExecuteScheduledPublishesFunction` calls `POST /api/publishing/mcp` with a Bearer token rather than calling `payload.update` directly. That means scheduled publishes go through the same composition / accessibility / approval pipeline as Claude-initiated publishes — a composition error in a scheduled publish gets the same treatment as one caught interactively.
|
|
99
|
+
|
|
100
|
+
A composition error fails the publish loudly (audit log + `lastError`) instead of silently writing through.
|
|
101
|
+
|
|
102
|
+
## Customizing audit fan-out
|
|
103
|
+
|
|
104
|
+
The audit echo function wires the approval workflow by default. Add custom handlers for any other action:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
createAuditEventEchoFunction({
|
|
108
|
+
inngest,
|
|
109
|
+
handlers: [
|
|
110
|
+
{
|
|
111
|
+
match: (e) => e.action === 'integration.failed',
|
|
112
|
+
handle: async (e) => {
|
|
113
|
+
await inngest.send({
|
|
114
|
+
name: 'notification/send-alert',
|
|
115
|
+
data: { severity: 'error', summary: 'Integration failure', ...e.data },
|
|
116
|
+
})
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Each handler runs in its own `step.run`, so failures isolate.
|
|
124
|
+
|
|
125
|
+
## Options reference
|
|
126
|
+
|
|
127
|
+
Every factory takes a typed options object. See `src/types.ts` for the full surface — defaults documented there:
|
|
128
|
+
|
|
129
|
+
- `RevalidateOnPublishOptions` — `revalidate?`, `urlBuilders?`, `collectionTags?`, `id?`
|
|
130
|
+
- `ExecuteScheduledPublishesOptions` — `collections[]`, `publishingServerUrl`, `publishingApiKey?` (env fallback), `schedule?` (default `*/5 * * * *`), `id?`
|
|
131
|
+
- `ExpireStaleApprovalsOptions` — `collectionSlug?` (default `approvals`), `schedule?` (default `0 2 * * *`), `id?`
|
|
132
|
+
- `AuditEventEchoOptions` — `handlers?`, `id?`
|
|
133
|
+
- `HealthcheckOptions` — `checks[]`, `schedule?` (default `*/15 * * * *`), `onFailure?`, `id?`
|
|
134
|
+
|
|
135
|
+
## Related packages
|
|
136
|
+
|
|
137
|
+
- `@forumone/throughline-core` — required peer; provides the audit writer the approval-expiration cron uses
|
|
138
|
+
- `@forumone/throughline-publishing` — emits the publishing events the revalidation cron subscribes to
|
|
139
|
+
- `@forumone/throughline-approvals` — owns the approvals collection the expiration cron reads
|
|
140
|
+
- `@forumone/throughline-email` (C11) — will subscribe to `notification/send-approval-*`
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { InngestFunction } from 'inngest';
|
|
2
|
+
import type { AuditEventEchoOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Subscribes to `audit/event.recorded` and fans out to notification
|
|
5
|
+
* workflows. The built-in handlers cover the approval lifecycle (which
|
|
6
|
+
* the email package in C11 subscribes to); custom handlers in
|
|
7
|
+
* `options.handlers` add other fan-outs (e.g. "post integration.failed
|
|
8
|
+
* to a #alerts Slack channel").
|
|
9
|
+
*
|
|
10
|
+
* Each handler runs in its own `step.run` so a failure in one fan-out
|
|
11
|
+
* does not affect the others.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createAuditEventEchoFunction(options: AuditEventEchoOptions): InngestFunction.Any;
|
|
14
|
+
//# sourceMappingURL=audit-event-echo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-event-echo.d.ts","sourceRoot":"","sources":["../src/audit-event-echo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAiBvD;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,qBAAqB,GAAG,eAAe,CAAC,GAAG,CA0ChG"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const APPROVAL_DECISION_ACTIONS = new Set([
|
|
2
|
+
'approval.granted',
|
|
3
|
+
'approval.declined',
|
|
4
|
+
'approval.changes_requested',
|
|
5
|
+
]);
|
|
6
|
+
/**
|
|
7
|
+
* Subscribes to `audit/event.recorded` and fans out to notification
|
|
8
|
+
* workflows. The built-in handlers cover the approval lifecycle (which
|
|
9
|
+
* the email package in C11 subscribes to); custom handlers in
|
|
10
|
+
* `options.handlers` add other fan-outs (e.g. "post integration.failed
|
|
11
|
+
* to a #alerts Slack channel").
|
|
12
|
+
*
|
|
13
|
+
* Each handler runs in its own `step.run` so a failure in one fan-out
|
|
14
|
+
* does not affect the others.
|
|
15
|
+
*/
|
|
16
|
+
export function createAuditEventEchoFunction(options) {
|
|
17
|
+
return options.inngest.createFunction({
|
|
18
|
+
id: options.id ?? 'audit-event-echo',
|
|
19
|
+
triggers: [{ event: 'audit/event.recorded' }],
|
|
20
|
+
}, async ({ event, step, logger }) => {
|
|
21
|
+
const data = (event.data ?? {});
|
|
22
|
+
logger.info('Audit event echoed', { action: data.action });
|
|
23
|
+
await step.run('handle-approval-requested', async () => {
|
|
24
|
+
if (data.action === 'approval.requested' && data.approvalRequestId) {
|
|
25
|
+
await options.inngest.send({
|
|
26
|
+
name: 'notification/send-approval-request',
|
|
27
|
+
data: { approvalId: data.approvalRequestId },
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
await step.run('handle-approval-decided', async () => {
|
|
32
|
+
if (APPROVAL_DECISION_ACTIONS.has(data.action) && data.approvalRequestId) {
|
|
33
|
+
await options.inngest.send({
|
|
34
|
+
name: 'notification/send-approval-decision',
|
|
35
|
+
data: { approvalId: data.approvalRequestId, decision: data.action },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const handlers = options.handlers ?? [];
|
|
40
|
+
for (let index = 0; index < handlers.length; index += 1) {
|
|
41
|
+
const handler = handlers[index];
|
|
42
|
+
await step.run(`custom-handler-${index}`, async () => {
|
|
43
|
+
if (handler.match({ action: data.action })) {
|
|
44
|
+
await handler.handle({
|
|
45
|
+
action: data.action,
|
|
46
|
+
data: data,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=audit-event-echo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-event-echo.js","sourceRoot":"","sources":["../src/audit-event-echo.ts"],"names":[],"mappings":"AAYA,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAC;IACxC,kBAAkB;IAClB,mBAAmB;IACnB,4BAA4B;CAC7B,CAAC,CAAA;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAAC,OAA8B;IACzE,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,CACnC;QACE,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,kBAAkB;QACpC,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC;KAC9C,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAsB,CAAA;QACpD,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;QAE1D,MAAM,IAAI,CAAC,GAAG,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;YACrD,IAAI,IAAI,CAAC,MAAM,KAAK,oBAAoB,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBACnE,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;oBACzB,IAAI,EAAE,oCAAoC;oBAC1C,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE;iBAC7C,CAAC,CAAA;YACJ,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,MAAM,IAAI,CAAC,GAAG,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;YACnD,IAAI,yBAAyB,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBACzE,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;oBACzB,IAAI,EAAE,qCAAqC;oBAC3C,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE;iBACpE,CAAC,CAAA;YACJ,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAA;QACvC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YACxD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAE,CAAA;YAChC,MAAM,IAAI,CAAC,GAAG,CAAC,kBAAkB,KAAK,EAAE,EAAE,KAAK,IAAI,EAAE;gBACnD,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;oBAC3C,MAAM,OAAO,CAAC,MAAM,CAAC;wBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;wBACnB,IAAI,EAAE,IAA0C;qBACjD,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { InngestFunction } from 'inngest';
|
|
2
|
+
import type { ExecuteScheduledPublishesOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Cron-driven scheduled publish executor. Every tick (default: every 5
|
|
5
|
+
* minutes) the function looks for documents in any of the configured
|
|
6
|
+
* collections whose `_status` is still `draft` and whose
|
|
7
|
+
* `scheduledPublishAt` has passed. For each, it calls the publishing
|
|
8
|
+
* server's MCP `publish` tool — going through the full publishing pipeline
|
|
9
|
+
* means scheduled publishes get the same composition / accessibility /
|
|
10
|
+
* approval checks as interactive publishes.
|
|
11
|
+
*
|
|
12
|
+
* The `publishingApiKey` (or `PUBLISHING_SYSTEM_API_KEY` env var) must
|
|
13
|
+
* carry `publishing.execute` scope. The factory throws at construction if
|
|
14
|
+
* neither is present so misconfigurations surface immediately.
|
|
15
|
+
*
|
|
16
|
+
* Failures from the publishing MCP are logged but do not throw; the
|
|
17
|
+
* unpublished document stays at `_status: draft` until either an admin
|
|
18
|
+
* intervenes or the next tick succeeds. Throwing would retry indefinitely
|
|
19
|
+
* on a permanent error (e.g. composition check failure), which is wrong
|
|
20
|
+
* for cron-style work.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createExecuteScheduledPublishesFunction(options: ExecuteScheduledPublishesOptions): InngestFunction.Any;
|
|
23
|
+
//# sourceMappingURL=execute-scheduled-publishes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute-scheduled-publishes.d.ts","sourceRoot":"","sources":["../src/execute-scheduled-publishes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,YAAY,CAAA;AAelE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uCAAuC,CACrD,OAAO,EAAE,gCAAgC,GACxC,eAAe,CAAC,GAAG,CA4DrB"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const DEFAULT_SCHEDULE = '*/5 * * * *';
|
|
2
|
+
/**
|
|
3
|
+
* Cron-driven scheduled publish executor. Every tick (default: every 5
|
|
4
|
+
* minutes) the function looks for documents in any of the configured
|
|
5
|
+
* collections whose `_status` is still `draft` and whose
|
|
6
|
+
* `scheduledPublishAt` has passed. For each, it calls the publishing
|
|
7
|
+
* server's MCP `publish` tool — going through the full publishing pipeline
|
|
8
|
+
* means scheduled publishes get the same composition / accessibility /
|
|
9
|
+
* approval checks as interactive publishes.
|
|
10
|
+
*
|
|
11
|
+
* The `publishingApiKey` (or `PUBLISHING_SYSTEM_API_KEY` env var) must
|
|
12
|
+
* carry `publishing.execute` scope. The factory throws at construction if
|
|
13
|
+
* neither is present so misconfigurations surface immediately.
|
|
14
|
+
*
|
|
15
|
+
* Failures from the publishing MCP are logged but do not throw; the
|
|
16
|
+
* unpublished document stays at `_status: draft` until either an admin
|
|
17
|
+
* intervenes or the next tick succeeds. Throwing would retry indefinitely
|
|
18
|
+
* on a permanent error (e.g. composition check failure), which is wrong
|
|
19
|
+
* for cron-style work.
|
|
20
|
+
*/
|
|
21
|
+
export function createExecuteScheduledPublishesFunction(options) {
|
|
22
|
+
const apiKey = options.publishingApiKey ?? process.env['PUBLISHING_SYSTEM_API_KEY'];
|
|
23
|
+
if (!apiKey) {
|
|
24
|
+
throw new Error('createExecuteScheduledPublishesFunction requires options.publishingApiKey or the PUBLISHING_SYSTEM_API_KEY env var.');
|
|
25
|
+
}
|
|
26
|
+
const schedule = options.schedule ?? DEFAULT_SCHEDULE;
|
|
27
|
+
const baseUrl = options.publishingServerUrl.replace(/\/$/, '');
|
|
28
|
+
return options.inngest.createFunction({
|
|
29
|
+
id: options.id ?? 'execute-scheduled-publishes',
|
|
30
|
+
triggers: [{ cron: schedule }],
|
|
31
|
+
}, async ({ step, logger }) => {
|
|
32
|
+
const nowIso = new Date().toISOString();
|
|
33
|
+
let publishedCount = 0;
|
|
34
|
+
let blockedCount = 0;
|
|
35
|
+
for (const config of options.collections) {
|
|
36
|
+
const statusField = config.statusField ?? '_status';
|
|
37
|
+
const scheduledField = config.scheduledField ?? 'scheduledPublishAt';
|
|
38
|
+
const due = await step.run(`find-due-${config.slug}`, async () => {
|
|
39
|
+
const result = await options.payload.find({
|
|
40
|
+
collection: config.slug,
|
|
41
|
+
where: {
|
|
42
|
+
and: [
|
|
43
|
+
{ [statusField]: { equals: 'draft' } },
|
|
44
|
+
{ [scheduledField]: { exists: true } },
|
|
45
|
+
{ [scheduledField]: { less_than_equal: nowIso } },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
limit: 100,
|
|
49
|
+
});
|
|
50
|
+
return result.docs.map((doc) => ({
|
|
51
|
+
id: String(doc['id']),
|
|
52
|
+
title: String(doc['title'] ?? doc['id']),
|
|
53
|
+
collection: config.slug,
|
|
54
|
+
}));
|
|
55
|
+
});
|
|
56
|
+
for (const doc of due) {
|
|
57
|
+
const outcome = await step.run(`publish-${config.slug}-${doc.id}`, async () => {
|
|
58
|
+
return executePublish({ doc, baseUrl, apiKey, logger });
|
|
59
|
+
});
|
|
60
|
+
if (outcome === 'published')
|
|
61
|
+
publishedCount += 1;
|
|
62
|
+
if (outcome === 'blocked')
|
|
63
|
+
blockedCount += 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
logger.info('Scheduled publish tick complete', { publishedCount, blockedCount });
|
|
67
|
+
return { publishedCount, blockedCount };
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function executePublish({ doc, baseUrl, apiKey, logger, }) {
|
|
71
|
+
let response;
|
|
72
|
+
try {
|
|
73
|
+
response = await fetch(`${baseUrl}/api/publishing/mcp`, {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: {
|
|
76
|
+
authorization: `Bearer ${apiKey}`,
|
|
77
|
+
'content-type': 'application/json',
|
|
78
|
+
},
|
|
79
|
+
body: JSON.stringify({
|
|
80
|
+
jsonrpc: '2.0',
|
|
81
|
+
id: 1,
|
|
82
|
+
method: 'tools/call',
|
|
83
|
+
params: {
|
|
84
|
+
name: 'publish',
|
|
85
|
+
arguments: {
|
|
86
|
+
collection: doc.collection,
|
|
87
|
+
id: doc.id,
|
|
88
|
+
_meta: { reasoning: 'Scheduled publish executed by workflow cron' },
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
logger.error('Scheduled publish HTTP error', {
|
|
96
|
+
document: doc.title,
|
|
97
|
+
error: error instanceof Error ? error.message : String(error),
|
|
98
|
+
});
|
|
99
|
+
return 'error';
|
|
100
|
+
}
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
logger.error('Scheduled publish HTTP non-2xx', {
|
|
103
|
+
document: doc.title,
|
|
104
|
+
status: response.status,
|
|
105
|
+
});
|
|
106
|
+
return 'error';
|
|
107
|
+
}
|
|
108
|
+
const body = (await response.json());
|
|
109
|
+
if (body.error) {
|
|
110
|
+
logger.warn('Scheduled publish blocked by policy', {
|
|
111
|
+
document: doc.title,
|
|
112
|
+
reason: body.error.message,
|
|
113
|
+
});
|
|
114
|
+
return 'blocked';
|
|
115
|
+
}
|
|
116
|
+
logger.info('Scheduled publish succeeded', { document: doc.title });
|
|
117
|
+
return 'published';
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=execute-scheduled-publishes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute-scheduled-publishes.js","sourceRoot":"","sources":["../src/execute-scheduled-publishes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InngestFunction } from 'inngest';
|
|
2
|
+
import type { ExpireStaleApprovalsOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Cron that finds pending approvals whose `expiresAt` has passed and flips
|
|
5
|
+
* them to `status: 'expired'`. For each expired record it writes an
|
|
6
|
+
* `approval.expired` audit event and fires an `approval/expired` Inngest
|
|
7
|
+
* event so notification workflows (e.g. C11 email) can let the requester
|
|
8
|
+
* know without polling.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createExpireStaleApprovalsFunction(options: ExpireStaleApprovalsOptions): InngestFunction.Any;
|
|
11
|
+
//# sourceMappingURL=expire-stale-approvals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expire-stale-approvals.d.ts","sourceRoot":"","sources":["../src/expire-stale-approvals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAa7D;;;;;;GAMG;AACH,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,2BAA2B,GACnC,eAAe,CAAC,GAAG,CA0ErB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { getAuditWriter } from '@forumone/throughline-core';
|
|
2
|
+
const DEFAULT_SCHEDULE = '0 2 * * *'; // daily at 2am UTC
|
|
3
|
+
const DEFAULT_SLUG = 'approvals';
|
|
4
|
+
/**
|
|
5
|
+
* Cron that finds pending approvals whose `expiresAt` has passed and flips
|
|
6
|
+
* them to `status: 'expired'`. For each expired record it writes an
|
|
7
|
+
* `approval.expired` audit event and fires an `approval/expired` Inngest
|
|
8
|
+
* event so notification workflows (e.g. C11 email) can let the requester
|
|
9
|
+
* know without polling.
|
|
10
|
+
*/
|
|
11
|
+
export function createExpireStaleApprovalsFunction(options) {
|
|
12
|
+
const schedule = options.schedule ?? DEFAULT_SCHEDULE;
|
|
13
|
+
const collectionSlug = options.collectionSlug ?? DEFAULT_SLUG;
|
|
14
|
+
return options.inngest.createFunction({
|
|
15
|
+
id: options.id ?? 'expire-stale-approvals',
|
|
16
|
+
triggers: [{ cron: schedule }],
|
|
17
|
+
}, async ({ step, logger }) => {
|
|
18
|
+
const now = new Date().toISOString();
|
|
19
|
+
const expired = await step.run('find-expired', async () => {
|
|
20
|
+
const result = await options.payload.find({
|
|
21
|
+
collection: collectionSlug,
|
|
22
|
+
where: {
|
|
23
|
+
and: [
|
|
24
|
+
{ status: { equals: 'pending' } },
|
|
25
|
+
{ expiresAt: { less_than: now } },
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
limit: 500,
|
|
29
|
+
});
|
|
30
|
+
return result.docs.map((doc) => ({
|
|
31
|
+
id: String(doc['id']),
|
|
32
|
+
targetCollection: String(doc['targetCollection'] ?? ''),
|
|
33
|
+
targetId: String(doc['targetId'] ?? ''),
|
|
34
|
+
targetTitle: String(doc['targetTitle'] ?? doc['targetId'] ?? '(unknown)'),
|
|
35
|
+
requesterId: extractRequesterId(doc['requestedBy']),
|
|
36
|
+
}));
|
|
37
|
+
});
|
|
38
|
+
if (expired.length === 0) {
|
|
39
|
+
logger.info('No stale approvals to expire');
|
|
40
|
+
return { expiredCount: 0 };
|
|
41
|
+
}
|
|
42
|
+
const auditWriter = getAuditWriter(options.payload);
|
|
43
|
+
for (const approval of expired) {
|
|
44
|
+
await step.run(`expire-${approval.id}`, async () => {
|
|
45
|
+
await options.payload.update({
|
|
46
|
+
collection: collectionSlug,
|
|
47
|
+
id: approval.id,
|
|
48
|
+
data: { status: 'expired' },
|
|
49
|
+
});
|
|
50
|
+
await auditWriter({
|
|
51
|
+
actor: { type: 'system', apiKeyName: 'workflow:expire-stale-approvals' },
|
|
52
|
+
action: 'approval.expired',
|
|
53
|
+
mcpServer: 'approvals',
|
|
54
|
+
mcpTool: 'expire-stale-approvals',
|
|
55
|
+
targetCollection: approval.targetCollection,
|
|
56
|
+
targetId: approval.targetId,
|
|
57
|
+
targetTitle: approval.targetTitle,
|
|
58
|
+
approvalRequestId: approval.id,
|
|
59
|
+
});
|
|
60
|
+
await options.inngest.send({
|
|
61
|
+
name: 'approval/expired',
|
|
62
|
+
data: {
|
|
63
|
+
approvalId: approval.id,
|
|
64
|
+
requesterId: approval.requesterId,
|
|
65
|
+
targetCollection: approval.targetCollection,
|
|
66
|
+
targetId: approval.targetId,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
logger.info('Expired stale approvals', { count: expired.length });
|
|
72
|
+
return { expiredCount: expired.length };
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function extractRequesterId(value) {
|
|
76
|
+
if (typeof value === 'string')
|
|
77
|
+
return value;
|
|
78
|
+
if (value && typeof value === 'object' && 'id' in value) {
|
|
79
|
+
return String(value.id);
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=expire-stale-approvals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expire-stale-approvals.js","sourceRoot":"","sources":["../src/expire-stale-approvals.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { InngestFunction } from 'inngest';
|
|
2
|
+
import type { HealthcheckDefinition, HealthcheckOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Cron healthcheck. Runs every check in its own `step.run` so one
|
|
5
|
+
* misbehaving probe can't take down the rest, collects failures, and
|
|
6
|
+
* routes them to `onFailure` (defaults to `console.error`). Always fires
|
|
7
|
+
* a `system/healthcheck` Inngest event so external dashboards can
|
|
8
|
+
* observe heartbeats independently of failure routing.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createHealthcheckFunction(options: HealthcheckOptions): InngestFunction.Any;
|
|
11
|
+
/**
|
|
12
|
+
* Check that Payload is reachable and able to query a collection. Defaults
|
|
13
|
+
* to `users` because every Payload deployment has it; pass a different
|
|
14
|
+
* slug if `users` isn't always present (rare).
|
|
15
|
+
*/
|
|
16
|
+
export declare function createPayloadReachableCheck(collectionSlug?: string): HealthcheckDefinition;
|
|
17
|
+
/**
|
|
18
|
+
* Check that a manifest URL is reachable. Aimed at the design-system
|
|
19
|
+
* manifest the components server consults, but works for any HTTPS
|
|
20
|
+
* endpoint that responds 2xx to a GET.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createManifestReachableCheck(manifestUrl: string): HealthcheckDefinition;
|
|
23
|
+
//# sourceMappingURL=healthcheck.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"healthcheck.d.ts","sourceRoot":"","sources":["../src/healthcheck.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAqB,MAAM,YAAY,CAAA;AAK9F;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,kBAAkB,GAAG,eAAe,CAAC,GAAG,CA4D1F;AAWD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,cAAc,GAAE,MAAgB,GAC/B,qBAAqB,CAevB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,GAAG,qBAAqB,CAkBvF"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const DEFAULT_SCHEDULE = '*/15 * * * *';
|
|
2
|
+
const REACHABLE_TIMEOUT_MS = 5_000;
|
|
3
|
+
/**
|
|
4
|
+
* Cron healthcheck. Runs every check in its own `step.run` so one
|
|
5
|
+
* misbehaving probe can't take down the rest, collects failures, and
|
|
6
|
+
* routes them to `onFailure` (defaults to `console.error`). Always fires
|
|
7
|
+
* a `system/healthcheck` Inngest event so external dashboards can
|
|
8
|
+
* observe heartbeats independently of failure routing.
|
|
9
|
+
*/
|
|
10
|
+
export function createHealthcheckFunction(options) {
|
|
11
|
+
const schedule = options.schedule ?? DEFAULT_SCHEDULE;
|
|
12
|
+
const onFailure = options.onFailure ?? defaultOnFailure;
|
|
13
|
+
return options.inngest.createFunction({
|
|
14
|
+
id: options.id ?? 'healthcheck',
|
|
15
|
+
triggers: [{ cron: schedule }],
|
|
16
|
+
}, async ({ step, logger }) => {
|
|
17
|
+
const results = [];
|
|
18
|
+
for (const check of options.checks) {
|
|
19
|
+
const outcome = await step.run(`check-${check.name}`, async () => {
|
|
20
|
+
try {
|
|
21
|
+
const result = await check.run({ payload: options.payload });
|
|
22
|
+
return { name: check.name, ...result };
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return {
|
|
26
|
+
name: check.name,
|
|
27
|
+
ok: false,
|
|
28
|
+
details: error instanceof Error ? error.message : 'Unknown error',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
results.push(outcome);
|
|
33
|
+
}
|
|
34
|
+
const failures = results
|
|
35
|
+
.filter((r) => !r.ok)
|
|
36
|
+
.map((r) => {
|
|
37
|
+
const entry = { name: r.name };
|
|
38
|
+
if (r.details)
|
|
39
|
+
entry.details = r.details;
|
|
40
|
+
return entry;
|
|
41
|
+
});
|
|
42
|
+
if (failures.length > 0) {
|
|
43
|
+
await step.run('report-failures', async () => {
|
|
44
|
+
await onFailure(failures);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
await step.run('emit-heartbeat', async () => {
|
|
48
|
+
await options.inngest.send({
|
|
49
|
+
name: 'system/healthcheck',
|
|
50
|
+
data: {
|
|
51
|
+
source: 'workflow',
|
|
52
|
+
timestamp: new Date().toISOString(),
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
logger.info('Healthcheck complete', {
|
|
57
|
+
totalChecks: results.length,
|
|
58
|
+
failures: failures.length,
|
|
59
|
+
});
|
|
60
|
+
return { results, failureCount: failures.length };
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async function defaultOnFailure(failures) {
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
65
|
+
console.error(`[healthcheck] ${failures.length} check(s) failed: ${failures
|
|
66
|
+
.map((f) => `${f.name}: ${f.details ?? 'no details'}`)
|
|
67
|
+
.join('; ')}`);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check that Payload is reachable and able to query a collection. Defaults
|
|
71
|
+
* to `users` because every Payload deployment has it; pass a different
|
|
72
|
+
* slug if `users` isn't always present (rare).
|
|
73
|
+
*/
|
|
74
|
+
export function createPayloadReachableCheck(collectionSlug = 'users') {
|
|
75
|
+
return {
|
|
76
|
+
name: 'payload-reachable',
|
|
77
|
+
run: async ({ payload }) => {
|
|
78
|
+
try {
|
|
79
|
+
await payload.find({ collection: collectionSlug, limit: 1 });
|
|
80
|
+
return { ok: true };
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
details: error instanceof Error ? error.message : 'Unknown error',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check that a manifest URL is reachable. Aimed at the design-system
|
|
93
|
+
* manifest the components server consults, but works for any HTTPS
|
|
94
|
+
* endpoint that responds 2xx to a GET.
|
|
95
|
+
*/
|
|
96
|
+
export function createManifestReachableCheck(manifestUrl) {
|
|
97
|
+
return {
|
|
98
|
+
name: 'manifest-reachable',
|
|
99
|
+
run: async () => {
|
|
100
|
+
try {
|
|
101
|
+
const response = await fetch(manifestUrl, {
|
|
102
|
+
signal: AbortSignal.timeout(REACHABLE_TIMEOUT_MS),
|
|
103
|
+
});
|
|
104
|
+
if (response.ok)
|
|
105
|
+
return { ok: true };
|
|
106
|
+
return { ok: false, details: `HTTP ${response.status}` };
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
details: error instanceof Error ? error.message : 'Unknown error',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=healthcheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"healthcheck.js","sourceRoot":"","sources":["../src/healthcheck.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createRevalidateOnPublishFunction } from './revalidate-on-publish.js';
|
|
2
|
+
export { createExecuteScheduledPublishesFunction } from './execute-scheduled-publishes.js';
|
|
3
|
+
export { createExpireStaleApprovalsFunction } from './expire-stale-approvals.js';
|
|
4
|
+
export { createAuditEventEchoFunction } from './audit-event-echo.js';
|
|
5
|
+
export { createHealthcheckFunction, createPayloadReachableCheck, createManifestReachableCheck, } from './healthcheck.js';
|
|
6
|
+
export type { BaseWorkflowOptions, RevalidateFn, RevalidatePathsInput, RevalidateOnPublishOptions, ScheduledCollectionConfig, ExecuteScheduledPublishesOptions, ExpireStaleApprovalsOptions, AuditEchoEvent, AuditEchoHandler, AuditEventEchoOptions, HealthcheckDefinition, HealthcheckOptions, HealthcheckResult, } from './types.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAA;AAC9E,OAAO,EAAE,uCAAuC,EAAE,MAAM,kCAAkC,CAAA;AAC1F,OAAO,EAAE,kCAAkC,EAAE,MAAM,6BAA6B,CAAA;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAA;AACpE,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,kBAAkB,CAAA;AAEzB,YAAY,EACV,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,2BAA2B,EAC3B,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Other factory exports land in subsequent commits.
|
|
2
|
+
export { createRevalidateOnPublishFunction } from './revalidate-on-publish.js';
|
|
3
|
+
export { createExecuteScheduledPublishesFunction } from './execute-scheduled-publishes.js';
|
|
4
|
+
export { createExpireStaleApprovalsFunction } from './expire-stale-approvals.js';
|
|
5
|
+
export { createAuditEventEchoFunction } from './audit-event-echo.js';
|
|
6
|
+
export { createHealthcheckFunction, createPayloadReachableCheck, createManifestReachableCheck, } from './healthcheck.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oDAAoD;AAEpD,OAAO,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAA;AAC9E,OAAO,EAAE,uCAAuC,EAAE,MAAM,kCAAkC,CAAA;AAC1F,OAAO,EAAE,kCAAkC,EAAE,MAAM,6BAA6B,CAAA;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAA;AACpE,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { InngestFunction } from 'inngest';
|
|
2
|
+
import type { RevalidateOnPublishOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds the `revalidate-on-publish` Inngest function. Subscribes to the
|
|
5
|
+
* publishing taxonomy and revalidates Next.js cache entries for the affected
|
|
6
|
+
* page, the listing routes, and the sitemap.
|
|
7
|
+
*
|
|
8
|
+
* The default revalidate function dynamically imports `next/cache`, so the
|
|
9
|
+
* package is safe to install in non-Next.js contexts. Pass `options.revalidate`
|
|
10
|
+
* to use a different cache invalidation strategy.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createRevalidateOnPublishFunction(options: RevalidateOnPublishOptions): InngestFunction.Any;
|
|
13
|
+
//# sourceMappingURL=revalidate-on-publish.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revalidate-on-publish.d.ts","sourceRoot":"","sources":["../src/revalidate-on-publish.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAgB,0BAA0B,EAAwB,MAAM,YAAY,CAAA;AAOhG;;;;;;;;GAQG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,0BAA0B,GAClC,eAAe,CAAC,GAAG,CAuCrB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const DEFAULT_URL_BUILDERS = {
|
|
2
|
+
pages: (slug) => (slug === 'home' || slug === '' ? '/' : `/${slug}`),
|
|
3
|
+
posts: (slug) => `/blog/${slug}`,
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Builds the `revalidate-on-publish` Inngest function. Subscribes to the
|
|
7
|
+
* publishing taxonomy and revalidates Next.js cache entries for the affected
|
|
8
|
+
* page, the listing routes, and the sitemap.
|
|
9
|
+
*
|
|
10
|
+
* The default revalidate function dynamically imports `next/cache`, so the
|
|
11
|
+
* package is safe to install in non-Next.js contexts. Pass `options.revalidate`
|
|
12
|
+
* to use a different cache invalidation strategy.
|
|
13
|
+
*/
|
|
14
|
+
export function createRevalidateOnPublishFunction(options) {
|
|
15
|
+
const urlBuilders = { ...DEFAULT_URL_BUILDERS, ...options.urlBuilders };
|
|
16
|
+
const collectionTags = options.collectionTags ?? {};
|
|
17
|
+
const revalidate = options.revalidate ?? defaultRevalidate;
|
|
18
|
+
return options.inngest.createFunction({
|
|
19
|
+
id: options.id ?? 'revalidate-on-publish',
|
|
20
|
+
retries: 5,
|
|
21
|
+
triggers: [
|
|
22
|
+
{ event: 'content/page.published' },
|
|
23
|
+
{ event: 'content/page.unpublished' },
|
|
24
|
+
{ event: 'content/page.rolled_back' },
|
|
25
|
+
],
|
|
26
|
+
}, async ({ event, step, logger }) => {
|
|
27
|
+
const data = (event.data ?? {});
|
|
28
|
+
const collection = data.collection ?? 'pages';
|
|
29
|
+
const slug = data.slug ?? data.id ?? '';
|
|
30
|
+
const tags = collectionTags[collection] ?? [collection];
|
|
31
|
+
await step.run('revalidate-page-path', async () => {
|
|
32
|
+
const builder = urlBuilders[collection] ?? ((s) => `/${s}`);
|
|
33
|
+
const path = builder(slug);
|
|
34
|
+
await revalidate({ path, tags });
|
|
35
|
+
logger.info('Revalidated page path', { path, tags });
|
|
36
|
+
});
|
|
37
|
+
await step.run('revalidate-listings', async () => {
|
|
38
|
+
await revalidate({ path: '', tags });
|
|
39
|
+
});
|
|
40
|
+
await step.run('revalidate-sitemap', async () => {
|
|
41
|
+
await revalidate({ path: '/sitemap.xml', tags: ['sitemap'] });
|
|
42
|
+
});
|
|
43
|
+
return { collection, slug, revalidated: true };
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Default revalidator: dynamic-imports `next/cache` so the package can be
|
|
48
|
+
* imported in environments without Next.js (e.g. test runners that don't
|
|
49
|
+
* stub the module). Calls `revalidatePath` only when `path` is non-empty
|
|
50
|
+
* because `revalidatePath('')` triggers a noisy Next.js warning.
|
|
51
|
+
*/
|
|
52
|
+
const defaultRevalidate = async ({ path, tags }) => {
|
|
53
|
+
const { revalidatePath, revalidateTag } = (await import('next/cache'));
|
|
54
|
+
if (path)
|
|
55
|
+
revalidatePath(path);
|
|
56
|
+
for (const tag of tags)
|
|
57
|
+
revalidateTag(tag);
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=revalidate-on-publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revalidate-on-publish.js","sourceRoot":"","sources":["../src/revalidate-on-publish.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { Inngest } from 'inngest';
|
|
2
|
+
import type { Payload } from 'payload';
|
|
3
|
+
/**
|
|
4
|
+
* Common dependencies for workflow factories. Every factory takes an
|
|
5
|
+
* Inngest client and a Payload instance — the workflows-as-factories shape
|
|
6
|
+
* means the client app is responsible for instantiating both and passing
|
|
7
|
+
* them in.
|
|
8
|
+
*/
|
|
9
|
+
export interface BaseWorkflowOptions {
|
|
10
|
+
inngest: Inngest;
|
|
11
|
+
payload: Payload;
|
|
12
|
+
}
|
|
13
|
+
export interface RevalidatePathsInput {
|
|
14
|
+
/** A page path (`/about`, `/blog/post-1`, or `''` for layout-level only). */
|
|
15
|
+
path: string;
|
|
16
|
+
/** Tag list passed through to Next.js's `revalidateTag`. */
|
|
17
|
+
tags: string[];
|
|
18
|
+
}
|
|
19
|
+
export type RevalidateFn = (input: RevalidatePathsInput) => Promise<void>;
|
|
20
|
+
export interface RevalidateOnPublishOptions extends BaseWorkflowOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Custom revalidation function. Defaults to Next.js `revalidatePath` +
|
|
23
|
+
* `revalidateTag` (loaded via dynamic import so non-Next.js consumers
|
|
24
|
+
* don't fail to import the package). Supply your own for non-Next.js
|
|
25
|
+
* frontends or richer cache invalidation.
|
|
26
|
+
*/
|
|
27
|
+
revalidate?: RevalidateFn;
|
|
28
|
+
/**
|
|
29
|
+
* Per-collection URL builders. Built-in defaults: pages → /slug
|
|
30
|
+
* (with `home` mapped to `/`), posts → /blog/slug. Other collections
|
|
31
|
+
* fall back to /<slug> unless you provide a builder.
|
|
32
|
+
*/
|
|
33
|
+
urlBuilders?: Record<string, (slug: string) => string>;
|
|
34
|
+
/**
|
|
35
|
+
* Per-collection cache tags. Defaults to `[<collection>]`. Multiple tags
|
|
36
|
+
* supported for collections that participate in shared listings (e.g.
|
|
37
|
+
* `programs` → `['programs', 'sitemap']`).
|
|
38
|
+
*/
|
|
39
|
+
collectionTags?: Record<string, string[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Override the function id. Default: `revalidate-on-publish`.
|
|
42
|
+
* Useful if multiple instances of the workflow run in the same Inngest
|
|
43
|
+
* deployment (rare).
|
|
44
|
+
*/
|
|
45
|
+
id?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface ScheduledCollectionConfig {
|
|
48
|
+
slug: string;
|
|
49
|
+
/**
|
|
50
|
+
* Field name carrying the publish status. Default: `_status`.
|
|
51
|
+
* Override only if the collection uses a non-default status field.
|
|
52
|
+
*/
|
|
53
|
+
statusField?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Field name carrying the scheduled-publish timestamp. Default: `scheduledPublishAt`.
|
|
56
|
+
*/
|
|
57
|
+
scheduledField?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface ExecuteScheduledPublishesOptions extends BaseWorkflowOptions {
|
|
60
|
+
/** Collections that participate in scheduled publishing. */
|
|
61
|
+
collections: ScheduledCollectionConfig[];
|
|
62
|
+
/** Cron schedule. Default: `*\/5 * * * *` (every 5 minutes). */
|
|
63
|
+
schedule?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Base URL of the deployment's publishing server. Scheduled publishes
|
|
66
|
+
* call through the publishing MCP so the full pipeline runs (composition
|
|
67
|
+
* checks, accessibility, approval gating). Direct Payload writes would
|
|
68
|
+
* skip those checks.
|
|
69
|
+
*/
|
|
70
|
+
publishingServerUrl: string;
|
|
71
|
+
/**
|
|
72
|
+
* API-key value for `Authorization: Bearer <key>` against the publishing
|
|
73
|
+
* MCP. Falls back to `process.env.PUBLISHING_SYSTEM_API_KEY` when omitted.
|
|
74
|
+
* Required at construction time — the factory throws if neither is set.
|
|
75
|
+
*/
|
|
76
|
+
publishingApiKey?: string;
|
|
77
|
+
/** Override the function id. Default: `execute-scheduled-publishes`. */
|
|
78
|
+
id?: string;
|
|
79
|
+
}
|
|
80
|
+
export interface ExpireStaleApprovalsOptions extends BaseWorkflowOptions {
|
|
81
|
+
/** Approvals collection slug. Default: 'approvals'. */
|
|
82
|
+
collectionSlug?: string;
|
|
83
|
+
/** Cron schedule. Default: `0 2 * * *` (daily at 2am UTC). */
|
|
84
|
+
schedule?: string;
|
|
85
|
+
/** Override the function id. Default: `expire-stale-approvals`. */
|
|
86
|
+
id?: string;
|
|
87
|
+
}
|
|
88
|
+
export interface AuditEchoEvent {
|
|
89
|
+
action: string;
|
|
90
|
+
data: Record<string, unknown>;
|
|
91
|
+
}
|
|
92
|
+
export interface AuditEchoHandler {
|
|
93
|
+
/** Predicate. Return true to run `handle`. */
|
|
94
|
+
match: (event: {
|
|
95
|
+
action: string;
|
|
96
|
+
}) => boolean;
|
|
97
|
+
/** Side-effects (e.g. fire a follow-on Inngest event). */
|
|
98
|
+
handle: (event: AuditEchoEvent) => Promise<void>;
|
|
99
|
+
}
|
|
100
|
+
export interface AuditEventEchoOptions {
|
|
101
|
+
inngest: Inngest;
|
|
102
|
+
/**
|
|
103
|
+
* Additional handlers run after the built-in approval fan-out. Each
|
|
104
|
+
* handler is wrapped in its own `step.run` so failures retry independently.
|
|
105
|
+
*/
|
|
106
|
+
handlers?: AuditEchoHandler[];
|
|
107
|
+
/** Override the function id. Default: `audit-event-echo`. */
|
|
108
|
+
id?: string;
|
|
109
|
+
}
|
|
110
|
+
export interface HealthcheckResult {
|
|
111
|
+
ok: boolean;
|
|
112
|
+
details?: string;
|
|
113
|
+
}
|
|
114
|
+
export interface HealthcheckDefinition {
|
|
115
|
+
name: string;
|
|
116
|
+
run: (ctx: {
|
|
117
|
+
payload: Payload;
|
|
118
|
+
}) => Promise<HealthcheckResult>;
|
|
119
|
+
}
|
|
120
|
+
export interface HealthcheckOptions extends BaseWorkflowOptions {
|
|
121
|
+
checks: HealthcheckDefinition[];
|
|
122
|
+
/** Cron schedule. Default: `*\/15 * * * *`. */
|
|
123
|
+
schedule?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Called once per run when at least one check fails. Defaults to
|
|
126
|
+
* `console.error`; production deployments route this to monitoring.
|
|
127
|
+
*/
|
|
128
|
+
onFailure?: (failures: Array<{
|
|
129
|
+
name: string;
|
|
130
|
+
details?: string;
|
|
131
|
+
}>) => Promise<void>;
|
|
132
|
+
/** Override the function id. Default: `healthcheck`. */
|
|
133
|
+
id?: string;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,10 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forumone/throughline-workflows",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Composable Inngest function factories for the Throughline framework — revalidation, scheduled publishing, approval expiration, audit fan-out, healthcheck.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"CHANGELOG.md"
|
|
18
|
+
],
|
|
5
19
|
"keywords": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
"throughline",
|
|
21
|
+
"payload",
|
|
22
|
+
"inngest",
|
|
23
|
+
"workflows"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/forumone/throughline.git",
|
|
29
|
+
"directory": "packages/workflows"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/forumone/throughline/tree/main/packages/workflows#readme",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/forumone/throughline/issues"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"inngest": "^4.0.0",
|
|
40
|
+
"next": ">=15.0.0",
|
|
41
|
+
"payload": "^3.0.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependenciesMeta": {
|
|
44
|
+
"next": {
|
|
45
|
+
"optional": true
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@forumone/throughline-core": "0.2.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^20.17.0",
|
|
53
|
+
"eslint": "^9.15.0",
|
|
54
|
+
"inngest": "^4.2.0",
|
|
55
|
+
"next": "^16.2.4",
|
|
56
|
+
"payload": "^3.83.0",
|
|
57
|
+
"typescript": "^5.6.0",
|
|
58
|
+
"vitest": "^2.1.0",
|
|
59
|
+
"@forumone/throughline-eslint-config": "0.0.0",
|
|
60
|
+
"@forumone/throughline-tsconfig": "0.0.0"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsc -b",
|
|
64
|
+
"dev": "tsc -b -w",
|
|
65
|
+
"clean": "rm -rf dist .turbo *.tsbuildinfo",
|
|
66
|
+
"typecheck": "tsc --noEmit",
|
|
67
|
+
"lint": "eslint src",
|
|
68
|
+
"test": "vitest run"
|
|
69
|
+
}
|
|
70
|
+
}
|