@forumone/throughline-workflows 0.4.0 → 0.5.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 +57 -0
- package/README.md +132 -8
- package/dist/cache-tags.d.ts +31 -0
- package/dist/cache-tags.d.ts.map +1 -0
- package/dist/cache-tags.js +53 -0
- package/dist/cache-tags.js.map +1 -0
- package/dist/failure-handler.d.ts +38 -0
- package/dist/failure-handler.d.ts.map +1 -0
- package/dist/failure-handler.js +60 -0
- package/dist/failure-handler.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/next-revalidate.d.ts +43 -0
- package/dist/next-revalidate.d.ts.map +1 -0
- package/dist/next-revalidate.js +42 -0
- package/dist/next-revalidate.js.map +1 -0
- package/dist/revalidate-on-publish.d.ts +10 -0
- package/dist/revalidate-on-publish.d.ts.map +1 -1
- package/dist/revalidate-on-publish.js +21 -25
- package/dist/revalidate-on-publish.js.map +1 -1
- package/dist/revalidate-tag-hooks.d.ts +80 -0
- package/dist/revalidate-tag-hooks.d.ts.map +1 -0
- package/dist/revalidate-tag-hooks.js +116 -0
- package/dist/revalidate-tag-hooks.js.map +1 -0
- package/dist/types.d.ts +23 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# @forumone/throughline-workflows
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ab623e1: The `payload` peer range moves from `^3.0.0` to `^3.89.0` for every package that has one. **A site on Payload older than 3.89.0 must upgrade Payload before upgrading these packages.**
|
|
8
|
+
|
|
9
|
+
Before 3.89.0, the `payload-mcp-api-keys` collection that `@payloadcms/plugin-mcp` adds registered Payload's API-key strategy on every REST route. Any key could then become `req.user` outside `/api/mcp` and pass access rules written as `Boolean(req.user)`. Every Throughline site runs that plugin, so the floor is the same for every package. No package's code changes with this bump.
|
|
10
|
+
|
|
11
|
+
- 653817e: Revalidation no longer guesses paths, and covers the changes a publish event never announces.
|
|
12
|
+
|
|
13
|
+
- **workflows (breaking)**: `createRevalidateOnPublishFunction` has no built-in URL
|
|
14
|
+
builders. They mapped `pages` to `/<slug>`, `posts` to `/blog/<slug>` and any other
|
|
15
|
+
collection to `/<slug>`, so a site whose routes differed revalidated the wrong path
|
|
16
|
+
without a word. `urlBuilders` is now required; a collection with no entry has its
|
|
17
|
+
tags dropped and no path revalidated, and the run logs a warning. To keep the old
|
|
18
|
+
behaviour, pass the old builders:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
urlBuilders: {
|
|
22
|
+
pages: (slug) => (slug === 'home' || slug === '' ? '/' : `/${slug}`),
|
|
23
|
+
posts: (slug) => `/blog/${slug}`,
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
and then check them against your routes.
|
|
28
|
+
|
|
29
|
+
- **workflows**: `createTagRevalidationHooks` — collection `afterChange` and
|
|
30
|
+
`afterDelete`, and global `afterChange`, hooks that call
|
|
31
|
+
`revalidateTag(tag, { expire: 0 })`. Draft saves and autosave drop nothing (via
|
|
32
|
+
publishing's `isDraftWrite`); an unpublish still does. A hook never throws: outside a
|
|
33
|
+
Next request (seeds, migrations, the CLI) it logs at `debug`, and any other failure
|
|
34
|
+
at `error` with the tag and the cause.
|
|
35
|
+
- **workflows**: `createCacheTags` builds every tag string, so the hooks, the publish
|
|
36
|
+
workflow (new `cacheTags` option) and cached reads cannot name different tags.
|
|
37
|
+
Defaults: the bare slug for a collection, `global_<slug>` for a global. Also on the
|
|
38
|
+
dependency-free `@forumone/throughline-workflows/cache-tags` subpath for frontend code.
|
|
39
|
+
`@forumone/throughline-publishing` is now a dependency of this package.
|
|
40
|
+
- **create-throughline**: the scaffold adds `apps/web/src/lib/cache-tags.ts`, attaches
|
|
41
|
+
the tag hooks to `Pages`, and passes explicit `urlBuilders` and the shared
|
|
42
|
+
`cacheTags` to the publish workflow.
|
|
43
|
+
|
|
44
|
+
- 70385c4: `onTerminalFailure` has a handler to pass it. `createTerminalFailureHandler({ payload })` logs a run that exhausted its retries, writes a `job-failures` row through core's `jobFailuresPlugin` when it is registered, and posts the report to `ERROR_WEBHOOK_URL`. `createHealthcheckFailureHandler({ payload })` does the same for the healthcheck's per-run `onFailure`. Neither throws, because a throwing `onFailure` is retried by Inngest. The terminal handler also works as the `onFailure` of any Inngest function.
|
|
45
|
+
|
|
46
|
+
Recording rows needs core's `jobFailuresPlugin` and its migration. Without the plugin, the handlers still log and report.
|
|
47
|
+
|
|
48
|
+
### Patch Changes
|
|
49
|
+
|
|
50
|
+
- Updated dependencies [006ae30]
|
|
51
|
+
- Updated dependencies [549d292]
|
|
52
|
+
- Updated dependencies [70385c4]
|
|
53
|
+
- Updated dependencies [c8a86bf]
|
|
54
|
+
- Updated dependencies [ab623e1]
|
|
55
|
+
- Updated dependencies [ab623e1]
|
|
56
|
+
- Updated dependencies [36728c4]
|
|
57
|
+
- @forumone/throughline-core@0.10.0
|
|
58
|
+
- @forumone/throughline-publishing@0.11.0
|
|
59
|
+
|
|
3
60
|
## 0.4.0
|
|
4
61
|
|
|
5
62
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @forumone/throughline-workflows
|
|
2
2
|
|
|
3
|
-
Composable Inngest function factories for the Throughline framework. Client apps import the factories they need and merge the functions into their Inngest endpoint.
|
|
3
|
+
Composable Inngest function factories for the Throughline framework, and the Payload hooks that keep a Next cache honest. Client apps import the factories they need and merge the functions into their Inngest endpoint.
|
|
4
4
|
|
|
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.
|
|
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. The cache-revalidation hooks are factories too: you attach them to the collections and globals you choose.
|
|
6
6
|
|
|
7
7
|
## What this package provides
|
|
8
8
|
|
|
@@ -14,6 +14,18 @@ This package has **no Payload plugin** — it exports factories only. Workflows
|
|
|
14
14
|
| `createExpireStaleApprovalsFunction` | Daily cron that flips pending approvals past `expiresAt` to `expired` and fires an `approval/expired` event |
|
|
15
15
|
| `createAuditEventEchoFunction` | Fan-out point: turns `audit/event.recorded` into `notification/send-approval-*` events plus custom handlers |
|
|
16
16
|
| `createHealthcheckFunction` | Periodic health monitoring with configurable checks |
|
|
17
|
+
| `createTagRevalidationHooks` | Payload `afterChange` / `afterDelete` hooks that drop Next cache tags when a collection or global changes |
|
|
18
|
+
| `createCacheTags` | The one tag scheme hooks, workflow and readers all build tags from (also on `@forumone/throughline-workflows/cache-tags`) |
|
|
19
|
+
|
|
20
|
+
Plus two failure handlers, for the `onTerminalFailure` every factory accepts and the healthcheck's own `onFailure`:
|
|
21
|
+
|
|
22
|
+
- `createTerminalFailureHandler({ payload })` — logs a run that exhausted its retries, writes a `job-failures` row (when core's `jobFailuresPlugin` is registered) and posts it to `ERROR_WEBHOOK_URL`. Never throws. Works as any Inngest function's `onFailure`.
|
|
23
|
+
- `createHealthcheckFailureHandler({ payload })` — the same, once per healthcheck run with failing checks.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const onTerminalFailure = createTerminalFailureHandler({ payload })
|
|
27
|
+
createExpireStaleApprovalsFunction({ inngest, payload, onTerminalFailure })
|
|
28
|
+
```
|
|
17
29
|
|
|
18
30
|
Plus two reusable check helpers used with the healthcheck factory:
|
|
19
31
|
|
|
@@ -26,7 +38,7 @@ Plus two reusable check helpers used with the healthcheck factory:
|
|
|
26
38
|
pnpm add @forumone/throughline-workflows
|
|
27
39
|
```
|
|
28
40
|
|
|
29
|
-
Peers: `payload@^3.
|
|
41
|
+
Peers: `payload@^3.89.0`, `inngest@^4.0.0`. `next` is an **optional** peer — install it only if you use `createRevalidateOnPublishFunction` or `createTagRevalidationHooks` with their default revalidators.
|
|
30
42
|
|
|
31
43
|
## Usage
|
|
32
44
|
|
|
@@ -52,7 +64,16 @@ const payload = await getPayload({ config })
|
|
|
52
64
|
export const { GET, POST, PUT } = serve({
|
|
53
65
|
client: inngest,
|
|
54
66
|
functions: [
|
|
55
|
-
createRevalidateOnPublishFunction({
|
|
67
|
+
createRevalidateOnPublishFunction({
|
|
68
|
+
inngest,
|
|
69
|
+
payload,
|
|
70
|
+
// Required. Where each publishable collection is served.
|
|
71
|
+
urlBuilders: {
|
|
72
|
+
pages: (slug) => (slug === 'home' ? '/' : `/${slug}`),
|
|
73
|
+
posts: (slug) => `/news/${slug}`,
|
|
74
|
+
},
|
|
75
|
+
cacheTags, // the same scheme your readers use — see below
|
|
76
|
+
}),
|
|
56
77
|
createExecuteScheduledPublishesFunction({
|
|
57
78
|
inngest,
|
|
58
79
|
payload,
|
|
@@ -77,14 +98,116 @@ By C10, every server package fires Inngest events when consequential things happ
|
|
|
77
98
|
|
|
78
99
|
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.
|
|
79
100
|
|
|
101
|
+
The tag-revalidation hooks are the one place this package reaches into Payload configuration, and only because you put them there: they are plain hook functions, not a plugin. They live here rather than in `publishing` because this package already owns Next cache invalidation — the optional `next` peer, the Next 16 `revalidateTag` profile, and the tag `createRevalidateOnPublishFunction` fires — and one tag scheme has to serve both. They use publishing's `isDraftWrite`, which makes `publishing` a dependency of this package. Publishing depends on nothing here, so there is no cycle.
|
|
102
|
+
|
|
103
|
+
## Cache tags: one scheme, both ends
|
|
104
|
+
|
|
105
|
+
A cache tag has two ends: a reader that caches under it and a writer that drops it. Nothing in the type system connects them. Name them differently and everything compiles, every test passes, the hook runs, and the page never refreshes. So build both from one object:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// src/lib/cache-tags.ts — imported by payload.config.ts, the Inngest route and readers
|
|
109
|
+
import { createCacheTags } from '@forumone/throughline-workflows/cache-tags'
|
|
110
|
+
|
|
111
|
+
export const cacheTags = createCacheTags()
|
|
112
|
+
// or name them your way:
|
|
113
|
+
// createCacheTags({ collection: (slug) => `c:${slug}`, global: (slug) => `g:${slug}` })
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The defaults are the bare slug for a collection (`pages`, which is also what `createRevalidateOnPublishFunction` has always fired) and `global_<slug>` for a global. The `/cache-tags` subpath imports nothing, so frontend code can use it without pulling in Payload.
|
|
117
|
+
|
|
118
|
+
A reader tags its cached read from the same object:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { unstable_cache } from 'next/cache'
|
|
122
|
+
import { cacheTags } from '@/lib/cache-tags'
|
|
123
|
+
|
|
124
|
+
export const getNavigation = unstable_cache(
|
|
125
|
+
async () => (await getPayload({ config })).findGlobal({ slug: 'navigation' }),
|
|
126
|
+
['navigation'],
|
|
127
|
+
{ tags: [cacheTags.global('navigation')] },
|
|
128
|
+
)
|
|
129
|
+
// With Cache Components: cacheTag(cacheTags.global('navigation')) inside a 'use cache' function.
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Tag-revalidation hooks
|
|
133
|
+
|
|
134
|
+
`createRevalidateOnPublishFunction` reacts to publishing events, so it covers publishes, unpublishes and rollbacks of publishable collections — and nothing else. A global (navigation, footer, settings) is read on every page and usually cached, and nothing publishes it. A collection an integration writes emits no publishing event. A delete is not a publish. Each of those leaves the cached copy standing until the next deploy unless a hook drops it:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { createTagRevalidationHooks } from '@forumone/throughline-workflows'
|
|
138
|
+
import { cacheTags } from './lib/cache-tags'
|
|
139
|
+
|
|
140
|
+
const revalidation = createTagRevalidationHooks({ cacheTags })
|
|
141
|
+
|
|
142
|
+
const Pages: CollectionConfig = {
|
|
143
|
+
slug: 'pages',
|
|
144
|
+
hooks: {
|
|
145
|
+
// Navigation links to pages and caches their slugs, so a page change drops it too.
|
|
146
|
+
afterChange: [
|
|
147
|
+
revalidation.afterCollectionChange({
|
|
148
|
+
tags: (t, slug) => [t.collection(slug), t.global('navigation')],
|
|
149
|
+
}),
|
|
150
|
+
],
|
|
151
|
+
afterDelete: [revalidation.afterCollectionDelete()],
|
|
152
|
+
},
|
|
153
|
+
// ...
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const Media: CollectionConfig = {
|
|
157
|
+
slug: 'media',
|
|
158
|
+
upload: true,
|
|
159
|
+
hooks: {
|
|
160
|
+
// An upload cannot be referenced by anything cached yet.
|
|
161
|
+
afterChange: [revalidation.afterCollectionChange({ operations: ['update'] })],
|
|
162
|
+
afterDelete: [revalidation.afterCollectionDelete()],
|
|
163
|
+
},
|
|
164
|
+
// ...
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const Navigation: GlobalConfig = {
|
|
168
|
+
slug: 'navigation',
|
|
169
|
+
hooks: { afterChange: [revalidation.afterGlobalChange()] },
|
|
170
|
+
// ...
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
What they do:
|
|
175
|
+
|
|
176
|
+
- **Drop tags, not paths.** Each hook calls `revalidateTag(tag, { expire: 0 })`, the immediate expiry Next 16 requires a profile for. Paths are the workflow's job. A collection hook defaults to `cacheTags.collection(slug)`, a global hook to `cacheTags.global(slug)`; `tags` replaces that list.
|
|
177
|
+
- **Skip draft writes.** On `update`, a collection hook asks publishing's `isDraftWrite`, so a draft save or an autosave tick drops nothing, while an unpublish still does. That answer is recorded by `publishingPlugin` for the collections it manages; on any other collection every write counts as visible, which is the safe direction. A `create` that lands as a draft is skipped too: nothing published existed a moment ago. Global hooks drop on every save.
|
|
178
|
+
- **Never throw.** A throwing `afterChange` fails the editor's save over a cache miss. Outside a Next request — seeds, migrations, the Payload CLI under `tsx` — Next throws `static generation store missing`; there is no server to be stale, so that is logged at `debug`. Any other failure means the cache did not clear while a server was running, and is logged at `error` with the cause and the tag.
|
|
179
|
+
- **Other frontends.** Pass `revalidateTag: (tag) => myCdn.purgeTag(tag)` to drop tags somewhere other than Next.
|
|
180
|
+
|
|
181
|
+
These overlap with `createRevalidateOnPublishFunction` on a publish — both drop the collection tag — which is harmless: dropping a tag twice is dropping it once. Use the workflow for page paths and the sitemap, and the hooks for everything a publish event never announces.
|
|
182
|
+
|
|
183
|
+
## Migrating from the built-in URL builders
|
|
184
|
+
|
|
185
|
+
Before 0.5, `createRevalidateOnPublishFunction` guessed paths: `pages` → `/<slug>` (`home` → `/`), `posts` → `/blog/<slug>`, and any other collection → `/<slug>`. A site whose routes differed revalidated the wrong path without a word. `urlBuilders` is now required and has no built-in entries. A collection with no entry still has its tags dropped, but no path is revalidated and the run logs a warning.
|
|
186
|
+
|
|
187
|
+
If you relied on the defaults, pass them explicitly — the same behaviour as before:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
createRevalidateOnPublishFunction({
|
|
191
|
+
inngest,
|
|
192
|
+
payload,
|
|
193
|
+
urlBuilders: {
|
|
194
|
+
pages: (slug) => (slug === 'home' || slug === '' ? '/' : `/${slug}`),
|
|
195
|
+
posts: (slug) => `/blog/${slug}`,
|
|
196
|
+
},
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Then check them against your routes: if your posts are not under `/blog`, they never were being revalidated.
|
|
201
|
+
|
|
80
202
|
## Non-Next.js frontends
|
|
81
203
|
|
|
82
|
-
The default `revalidate` function dynamically imports `next/cache
|
|
204
|
+
The default `revalidate` function dynamically imports `next/cache`, so the package installs and imports without Next. For other frameworks supply your own:
|
|
83
205
|
|
|
84
206
|
```typescript
|
|
85
207
|
createRevalidateOnPublishFunction({
|
|
86
208
|
inngest,
|
|
87
209
|
payload,
|
|
210
|
+
urlBuilders,
|
|
88
211
|
revalidate: async ({ path, tags }) => {
|
|
89
212
|
if (path) await myCdn.purge(path)
|
|
90
213
|
for (const tag of tags) await myCdn.purgeTag(tag)
|
|
@@ -192,9 +315,10 @@ Each handler runs in its own `step.run`, so failures isolate.
|
|
|
192
315
|
|
|
193
316
|
## Options reference
|
|
194
317
|
|
|
195
|
-
Every factory takes a typed options object. See `src/types.ts` for the full surface — defaults documented there
|
|
318
|
+
Every factory takes a typed options object. See `src/types.ts` for the full surface — defaults documented there. All of them also accept `onTerminalFailure?` and `concurrency?`:
|
|
196
319
|
|
|
197
|
-
- `RevalidateOnPublishOptions` — `
|
|
320
|
+
- `RevalidateOnPublishOptions` — `urlBuilders` (required), `cacheTags?`, `collectionTags?`, `revalidate?`, `id?`
|
|
321
|
+
- `TagRevalidationOptions` — `cacheTags?`, `revalidateTag?`; per hook, `tags?` and (collection `afterChange`) `operations?`
|
|
198
322
|
- `PublishAtScheduledTimeOptions` — `collections[]`, `publish`, `maxSleepMs?` (default six days), `id?`
|
|
199
323
|
- `ExecuteScheduledPublishesOptions` — `collections[]`, `publish`, `schedule?` (default `*/5 * * * *`), `overdueByMs?` (default 0), `id?`
|
|
200
324
|
- `ExpireStaleApprovalsOptions` — `collectionSlug?` (default `approvals`), `schedule?` (default `0 2 * * *`), `id?`
|
|
@@ -204,6 +328,6 @@ Every factory takes a typed options object. See `src/types.ts` for the full surf
|
|
|
204
328
|
## Related packages
|
|
205
329
|
|
|
206
330
|
- `@forumone/throughline-core` — required peer; provides the audit writer the approval-expiration cron uses
|
|
207
|
-
- `@forumone/throughline-publishing` — emits the publishing events the revalidation
|
|
331
|
+
- `@forumone/throughline-publishing` — emits the publishing events the revalidation workflow subscribes to, and provides the `isDraftWrite` predicate the tag hooks use (a dependency)
|
|
208
332
|
- `@forumone/throughline-approvals` — owns the approvals collection the expiration cron reads
|
|
209
333
|
- `@forumone/throughline-email` (C11) — will subscribe to `notification/send-approval-*`
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** How a site names its tags. Each builder is optional; omitted ones use the default. */
|
|
2
|
+
export interface CacheTagScheme {
|
|
3
|
+
/**
|
|
4
|
+
* The tag for cached reads derived from a collection's documents.
|
|
5
|
+
* Default: the bare slug (`pages`), which is also what
|
|
6
|
+
* `createRevalidateOnPublishFunction` has always fired.
|
|
7
|
+
*/
|
|
8
|
+
collection?: (slug: string) => string;
|
|
9
|
+
/** The tag for a cached global. Default: `global_<slug>`. */
|
|
10
|
+
global?: (slug: string) => string;
|
|
11
|
+
}
|
|
12
|
+
/** The built scheme. Pass the same instance to writers and readers. */
|
|
13
|
+
export interface CacheTags {
|
|
14
|
+
collection(slug: string): string;
|
|
15
|
+
global(slug: string): string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Builds the tag scheme both ends of the cache use.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* // src/lib/cache-tags.ts — imported by payload.config.ts and by readers
|
|
22
|
+
* export const cacheTags = createCacheTags()
|
|
23
|
+
*
|
|
24
|
+
* // a reader
|
|
25
|
+
* unstable_cache(load, ['navigation'], { tags: [cacheTags.global('navigation')] })
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function createCacheTags(scheme?: CacheTagScheme): CacheTags;
|
|
29
|
+
/** The scheme every writer uses when it is given none. */
|
|
30
|
+
export declare const defaultCacheTags: CacheTags;
|
|
31
|
+
//# sourceMappingURL=cache-tags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-tags.d.ts","sourceRoot":"","sources":["../src/cache-tags.ts"],"names":[],"mappings":"AAqBA,yFAAyF;AACzF,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACrC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAClC;AAED,uEAAuE;AACvE,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;IAChC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CAC7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,GAAE,cAAmB,GAAG,SAAS,CAOtE;AAED,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,EAAE,SAA6B,CAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Next cache tags, named in one place.
|
|
3
|
+
|
|
4
|
+
A tag is one string with two ends: the reader that caches under it
|
|
5
|
+
(`unstable_cache(..., { tags })`, `cacheTag()`, `fetch(..., { next: { tags } })`)
|
|
6
|
+
and the writer that drops it (the hooks in `revalidate-tag-hooks.ts`, and
|
|
7
|
+
`createRevalidateOnPublishFunction`). Nothing in the type system connects the two
|
|
8
|
+
ends. Name them differently and everything compiles, every test passes, the hook
|
|
9
|
+
runs, and the page never refreshes — the first site on this suite shipped a hook
|
|
10
|
+
that had never invalidated anything for exactly that reason.
|
|
11
|
+
|
|
12
|
+
So both ends build tags from one object. A site makes it once, in a module its
|
|
13
|
+
`payload.config.ts` and its frontend both import, and hands it to the hooks and
|
|
14
|
+
the readers alike.
|
|
15
|
+
|
|
16
|
+
Deliberately dependency-free, and published on its own subpath
|
|
17
|
+
(`@forumone/throughline-workflows/cache-tags`): one of its importers is
|
|
18
|
+
`payload.config.ts` and the other is frontend code, and neither should pull in
|
|
19
|
+
the other's world to name a string.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Builds the tag scheme both ends of the cache use.
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* // src/lib/cache-tags.ts — imported by payload.config.ts and by readers
|
|
26
|
+
* export const cacheTags = createCacheTags()
|
|
27
|
+
*
|
|
28
|
+
* // a reader
|
|
29
|
+
* unstable_cache(load, ['navigation'], { tags: [cacheTags.global('navigation')] })
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function createCacheTags(scheme = {}) {
|
|
33
|
+
const collection = scheme.collection ?? ((slug) => slug);
|
|
34
|
+
const global = scheme.global ?? ((slug) => `global_${slug}`);
|
|
35
|
+
return {
|
|
36
|
+
collection: (slug) => nonEmpty(collection(slug), 'collection', slug),
|
|
37
|
+
global: (slug) => nonEmpty(global(slug), 'global', slug),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** The scheme every writer uses when it is given none. */
|
|
41
|
+
export const defaultCacheTags = createCacheTags();
|
|
42
|
+
/*
|
|
43
|
+
An empty tag is the silent version of the mismatch above: `revalidateTag('')`
|
|
44
|
+
does nothing and says nothing. A builder that produces one is a bug in the
|
|
45
|
+
site's configuration, and the place to hear about it is the first call.
|
|
46
|
+
*/
|
|
47
|
+
function nonEmpty(tag, kind, slug) {
|
|
48
|
+
if (typeof tag !== 'string' || tag.length === 0) {
|
|
49
|
+
throw new Error(`The cache tag scheme built an empty ${kind} tag for "${slug}".`);
|
|
50
|
+
}
|
|
51
|
+
return tag;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=cache-tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-tags.js","sourceRoot":"","sources":["../src/cache-tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;EAmBE;AAoBF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,SAAyB,EAAE;IACzD,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,CAAA;IAChE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC,CAAA;IACpE,OAAO;QACL,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC;QACpE,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC;KACzD,CAAA;AACH,CAAC;AAED,0DAA0D;AAC1D,MAAM,CAAC,MAAM,gBAAgB,GAAc,eAAe,EAAE,CAAA;AAE5D;;;;EAIE;AACF,SAAS,QAAQ,CAAC,GAAW,EAAE,IAAY,EAAE,IAAY;IACvD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,uCAAuC,IAAI,aAAa,IAAI,IAAI,CAAC,CAAA;IACnF,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
import type { Logger } from '@forumone/throughline-core';
|
|
3
|
+
import { type ErrorReporter, type JobFailureWriter } from '@forumone/throughline-core';
|
|
4
|
+
import type { HealthcheckOptions, WorkflowFailureHandler } from './types.js';
|
|
5
|
+
export interface FailureHandlerOptions {
|
|
6
|
+
/**
|
|
7
|
+
* The Payload instance whose `jobFailuresPlugin` writer receives the row.
|
|
8
|
+
* Without one — or without the plugin — the failure is logged and reported
|
|
9
|
+
* but not recorded.
|
|
10
|
+
*/
|
|
11
|
+
payload?: Payload | undefined;
|
|
12
|
+
/** Replaces the writer found on `payload`. */
|
|
13
|
+
writer?: JobFailureWriter | undefined;
|
|
14
|
+
/** Default: `reportError` from core, which posts to `ERROR_WEBHOOK_URL`. Pass `false` to not report. */
|
|
15
|
+
report?: ErrorReporter | false | undefined;
|
|
16
|
+
/** Default: core's console logger. */
|
|
17
|
+
logger?: Logger | undefined;
|
|
18
|
+
}
|
|
19
|
+
export interface HealthcheckFailureHandlerOptions extends FailureHandlerOptions {
|
|
20
|
+
/** The healthcheck function's id, recorded as the failure's source. Default: `'healthcheck'`. */
|
|
21
|
+
functionId?: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* An `onTerminalFailure` handler for any workflow factory here — or for any
|
|
25
|
+
* Inngest function, as its `onFailure`.
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* const onTerminalFailure = createTerminalFailureHandler({ payload })
|
|
29
|
+
* createExpireStaleApprovalsFunction({ inngest, payload, onTerminalFailure })
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function createTerminalFailureHandler(options?: FailureHandlerOptions): WorkflowFailureHandler;
|
|
33
|
+
/**
|
|
34
|
+
* A `HealthcheckOptions.onFailure` handler: once per run with failing checks,
|
|
35
|
+
* to the same three places.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createHealthcheckFailureHandler(options?: HealthcheckFailureHandlerOptions): NonNullable<HealthcheckOptions['onFailure']>;
|
|
38
|
+
//# sourceMappingURL=failure-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-handler.d.ts","sourceRoot":"","sources":["../src/failure-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAOL,KAAK,aAAa,EAGlB,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAA;AACnC,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAuB5E,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,8CAA8C;IAC9C,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAA;IACrC,wGAAwG;IACxG,MAAM,CAAC,EAAE,aAAa,GAAG,KAAK,GAAG,SAAS,CAAA;IAC1C,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC5B;AAED,MAAM,WAAW,gCAAiC,SAAQ,qBAAqB;IAC7E,iGAAiG;IACjG,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,GAAE,qBAA0B,GAClC,sBAAsB,CAgBxB;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,gCAAqC,GAC7C,WAAW,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAa9C"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { buildHealthcheckFailureReport, buildJobFailureReport, defaultLogger, getJobFailureWriter, reportError, summariseReport, } from '@forumone/throughline-core';
|
|
2
|
+
/**
|
|
3
|
+
* An `onTerminalFailure` handler for any workflow factory here — or for any
|
|
4
|
+
* Inngest function, as its `onFailure`.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* const onTerminalFailure = createTerminalFailureHandler({ payload })
|
|
8
|
+
* createExpireStaleApprovalsFunction({ inngest, payload, onTerminalFailure })
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export function createTerminalFailureHandler(options = {}) {
|
|
12
|
+
return async ({ error, event }) => {
|
|
13
|
+
let report;
|
|
14
|
+
try {
|
|
15
|
+
const data = event?.data;
|
|
16
|
+
report = buildJobFailureReport({
|
|
17
|
+
functionId: data?.function_id,
|
|
18
|
+
runId: data?.run_id,
|
|
19
|
+
triggerEvent: data?.event?.name,
|
|
20
|
+
error,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
await dispatch(report, '[job-failed]', options);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A `HealthcheckOptions.onFailure` handler: once per run with failing checks,
|
|
31
|
+
* to the same three places.
|
|
32
|
+
*/
|
|
33
|
+
export function createHealthcheckFailureHandler(options = {}) {
|
|
34
|
+
return async (failures) => {
|
|
35
|
+
let report;
|
|
36
|
+
try {
|
|
37
|
+
report = buildHealthcheckFailureReport(failures, options.functionId ? { functionId: options.functionId } : {});
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
await dispatch(report, '[healthcheck-failed]', options);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async function dispatch(report, tag, options) {
|
|
46
|
+
const logger = options.logger ?? defaultLogger;
|
|
47
|
+
try {
|
|
48
|
+
// The stack goes to the log and the webhook; the row keeps the message.
|
|
49
|
+
logger.error(`${tag} ${summariseReport(report)}`, { report });
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Carry on to the other two.
|
|
53
|
+
}
|
|
54
|
+
const writer = options.writer ?? (options.payload ? getJobFailureWriter(options.payload) : undefined);
|
|
55
|
+
const reporter = options.report === false ? undefined : (options.report ?? reportError);
|
|
56
|
+
// Core's writer and reporter never reject. These wrappers are for a host's
|
|
57
|
+
// own, which may not keep that promise — or may throw before returning one.
|
|
58
|
+
await Promise.allSettled([(async () => writer?.(report))(), (async () => reporter?.(report))()]);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=failure-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-handler.js","sourceRoot":"","sources":["../src/failure-handler.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,eAAe,GAKhB,MAAM,4BAA4B,CAAA;AA4CnC;;;;;;;;GAQG;AACH,MAAM,UAAU,4BAA4B,CAC1C,UAAiC,EAAE;IAEnC,OAAO,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE;QAChC,IAAI,MAAwB,CAAA;QAC5B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,KAAK,EAAE,IAAI,CAAA;YACxB,MAAM,GAAG,qBAAqB,CAAC;gBAC7B,UAAU,EAAE,IAAI,EAAE,WAAW;gBAC7B,KAAK,EAAE,IAAI,EAAE,MAAM;gBACnB,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI;gBAC/B,KAAK;aACN,CAAC,CAAA;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAM;QACR,CAAC;QACD,MAAM,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,CAAC,CAAA;IACjD,CAAC,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,+BAA+B,CAC7C,UAA4C,EAAE;IAE9C,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE;QACxB,IAAI,MAAgC,CAAA;QACpC,IAAI,CAAC;YACH,MAAM,GAAG,6BAA6B,CACpC,QAAQ,EACR,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAC7D,CAAA;QACH,CAAC;QAAC,MAAM,CAAC;YACP,OAAM;QACR,CAAC;QACD,MAAM,QAAQ,CAAC,MAAM,EAAE,sBAAsB,EAAE,OAAO,CAAC,CAAA;IACzD,CAAC,CAAA;AACH,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,MAAmD,EACnD,GAAW,EACX,OAA8B;IAE9B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,aAAa,CAAA;IAC9C,IAAI,CAAC;QACH,wEAAwE;QACxE,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,6BAA6B;IAC/B,CAAC;IAED,MAAM,MAAM,GACV,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IACxF,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC,CAAA;IAEvF,2EAA2E;IAC3E,4EAA4E;IAC5E,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAClG,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
export { createRevalidateOnPublishFunction } from './revalidate-on-publish.js';
|
|
2
|
+
export { createTagRevalidationHooks } from './revalidate-tag-hooks.js';
|
|
3
|
+
export type { CollectionTagHookOptions, GlobalTagHookOptions, RevalidateTagFn, TagRevalidationHooks, TagRevalidationOptions, TagSelector, } from './revalidate-tag-hooks.js';
|
|
4
|
+
export { createCacheTags, defaultCacheTags } from './cache-tags.js';
|
|
5
|
+
export type { CacheTags, CacheTagScheme } from './cache-tags.js';
|
|
2
6
|
export { createExecuteScheduledPublishesFunction } from './execute-scheduled-publishes.js';
|
|
3
7
|
export { createPublishAtScheduledTimeFunction } from './publish-at-scheduled-time.js';
|
|
4
8
|
export { createExpireStaleApprovalsFunction } from './expire-stale-approvals.js';
|
|
5
9
|
export { createAuditEventEchoFunction } from './audit-event-echo.js';
|
|
6
10
|
export { createHealthcheckFunction, createPayloadReachableCheck, createManifestReachableCheck, } from './healthcheck.js';
|
|
7
11
|
export { failureOptions } from './types.js';
|
|
12
|
+
export { createHealthcheckFailureHandler, createTerminalFailureHandler, } from './failure-handler.js';
|
|
13
|
+
export type { FailureHandlerOptions, HealthcheckFailureHandlerOptions, } from './failure-handler.js';
|
|
8
14
|
export type { BaseWorkflowOptions, WorkflowFailureHandler, FailureAwareOptions, RevalidateFn, RevalidatePathsInput, RevalidateOnPublishOptions, ScheduledCollectionConfig, ExecuteScheduledPublishesOptions, PublishAtScheduledTimeOptions, ScheduledPublishRequest, ScheduledPublishResult, ExpireStaleApprovalsOptions, AuditEchoEvent, AuditEchoHandler, AuditEventEchoOptions, HealthcheckDefinition, HealthcheckOptions, HealthcheckResult, } from './types.js';
|
|
9
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAA;AAC9E,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAA;AACtE,YAAY,EACV,wBAAwB,EACxB,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,WAAW,GACZ,MAAM,2BAA2B,CAAA;AAKlC,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACnE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChE,OAAO,EAAE,uCAAuC,EAAE,MAAM,kCAAkC,CAAA;AAC1F,OAAO,EAAE,oCAAoC,EAAE,MAAM,gCAAgC,CAAA;AACrF,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,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EACL,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EACV,qBAAqB,EACrB,gCAAgC,GACjC,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EACV,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,6BAA6B,EAC7B,uBAAuB,EACvB,sBAAsB,EACtB,2BAA2B,EAC3B,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
// Other factory exports land in subsequent commits.
|
|
2
1
|
export { createRevalidateOnPublishFunction } from './revalidate-on-publish.js';
|
|
2
|
+
export { createTagRevalidationHooks } from './revalidate-tag-hooks.js';
|
|
3
|
+
/*
|
|
4
|
+
Also on `@forumone/throughline-workflows/cache-tags`, which imports nothing —
|
|
5
|
+
readers in frontend code should import it from there.
|
|
6
|
+
*/
|
|
7
|
+
export { createCacheTags, defaultCacheTags } from './cache-tags.js';
|
|
3
8
|
export { createExecuteScheduledPublishesFunction } from './execute-scheduled-publishes.js';
|
|
4
9
|
export { createPublishAtScheduledTimeFunction } from './publish-at-scheduled-time.js';
|
|
5
10
|
export { createExpireStaleApprovalsFunction } from './expire-stale-approvals.js';
|
|
6
11
|
export { createAuditEventEchoFunction } from './audit-event-echo.js';
|
|
7
12
|
export { createHealthcheckFunction, createPayloadReachableCheck, createManifestReachableCheck, } from './healthcheck.js';
|
|
8
13
|
export { failureOptions } from './types.js';
|
|
14
|
+
export { createHealthcheckFailureHandler, createTerminalFailureHandler, } from './failure-handler.js';
|
|
9
15
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAA;AAC9E,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAA;AAStE;;;EAGE;AACF,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEnE,OAAO,EAAE,uCAAuC,EAAE,MAAM,kCAAkC,CAAA;AAC1F,OAAO,EAAE,oCAAoC,EAAE,MAAM,gCAAgC,CAAA;AACrF,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,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EACL,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type * as NextCacheModule from 'next/cache';
|
|
2
|
+
type NextCache = typeof NextCacheModule;
|
|
3
|
+
/**
|
|
4
|
+
* How stale a caller may find a tag after it is dropped: not at all.
|
|
5
|
+
*
|
|
6
|
+
* Next 16 made `revalidateTag`'s second argument required — it is a cache-life
|
|
7
|
+
* profile — and one-argument calls log a deprecation warning on every call.
|
|
8
|
+
* `{ expire: 0 }` is the immediate expiry the one-argument form used to mean,
|
|
9
|
+
* and it is what an editor pressing Publish or Save expects.
|
|
10
|
+
*
|
|
11
|
+
* Passed unconditionally rather than behind a version check. The peer range is
|
|
12
|
+
* `next >= 15`, and Next 15's `revalidateTag` takes one parameter and ignores a
|
|
13
|
+
* second, so the two-argument call is correct on both.
|
|
14
|
+
*
|
|
15
|
+
* `updateTag`, which Next offers as the other way out of the deprecation, is
|
|
16
|
+
* not usable here: it throws outside a Server Action, and these run in an
|
|
17
|
+
* Inngest step or in Payload's route handler.
|
|
18
|
+
*/
|
|
19
|
+
export declare const IMMEDIATE: {
|
|
20
|
+
readonly expire: 0;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* `next/cache`, loaded on first use.
|
|
24
|
+
*
|
|
25
|
+
* Dynamic so the package can be imported where Next is not installed (test
|
|
26
|
+
* runners, a non-Next frontend that passes its own revalidator). `next` is an
|
|
27
|
+
* optional peer.
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadNextCache(): Promise<NextCache>;
|
|
30
|
+
/**
|
|
31
|
+
* Whether `error` is Next saying there is no request to revalidate for.
|
|
32
|
+
*
|
|
33
|
+
* Seeds, migrations and the Payload CLI write through the same hooks under
|
|
34
|
+
* `tsx` rather than inside a Next server. `revalidateTag` then throws
|
|
35
|
+
* `Invariant: static generation store missing in revalidateTag ...`. Nothing is
|
|
36
|
+
* stale in that case — there is no server to be stale — so it is the one
|
|
37
|
+
* failure that is not a fault.
|
|
38
|
+
*
|
|
39
|
+
* Matched on the message, the only stable surface Next gives it.
|
|
40
|
+
*/
|
|
41
|
+
export declare function isOutsideNextRequest(error: unknown): boolean;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=next-revalidate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-revalidate.d.ts","sourceRoot":"","sources":["../src/next-revalidate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,eAAe,MAAM,YAAY,CAAA;AAElD,KAAK,SAAS,GAAG,OAAO,eAAe,CAAA;AAEvC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,SAAS;;CAAyB,CAAA;AAE/C;;;;;;GAMG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,SAAS,CAAC,CAExD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE5D"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How stale a caller may find a tag after it is dropped: not at all.
|
|
3
|
+
*
|
|
4
|
+
* Next 16 made `revalidateTag`'s second argument required — it is a cache-life
|
|
5
|
+
* profile — and one-argument calls log a deprecation warning on every call.
|
|
6
|
+
* `{ expire: 0 }` is the immediate expiry the one-argument form used to mean,
|
|
7
|
+
* and it is what an editor pressing Publish or Save expects.
|
|
8
|
+
*
|
|
9
|
+
* Passed unconditionally rather than behind a version check. The peer range is
|
|
10
|
+
* `next >= 15`, and Next 15's `revalidateTag` takes one parameter and ignores a
|
|
11
|
+
* second, so the two-argument call is correct on both.
|
|
12
|
+
*
|
|
13
|
+
* `updateTag`, which Next offers as the other way out of the deprecation, is
|
|
14
|
+
* not usable here: it throws outside a Server Action, and these run in an
|
|
15
|
+
* Inngest step or in Payload's route handler.
|
|
16
|
+
*/
|
|
17
|
+
export const IMMEDIATE = { expire: 0 };
|
|
18
|
+
/**
|
|
19
|
+
* `next/cache`, loaded on first use.
|
|
20
|
+
*
|
|
21
|
+
* Dynamic so the package can be imported where Next is not installed (test
|
|
22
|
+
* runners, a non-Next frontend that passes its own revalidator). `next` is an
|
|
23
|
+
* optional peer.
|
|
24
|
+
*/
|
|
25
|
+
export async function loadNextCache() {
|
|
26
|
+
return await import('next/cache');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Whether `error` is Next saying there is no request to revalidate for.
|
|
30
|
+
*
|
|
31
|
+
* Seeds, migrations and the Payload CLI write through the same hooks under
|
|
32
|
+
* `tsx` rather than inside a Next server. `revalidateTag` then throws
|
|
33
|
+
* `Invariant: static generation store missing in revalidateTag ...`. Nothing is
|
|
34
|
+
* stale in that case — there is no server to be stale — so it is the one
|
|
35
|
+
* failure that is not a fault.
|
|
36
|
+
*
|
|
37
|
+
* Matched on the message, the only stable surface Next gives it.
|
|
38
|
+
*/
|
|
39
|
+
export function isOutsideNextRequest(error) {
|
|
40
|
+
return error instanceof Error && error.message.includes('static generation store missing');
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=next-revalidate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-revalidate.js","sourceRoot":"","sources":["../src/next-revalidate.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,MAAM,EAAE,CAAC,EAAW,CAAA;AAE/C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,MAAM,MAAM,CAAC,YAAY,CAAC,CAAA;AACnC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,OAAO,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,iCAAiC,CAAC,CAAA;AAC5F,CAAC"}
|
|
@@ -5,6 +5,16 @@ import type { RevalidateOnPublishOptions } from './types.js';
|
|
|
5
5
|
* publishing taxonomy and revalidates Next.js cache entries for the affected
|
|
6
6
|
* page, the listing routes, and the sitemap.
|
|
7
7
|
*
|
|
8
|
+
* The page path comes from `options.urlBuilders`, which is required: only the
|
|
9
|
+
* site knows where a collection's documents are served. A collection with no
|
|
10
|
+
* builder gets its tags dropped and no path revalidated, with a warning —
|
|
11
|
+
* there used to be built-in builders (`pages` → `/<slug>`, `posts` →
|
|
12
|
+
* `/blog/<slug>`, anything else → `/<slug>`), and a site whose routes differed
|
|
13
|
+
* revalidated the wrong path without a word.
|
|
14
|
+
*
|
|
15
|
+
* Tags default to `cacheTags.collection(<collection>)`. Pass the same
|
|
16
|
+
* `cacheTags` your readers and your tag hooks use.
|
|
17
|
+
*
|
|
8
18
|
* The default revalidate function dynamically imports `next/cache`, so the
|
|
9
19
|
* package is safe to install in non-Next.js contexts. Pass `options.revalidate`
|
|
10
20
|
* to use a different cache invalidation strategy.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"revalidate-on-publish.d.ts","sourceRoot":"","sources":["../src/revalidate-on-publish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"revalidate-on-publish.d.ts","sourceRoot":"","sources":["../src/revalidate-on-publish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAG9C,OAAO,KAAK,EAAgB,0BAA0B,EAAwB,MAAM,YAAY,CAAA;AAEhG;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,0BAA0B,GAClC,eAAe,CAAC,GAAG,CAqDrB"}
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { failureOptions } from './types.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
posts: (slug) => `/blog/${slug}`,
|
|
5
|
-
};
|
|
2
|
+
import { defaultCacheTags } from './cache-tags.js';
|
|
3
|
+
import { IMMEDIATE, loadNextCache } from './next-revalidate.js';
|
|
6
4
|
/**
|
|
7
5
|
* Builds the `revalidate-on-publish` Inngest function. Subscribes to the
|
|
8
6
|
* publishing taxonomy and revalidates Next.js cache entries for the affected
|
|
9
7
|
* page, the listing routes, and the sitemap.
|
|
10
8
|
*
|
|
9
|
+
* The page path comes from `options.urlBuilders`, which is required: only the
|
|
10
|
+
* site knows where a collection's documents are served. A collection with no
|
|
11
|
+
* builder gets its tags dropped and no path revalidated, with a warning —
|
|
12
|
+
* there used to be built-in builders (`pages` → `/<slug>`, `posts` →
|
|
13
|
+
* `/blog/<slug>`, anything else → `/<slug>`), and a site whose routes differed
|
|
14
|
+
* revalidated the wrong path without a word.
|
|
15
|
+
*
|
|
16
|
+
* Tags default to `cacheTags.collection(<collection>)`. Pass the same
|
|
17
|
+
* `cacheTags` your readers and your tag hooks use.
|
|
18
|
+
*
|
|
11
19
|
* The default revalidate function dynamically imports `next/cache`, so the
|
|
12
20
|
* package is safe to install in non-Next.js contexts. Pass `options.revalidate`
|
|
13
21
|
* to use a different cache invalidation strategy.
|
|
14
22
|
*/
|
|
15
23
|
export function createRevalidateOnPublishFunction(options) {
|
|
16
|
-
const urlBuilders =
|
|
24
|
+
const urlBuilders = options.urlBuilders;
|
|
25
|
+
const cacheTags = options.cacheTags ?? defaultCacheTags;
|
|
17
26
|
const collectionTags = options.collectionTags ?? {};
|
|
18
27
|
const revalidate = options.revalidate ?? defaultRevalidate;
|
|
19
28
|
return options.inngest.createFunction({
|
|
@@ -34,9 +43,13 @@ export function createRevalidateOnPublishFunction(options) {
|
|
|
34
43
|
const data = (event.data ?? {});
|
|
35
44
|
const collection = data.collection ?? 'pages';
|
|
36
45
|
const slug = data.slug ?? data.id ?? '';
|
|
37
|
-
const tags = collectionTags[collection] ?? [collection];
|
|
46
|
+
const tags = collectionTags[collection] ?? [cacheTags.collection(collection)];
|
|
38
47
|
await step.run('revalidate-page-path', async () => {
|
|
39
|
-
const builder = urlBuilders[collection]
|
|
48
|
+
const builder = Object.hasOwn(urlBuilders, collection) ? urlBuilders[collection] : undefined;
|
|
49
|
+
if (!builder) {
|
|
50
|
+
logger.warn('No urlBuilders entry for this collection, so no page path was revalidated. Add one to createRevalidateOnPublishFunction.', { collection, slug });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
40
53
|
const path = builder(slug);
|
|
41
54
|
await revalidate({ path, tags });
|
|
42
55
|
logger.info('Revalidated page path', { path, tags });
|
|
@@ -50,23 +63,6 @@ export function createRevalidateOnPublishFunction(options) {
|
|
|
50
63
|
return { collection, slug, revalidated: true };
|
|
51
64
|
});
|
|
52
65
|
}
|
|
53
|
-
/**
|
|
54
|
-
* How stale a caller may find a tag after this runs: not at all.
|
|
55
|
-
*
|
|
56
|
-
* Next 16 made `revalidateTag`'s second argument required — it is a cache-life
|
|
57
|
-
* profile — and one-argument calls log a deprecation warning on every publish.
|
|
58
|
-
* `{ expire: 0 }` is the immediate expiry the one-argument form used to mean,
|
|
59
|
-
* and it is what an editor pressing Publish expects.
|
|
60
|
-
*
|
|
61
|
-
* Passed unconditionally rather than behind a version check. The peer range is
|
|
62
|
-
* `next >= 15`, and Next 15's `revalidateTag` takes one parameter and ignores a
|
|
63
|
-
* second, so the two-argument call is correct on both.
|
|
64
|
-
*
|
|
65
|
-
* `updateTag`, which Next offers as the other way out of the deprecation, is
|
|
66
|
-
* not usable here: it throws outside a Server Action, and this runs in an
|
|
67
|
-
* Inngest step behind a route handler.
|
|
68
|
-
*/
|
|
69
|
-
const IMMEDIATE = { expire: 0 };
|
|
70
66
|
/**
|
|
71
67
|
* Default revalidator: dynamic-imports `next/cache` so the package can be
|
|
72
68
|
* imported in environments without Next.js (e.g. test runners that don't
|
|
@@ -74,7 +70,7 @@ const IMMEDIATE = { expire: 0 };
|
|
|
74
70
|
* because `revalidatePath('')` triggers a noisy Next.js warning.
|
|
75
71
|
*/
|
|
76
72
|
const defaultRevalidate = async ({ path, tags }) => {
|
|
77
|
-
const { revalidatePath, revalidateTag } =
|
|
73
|
+
const { revalidatePath, revalidateTag } = await loadNextCache();
|
|
78
74
|
if (path)
|
|
79
75
|
revalidatePath(path);
|
|
80
76
|
for (const tag of tags)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"revalidate-on-publish.js","sourceRoot":"","sources":["../src/revalidate-on-publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"revalidate-on-publish.js","sourceRoot":"","sources":["../src/revalidate-on-publish.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { CollectionAfterChangeHook, CollectionAfterDeleteHook, GlobalAfterChangeHook } from 'payload';
|
|
2
|
+
import { type CacheTags } from './cache-tags.js';
|
|
3
|
+
/** Drops one tag. Return a promise to have it awaited. */
|
|
4
|
+
export type RevalidateTagFn = (tag: string) => void | Promise<void>;
|
|
5
|
+
export interface TagRevalidationOptions {
|
|
6
|
+
/**
|
|
7
|
+
* The tag scheme. Pass the same object your readers build their tags from —
|
|
8
|
+
* that is the whole point of it. Default: `defaultCacheTags`.
|
|
9
|
+
*/
|
|
10
|
+
cacheTags?: CacheTags;
|
|
11
|
+
/**
|
|
12
|
+
* How to drop a tag. Default: Next's `revalidateTag(tag, { expire: 0 })`,
|
|
13
|
+
* loaded from `next/cache` on first use. Supply your own for a non-Next
|
|
14
|
+
* frontend or a CDN.
|
|
15
|
+
*/
|
|
16
|
+
revalidateTag?: RevalidateTagFn;
|
|
17
|
+
}
|
|
18
|
+
/** Which tags a hook drops, built from the scheme. */
|
|
19
|
+
export type TagSelector = (cacheTags: CacheTags, slug: string) => readonly string[];
|
|
20
|
+
export interface CollectionTagHookOptions {
|
|
21
|
+
/**
|
|
22
|
+
* The tags to drop. Default: the collection's own tag,
|
|
23
|
+
* `cacheTags.collection(slug)`. Use it to drop a tag a *different* reader
|
|
24
|
+
* owns — a navigation global that links to pages caches the pages' slugs:
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* tags: (t, slug) => [t.collection(slug), t.global('navigation')]
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
tags?: TagSelector;
|
|
31
|
+
/**
|
|
32
|
+
* Which `afterChange` operations drop. Default: both. An upload collection
|
|
33
|
+
* can pass `['update']`: nothing can reference a document that did not exist
|
|
34
|
+
* a moment ago, so creating one makes no cached page untrue.
|
|
35
|
+
*/
|
|
36
|
+
operations?: ReadonlyArray<'create' | 'update'>;
|
|
37
|
+
}
|
|
38
|
+
export interface GlobalTagHookOptions {
|
|
39
|
+
/** The tags to drop. Default: the global's own tag, `cacheTags.global(slug)`. */
|
|
40
|
+
tags?: TagSelector;
|
|
41
|
+
}
|
|
42
|
+
export interface TagRevalidationHooks {
|
|
43
|
+
/** `afterChange` for a collection. Skips draft writes. */
|
|
44
|
+
afterCollectionChange(options?: CollectionTagHookOptions): CollectionAfterChangeHook;
|
|
45
|
+
/** `afterDelete` for a collection. */
|
|
46
|
+
afterCollectionDelete(options?: Pick<CollectionTagHookOptions, 'tags'>): CollectionAfterDeleteHook;
|
|
47
|
+
/** `afterChange` for a global. */
|
|
48
|
+
afterGlobalChange(options?: GlobalTagHookOptions): GlobalAfterChangeHook;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Builds the tag-revalidation hooks for one tag scheme.
|
|
52
|
+
*
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { createTagRevalidationHooks } from '@forumone/throughline-workflows'
|
|
55
|
+
* import { cacheTags } from './lib/cache-tags'
|
|
56
|
+
*
|
|
57
|
+
* const revalidation = createTagRevalidationHooks({ cacheTags })
|
|
58
|
+
*
|
|
59
|
+
* const Pages: CollectionConfig = {
|
|
60
|
+
* slug: 'pages',
|
|
61
|
+
* hooks: {
|
|
62
|
+
* afterChange: [revalidation.afterCollectionChange()],
|
|
63
|
+
* afterDelete: [revalidation.afterCollectionDelete()],
|
|
64
|
+
* },
|
|
65
|
+
* ...
|
|
66
|
+
* }
|
|
67
|
+
* const Navigation: GlobalConfig = {
|
|
68
|
+
* slug: 'navigation',
|
|
69
|
+
* hooks: { afterChange: [revalidation.afterGlobalChange()] },
|
|
70
|
+
* ...
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* A hook never throws. Failing it would roll back the editor's save over a
|
|
75
|
+
* cache miss. A failure is logged at `error` instead, and the one expected
|
|
76
|
+
* failure — running outside a Next request, as seeds, migrations and the CLI
|
|
77
|
+
* do — at `debug`.
|
|
78
|
+
*/
|
|
79
|
+
export declare function createTagRevalidationHooks(options?: TagRevalidationOptions): TagRevalidationHooks;
|
|
80
|
+
//# sourceMappingURL=revalidate-tag-hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revalidate-tag-hooks.d.ts","sourceRoot":"","sources":["../src/revalidate-tag-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,yBAAyB,EACzB,qBAAqB,EAEtB,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,iBAAiB,CAAA;AAgBlE,0DAA0D;AAC1D,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAEnE,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB;;;;OAIG;IACH,aAAa,CAAC,EAAE,eAAe,CAAA;CAChC;AAED,sDAAsD;AACtD,MAAM,MAAM,WAAW,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,CAAA;AAEnF,MAAM,WAAW,wBAAwB;IACvC;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAA;CAChD;AAED,MAAM,WAAW,oBAAoB;IACnC,iFAAiF;IACjF,IAAI,CAAC,EAAE,WAAW,CAAA;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,0DAA0D;IAC1D,qBAAqB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,yBAAyB,CAAA;IACpF,sCAAsC;IACtC,qBAAqB,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC,GAAG,yBAAyB,CAAA;IAClG,kCAAkC;IAClC,iBAAiB,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,qBAAqB,CAAA;CACzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,GAAE,sBAA2B,GACnC,oBAAoB,CA2DtB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { isDraftWrite } from '@forumone/throughline-publishing';
|
|
2
|
+
import { defaultCacheTags } from './cache-tags.js';
|
|
3
|
+
import { IMMEDIATE, isOutsideNextRequest, loadNextCache } from './next-revalidate.js';
|
|
4
|
+
/**
|
|
5
|
+
* Builds the tag-revalidation hooks for one tag scheme.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createTagRevalidationHooks } from '@forumone/throughline-workflows'
|
|
9
|
+
* import { cacheTags } from './lib/cache-tags'
|
|
10
|
+
*
|
|
11
|
+
* const revalidation = createTagRevalidationHooks({ cacheTags })
|
|
12
|
+
*
|
|
13
|
+
* const Pages: CollectionConfig = {
|
|
14
|
+
* slug: 'pages',
|
|
15
|
+
* hooks: {
|
|
16
|
+
* afterChange: [revalidation.afterCollectionChange()],
|
|
17
|
+
* afterDelete: [revalidation.afterCollectionDelete()],
|
|
18
|
+
* },
|
|
19
|
+
* ...
|
|
20
|
+
* }
|
|
21
|
+
* const Navigation: GlobalConfig = {
|
|
22
|
+
* slug: 'navigation',
|
|
23
|
+
* hooks: { afterChange: [revalidation.afterGlobalChange()] },
|
|
24
|
+
* ...
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* A hook never throws. Failing it would roll back the editor's save over a
|
|
29
|
+
* cache miss. A failure is logged at `error` instead, and the one expected
|
|
30
|
+
* failure — running outside a Next request, as seeds, migrations and the CLI
|
|
31
|
+
* do — at `debug`.
|
|
32
|
+
*/
|
|
33
|
+
export function createTagRevalidationHooks(options = {}) {
|
|
34
|
+
const cacheTags = options.cacheTags ?? defaultCacheTags;
|
|
35
|
+
const revalidateTag = options.revalidateTag ?? nextRevalidateTag;
|
|
36
|
+
async function drop(tags, req) {
|
|
37
|
+
for (const tag of new Set(tags)) {
|
|
38
|
+
try {
|
|
39
|
+
await revalidateTag(tag);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
/*
|
|
43
|
+
Outside Next there is no server to be stale, so that one is noise.
|
|
44
|
+
Anything else means the cache did *not* clear while a server was
|
|
45
|
+
running — a real fault, so it is logged loudly. It is still not
|
|
46
|
+
thrown: an `afterChange` that throws fails the editor's save.
|
|
47
|
+
*/
|
|
48
|
+
if (isOutsideNextRequest(error)) {
|
|
49
|
+
req.payload.logger.debug({ tag }, 'Skipped cache revalidation: not inside a Next request.');
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
req.payload.logger.error({ err: error, tag }, 'Failed to revalidate a cache tag; the cached copy is stale.');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
afterCollectionChange(hookOptions = {}) {
|
|
59
|
+
const select = hookOptions.tags ?? ((t, slug) => [t.collection(slug)]);
|
|
60
|
+
const operations = new Set(hookOptions.operations ?? ['create', 'update']);
|
|
61
|
+
return async ({ collection, doc, operation, req }) => {
|
|
62
|
+
if (!operations.has(operation))
|
|
63
|
+
return doc;
|
|
64
|
+
if (isUnpublishedWrite(req, collection.slug, doc, operation))
|
|
65
|
+
return doc;
|
|
66
|
+
await drop(select(cacheTags, collection.slug), req);
|
|
67
|
+
return doc;
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
afterCollectionDelete(hookOptions = {}) {
|
|
71
|
+
const select = hookOptions.tags ?? ((t, slug) => [t.collection(slug)]);
|
|
72
|
+
return async ({ collection, doc, req }) => {
|
|
73
|
+
await drop(select(cacheTags, collection.slug), req);
|
|
74
|
+
return doc;
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
afterGlobalChange(hookOptions = {}) {
|
|
78
|
+
const select = hookOptions.tags ?? ((t, slug) => [t.global(slug)]);
|
|
79
|
+
return async ({ doc, global, req }) => {
|
|
80
|
+
await drop(select(cacheTags, global.slug), req);
|
|
81
|
+
return doc;
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/*
|
|
87
|
+
A write the public cannot see changes nothing cached, so there is nothing to
|
|
88
|
+
drop — and with autosave on, "a draft save" is every few seconds of an open
|
|
89
|
+
editor. Without this, one editing session evicts the tag over and over and every
|
|
90
|
+
request in between pays a cold read for content that did not change.
|
|
91
|
+
|
|
92
|
+
`isDraftWrite` is publishing's own predicate, the one its trust boundary uses.
|
|
93
|
+
An `afterChange` hook cannot answer this for itself: Payload sets
|
|
94
|
+
`data._status = 'draft'` on any `draft: true` update before hooks run, and
|
|
95
|
+
`previousDoc` is the latest *version*, so a draft save of a published document
|
|
96
|
+
and an unpublish of it look the same from in here. The publishing plugin records
|
|
97
|
+
the operation's real `draft` argument in `beforeOperation`. That is recorded for
|
|
98
|
+
the collections the plugin manages; on any other collection `isDraftWrite` is
|
|
99
|
+
false and the hook drops, which is the safe direction.
|
|
100
|
+
|
|
101
|
+
An unpublish is not a draft write and still drops — it is the case that would
|
|
102
|
+
otherwise leave a cached link pointing at a 404.
|
|
103
|
+
|
|
104
|
+
A *create* that lands as a draft is unambiguous on its own: nothing published
|
|
105
|
+
existed a moment ago, so nothing cached can be about it.
|
|
106
|
+
*/
|
|
107
|
+
function isUnpublishedWrite(req, collectionSlug, doc, operation) {
|
|
108
|
+
if (operation === 'create')
|
|
109
|
+
return doc?._status === 'draft';
|
|
110
|
+
return isDraftWrite(req, collectionSlug, doc?.id);
|
|
111
|
+
}
|
|
112
|
+
async function nextRevalidateTag(tag) {
|
|
113
|
+
const { revalidateTag } = await loadNextCache();
|
|
114
|
+
revalidateTag(tag, IMMEDIATE);
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=revalidate-tag-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revalidate-tag-hooks.js","sourceRoot":"","sources":["../src/revalidate-tag-hooks.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Inngest } from 'inngest';
|
|
2
2
|
import type { Payload } from 'payload';
|
|
3
|
+
import type { CacheTags } from './cache-tags.js';
|
|
3
4
|
/**
|
|
4
5
|
* Common dependencies for workflow factories. Every factory takes an
|
|
5
6
|
* Inngest client and a Payload instance — the workflows-as-factories shape
|
|
@@ -92,15 +93,30 @@ export interface RevalidateOnPublishOptions extends BaseWorkflowOptions {
|
|
|
92
93
|
*/
|
|
93
94
|
revalidate?: RevalidateFn;
|
|
94
95
|
/**
|
|
95
|
-
* Per-collection URL builders
|
|
96
|
-
*
|
|
97
|
-
*
|
|
96
|
+
* Per-collection URL builders: where a document of each collection is
|
|
97
|
+
* served, given its slug. Required, and there are no built-in entries —
|
|
98
|
+
* only the site knows its routes. A collection with no entry has its tags
|
|
99
|
+
* dropped and no path revalidated, and the run logs a warning.
|
|
100
|
+
*
|
|
101
|
+
* ```ts
|
|
102
|
+
* urlBuilders: {
|
|
103
|
+
* pages: (slug) => (slug === 'home' ? '/' : `/${slug}`),
|
|
104
|
+
* posts: (slug) => `/news/${slug}`,
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
urlBuilders: Record<string, (slug: string) => string>;
|
|
109
|
+
/**
|
|
110
|
+
* The tag scheme, shared with readers and with
|
|
111
|
+
* `createTagRevalidationHooks`. A collection with no `collectionTags` entry
|
|
112
|
+
* drops `[cacheTags.collection(<collection>)]`. Default: `defaultCacheTags`,
|
|
113
|
+
* whose collection tag is the bare slug.
|
|
98
114
|
*/
|
|
99
|
-
|
|
115
|
+
cacheTags?: CacheTags;
|
|
100
116
|
/**
|
|
101
|
-
* Per-collection cache tags
|
|
102
|
-
* supported for collections that participate in shared
|
|
103
|
-
* `programs` → `['programs', 'sitemap']`).
|
|
117
|
+
* Per-collection cache tags, overriding the scheme for that collection.
|
|
118
|
+
* Multiple tags supported for collections that participate in shared
|
|
119
|
+
* listings (e.g. `programs` → `['programs', 'sitemap']`).
|
|
104
120
|
*/
|
|
105
121
|
collectionTags?: Record<string, string[]>;
|
|
106
122
|
/**
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD;;;;;GAKG;AAkBH,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,EAAE;IAC1C,4CAA4C;IAC5C,KAAK,EAAE,KAAK,CAAA;IACZ,gEAAgE;IAChE,KAAK,EAAE;QACL,IAAI,CAAC,EAAE;YACL,WAAW,CAAC,EAAE,MAAM,CAAA;YACpB,MAAM,CAAC,EAAE,MAAM,CAAA;YACf,KAAK,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAC1B,CAAA;KACF,CAAA;CACF,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAE1B,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CAAC,EAAE,sBAAsB,CAAA;IAC1C;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,mBAAmB,EAC5B,kBAAkB,CAAC,EAAE,MAAM,GAC1B;IAAE,SAAS,CAAC,EAAE,sBAAsB,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAS9D;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,CAAC,EAAE,sBAAsB,CAAA;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,IAAI,EAAE,MAAM,EAAE,CAAA;CACf;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAEzE,MAAM,WAAW,0BAA2B,SAAQ,mBAAmB;IACrE;;;;;OAKG;IACH,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB;;;;;;;;;;;;OAYG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;IACrD;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACzC;;;;OAIG;IACH,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,4DAA4D;AAC5D,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,EAAE,EAAE,MAAM,CAAA;IACV,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC5B;AAED,MAAM,WAAW,gCAAiC,SAAQ,mBAAmB;IAC3E,4DAA4D;IAC5D,WAAW,EAAE,yBAAyB,EAAE,CAAA;IACxC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAA;IAC9E;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,6BAA8B,SAAQ,mBAAmB;IACxE;;;OAGG;IACH,WAAW,EAAE,yBAAyB,EAAE,CAAA;IACxC,2HAA2H;IAC3H,OAAO,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAA;IAC9E;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sEAAsE;IACtE,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,2BAA4B,SAAQ,mBAAmB;IACtE,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mEAAmE;IACnE,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,KAAK,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAA;IAC7C,0DAA0D;IAC1D,MAAM,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACjD;AAED,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IAChE,OAAO,EAAE,OAAO,CAAA;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC7B,6DAA6D;IAC7D,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,CAAC,GAAG,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAA;CAC/D;AAED,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D,MAAM,EAAE,qBAAqB,EAAE,CAAA;IAC/B,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAClF,wDAAwD;IACxD,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAqEA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC5B,OAA4B,EAC5B,kBAA2B;IAE3B,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,kBAAkB,CAAA;IAE7D,OAAO;QACL,wEAAwE;QACxE,oEAAoE;QACpE,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forumone/throughline-workflows",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Composable Inngest function factories for the Throughline framework — revalidation, scheduled publishing, approval expiration, audit fan-out, healthcheck.",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Composable Inngest function factories and Payload cache-revalidation hooks for the Throughline framework — revalidation, scheduled publishing, approval expiration, audit fan-out, healthcheck.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./cache-tags": {
|
|
14
|
+
"types": "./dist/cache-tags.d.ts",
|
|
15
|
+
"default": "./dist/cache-tags.js"
|
|
12
16
|
}
|
|
13
17
|
},
|
|
14
18
|
"files": [
|
|
@@ -38,7 +42,7 @@
|
|
|
38
42
|
"peerDependencies": {
|
|
39
43
|
"inngest": "^4.0.0",
|
|
40
44
|
"next": ">=15.0.0",
|
|
41
|
-
"payload": "^3.
|
|
45
|
+
"payload": "^3.89.0"
|
|
42
46
|
},
|
|
43
47
|
"peerDependenciesMeta": {
|
|
44
48
|
"next": {
|
|
@@ -46,7 +50,8 @@
|
|
|
46
50
|
}
|
|
47
51
|
},
|
|
48
52
|
"dependencies": {
|
|
49
|
-
"@forumone/throughline-core": "0.
|
|
53
|
+
"@forumone/throughline-core": "0.10.0",
|
|
54
|
+
"@forumone/throughline-publishing": "0.11.0"
|
|
50
55
|
},
|
|
51
56
|
"devDependencies": {
|
|
52
57
|
"@types/node": "^24.13.2",
|