@justanarthur/payload-www 1.4.1 → 2.0.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/README.md CHANGED
@@ -13,14 +13,14 @@ The composition root is `createWWWConfig()` — see [Quick start](#quick-start)
13
13
  | piece | exported from | purpose |
14
14
  |---|---|---|
15
15
  | Composer | [`/config`](#quick-start) | `createWWWConfig()` — returns `{ withWWWConfig }`. One call wires Pages + Posts + Header + Footer + the default plugin set. |
16
- | Pages + Posts collections | `createPagesCollection`, `createPostsCollection` (internal — composed by the composer) | Pages (title, blocks tab, slug, drafts, revalidation); Posts (title, excerpt, richText, drafts, revalidation). |
16
+ | Pages + Posts collections | `createPagesCollection`, `createPostsCollection` (internal — composed by the composer) | Pages (title, blocks tab, slug, drafts); Posts (title, excerpt, richText, drafts). Caching comes from the host's `revalidatePlugin()` registration. |
17
17
  | Header + Footer globals | `createHeaderGlobal`, `createFooterGlobal` (internal) | Both nav blocks with `navColumn` / `navItem`. Extend via `link({ extraFields })`. |
18
18
  | Static-page collection | `createWWWCollectionGlobal` ([`/collections`](#collections)) | Generic factory for system pages (404, 500, search-empty) — keyed by a discriminator, no slug. |
19
19
  | Default plugins | composed by `createWWWConfig()` | `seoPlugin`, `imageHashPlugin`, `translator`, `mcpPlugin`. Tune via `defaultPluginsConfigs`. |
20
20
  | Fields | [`/fields`](#fields) | `link`, `linkGroup` (with `disableLabel` / `appearances` / `localized` / `relationTo` / `extraFields`), `slugField`, `appearanceOptions`. |
21
21
  | Access | [`/access`](#access) | `anyone`, `authenticated`, `authenticatedOrPublished`. |
22
- | Revalidation hooks | [`/collections`](#collections) (`createRevalidateCollectionGlobalHook`, `createCollectionCacheKey`, `populatePublishedAt`) | Tag-based cache invalidation + published-at population. |
23
- | Metadata | [`/metadata`](#metadata) | `buildArticleLd`, `buildBreadcrumbsLd`, `buildOrganizationLd`, `buildWebSiteLd`, `buildProductLd`, `buildRootJsonLd`; slug transforms; `queryDocBySlug`, `queryAllDocs`, `queryAllLocaleSlugs`. |
22
+ | Caching | [`Caching`](#caching) | Cached query getters wrap `@pro-laico/payload-revalidate` finders; tag-based invalidation via `revalidatePlugin()`; `seedPayloadCache` singleton seed. |
23
+ | Metadata | [`/metadata`](#metadata) | `buildArticleLd`, `buildBreadcrumbsLd`, `buildOrganizationLd`, `buildWebSiteLd`, `buildProductLd`, `buildRootJsonLd`; slug transforms; `queryDocBySlug`, `queryDocByID`, `queryGlobal`, `queryAllDocs`, `queryAllLocaleSlugs`; `seedPayloadCache`, `tagsFor`. |
24
24
  | Next.js page renderers | [`/render-pages`](#render-pages) | `createCollectionPageExports`, `createRootLayoutExports`; default render components `PagesPage`, `PostsPage`, `HeaderPage`, `FooterPage`, `RootJsonLd`. |
25
25
  | Sitemap | [`/sitemap`](#sitemap) | `createSitemapFromCollections` (Next.js file-convention helper). |
26
26
  | Plugin re-exports | [`/imagehash`](#plugin-re-exports), [`/translator`](#plugin-re-exports) | Drop-in for hosts that don't want to import the sibling packages directly. |
@@ -140,7 +140,7 @@ const { default: PostPage, generateMetadata, generateStaticParams } = createColl
140
140
  | dep | type | notes |
141
141
  |---|---|---|
142
142
  | `getServerSideURL` | `() => string` | host's absolute-URL helper |
143
- | `pagePathPrefix` | `string` | optional URL prefix for `generateSitemap` only not used for rendering |
143
+ | `pagePathPrefix` | `string \| Record<locale, string>` | URL segment the collection is mounted under, used for `generateSitemap` and for canonical/hreflang URLs. Pass a record to localize the segment (`{ en: 'posts', sk: 'prispevky' }`); locales absent from it fall back to the default locale. The sitemap *index* route always uses the default locale's segment. |
144
144
 
145
145
  ### What you get back
146
146
 
@@ -193,9 +193,10 @@ export default createSitemapFromCollections({
193
193
  `createCollectionPageExports`'s deps accept) and returns a Next.js `MetadataRoute.Sitemap`-compatible
194
194
  function. Mount one per collection under a sub-route, or a single call for the root.
195
195
 
196
- The lib's Pages `afterChange` hook fires `revalidateTag(collection_pages_<slug>_<locale>, 'max')`,
197
- which is the key `createCollectionPageExports` / `createSitemapFromCollections` reads from so
198
- edits refresh the sitemap without manual rebuilds.
196
+ The lib reads through `@pro-laico/payload-revalidate`'s cached finders, so registering
197
+ `revalidatePlugin()` in `payload.config.ts` makes every collection / global save revalidate
198
+ its tags automatically — no lib-side hooks to wire. Edits refresh the sitemap without
199
+ manual rebuilds.
199
200
 
200
201
  ## Static pages (404 / 500 / system)
201
202
 
@@ -213,16 +214,14 @@ const { default: NotFound } = createCollectionPageExports(
213
214
  export default NotFound
214
215
  ```
215
216
 
216
- The `static-pages` collection is built via `createWWWCollectionGlobal({...}, { slug: 'static-pages', renderPath: '@/components/StaticPage/Component#StaticPage', isDraft: false })`. Editors pick a `kind` (`'not-found'`, `'server-error'`, `'search-empty'`, `'offline'`), populate the `blocks` tab with the same block set you passed to `createWWWConfig`, and the host's not-found / server-error route renders the row. `populatePublishedAt` and the revalidation hooks are wired automatically.
217
+ The `static-pages` collection is built via `createWWWCollectionGlobal({...}, { slug: 'static-pages', renderPath: '@/components/StaticPage/Component#StaticPage', isDraft: false })`. Editors pick a `kind` (`'not-found'`, `'server-error'`, `'search-empty'`, `'offline'`), populate the `blocks` tab with the same block set you passed to `createWWWConfig`, and the host's not-found / server-error route renders the row. `populatePublishedAt` is wired automatically; revalidation comes from the host's `revalidatePlugin()` registration.
217
218
 
218
219
  ## Collections
219
220
 
220
221
  ```ts
221
222
  import {
222
223
  createWWWCollectionGlobal, // generic factory (used internally for static-pages)
223
- createRevalidateCollectionGlobalHook, // the afterChange/afterDelete pair
224
- createCollectionCacheKey, // produces the tag string
225
- queryDoc // server-side helper used by renderers
224
+ queryDoc // server-side helper used by renderers
226
225
  } from '@justanarthur/payload-www/collections'
227
226
  ```
228
227
 
@@ -230,32 +229,71 @@ import {
230
229
 
231
230
  | arg | type | notes |
232
231
  |---|---|---|
233
- | `fields` | `Field[]` | the collection's field set (the factory adds `slug`, `publishedAt`, access, hooks) |
232
+ | `fields` | `Field[]` | the collection's field set (the factory adds `slug`, `publishedAt`, access, the `populatePublishedAt` `beforeChange`) |
234
233
  | `slug` | `string` | collection slug |
235
234
  | `renderPath` | `string` | import-map path to the render component (`'@/components/Foo/Component#Foo'`) |
236
235
  | `isGlobalConfig` | `boolean` | `true` for globals, `false` (default) for collections |
237
236
  | `isDraft` | `boolean` | `true` (default) enables Payload's drafts + autosave; `false` for system pages |
238
237
 
239
238
  The factory wires `custom[packageName] = { path: renderPath }`, access (`create`/`update`/`delete`
240
- require auth, `read` is `anyone` or `authenticatedOrPublished` depending on `isDraft`), the
241
- `afterChange`/`afterDelete` revalidation hook, and `populatePublishedAt` (`beforeChange`).
239
+ require auth, `read` is `anyone` or `authenticatedOrPublished` depending on `isDraft`), and
240
+ `populatePublishedAt` (`beforeChange`). It does **not** install revalidation hooks — those live
241
+ in the host's `revalidatePlugin()` registration (see [Caching](#caching) below).
242
242
 
243
- ### Revalidation
243
+ ### Caching
244
244
 
245
- `createRevalidateCollectionGlobalHook()` returns `{ afterChange, afterDelete }` the same function
246
- used for both. On every save / delete of a published doc it fires:
245
+ The lib's cached query layer (`queryDocBySlug`, `queryGlobal`, `queryAllDocs`, `queryAllLocaleSlugs`,
246
+ `queryDocByID`) wraps `@pro-laico/payload-revalidate`'s finders in `'use cache'` + `cacheLife('weeks')`
247
+ scopes. Setup:
247
248
 
248
- ```
249
- revalidateTag(`<slug><slug>_<locale>`, 'max') // collections
250
- revalidateTag(`<globalSlug>_<locale>`, 'max') // globals
251
- ```
249
+ 1. **Install the peer deps at the workspace root** (not in the lib's `dependencies` — `file:` links
250
+ in a published `package.json` break downstream installs):
251
+
252
+ ```bash
253
+ bun add @pro-laico/core @pro-laico/payload-revalidate
254
+ ```
255
+
256
+ 2. **Register `revalidatePlugin()` last in `payload.config.ts#plugins`:**
257
+
258
+ ```ts
259
+ import { revalidatePlugin } from '@pro-laico/payload-revalidate'
260
+
261
+ export default buildConfig({
262
+ // ...
263
+ plugins: [...otherPlugins, revalidatePlugin()],
264
+ })
265
+ ```
266
+
267
+ 3. **Turn on Next's cacheComponents pipeline** in `next.config.ts`:
268
+
269
+ ```ts
270
+ const nextConfig: NextConfig = { cacheComponents: true, /* ... */ }
271
+ ```
272
+
273
+ 4. **Use the lib's getters in renderers** — they're `'use cache'`-wrapped already. No need to
274
+ re-wrap or hand-tag:
275
+
276
+ ```ts
277
+ import { queryDocBySlug, queryGlobal } from '@justanarthur/payload-www/metadata'
278
+
279
+ const doc = await queryDocBySlug({ collectionSlug: 'pages', slug, locale })
280
+ const header = await queryGlobal({ globalSlug: 'header', locale })
281
+ ```
282
+
283
+ 5. **`seedPayloadCache({ config })`** runs once per process to initialize the pro-laico cache
284
+ helpers from the host's Payload config. `createCollectionPageExports` and
285
+ `createRootLayoutExports` call it automatically — hosts with custom layouts call it directly:
286
+
287
+ ```ts
288
+ import { seedPayloadCache } from '@justanarthur/payload-www/metadata'
252
289
 
253
- `createCollectionCacheKey({ collectionSlug, slug, locale })` produces the same key string — use it
254
- in your own `unstable_cache` / `fetch` cache keys if you want them invalidated by the lib's hooks.
290
+ seedPayloadCache({ config })
291
+ ```
255
292
 
256
- > The `// @ts-expect-error` and `// todo sitemap caching and revalidation` markers in
257
- > `createRevalidateCollectionGlobalHook.ts` are intentional, not bugs the hook covers tag-based
258
- > invalidation; URL revalidation and the unified sitemap tag are still TODO.
293
+ The cached profile is `cacheLife('weeks')` Next 16's built-in long-tail profile (5 m stale,
294
+ 1 w revalidate, 30 d expire). No custom `cacheLife` config entry needed. Invalidations are
295
+ purely tag-based: `revalidatePlugin()` fires the same tags the finders emit, so every save /
296
+ delete / publish of a Payload doc revalidates the cached render without manual rebuilds.
259
297
 
260
298
  ## Fields
261
299
 
@@ -292,9 +330,13 @@ import {
292
330
  buildWebSiteLd,
293
331
  buildProductLd,
294
332
  buildRootJsonLd, // combined Organization + WebSite + Product (used by RootJsonLd)
295
- queryDocBySlug,
333
+ queryDocBySlug, // cached collection fetch
334
+ queryDocByID, // cached collection fetch by id
335
+ queryGlobal, // cached global fetch
296
336
  queryAllDocs, // for generateStaticParams
297
337
  queryAllLocaleSlugs, // for hreflang alternates
338
+ seedPayloadCache, // one-time seed for the pro-laico cache helpers
339
+ tagsFor, // build cache tags by hand (passthrough of @pro-laico/payload-revalidate)
298
340
  paramsSlugToSlug, // turn [locale]/[[...slug]] params → stored slug
299
341
  slugToParamsSlug // turn stored slug → params for generateStaticParams
300
342
  } from '@justanarthur/payload-www/metadata'
@@ -340,10 +382,10 @@ The package's `package.json#exports` map:
340
382
  | `@justanarthur/payload-www/pages` | subset of `/render-pages` (no `createRootLayoutExports`, no `PostsPage`, no `RootJsonLd`) |
341
383
  | `@justanarthur/payload-www/sitemap` | `createSitemapFromCollections` |
342
384
  | `@justanarthur/payload-www/blocks` | `RenderBlocks`, `RenderBlocksProps` |
343
- | `@justanarthur/payload-www/collections` | `createWWWCollectionGlobal`, `createRevalidateCollectionGlobalHook`, `createCollectionCacheKey`, `queryDoc` |
385
+ | `@justanarthur/payload-www/collections` | `createWWWCollectionGlobal`, `queryDoc` |
344
386
  | `@justanarthur/payload-www/fields` | `link`, `linkGroup`, `appearanceOptions`, `slugField`, `LinkAppearances`, `LinkOptions` |
345
387
  | `@justanarthur/payload-www/access` | `anyone`, `authenticated`, `authenticatedOrPublished` |
346
- | `@justanarthur/payload-www/metadata` | `buildArticleLd`, `buildBreadcrumbsLd`, `buildOrganizationLd`, `buildWebSiteLd`, `buildProductLd`, `buildRootJsonLd`, `queryDocBySlug`, `queryAllDocs`, `queryAllLocaleSlugs`, `paramsSlugToSlug`, `slugToParamsSlug` + types |
388
+ | `@justanarthur/payload-www/metadata` | `buildArticleLd`, `buildBreadcrumbsLd`, `buildOrganizationLd`, `buildWebSiteLd`, `buildProductLd`, `buildRootJsonLd`, `queryDocBySlug`, `queryDocByID`, `queryGlobal`, `queryAllDocs`, `queryAllLocaleSlugs`, `seedPayloadCache`, `tagsFor`, `paramsSlugToSlug`, `slugToParamsSlug` + types |
347
389
  | `@justanarthur/payload-www/utils` | `generateImportName`, `getFromImportMap` |
348
390
  | `@justanarthur/payload-www/imagehash` | `imageHashPlugin`, `BlurhashPluginOptions` (re-export of `@justanarthur/payload-imagehash-plugin`) |
349
391
  | `@justanarthur/payload-www/translator` | `translator` (re-export of `@justanarthur/payload-plugin-translator`) |
@@ -363,10 +405,16 @@ For agents migrating from older docs:
363
405
  Payload `localization` config; `blocks` come from the `WWWInputConfig.blocks` field.
364
406
  - `defaultPlugins` callback → now `defaultPluginsConfigs: { seo, imageHash, translator, mcp }` map
365
407
  on `WWWInputConfig`.
366
- - `createRevalidateCollectionHook({ collectionSlug, urlPathPrefix, … })` (CHANGELOG `[Unreleased]`)
367
- → **not implemented yet**. The current hook is `createRevalidateCollectionGlobalHook()` (no args),
368
- exported from `/collections`. Per-locale URL fan-out + `revalidatePath` does not happen — only
369
- `revalidateTag` fires.
408
+ - `createRevalidateCollectionHook({ collectionSlug, urlPathPrefix, … })` (older CHANGELOG) and
409
+ `createRevalidateCollectionGlobalHook()` (no args, 1.0.0) → **both removed**. Caching now goes
410
+ through `@pro-laico/payload-revalidate`: install it as a peer dep, register `revalidatePlugin()`
411
+ last in `payload.config.ts#plugins`, and the lib's cached query getters (`queryDocBySlug`,
412
+ `queryGlobal`, `queryAllDocs`, `queryAllLocaleSlugs`, `queryDocByID`) wrap its finders so the
413
+ revalidation tags line up automatically. See [Caching](#caching) above.
414
+ - `@justanarthur/payload-www/cache-keys` subpath (older `[Unreleased]`) → **removed**. There is
415
+ no in-tree tag vocabulary to export — tags come from `@pro-laico/payload-revalidate`'s finders.
416
+ - `defaultPluginsConfigs.revalidate` slot (older `[Unreleased]`) → **removed**. The in-lib
417
+ revalidation composer is gone; revalidation is the host's responsibility via `revalidatePlugin()`.
370
418
  - `createSitemapFile` → **not implemented**. The current export is `createSitemapFromCollections`,
371
419
  used per-collection from your `app/(frontend)/sitemap.ts`.
372
420
  - `createPreviewHandler`, `LocaleSwitcher`, `LivePreviewListener`, `PageShowcase`, `HomePage`,
package/dist/access.d.ts CHANGED
@@ -8,4 +8,4 @@ declare const access: {
8
8
  authenticated: Access_wc6ft0;
9
9
  authenticatedOrPublished: Access_wc6ft0;
10
10
  };
11
- export { access as default, authenticatedOrPublished, authenticated, anyone };
11
+ export { anyone, authenticated, authenticatedOrPublished, access as default };
package/dist/access.js CHANGED
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  // src/collections/access/index.ts
4
2
  var anyone = () => true;
5
3
  var authenticated = ({ req: { user } }) => Boolean(user);
@@ -13,8 +11,8 @@ var authenticatedOrPublished = ({ req: { user } }) => {
13
11
  var access = { anyone, authenticated, authenticatedOrPublished };
14
12
  var access_default = access;
15
13
  export {
16
- access_default as default,
17
- authenticatedOrPublished,
14
+ anyone,
18
15
  authenticated,
19
- anyone
16
+ authenticatedOrPublished,
17
+ access_default as default
20
18
  };
package/dist/blocks.d.ts CHANGED
@@ -16,4 +16,4 @@ type RenderBlocksProps = {
16
16
  searchParams?: Record<string, string | string[] | undefined>;
17
17
  };
18
18
  declare const RenderBlocks: FC<RenderBlocksProps>;
19
- export { RenderBlocks as default, RenderBlocksProps, RenderBlocks };
19
+ export { RenderBlocks, RenderBlocksProps, RenderBlocks as default };
package/dist/blocks.js CHANGED
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  // src/render/getFromImportMap.ts
4
2
  function getFromImportMap(key, importMap) {
5
3
  if (!key)
@@ -55,6 +53,6 @@ var RenderBlocks = async ({ blocks, blockProps, config, importMap, locale, searc
55
53
  // src/exports/blocks.ts
56
54
  var blocks_default = RenderBlocks;
57
55
  export {
58
- blocks_default as default,
59
- RenderBlocks
56
+ RenderBlocks,
57
+ blocks_default as default
60
58
  };
package/dist/cli.js CHANGED
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { createRequire } from "node:module";
3
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
2
 
5
3
  // src/cli/index.ts
6
4
  import { register } from "tsx/esm/api";
@@ -4,34 +4,34 @@ type CreateWWWCollectionArgs<IsGlobalConfig extends boolean> = {
4
4
  renderPath: string;
5
5
  isGlobalConfig?: IsGlobalConfig;
6
6
  isDraft?: boolean;
7
+ useAsTitle?: string;
7
8
  };
8
9
  declare function createWWWCollectionGlobal<
9
10
  IsGlobalConfig extends boolean = false,
10
11
  Config = IsGlobalConfig extends true ? GlobalConfig : CollectionConfig
11
- >(fields: Field[], { slug: collectionSlug, renderPath, isGlobalConfig, isDraft }: CreateWWWCollectionArgs<IsGlobalConfig>): Config;
12
- import { CollectionAfterChangeHook, CollectionAfterDeleteHook, GlobalAfterChangeHook } from "payload";
13
- declare function createRevalidateCollectionGlobalHook(): {
14
- afterChange: (CollectionAfterChangeHook & CollectionAfterDeleteHook) | GlobalAfterChangeHook;
15
- afterDelete: (CollectionAfterChangeHook & CollectionAfterDeleteHook) | GlobalAfterChangeHook;
12
+ >(fields: Field[], { slug: collectionSlug, renderPath, isGlobalConfig, isDraft, useAsTitle }: CreateWWWCollectionArgs<IsGlobalConfig>): Config;
13
+ import { SanitizedConfig } from "payload";
14
+ import { JsonObject as JsonObject_wrdbbt } from "payload";
15
+ type SeedPayloadCacheArgs = {
16
+ config: SanitizedConfig | Promise<SanitizedConfig>;
16
17
  };
17
- type CollectionGlobalLocaleIdentifiersArgs = ({
18
+ declare function seedPayloadCache({ config }: SeedPayloadCacheArgs): void;
19
+ type QueryDocArgs = ({
20
+ globalSlug: string;
21
+ locale: string;
22
+ draft?: boolean;
23
+ }) | ({
18
24
  collectionSlug: string;
19
25
  slug: string;
20
- } | {
21
- globalSlug: string;
22
- }) & {
26
+ slugField?: string;
23
27
  locale: string;
24
- };
25
- declare function createCollectionCacheKey(args: CollectionGlobalLocaleIdentifiersArgs): string;
26
- import { SanitizedConfig } from "payload";
27
- import { JsonObject as JsonObject_wrdbbt } from "payload";
28
- declare function queryDoc(args: CollectionGlobalLocaleIdentifiersArgs, { config: configPromise }: {
29
- config: Promise<SanitizedConfig>;
30
- }): Promise<JsonObject_wrdbbt | null>;
28
+ draft?: boolean;
29
+ depth?: number;
30
+ });
31
+ declare function queryDoc(args: QueryDocArgs): Promise<JsonObject_wrdbbt | null>;
31
32
  declare const collections: {
32
33
  createWWWCollectionGlobal: typeof createWWWCollectionGlobal;
33
- createRevalidateCollectionGlobalHook: typeof createRevalidateCollectionGlobalHook;
34
- createCollectionCacheKey: typeof createCollectionCacheKey;
35
34
  queryDoc: typeof queryDoc;
35
+ seedPayloadCache: typeof seedPayloadCache;
36
36
  };
37
- export { queryDoc, collections as default, createWWWCollectionGlobal, createRevalidateCollectionGlobalHook, createCollectionCacheKey };
37
+ export { createWWWCollectionGlobal, collections as default, queryDoc, seedPayloadCache };
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  // package.json
4
2
  var name = "@justanarthur/payload-www";
5
3
 
@@ -17,37 +15,6 @@ var populatePublishedAt = ({ data, operation, req }) => {
17
15
  return data;
18
16
  };
19
17
 
20
- // src/collections/hooks/createRevalidateCollectionGlobalHook.ts
21
- import { revalidateTag } from "next/cache";
22
- function createRevalidateCollectionGlobalHook() {
23
- const afterChangeDeleteHook = async ({ doc, req, ...args }) => {
24
- const previousDoc = "previousDoc" in args ? args.previousDoc : null, locale = req.locale;
25
- function revalidate(args2) {
26
- try {
27
- revalidateTag(createCollectionCacheKey({ ...args2, locale }), "max");
28
- } catch (error) {
29
- const msg = error instanceof Error ? error.message : String(error);
30
- if (msg.includes("static generation store missing"))
31
- return;
32
- console.error(error);
33
- }
34
- }
35
- if ("collection" in args) {
36
- [
37
- doc && doc._status !== "draft" && doc.slug,
38
- previousDoc && previousDoc._status === "published" && previousDoc.slug
39
- ].filter((slug) => slug !== undefined && slug !== false).map((slug) => revalidate({ collectionSlug: args.collection.slug, slug }));
40
- } else {
41
- revalidate({ globalSlug: args.global.slug });
42
- }
43
- return doc;
44
- };
45
- return { afterChange: afterChangeDeleteHook, afterDelete: afterChangeDeleteHook };
46
- }
47
- function createCollectionCacheKey(args) {
48
- return `${"globalSlug" in args ? args.globalSlug : args.collectionSlug + args.slug}_${args.locale}`;
49
- }
50
-
51
18
  // src/collections/fields/slug.ts
52
19
  var slugField = () => ({
53
20
  name: "slug",
@@ -83,7 +50,8 @@ function createWWWCollectionGlobal(fields, {
83
50
  slug: collectionSlug,
84
51
  renderPath,
85
52
  isGlobalConfig = false,
86
- isDraft = true
53
+ isDraft = true,
54
+ useAsTitle
87
55
  }) {
88
56
  return {
89
57
  slug: collectionSlug,
@@ -96,6 +64,7 @@ function createWWWCollectionGlobal(fields, {
96
64
  },
97
65
  ...fields
98
66
  ],
67
+ admin: useAsTitle ? { useAsTitle, defaultColumns: useAsTitle === "title" ? [useAsTitle, "slug", "publishedAt"] : undefined } : undefined,
99
68
  custom: { [name]: { path: renderPath } },
100
69
  access: {
101
70
  create: authenticated,
@@ -103,14 +72,9 @@ function createWWWCollectionGlobal(fields, {
103
72
  read: isDraft ? authenticatedOrPublished : anyone,
104
73
  update: authenticated
105
74
  },
106
- hooks: (() => {
107
- const { afterChange, afterDelete } = createRevalidateCollectionGlobalHook();
108
- return {
109
- afterChange: [afterChange],
110
- beforeChange: [populatePublishedAt],
111
- afterDelete: [afterDelete]
112
- };
113
- })(),
75
+ hooks: {
76
+ beforeChange: [populatePublishedAt]
77
+ },
114
78
  ...isDraft && {
115
79
  versions: { drafts: { autosave: { interval: 3000 } } }
116
80
  }
@@ -118,105 +82,107 @@ function createWWWCollectionGlobal(fields, {
118
82
  }
119
83
 
120
84
  // src/render/metadata/query.ts
85
+ import { createCacheHelpers } from "@pro-laico/payload-revalidate/cache";
86
+ import { tagsFor } from "@pro-laico/payload-revalidate";
87
+ import { cacheLife } from "next/cache";
121
88
  import { getPayload } from "payload";
122
- async function withUnstableCache(keyParts, tags, fn) {
123
- const { unstable_cache } = await import("next/cache");
124
- return unstable_cache(fn, keyParts.map(String), { tags })();
89
+ var _helpers = null;
90
+ var _seedPromise = null;
91
+ function seedPayloadCache({ config }) {
92
+ if (_helpers || _seedPromise)
93
+ return;
94
+ _seedPromise = (async () => {
95
+ const payload = await getPayload({ config: await config });
96
+ _helpers = createCacheHelpers(payload);
97
+ })();
125
98
  }
126
- async function queryDoc(args, { config: configPromise }) {
127
- return withUnstableCache(Object.values(args), [createCollectionCacheKey(args)], async () => {
128
- if ("globalSlug" in args)
129
- return queryGlobal({ ...args, config: configPromise });
130
- else
131
- return queryDocBySlug({ ...args, config: configPromise });
99
+ async function requireCacheHelpers() {
100
+ if (_helpers)
101
+ return _helpers;
102
+ if (_seedPromise) {
103
+ await _seedPromise;
104
+ if (_helpers)
105
+ return _helpers;
106
+ }
107
+ throw new Error("[payload-www] seedPayloadCache({ config }) must be called before any query getter. " + "Call it from createCollectionPageExports / createRootLayoutExports factory, or in your root layout.");
108
+ }
109
+ async function queryDocBySlug(args) {
110
+ "use cache";
111
+ cacheLife("weeks");
112
+ const { findDoc } = await requireCacheHelpers();
113
+ const slugField = args.slugField ?? "slug";
114
+ const result = await findDoc(args.collectionSlug, {
115
+ where: { [slugField]: { equals: args.slug } },
116
+ locale: args.locale,
117
+ draft: args.draft ?? false,
118
+ depth: args.depth
132
119
  });
120
+ return result ?? null;
133
121
  }
134
- async function queryDocBySlug({
135
- collectionSlug,
136
- slug,
137
- slugField: slugField2 = "slug",
138
- locale,
139
- draft = false,
140
- config
141
- }) {
142
- return withUnstableCache([collectionSlug, slug, locale, draft], [createCollectionCacheKey({ collectionSlug, slug, locale })], async () => {
143
- const payload = await getPayload({ config });
144
- const result = await payload.find({
145
- collection: collectionSlug,
146
- draft,
147
- limit: 1,
148
- pagination: false,
149
- overrideAccess: draft,
150
- where: { [slugField2]: { equals: slug } },
151
- locale
122
+ async function queryGlobal(args) {
123
+ "use cache";
124
+ cacheLife("weeks");
125
+ const { findGlobal } = await requireCacheHelpers();
126
+ try {
127
+ const result = await findGlobal(args.globalSlug, {
128
+ locale: args.locale,
129
+ draft: args.draft ?? false,
130
+ depth: args.depth
152
131
  });
153
- return result.docs?.[0] ?? null;
154
- });
132
+ return result ?? null;
133
+ } catch (error) {
134
+ console.warn("[WWW] queryGlobal failed", { globalSlug: args.globalSlug, locale: args.locale, error: String(error) });
135
+ return null;
136
+ }
155
137
  }
156
- async function queryGlobal({
157
- globalSlug,
158
- locale,
159
- draft = false,
160
- config
161
- }) {
162
- return withUnstableCache([globalSlug, locale, draft], [createCollectionCacheKey({ globalSlug, locale })], async () => {
163
- const payload = await getPayload({ config });
164
- try {
165
- return await payload.findGlobal({ slug: globalSlug, draft, locale });
166
- } catch (error) {
167
- console.warn("[WWW] queryGlobal failed", { globalSlug, locale, error: String(error) });
168
- return null;
169
- }
170
- });
138
+ async function queryAllDocs(args) {
139
+ "use cache";
140
+ cacheLife("weeks");
141
+ const { findIds, findDocByID } = await requireCacheHelpers();
142
+ const collection = args.collectionSlug;
143
+ const { ids } = await findIds(collection, { locale: args.locale });
144
+ if (ids.length === 0)
145
+ return [];
146
+ const docs = await Promise.all(ids.map((id) => findDocByID(collection, id, { locale: args.locale })));
147
+ return docs.filter((d) => d !== null);
171
148
  }
172
- async function queryAllDocs({
173
- collectionSlug,
174
- slugField: slugField2 = "slug",
175
- locale,
176
- config
177
- }) {
178
- return withUnstableCache([collectionSlug, slugField2, locale], [createCollectionCacheKey({ collectionSlug, slug: "__all__", locale })], async () => {
179
- const payload = await getPayload({ config });
180
- const result = await payload.find({
181
- collection: collectionSlug,
182
- draft: false,
183
- limit: 1000,
184
- pagination: false,
185
- overrideAccess: false,
186
- select: { [slugField2]: true },
187
- locale
188
- });
189
- return result.docs ?? [];
190
- });
149
+ async function queryDoc(args) {
150
+ if ("globalSlug" in args)
151
+ return queryGlobal(args);
152
+ return queryDocBySlug(args);
191
153
  }
192
- async function queryAllLocaleSlugs({
193
- collectionSlug,
194
- id,
195
- slugField: slugField2 = "slug",
196
- config
197
- }) {
198
- const payload = await getPayload({ config });
199
- const doc = await payload.findByID({
200
- collection: collectionSlug,
201
- id,
202
- locale: "all",
203
- select: { [slugField2]: true }
154
+ async function queryAllLocaleSlugs(args) {
155
+ "use cache";
156
+ cacheLife("weeks");
157
+ const { findDocByID } = await requireCacheHelpers();
158
+ const slugField = args.slugField ?? "slug";
159
+ const doc = await findDocByID(args.collectionSlug, args.id, { locale: "all", select: { [slugField]: true } });
160
+ const localeMap = doc?.[slugField];
161
+ if (localeMap && typeof localeMap === "object")
162
+ return localeMap;
163
+ return null;
164
+ }
165
+ async function queryDocByID(args) {
166
+ "use cache";
167
+ cacheLife("weeks");
168
+ const { findDocByID } = await requireCacheHelpers();
169
+ return await findDocByID(args.collectionSlug, args.id, {
170
+ locale: args.locale,
171
+ draft: args.draft ?? false,
172
+ depth: args.depth
204
173
  });
205
- return doc?.[slugField2];
206
174
  }
207
175
 
208
176
  // src/exports/collections.ts
209
177
  var collections = {
210
178
  createWWWCollectionGlobal,
211
- createRevalidateCollectionGlobalHook,
212
- createCollectionCacheKey,
213
- queryDoc
179
+ queryDoc,
180
+ seedPayloadCache
214
181
  };
215
182
  var collections_default = collections;
216
183
  export {
217
- queryDoc,
218
- collections_default as default,
219
184
  createWWWCollectionGlobal,
220
- createRevalidateCollectionGlobalHook,
221
- createCollectionCacheKey
185
+ collections_default as default,
186
+ queryDoc,
187
+ seedPayloadCache
222
188
  };
package/dist/config.d.ts CHANGED
@@ -15,8 +15,9 @@ type WWWInputConfig = Omit<Config, "collections" | "globals" | "plugins"> & {
15
15
  imageHash?: MergeOrOverride<BlurhashPluginOptions>;
16
16
  translator?: MergeOrOverride<TranslatorConfig>;
17
17
  mcp?: MergeOrOverride<MCPPluginConfig>;
18
+ revalidate?: false | (<D>(d: D) => D);
18
19
  };
19
20
  };
20
21
  declare function createWWWConfig(): WWWConfigApi;
21
22
  type MergeOrOverride<T> = T | ((defaultValue: T) => T);
22
- export { createWWWConfig as default, createWWWConfig, WWWInputConfig, WWWConfigApi };
23
+ export { WWWConfigApi, WWWInputConfig, createWWWConfig, createWWWConfig as default };