@tetherto/dev-websites-core 0.3.4 → 0.3.6

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.
@@ -0,0 +1,297 @@
1
+ import { t as createLogger } from "../logger.mjs";
2
+ import { readFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { revalidatePath, revalidateTag } from "next/cache.js";
5
+ import { after } from "next/server.js";
6
+ //#region src/cache/shouldSkipCacheWrite.ts
7
+ /**
8
+ * ISR pages with `dynamicParams = true` still render for unknown paths, call
9
+ * `notFound()`, and Next then asks the handler to persist the 404. Scanners
10
+ * hitting junk URLs rewrite that row on every request and bloat the cache
11
+ * collection and the Mongo oplog, so non-success page/route responses are
12
+ * never stored.
13
+ *
14
+ * `FETCH` entries are unaffected: Next nests their upstream status under
15
+ * `value.data.status`, not the top-level `status` read here.
16
+ */
17
+ function shouldSkipCacheWrite(value) {
18
+ if (!value || typeof value !== "object") return false;
19
+ const status = value.status;
20
+ if (typeof status === "number" && (status < 200 || status >= 400)) return true;
21
+ if (typeof value.html === "string" && value.html.includes("id=\"__next_error__\"")) return true;
22
+ return false;
23
+ }
24
+ //#endregion
25
+ //#region src/cache/createMongoCacheHandler.ts
26
+ const DEFAULT_DB_NAME = "next_cache";
27
+ const DEFAULT_COLLECTION_NAME = "fetch_cache";
28
+ /**
29
+ * `get()` deliberately returns rows of any age so Next can serve them stale
30
+ * while it revalidates, which means nothing expires on its own. Mongo drops a
31
+ * row this long after its last write; anything still in use is rewritten by
32
+ * revalidation far sooner, so only paths nobody requests anymore fall out.
33
+ */
34
+ const DEFAULT_ENTRY_TTL_SECONDS = 604800;
35
+ function readBuildId() {
36
+ try {
37
+ return readFileSync(path.join(process.cwd(), ".next", "BUILD_ID"), "utf8").trim();
38
+ } catch {
39
+ return "development";
40
+ }
41
+ }
42
+ /**
43
+ * Next only passes `ctx.tags` for fetch entries. Page and route entries carry
44
+ * theirs — including the `_N_T_<path>` tag behind `revalidatePath` — in the
45
+ * response headers, so without this fallback `revalidateTag` can never purge
46
+ * rendered HTML and CMS edits wait out the route's full revalidate window.
47
+ */
48
+ function resolveTags(value, ctx) {
49
+ if (ctx?.tags?.length) return ctx.tags;
50
+ const header = value?.headers?.["x-next-cache-tags"];
51
+ return typeof header === "string" ? header.split(",").filter(Boolean) : [];
52
+ }
53
+ /**
54
+ * Mongo-backed `cacheHandler` for Next's ISR / route / fetch cache, so every
55
+ * instance of a deployment shares one cache instead of a per-pod filesystem.
56
+ *
57
+ * Sites normally point `cacheHandler` at `@tetherto/dev-websites-core/cache/handler`,
58
+ * which builds the client from `MONGODB_URI`. Use this factory when the site
59
+ * already owns a client and wants to pass it in.
60
+ *
61
+ * Wire it with `cacheMaxMemorySize: 0`, and include
62
+ * `node_modules/@tetherto/dev-websites-core/dist/cache` in
63
+ * `outputFileTracingIncludes` so the standalone server can load the handler.
64
+ */
65
+ function createMongoCacheHandler(options) {
66
+ const { clientPromise, dbName = DEFAULT_DB_NAME, collectionName = DEFAULT_COLLECTION_NAME, entryTtlSeconds = DEFAULT_ENTRY_TTL_SECONDS } = options;
67
+ /**
68
+ * Rendered HTML embeds the chunk filenames of the build that produced it, so
69
+ * an entry left by a previous build loads but never hydrates — its chunks are
70
+ * gone (404). Page keys are therefore scoped per build, while instances of
71
+ * the same build keep sharing them.
72
+ *
73
+ * During `next build` `.next/BUILD_ID` does not exist yet, which is safe
74
+ * because Next writes prerenders to disk and never calls `set()` for page
75
+ * entries during the build.
76
+ */
77
+ const buildId = options.buildId ?? readBuildId();
78
+ /**
79
+ * Page and route entries are keyed by pathname; fetch entries are keyed by a
80
+ * content hash and stay shared across builds, so upstream data survives a
81
+ * deploy.
82
+ */
83
+ const scopeKey = (key) => key.startsWith("/") ? `${buildId}${key}` : key;
84
+ let indexPromise;
85
+ const getCollection = async () => {
86
+ const collection = (await clientPromise).db(dbName).collection(collectionName);
87
+ indexPromise ??= Promise.all([
88
+ collection.createIndex({ key: 1 }, { unique: true }),
89
+ collection.createIndex({ tags: 1 }),
90
+ collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 })
91
+ ]).catch(() => {});
92
+ await indexPromise;
93
+ return collection;
94
+ };
95
+ return class MongoCacheHandlerImpl {
96
+ /**
97
+ * Returns the entry whatever its age. Next compares `lastModified` against
98
+ * the route's own revalidate window and decides itself whether to serve it
99
+ * as fresh, serve it stale while refreshing in the background, or
100
+ * re-render. Dropping stale rows here turns every expiry into a blocking
101
+ * render.
102
+ */
103
+ async get(key) {
104
+ const collection = await getCollection();
105
+ const scoped = scopeKey(key);
106
+ const entry = await collection.findOne({ key: scoped });
107
+ if (!entry) return null;
108
+ if (shouldSkipCacheWrite(entry.value)) {
109
+ await collection.deleteOne({ key: scoped });
110
+ return null;
111
+ }
112
+ const value = entry.value ?? null;
113
+ const segmentData = value?.segmentData;
114
+ return {
115
+ lastModified: entry.lastModified,
116
+ value: segmentData && !(segmentData instanceof Map) ? {
117
+ ...value,
118
+ segmentData: new Map(Object.entries(segmentData))
119
+ } : value
120
+ };
121
+ }
122
+ async set(key, value, ctx) {
123
+ if (shouldSkipCacheWrite(value)) return;
124
+ const collection = await getCollection();
125
+ const scoped = scopeKey(key);
126
+ const now = Date.now();
127
+ await collection.updateOne({ key: scoped }, { $set: {
128
+ key: scoped,
129
+ value,
130
+ lastModified: now,
131
+ expiresAt: new Date(now + entryTtlSeconds * 1e3),
132
+ tags: resolveTags(value, ctx)
133
+ } }, { upsert: true });
134
+ }
135
+ async delete(key) {
136
+ await (await getCollection()).deleteOne({ key: scopeKey(key) });
137
+ }
138
+ resetRequestCache() {}
139
+ async revalidateTag(tags, _durations) {
140
+ const list = typeof tags === "string" ? [tags] : tags;
141
+ if (!list?.length) return;
142
+ await (await getCollection()).deleteMany({ tags: { $in: list } });
143
+ }
144
+ };
145
+ }
146
+ //#endregion
147
+ //#region src/cache/shouldSkipRevalidate.ts
148
+ /**
149
+ * Standalone scripts (e.g. seed) pass `context: { disableRevalidate: true }`
150
+ * through the Payload Local API so collection hooks can skip cache invalidation —
151
+ * `revalidatePath` throws outside an active Next.js request context.
152
+ */
153
+ function shouldSkipRevalidate(req) {
154
+ return req?.context?.disableRevalidate === true;
155
+ }
156
+ //#endregion
157
+ //#region src/cache/safeRevalidatePath.ts
158
+ const log = createLogger("cache:revalidate");
159
+ /**
160
+ * Payload `jobs.autoRun` can inherit a stale Next request store (often `/admin`).
161
+ * `revalidatePath` then throws "during render", which would fail schedulePublish.
162
+ *
163
+ * Prefer `after()` so the bust runs once the response/render finishes. Fall back
164
+ * to a direct call (jobs / seed) and swallow so the CMS write still commits.
165
+ */
166
+ function runAfterRequest(task, label) {
167
+ try {
168
+ after(task);
169
+ return;
170
+ } catch {}
171
+ try {
172
+ task();
173
+ } catch (error) {
174
+ log.warn(`${label}: ${String(error)}`);
175
+ }
176
+ }
177
+ function safeRevalidatePath(path, type) {
178
+ runAfterRequest(() => {
179
+ if (type) {
180
+ revalidatePath(path, type);
181
+ return;
182
+ }
183
+ revalidatePath(path);
184
+ }, `revalidatePath skipped for ${path}`);
185
+ }
186
+ function safeRevalidateTag(tag, profile) {
187
+ runAfterRequest(() => {
188
+ revalidateTag(tag, profile);
189
+ }, `revalidateTag skipped for ${tag}`);
190
+ }
191
+ //#endregion
192
+ //#region src/cache/createPublishedSlugRevalidateHooks.ts
193
+ /**
194
+ * Payload hooks that revalidate when a draft/published doc with a `slug` changes.
195
+ * Skips when `req.context.disableRevalidate` is set (seeds).
196
+ */
197
+ function createPublishedSlugRevalidateHooks(handlers) {
198
+ const afterChange = ({ doc, previousDoc, req }) => {
199
+ if (shouldSkipRevalidate(req)) return doc;
200
+ const isPublished = doc?._status === "published";
201
+ const wasPublished = previousDoc?._status === "published";
202
+ if (!isPublished && !wasPublished) return doc;
203
+ const slugs = /* @__PURE__ */ new Set();
204
+ if (doc?.slug) slugs.add(doc.slug);
205
+ if (previousDoc?.slug) slugs.add(previousDoc.slug);
206
+ handlers.onPublishedSlugs(slugs);
207
+ handlers.onPublishedList?.();
208
+ return doc;
209
+ };
210
+ const afterDelete = ({ doc, req }) => {
211
+ if (shouldSkipRevalidate(req)) return doc;
212
+ if (doc?._status !== "published") return doc;
213
+ if (doc?.slug) handlers.onPublishedSlugs([doc.slug]);
214
+ handlers.onPublishedList?.();
215
+ return doc;
216
+ };
217
+ return {
218
+ afterChange,
219
+ afterDelete
220
+ };
221
+ }
222
+ /** Hooks that always call `onChange` (e.g. categories / tags that affect listings). */
223
+ function createAlwaysRevalidateHooks(onChange) {
224
+ const afterChange = ({ doc, req }) => {
225
+ if (shouldSkipRevalidate(req)) return doc;
226
+ onChange();
227
+ return doc;
228
+ };
229
+ const afterDelete = ({ doc, req }) => {
230
+ if (shouldSkipRevalidate(req)) return doc;
231
+ onChange();
232
+ return doc;
233
+ };
234
+ return {
235
+ afterChange,
236
+ afterDelete
237
+ };
238
+ }
239
+ //#endregion
240
+ //#region src/cache/createPathRevalidators.ts
241
+ function createBlogRevalidators(locales, paths) {
242
+ const revalidatePost = (slugs) => {
243
+ for (const slug of slugs) {
244
+ if (!slug) continue;
245
+ for (const locale of locales) safeRevalidatePath(paths.post(slug, locale));
246
+ }
247
+ safeRevalidatePath(paths.postDynamic, "page");
248
+ };
249
+ const revalidateList = () => {
250
+ safeRevalidatePath(paths.list, "page");
251
+ if (paths.serverSitemap) safeRevalidatePath(paths.serverSitemap);
252
+ if (paths.llms) safeRevalidatePath(paths.llms);
253
+ };
254
+ const revalidateAllSurfaces = () => {
255
+ safeRevalidatePath(paths.list, "page");
256
+ safeRevalidatePath(paths.postDynamic, "page");
257
+ if (paths.sitemap) safeRevalidatePath(paths.sitemap);
258
+ if (paths.llms) safeRevalidatePath(paths.llms);
259
+ };
260
+ return {
261
+ revalidatePost,
262
+ revalidateList,
263
+ revalidateAllSurfaces
264
+ };
265
+ }
266
+ function createChangelogRevalidators(locales, paths) {
267
+ const revalidateEntry = (slugs) => {
268
+ for (const slug of slugs) {
269
+ if (!slug) continue;
270
+ for (const locale of locales) safeRevalidatePath(paths.detail(slug, locale));
271
+ }
272
+ safeRevalidatePath(paths.detailDynamic, "page");
273
+ };
274
+ const revalidateList = () => {
275
+ safeRevalidatePath(paths.list, "page");
276
+ if (paths.rss) safeRevalidatePath(paths.rss, "page");
277
+ if (paths.serverSitemap) safeRevalidatePath(paths.serverSitemap);
278
+ if (paths.llms) safeRevalidatePath(paths.llms);
279
+ };
280
+ const revalidateAllSurfaces = () => {
281
+ safeRevalidatePath(paths.list, "page");
282
+ safeRevalidatePath(paths.detailDynamic, "page");
283
+ if (paths.rss) safeRevalidatePath(paths.rss, "page");
284
+ if (paths.serverSitemap) safeRevalidatePath(paths.serverSitemap);
285
+ if (paths.sitemap) safeRevalidatePath(paths.sitemap);
286
+ if (paths.llms) safeRevalidatePath(paths.llms);
287
+ };
288
+ return {
289
+ revalidateEntry,
290
+ revalidateList,
291
+ revalidateAllSurfaces
292
+ };
293
+ }
294
+ //#endregion
295
+ export { createAlwaysRevalidateHooks, createBlogRevalidators, createChangelogRevalidators, createMongoCacheHandler, createPublishedSlugRevalidateHooks, safeRevalidatePath, safeRevalidateTag, shouldSkipCacheWrite, shouldSkipRevalidate };
296
+
297
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/cache/shouldSkipCacheWrite.ts","../../src/cache/createMongoCacheHandler.ts","../../src/cache/shouldSkipRevalidate.ts","../../src/cache/safeRevalidatePath.ts","../../src/cache/createPublishedSlugRevalidateHooks.ts","../../src/cache/createPathRevalidators.ts"],"sourcesContent":["import type { CachedValue } from './types.js'\n\n/**\n * ISR pages with `dynamicParams = true` still render for unknown paths, call\n * `notFound()`, and Next then asks the handler to persist the 404. Scanners\n * hitting junk URLs rewrite that row on every request and bloat the cache\n * collection and the Mongo oplog, so non-success page/route responses are\n * never stored.\n *\n * `FETCH` entries are unaffected: Next nests their upstream status under\n * `value.data.status`, not the top-level `status` read here.\n */\nexport function shouldSkipCacheWrite(value: CachedValue | null | undefined): boolean {\n if (!value || typeof value !== 'object') return false\n\n const status = value.status\n if (typeof status === 'number' && (status < 200 || status >= 400)) return true\n\n if (typeof value.html === 'string' && value.html.includes('id=\"__next_error__\"')) {\n return true\n }\n\n return false\n}\n","import { readFileSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { shouldSkipCacheWrite } from './shouldSkipCacheWrite.js'\n\nimport type {\n CacheSetContext,\n CachedEntry,\n CachedValue,\n MongoCacheClient,\n MongoCacheCollection,\n} from './types.js'\n\nconst DEFAULT_DB_NAME = 'next_cache'\nconst DEFAULT_COLLECTION_NAME = 'fetch_cache'\n\n/**\n * `get()` deliberately returns rows of any age so Next can serve them stale\n * while it revalidates, which means nothing expires on its own. Mongo drops a\n * row this long after its last write; anything still in use is rewritten by\n * revalidation far sooner, so only paths nobody requests anymore fall out.\n */\nconst DEFAULT_ENTRY_TTL_SECONDS = 60 * 60 * 24 * 7\n\nexport type MongoCacheHandlerOptions = {\n /** Connected client, owned by the consuming app so this package needs no driver. */\n clientPromise: Promise<MongoCacheClient>\n dbName?: string\n collectionName?: string\n /** TTL applied on every write. Defaults to 7 days. */\n entryTtlSeconds?: number\n /** Overrides the build id read from `.next/BUILD_ID`. */\n buildId?: string\n}\n\nexport type MongoCacheHandler = {\n get(key: string): Promise<CachedEntry | null>\n set(key: string, value: CachedValue | null, ctx?: CacheSetContext): Promise<void>\n delete(key: string): Promise<void>\n resetRequestCache(): void\n revalidateTag(tags: string | string[], durations?: unknown): Promise<void>\n}\n\n/**\n * Next instantiates the configured handler with `new Handler(ctx)`, so the\n * factory returns a class rather than an instance.\n */\nexport type MongoCacheHandlerConstructor = new (ctx?: unknown) => MongoCacheHandler\n\nfunction readBuildId(): string {\n try {\n return readFileSync(path.join(process.cwd(), '.next', 'BUILD_ID'), 'utf8').trim()\n } catch {\n return 'development'\n }\n}\n\n/**\n * Next only passes `ctx.tags` for fetch entries. Page and route entries carry\n * theirs — including the `_N_T_<path>` tag behind `revalidatePath` — in the\n * response headers, so without this fallback `revalidateTag` can never purge\n * rendered HTML and CMS edits wait out the route's full revalidate window.\n */\nfunction resolveTags(value: CachedValue | null | undefined, ctx?: CacheSetContext): string[] {\n if (ctx?.tags?.length) return ctx.tags\n\n const header = value?.headers?.['x-next-cache-tags']\n return typeof header === 'string' ? header.split(',').filter(Boolean) : []\n}\n\n/**\n * Mongo-backed `cacheHandler` for Next's ISR / route / fetch cache, so every\n * instance of a deployment shares one cache instead of a per-pod filesystem.\n *\n * Sites normally point `cacheHandler` at `@tetherto/dev-websites-core/cache/handler`,\n * which builds the client from `MONGODB_URI`. Use this factory when the site\n * already owns a client and wants to pass it in.\n *\n * Wire it with `cacheMaxMemorySize: 0`, and include\n * `node_modules/@tetherto/dev-websites-core/dist/cache` in\n * `outputFileTracingIncludes` so the standalone server can load the handler.\n */\nexport function createMongoCacheHandler(\n options: MongoCacheHandlerOptions\n): MongoCacheHandlerConstructor {\n const {\n clientPromise,\n dbName = DEFAULT_DB_NAME,\n collectionName = DEFAULT_COLLECTION_NAME,\n entryTtlSeconds = DEFAULT_ENTRY_TTL_SECONDS,\n } = options\n\n /**\n * Rendered HTML embeds the chunk filenames of the build that produced it, so\n * an entry left by a previous build loads but never hydrates — its chunks are\n * gone (404). Page keys are therefore scoped per build, while instances of\n * the same build keep sharing them.\n *\n * During `next build` `.next/BUILD_ID` does not exist yet, which is safe\n * because Next writes prerenders to disk and never calls `set()` for page\n * entries during the build.\n */\n const buildId = options.buildId ?? readBuildId()\n\n /**\n * Page and route entries are keyed by pathname; fetch entries are keyed by a\n * content hash and stay shared across builds, so upstream data survives a\n * deploy.\n */\n const scopeKey = (key: string): string => (key.startsWith('/') ? `${buildId}${key}` : key)\n\n // Next builds a fresh handler per request, so this has to live outside the\n // class to stay a one-off instead of an extra round-trip on every read.\n let indexPromise: Promise<unknown> | undefined\n\n const getCollection = async (): Promise<MongoCacheCollection> => {\n const client = await clientPromise\n const collection = client.db(dbName).collection(collectionName)\n\n indexPromise ??= Promise.all([\n collection.createIndex({ key: 1 }, { unique: true }),\n // `revalidateTag` deletes by `{ tags: { $in } }`; without this it COLLSCANs.\n collection.createIndex({ tags: 1 }),\n collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 }),\n ]).catch(() => {})\n await indexPromise\n\n return collection\n }\n\n return class MongoCacheHandlerImpl implements MongoCacheHandler {\n /**\n * Returns the entry whatever its age. Next compares `lastModified` against\n * the route's own revalidate window and decides itself whether to serve it\n * as fresh, serve it stale while refreshing in the background, or\n * re-render. Dropping stale rows here turns every expiry into a blocking\n * render.\n */\n async get(key: string): Promise<CachedEntry | null> {\n const collection = await getCollection()\n const scoped = scopeKey(key)\n const entry = await collection.findOne({ key: scoped })\n if (!entry) return null\n\n // Heals rows written before error responses were skipped.\n if (shouldSkipCacheWrite(entry.value)) {\n await collection.deleteOne({ key: scoped })\n return null\n }\n\n const value = entry.value ?? null\n const segmentData = value?.segmentData\n\n return {\n lastModified: entry.lastModified,\n value:\n segmentData && !(segmentData instanceof Map)\n ? { ...value, segmentData: new Map(Object.entries(segmentData)) }\n : value,\n }\n }\n\n async set(key: string, value: CachedValue | null, ctx?: CacheSetContext): Promise<void> {\n if (shouldSkipCacheWrite(value)) return\n\n const collection = await getCollection()\n const scoped = scopeKey(key)\n const now = Date.now()\n\n await collection.updateOne(\n { key: scoped },\n {\n $set: {\n key: scoped,\n value,\n lastModified: now,\n expiresAt: new Date(now + entryTtlSeconds * 1000),\n tags: resolveTags(value, ctx),\n },\n },\n { upsert: true }\n )\n }\n\n async delete(key: string): Promise<void> {\n const collection = await getCollection()\n await collection.deleteOne({ key: scopeKey(key) })\n }\n\n resetRequestCache(): void {}\n\n async revalidateTag(tags: string | string[], _durations?: unknown): Promise<void> {\n const list = typeof tags === 'string' ? [tags] : tags\n if (!list?.length) return\n\n const collection = await getCollection()\n await collection.deleteMany({ tags: { $in: list } })\n }\n }\n}\n","import type { PayloadRequest } from 'payload'\n\n/**\n * Standalone scripts (e.g. seed) pass `context: { disableRevalidate: true }`\n * through the Payload Local API so collection hooks can skip cache invalidation —\n * `revalidatePath` throws outside an active Next.js request context.\n */\nexport function shouldSkipRevalidate(req: PayloadRequest | undefined): boolean {\n return req?.context?.disableRevalidate === true\n}\n","import { revalidatePath, revalidateTag } from 'next/cache'\nimport { after } from 'next/server'\n\nimport { createLogger } from '../utils/logger/logger.js'\n\nconst log = createLogger('cache:revalidate')\n\n/**\n * Payload `jobs.autoRun` can inherit a stale Next request store (often `/admin`).\n * `revalidatePath` then throws \"during render\", which would fail schedulePublish.\n *\n * Prefer `after()` so the bust runs once the response/render finishes. Fall back\n * to a direct call (jobs / seed) and swallow so the CMS write still commits.\n */\nfunction runAfterRequest(task: () => void, label: string): void {\n try {\n after(task)\n return\n } catch {\n // No Next request scope (Payload cron, seed scripts).\n }\n\n try {\n task()\n } catch (error) {\n log.warn(`${label}: ${String(error)}`)\n }\n}\n\nexport function safeRevalidatePath(path: string, type?: 'layout' | 'page'): void {\n runAfterRequest(() => {\n if (type) {\n revalidatePath(path, type)\n return\n }\n revalidatePath(path)\n }, `revalidatePath skipped for ${path}`)\n}\n\nexport function safeRevalidateTag(tag: string, profile: 'max'): void {\n runAfterRequest(() => {\n revalidateTag(tag, profile)\n }, `revalidateTag skipped for ${tag}`)\n}\n","import { shouldSkipRevalidate } from './shouldSkipRevalidate.js'\n\nimport type { CollectionAfterChangeHook, CollectionAfterDeleteHook } from 'payload'\n\nexport type PublishedSlugRevalidateHandlers = {\n onPublishedSlugs: (slugs: Iterable<string>) => void\n onPublishedList?: () => void\n}\n\n/**\n * Payload hooks that revalidate when a draft/published doc with a `slug` changes.\n * Skips when `req.context.disableRevalidate` is set (seeds).\n */\nexport function createPublishedSlugRevalidateHooks(handlers: PublishedSlugRevalidateHandlers): {\n afterChange: CollectionAfterChangeHook\n afterDelete: CollectionAfterDeleteHook\n} {\n const afterChange: CollectionAfterChangeHook = ({ doc, previousDoc, req }) => {\n if (shouldSkipRevalidate(req)) return doc\n\n const isPublished = doc?._status === 'published'\n const wasPublished = previousDoc?._status === 'published'\n if (!isPublished && !wasPublished) return doc\n\n const slugs = new Set<string>()\n if (doc?.slug) slugs.add(doc.slug)\n if (previousDoc?.slug) slugs.add(previousDoc.slug)\n handlers.onPublishedSlugs(slugs)\n handlers.onPublishedList?.()\n return doc\n }\n\n const afterDelete: CollectionAfterDeleteHook = ({ doc, req }) => {\n if (shouldSkipRevalidate(req)) return doc\n if (doc?._status !== 'published') return doc\n\n if (doc?.slug) handlers.onPublishedSlugs([doc.slug])\n handlers.onPublishedList?.()\n return doc\n }\n\n return { afterChange, afterDelete }\n}\n\n/** Hooks that always call `onChange` (e.g. categories / tags that affect listings). */\nexport function createAlwaysRevalidateHooks(onChange: () => void): {\n afterChange: CollectionAfterChangeHook\n afterDelete: CollectionAfterDeleteHook\n} {\n const afterChange: CollectionAfterChangeHook = ({ doc, req }) => {\n if (shouldSkipRevalidate(req)) return doc\n onChange()\n return doc\n }\n\n const afterDelete: CollectionAfterDeleteHook = ({ doc, req }) => {\n if (shouldSkipRevalidate(req)) return doc\n onChange()\n return doc\n }\n\n return { afterChange, afterDelete }\n}\n","import { safeRevalidatePath } from './safeRevalidatePath.js'\n\nexport type BlogRevalidatorPaths = {\n /** e.g. `(slug, locale) => \\`/${locale}/blog/${slug}\\`` — concrete URL, no `type` */\n post: (slug: string, locale: string) => string\n /** Route pattern including route groups, e.g. `/[locale]/(marketing)/blog/[slug]` */\n postDynamic: string\n list: string\n serverSitemap?: string\n sitemap?: string\n llms?: string\n}\n\nexport type BlogRevalidators = {\n revalidatePost: (slugs: Iterable<string>) => void\n revalidateList: () => void\n revalidateAllSurfaces: () => void\n}\n\nexport function createBlogRevalidators(\n locales: readonly string[],\n paths: BlogRevalidatorPaths\n): BlogRevalidators {\n const revalidatePost = (slugs: Iterable<string>) => {\n for (const slug of slugs) {\n if (!slug) continue\n for (const locale of locales) {\n // Concrete URLs carry `_N_T_/en/blog/x` — passing `type` appends `/page` and misses.\n safeRevalidatePath(paths.post(slug, locale))\n }\n }\n safeRevalidatePath(paths.postDynamic, 'page')\n }\n\n const revalidateList = () => {\n safeRevalidatePath(paths.list, 'page')\n if (paths.serverSitemap) safeRevalidatePath(paths.serverSitemap)\n if (paths.llms) safeRevalidatePath(paths.llms)\n }\n\n const revalidateAllSurfaces = () => {\n safeRevalidatePath(paths.list, 'page')\n safeRevalidatePath(paths.postDynamic, 'page')\n if (paths.sitemap) safeRevalidatePath(paths.sitemap)\n if (paths.llms) safeRevalidatePath(paths.llms)\n }\n\n return { revalidatePost, revalidateList, revalidateAllSurfaces }\n}\n\nexport type ChangelogRevalidatorPaths = {\n detail: (slug: string, locale: string) => string\n detailDynamic: string\n list: string\n rss?: string\n serverSitemap?: string\n sitemap?: string\n llms?: string\n}\n\nexport type ChangelogRevalidators = {\n revalidateEntry: (slugs: Iterable<string>) => void\n revalidateList: () => void\n revalidateAllSurfaces: () => void\n}\n\nexport function createChangelogRevalidators(\n locales: readonly string[],\n paths: ChangelogRevalidatorPaths\n): ChangelogRevalidators {\n const revalidateEntry = (slugs: Iterable<string>) => {\n for (const slug of slugs) {\n if (!slug) continue\n for (const locale of locales) {\n safeRevalidatePath(paths.detail(slug, locale))\n }\n }\n safeRevalidatePath(paths.detailDynamic, 'page')\n }\n\n const revalidateList = () => {\n safeRevalidatePath(paths.list, 'page')\n if (paths.rss) safeRevalidatePath(paths.rss, 'page')\n if (paths.serverSitemap) safeRevalidatePath(paths.serverSitemap)\n if (paths.llms) safeRevalidatePath(paths.llms)\n }\n\n const revalidateAllSurfaces = () => {\n safeRevalidatePath(paths.list, 'page')\n safeRevalidatePath(paths.detailDynamic, 'page')\n if (paths.rss) safeRevalidatePath(paths.rss, 'page')\n if (paths.serverSitemap) safeRevalidatePath(paths.serverSitemap)\n if (paths.sitemap) safeRevalidatePath(paths.sitemap)\n if (paths.llms) safeRevalidatePath(paths.llms)\n }\n\n return { revalidateEntry, revalidateList, revalidateAllSurfaces }\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { t as createLogger } from "../logger.mjs";
2
2
  import { t as getSiteUrl } from "../site-url.mjs";
3
3
  import { getFieldsToSign, getPayload, jwtSign } from "payload";
4
- import { OAuth2Client } from "google-auth-library";
5
4
  import { NextResponse } from "next/server.js";
5
+ import { OAuth2Client } from "google-auth-library";
6
6
  import { addSessionToUser, generatePayloadCookie } from "payload/shared";
7
7
  //#region src/cms/auth/google-oauth.ts
8
8
  const log = createLogger("googleOAuth");
@@ -1,3 +1,3 @@
1
- import { $ as createAccessHelpers, A as downloadCtaBlock, B as seoFields, C as codeBlockLanguages, D as sectionHeadingBlock, E as leafBlocks, F as richTextBlock, G as STOCK_COLLECTION_SLUGS, H as BuildCmsConfigOptions, I as coerceBlogPostCategoriesOnRead, J as GlobalOverride, K as StockCollectionSlug, L as coerceBlogPostCategoriesOnWrite, M as imageBlock, N as codeTabsBlock, O as accordionBlock, P as codeBlock, Q as AccessOptions, R as setPublishedAt, S as showcaseContentBlocks, T as languageOptions, U as buildCmsConfig, V as draftsWithAutosave, W as Overrides, X as applyGlobalOverride, Y as applyCollectionOverride, Z as AccessHelpers, _ as requireEnvAtRuntime, a as createChangelog, b as blogContentBlocks, c as createPages, d as createAuthors, f as createMedia, g as createS3MediaPlugin, h as CreateS3MediaPluginOptions, i as createEventTags, j as videoBlock, k as stepsBlock, l as createBlogPosts, m as createUsers, n as createSiteSettings, o as createChangelogTags, p as createApiTokens, q as CollectionOverride, r as createEvents, s as createLegalPages, t as createAnnouncementBanner, u as createCategories, v as getRichTextEditorConfig, w as defaultCodeLanguage, x as pageContentBlocks, y as richTextEditor, z as slugify } from "../index.mjs";
1
+ import { $ as createAccessHelpers, A as downloadCtaBlock, B as seoFields, C as codeBlockLanguages, D as sectionHeadingBlock, E as leafBlocks, F as richTextBlock, G as STOCK_COLLECTION_SLUGS, H as BuildCmsConfigOptions, I as coerceBlogPostCategoriesOnRead, J as GlobalOverride, K as StockCollectionSlug, L as coerceBlogPostCategoriesOnWrite, M as imageBlock, N as codeTabsBlock, O as accordionBlock, P as codeBlock, Q as AccessOptions, R as setPublishedAt, S as showcaseContentBlocks, T as languageOptions, U as buildCmsConfig, V as draftsWithAutosave, W as Overrides, X as applyGlobalOverride, Y as applyCollectionOverride, Z as AccessHelpers, _ as requireEnvAtRuntime, _t as writeAccess, a as createChangelog, at as RoleRule, b as blogContentBlocks, c as createPages, ct as collectionRoleRule, d as createAuthors, dt as hiddenUnless, et as ADMIN_ROLE, f as createMedia, ft as isCmsAdmin, g as createS3MediaPlugin, gt as withRoles, h as CreateS3MediaPluginOptions, ht as userHasAnyRole, i as createEventTags, it as RoleOption, j as videoBlock, k as stepsBlock, l as createBlogPosts, lt as effectiveRoles, m as createUsers, mt as resolveRoleOptions, n as createSiteSettings, nt as DEFAULT_GLOBAL_ROLES, o as createChangelogTags, ot as RolesOptions, p as createApiTokens, pt as publishedOrRoleRead, q as CollectionOverride, r as createEvents, rt as DEFAULT_ROLES, s as createLegalPages, st as adminRoleAccess, t as createAnnouncementBanner, tt as DEFAULT_COLLECTION_ROLES, u as createCategories, ut as globalRoleRule, v as getRichTextEditorConfig, w as defaultCodeLanguage, x as pageContentBlocks, y as richTextEditor, z as slugify } from "../index.mjs";
2
2
  import { n as readPayloadMediaAsDataUrl, r as readPayloadMediaBuffer, t as PayloadMedia } from "../readMediaBuffer.mjs";
3
- export { type AccessHelpers, type AccessOptions, type BuildCmsConfigOptions, type CollectionOverride, type CreateS3MediaPluginOptions, type GlobalOverride, type Overrides, type PayloadMedia, STOCK_COLLECTION_SLUGS, type StockCollectionSlug, accordionBlock, applyCollectionOverride, applyGlobalOverride, blogContentBlocks, buildCmsConfig, codeBlock, codeBlockLanguages, codeTabsBlock, coerceBlogPostCategoriesOnRead, coerceBlogPostCategoriesOnWrite, createAccessHelpers, createAnnouncementBanner, createApiTokens, createAuthors, createBlogPosts, createCategories, createChangelog, createChangelogTags, createEventTags, createEvents, createLegalPages, createMedia, createPages, createS3MediaPlugin, createSiteSettings, createUsers, defaultCodeLanguage, downloadCtaBlock, draftsWithAutosave, getRichTextEditorConfig, imageBlock, languageOptions, leafBlocks, pageContentBlocks, readPayloadMediaAsDataUrl, readPayloadMediaBuffer, requireEnvAtRuntime, richTextBlock, richTextEditor, sectionHeadingBlock, seoFields, setPublishedAt, showcaseContentBlocks, slugify, stepsBlock, videoBlock };
3
+ export { ADMIN_ROLE, type AccessHelpers, type AccessOptions, type BuildCmsConfigOptions, type CollectionOverride, type CreateS3MediaPluginOptions, DEFAULT_COLLECTION_ROLES, DEFAULT_GLOBAL_ROLES, DEFAULT_ROLES, type GlobalOverride, type Overrides, type PayloadMedia, type RoleOption, type RoleRule, type RolesOptions, STOCK_COLLECTION_SLUGS, type StockCollectionSlug, accordionBlock, adminRoleAccess, applyCollectionOverride, applyGlobalOverride, blogContentBlocks, buildCmsConfig, codeBlock, codeBlockLanguages, codeTabsBlock, coerceBlogPostCategoriesOnRead, coerceBlogPostCategoriesOnWrite, collectionRoleRule, createAccessHelpers, createAnnouncementBanner, createApiTokens, createAuthors, createBlogPosts, createCategories, createChangelog, createChangelogTags, createEventTags, createEvents, createLegalPages, createMedia, createPages, createS3MediaPlugin, createSiteSettings, createUsers, defaultCodeLanguage, downloadCtaBlock, draftsWithAutosave, effectiveRoles, getRichTextEditorConfig, globalRoleRule, hiddenUnless, imageBlock, isCmsAdmin, languageOptions, leafBlocks, pageContentBlocks, publishedOrRoleRead, readPayloadMediaAsDataUrl, readPayloadMediaBuffer, requireEnvAtRuntime, resolveRoleOptions, richTextBlock, richTextEditor, sectionHeadingBlock, seoFields, setPublishedAt, showcaseContentBlocks, slugify, stepsBlock, userHasAnyRole, videoBlock, withRoles, writeAccess };
@@ -1,3 +1,3 @@
1
- import { A as richTextBlock, B as languageOptions, C as blogContentBlocks, D as accordionBlock, E as sectionHeadingBlock, F as downloadCtaBlock, G as createAccessHelpers, H as seoFields, I as codeTabsBlock, L as codeBlock, M as richTextEditor, N as videoBlock, O as leafBlocks, P as imageBlock, R as codeBlockLanguages, S as coerceBlogPostCategoriesOnWrite, T as showcaseContentBlocks, U as slugify, V as createAuthors, W as createApiTokens, _ as createCategories, a as STOCK_COLLECTION_SLUGS, b as setPublishedAt, c as createSiteSettings, d as createPages, f as createMedia, g as createChangelog, h as createChangelogTags, i as buildCmsConfig, j as getRichTextEditorConfig, k as stepsBlock, l as createUsers, m as createEventTags, n as createS3MediaPlugin, o as applyCollectionOverride, p as createEvents, r as requireEnvAtRuntime, s as applyGlobalOverride, t as createAnnouncementBanner, u as createLegalPages, v as createBlogPosts, w as pageContentBlocks, x as coerceBlogPostCategoriesOnRead, y as draftsWithAutosave, z as defaultCodeLanguage } from "../cms.mjs";
1
+ import { $ as hiddenUnless, A as richTextBlock, B as languageOptions, C as blogContentBlocks, D as accordionBlock, E as sectionHeadingBlock, F as downloadCtaBlock, G as ADMIN_ROLE, H as seoFields, I as codeTabsBlock, J as DEFAULT_ROLES, K as DEFAULT_COLLECTION_ROLES, L as codeBlock, M as richTextEditor, N as videoBlock, O as leafBlocks, P as imageBlock, Q as globalRoleRule, R as codeBlockLanguages, S as coerceBlogPostCategoriesOnWrite, T as showcaseContentBlocks, U as slugify, V as createAuthors, W as createApiTokens, X as collectionRoleRule, Y as adminRoleAccess, Z as effectiveRoles, _ as createCategories, a as STOCK_COLLECTION_SLUGS, at as writeAccess, b as setPublishedAt, c as createSiteSettings, d as createPages, et as isCmsAdmin, f as createMedia, g as createChangelog, h as createChangelogTags, i as buildCmsConfig, it as withRoles, j as getRichTextEditorConfig, k as stepsBlock, l as createUsers, m as createEventTags, n as createS3MediaPlugin, nt as resolveRoleOptions, o as applyCollectionOverride, ot as createAccessHelpers, p as createEvents, q as DEFAULT_GLOBAL_ROLES, r as requireEnvAtRuntime, rt as userHasAnyRole, s as applyGlobalOverride, t as createAnnouncementBanner, tt as publishedOrRoleRead, u as createLegalPages, v as createBlogPosts, w as pageContentBlocks, x as coerceBlogPostCategoriesOnRead, y as draftsWithAutosave, z as defaultCodeLanguage } from "../cms.mjs";
2
2
  import { n as readPayloadMediaBuffer, t as readPayloadMediaAsDataUrl } from "../readMediaBuffer.mjs";
3
- export { STOCK_COLLECTION_SLUGS, accordionBlock, applyCollectionOverride, applyGlobalOverride, blogContentBlocks, buildCmsConfig, codeBlock, codeBlockLanguages, codeTabsBlock, coerceBlogPostCategoriesOnRead, coerceBlogPostCategoriesOnWrite, createAccessHelpers, createAnnouncementBanner, createApiTokens, createAuthors, createBlogPosts, createCategories, createChangelog, createChangelogTags, createEventTags, createEvents, createLegalPages, createMedia, createPages, createS3MediaPlugin, createSiteSettings, createUsers, defaultCodeLanguage, downloadCtaBlock, draftsWithAutosave, getRichTextEditorConfig, imageBlock, languageOptions, leafBlocks, pageContentBlocks, readPayloadMediaAsDataUrl, readPayloadMediaBuffer, requireEnvAtRuntime, richTextBlock, richTextEditor, sectionHeadingBlock, seoFields, setPublishedAt, showcaseContentBlocks, slugify, stepsBlock, videoBlock };
3
+ export { ADMIN_ROLE, DEFAULT_COLLECTION_ROLES, DEFAULT_GLOBAL_ROLES, DEFAULT_ROLES, STOCK_COLLECTION_SLUGS, accordionBlock, adminRoleAccess, applyCollectionOverride, applyGlobalOverride, blogContentBlocks, buildCmsConfig, codeBlock, codeBlockLanguages, codeTabsBlock, coerceBlogPostCategoriesOnRead, coerceBlogPostCategoriesOnWrite, collectionRoleRule, createAccessHelpers, createAnnouncementBanner, createApiTokens, createAuthors, createBlogPosts, createCategories, createChangelog, createChangelogTags, createEventTags, createEvents, createLegalPages, createMedia, createPages, createS3MediaPlugin, createSiteSettings, createUsers, defaultCodeLanguage, downloadCtaBlock, draftsWithAutosave, effectiveRoles, getRichTextEditorConfig, globalRoleRule, hiddenUnless, imageBlock, isCmsAdmin, languageOptions, leafBlocks, pageContentBlocks, publishedOrRoleRead, readPayloadMediaAsDataUrl, readPayloadMediaBuffer, requireEnvAtRuntime, resolveRoleOptions, richTextBlock, richTextEditor, sectionHeadingBlock, seoFields, setPublishedAt, showcaseContentBlocks, slugify, stepsBlock, userHasAnyRole, videoBlock, withRoles, writeAccess };