@justanarthur/payload-www 2.2.9 → 2.3.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
@@ -20,7 +20,7 @@ The composition root is `createWWWConfig()` — see [Quick start](#quick-start)
20
20
  | Fields | [`/fields`](#fields) | `link`, `linkGroup` (with `disableLabel` / `appearances` / `localized` / `relationTo` / `extraFields`), `slugField`, `appearanceOptions`. |
21
21
  | Access | [`/access`](#access) | `anyone`, `authenticated`, `authenticatedOrPublished`. |
22
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`. |
23
+ | Metadata | [`/metadata`](#metadata) | `buildArticleLd`, `buildBreadcrumbsLd`, `buildOrganizationLd`, `buildWebSiteLd`, `buildProductLd`, `buildRootJsonLd`; slug transforms; `queryDocBySlug`, `queryDocByID`, `queryGlobal`, `queryDocs`, `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. |
@@ -255,8 +255,8 @@ in the host's `revalidatePlugin()` registration (see [Caching](#caching) below).
255
255
 
256
256
  ### Caching
257
257
 
258
- The lib's cached query layer (`queryDocBySlug`, `queryGlobal`, `queryAllDocs`, `queryAllLocaleSlugs`,
259
- `queryDocByID`) wraps `@pro-laico/payload-revalidate`'s finders in `'use cache'` + `cacheLife('weeks')`
258
+ The lib's cached query layer (`queryDocBySlug`, `queryGlobal`, `queryDocs`, `queryAllDocs`,
259
+ `queryAllLocaleSlugs`, `queryDocByID`) wraps `@pro-laico/payload-revalidate`'s finders in `'use cache'` + `cacheLife('weeks')`
260
260
  scopes. Setup:
261
261
 
262
262
  1. **Install the peer deps at the workspace root** (not in the lib's `dependencies` — `file:` links
@@ -303,6 +303,34 @@ scopes. Setup:
303
303
  seedPayloadCache({ config })
304
304
  ```
305
305
 
306
+ 6. **List with `queryDocs`, never a raw `payload.find`** — a read inside `'use cache'` that
307
+ doesn't go through the finders carries no tags, so nothing the admin does can bust it.
308
+ `queryDocs` tags list membership plus every returned doc and what is populated into it:
309
+
310
+ ```ts
311
+ import { queryDocs } from '@justanarthur/payload-www/metadata'
312
+
313
+ const { docs, totalPages } = await queryDocs({
314
+ collectionSlug: 'posts',
315
+ locale,
316
+ sort: '-publishedAt',
317
+ limit: 10,
318
+ depth: 1,
319
+ list: 'archive'
320
+ })
321
+ ```
322
+
323
+ `list` names a scope whose sort and filter fields are declared on the plugin, so moving a
324
+ post's date or category reorders the archive even when that post wasn't on the page:
325
+
326
+ ```ts
327
+ createWWWConfig().withWWWConfig({
328
+ defaultPluginsConfigs: {
329
+ revalidate: { collections: { posts: { lists: { archive: ['publishedAt', 'categories'] } } } }
330
+ }
331
+ })
332
+ ```
333
+
306
334
  The cached profile is `cacheLife('weeks')` — Next 16's built-in long-tail profile (5 m stale,
307
335
  1 w revalidate, 30 d expire). No custom `cacheLife` config entry needed. Invalidations are
308
336
  purely tag-based: `revalidatePlugin()` fires the same tags the finders emit, so every save /
@@ -346,6 +374,7 @@ import {
346
374
  queryDocBySlug, // cached collection fetch
347
375
  queryDocByID, // cached collection fetch by id
348
376
  queryGlobal, // cached global fetch
377
+ queryDocs, // cached, tagged listing (where / sort / limit / page)
349
378
  queryAllDocs, // for generateStaticParams
350
379
  queryAllLocaleSlugs, // for hreflang alternates
351
380
  seedPayloadCache, // one-time seed for the pro-laico cache helpers
@@ -398,7 +427,7 @@ The package's `package.json#exports` map:
398
427
  | `@justanarthur/payload-www/collections` | `createWWWCollectionGlobal`, `queryDoc` |
399
428
  | `@justanarthur/payload-www/fields` | `link`, `linkGroup`, `appearanceOptions`, `slugField`, `LinkAppearances`, `LinkOptions` |
400
429
  | `@justanarthur/payload-www/access` | `anyone`, `authenticated`, `authenticatedOrPublished` |
401
- | `@justanarthur/payload-www/metadata` | `buildArticleLd`, `buildBreadcrumbsLd`, `buildOrganizationLd`, `buildWebSiteLd`, `buildProductLd`, `buildRootJsonLd`, `queryDocBySlug`, `queryDocByID`, `queryGlobal`, `queryAllDocs`, `queryAllLocaleSlugs`, `seedPayloadCache`, `tagsFor`, `paramsSlugToSlug`, `slugToParamsSlug` + types |
430
+ | `@justanarthur/payload-www/metadata` | `buildArticleLd`, `buildBreadcrumbsLd`, `buildOrganizationLd`, `buildWebSiteLd`, `buildProductLd`, `buildRootJsonLd`, `queryDocBySlug`, `queryDocByID`, `queryGlobal`, `queryDocs`, `queryAllDocs`, `queryAllLocaleSlugs`, `seedPayloadCache`, `tagsFor`, `paramsSlugToSlug`, `slugToParamsSlug` + types |
402
431
  | `@justanarthur/payload-www/utils` | `generateImportName`, `getFromImportMap` |
403
432
  | `@justanarthur/payload-www/imagehash` | `imageHashPlugin`, `BlurhashPluginOptions` (re-export of `@justanarthur/payload-imagehash-plugin`) |
404
433
  | `@justanarthur/payload-www/translator` | `translator` (re-export of `@justanarthur/payload-plugin-translator`) |
@@ -119,7 +119,7 @@ function seedPayloadCache({ config }) {
119
119
  return;
120
120
  _seedPromise = (async () => {
121
121
  const payload = await getPayload({ config: await config });
122
- _helpers = createCacheHelpers(payload);
122
+ _helpers = { ...createCacheHelpers(payload), payload };
123
123
  })();
124
124
  }
125
125
  async function requireCacheHelpers() {
@@ -176,6 +176,28 @@ async function queryAllDocs(args) {
176
176
  const docs = await Promise.all(ids.map((id) => findDocByID(collection, id, { locale: args.locale, overrideAccess: false, depth: 0 })));
177
177
  return docs.filter((d) => d !== null);
178
178
  }
179
+ async function queryDocs({
180
+ collectionSlug,
181
+ list,
182
+ draft = false,
183
+ depth = RENDER_DEPTH,
184
+ ...find
185
+ }) {
186
+ "use cache";
187
+ cacheLife("weeks");
188
+ const { payload, cacheIds, cacheDoc } = await requireCacheHelpers();
189
+ const collection = collectionSlug;
190
+ const result = await payload.find({ ...find, collection, draft, depth, overrideAccess: draft });
191
+ await cacheIds(result, collection, { list, draft });
192
+ for (const doc of result.docs)
193
+ await cacheDoc(doc, collection, { draft });
194
+ return {
195
+ docs: result.docs,
196
+ totalDocs: result.totalDocs,
197
+ totalPages: result.totalPages,
198
+ page: result.page ?? 1
199
+ };
200
+ }
179
201
  async function queryDoc(args) {
180
202
  if ("globalSlug" in args)
181
203
  return queryGlobal(args);
package/dist/config.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { CollectionConfig, Config, GlobalConfig, Plugin } from "payload";
2
2
  import { MCPPluginConfig } from "@payloadcms/plugin-mcp";
3
+ import { RevalidatePluginOptions } from "@pro-laico/payload-revalidate";
3
4
  import { SEOPluginConfig } from "@justanarthur/payload-plugin-seo/types";
4
5
  import { BlurhashPluginOptions } from "@justanarthur/payload-imagehash-plugin/types";
5
6
  import { TranslatorConfig } from "@justanarthur/payload-plugin-translator/types";
@@ -15,7 +16,7 @@ type WWWInputConfig = Omit<Config, "collections" | "globals" | "plugins"> & {
15
16
  imageHash?: MergeOrOverride<BlurhashPluginOptions>;
16
17
  translator?: MergeOrOverride<TranslatorConfig>;
17
18
  mcp?: MergeOrOverride<MCPPluginConfig>;
18
- revalidate?: false | (<D>(d: D) => D);
19
+ revalidate?: false | MergeOrOverride<RevalidatePluginOptions>;
19
20
  };
20
21
  };
21
22
  declare function createWWWConfig(): WWWConfigApi;
package/dist/config.js CHANGED
@@ -317,6 +317,7 @@ import { revalidatePlugin } from "@pro-laico/payload-revalidate";
317
317
  function createWWWConfig() {
318
318
  function withWWWConfig(input) {
319
319
  const { defaultPluginsConfigs, ...config } = input;
320
+ const revalidate = defaultPluginsConfigs?.revalidate;
320
321
  const blocks = config.blocks || [];
321
322
  const collections = mergeOrOverride([
322
323
  createPagesCollection(blocks),
@@ -362,7 +363,7 @@ function createWWWConfig() {
362
363
  ];
363
364
  const plugins = [
364
365
  ...mergeOrOverride(defaultPlugins, config.plugins),
365
- revalidatePlugin()
366
+ revalidatePlugin(revalidate === false ? { enabled: false } : mergeOrOverride({}, revalidate))
366
367
  ];
367
368
  return {
368
369
  ...config,
@@ -22,7 +22,7 @@ declare function paramsSlugToSlug(slug: string | string[] | undefined, _shape: S
22
22
  declare function slugToParamsSlug(slug: string, shape: SlugShape): string | string[];
23
23
  declare function isIndexSlug(slug: string | null | undefined): boolean;
24
24
  declare function shapeHasIndexPath(shape: SlugShape): boolean;
25
- import { DataFromCollectionSlug, DataFromGlobalSlug, SanitizedConfig } from "payload";
25
+ import { DataFromCollectionSlug, DataFromGlobalSlug, SanitizedConfig, SelectType, Sort, Where } from "payload";
26
26
  import { tagsFor } from "@pro-laico/payload-revalidate";
27
27
  type SeedPayloadCacheArgs = {
28
28
  config: SanitizedConfig | Promise<SanitizedConfig>;
@@ -47,9 +47,28 @@ type QueryListArgs<S extends string> = {
47
47
  slugField?: string;
48
48
  locale: string;
49
49
  };
50
+ type QueryDocsArgs<S extends string> = {
51
+ collectionSlug: S;
52
+ locale: string;
53
+ where?: Where;
54
+ sort?: Sort;
55
+ limit?: number;
56
+ page?: number;
57
+ depth?: number;
58
+ select?: SelectType;
59
+ draft?: boolean;
60
+ list?: string;
61
+ };
62
+ type QueryDocsResult<S extends string> = {
63
+ docs: DataFromCollectionSlug<S>[];
64
+ totalDocs: number;
65
+ totalPages: number;
66
+ page: number;
67
+ };
50
68
  declare function queryDocBySlug<S extends string>(args: QueryCollectionArgs<S>): Promise<DataFromCollectionSlug<S> | null>;
51
69
  declare function queryGlobal<G extends string>(args: QueryGlobalArgs<G>): Promise<DataFromGlobalSlug<G> | null>;
52
70
  declare function queryAllDocs<S extends string = string>(args: QueryListArgs<S>): Promise<DataFromCollectionSlug<S>[]>;
71
+ declare function queryDocs<S extends string>({ collectionSlug, list, draft, depth,...find }: QueryDocsArgs<S>): Promise<QueryDocsResult<S>>;
53
72
  declare function queryAllLocaleSlugs(args: {
54
73
  collectionSlug: string;
55
74
  id: number | string;
@@ -74,6 +93,7 @@ declare const metadata: {
74
93
  queryDocBySlug: typeof queryDocBySlug;
75
94
  queryDocByID: typeof queryDocByID;
76
95
  queryGlobal: typeof queryGlobal;
96
+ queryDocs: typeof queryDocs;
77
97
  queryAllDocs: typeof queryAllDocs;
78
98
  queryAllLocaleSlugs: typeof queryAllLocaleSlugs;
79
99
  paramsSlugToSlug: typeof paramsSlugToSlug;
@@ -83,4 +103,4 @@ declare const metadata: {
83
103
  seedPayloadCache: typeof seedPayloadCache;
84
104
  tagsFor: (payload: Payload_eipjla) => Tags_1jpdl;
85
105
  };
86
- export { ArticleLdOptions, BreadcrumbItem, BuildOrganizationLdOptions, BuildProductLdOptions, BuildRootJsonLdOptions, BuildWebSiteLdOptions, SeedPayloadCacheArgs, SlugShape, buildArticleLd, buildBreadcrumbsLd, buildOrganizationLd, buildProductLd, buildRootJsonLd, buildWebSiteLd, metadata as default, isIndexSlug, paramsSlugToSlug, queryAllDocs, queryAllLocaleSlugs, queryDocByID, queryDocBySlug, queryGlobal, seedPayloadCache, shapeHasIndexPath, slugToParamsSlug, tagsFor };
106
+ export { ArticleLdOptions, BreadcrumbItem, BuildOrganizationLdOptions, BuildProductLdOptions, BuildRootJsonLdOptions, BuildWebSiteLdOptions, QueryDocsArgs, QueryDocsResult, SeedPayloadCacheArgs, SlugShape, buildArticleLd, buildBreadcrumbsLd, buildOrganizationLd, buildProductLd, buildRootJsonLd, buildWebSiteLd, metadata as default, isIndexSlug, paramsSlugToSlug, queryAllDocs, queryAllLocaleSlugs, queryDocByID, queryDocBySlug, queryDocs, queryGlobal, seedPayloadCache, shapeHasIndexPath, slugToParamsSlug, tagsFor };
package/dist/metadata.js CHANGED
@@ -127,7 +127,7 @@ function seedPayloadCache({ config }) {
127
127
  return;
128
128
  _seedPromise = (async () => {
129
129
  const payload = await getPayload({ config: await config });
130
- _helpers = createCacheHelpers(payload);
130
+ _helpers = { ...createCacheHelpers(payload), payload };
131
131
  })();
132
132
  }
133
133
  async function requireCacheHelpers() {
@@ -184,6 +184,28 @@ async function queryAllDocs(args) {
184
184
  const docs = await Promise.all(ids.map((id) => findDocByID(collection, id, { locale: args.locale, overrideAccess: false, depth: 0 })));
185
185
  return docs.filter((d) => d !== null);
186
186
  }
187
+ async function queryDocs({
188
+ collectionSlug,
189
+ list,
190
+ draft = false,
191
+ depth = RENDER_DEPTH,
192
+ ...find
193
+ }) {
194
+ "use cache";
195
+ cacheLife("weeks");
196
+ const { payload, cacheIds, cacheDoc } = await requireCacheHelpers();
197
+ const collection = collectionSlug;
198
+ const result = await payload.find({ ...find, collection, draft, depth, overrideAccess: draft });
199
+ await cacheIds(result, collection, { list, draft });
200
+ for (const doc of result.docs)
201
+ await cacheDoc(doc, collection, { draft });
202
+ return {
203
+ docs: result.docs,
204
+ totalDocs: result.totalDocs,
205
+ totalPages: result.totalPages,
206
+ page: result.page ?? 1
207
+ };
208
+ }
187
209
  async function queryDoc(args) {
188
210
  if ("globalSlug" in args)
189
211
  return queryGlobal(args);
@@ -223,6 +245,7 @@ var metadata = {
223
245
  queryDocBySlug,
224
246
  queryDocByID,
225
247
  queryGlobal,
248
+ queryDocs,
226
249
  queryAllDocs,
227
250
  queryAllLocaleSlugs,
228
251
  paramsSlugToSlug,
@@ -247,6 +270,7 @@ export {
247
270
  queryAllLocaleSlugs,
248
271
  queryDocByID,
249
272
  queryDocBySlug,
273
+ queryDocs,
250
274
  queryGlobal,
251
275
  seedPayloadCache,
252
276
  shapeHasIndexPath,
package/dist/pages.js CHANGED
@@ -212,7 +212,7 @@ function seedPayloadCache({ config }) {
212
212
  return;
213
213
  _seedPromise = (async () => {
214
214
  const payload = await getPayload({ config: await config });
215
- _helpers = createCacheHelpers(payload);
215
+ _helpers = { ...createCacheHelpers(payload), payload };
216
216
  })();
217
217
  }
218
218
  async function requireCacheHelpers() {
@@ -269,6 +269,28 @@ async function queryAllDocs(args) {
269
269
  const docs = await Promise.all(ids.map((id) => findDocByID(collection, id, { locale: args.locale, overrideAccess: false, depth: 0 })));
270
270
  return docs.filter((d) => d !== null);
271
271
  }
272
+ async function queryDocs({
273
+ collectionSlug,
274
+ list,
275
+ draft = false,
276
+ depth = RENDER_DEPTH,
277
+ ...find
278
+ }) {
279
+ "use cache";
280
+ cacheLife("weeks");
281
+ const { payload, cacheIds, cacheDoc } = await requireCacheHelpers();
282
+ const collection = collectionSlug;
283
+ const result = await payload.find({ ...find, collection, draft, depth, overrideAccess: draft });
284
+ await cacheIds(result, collection, { list, draft });
285
+ for (const doc of result.docs)
286
+ await cacheDoc(doc, collection, { draft });
287
+ return {
288
+ docs: result.docs,
289
+ totalDocs: result.totalDocs,
290
+ totalPages: result.totalPages,
291
+ page: result.page ?? 1
292
+ };
293
+ }
272
294
  async function queryDoc(args) {
273
295
  if ("globalSlug" in args)
274
296
  return queryGlobal(args);
@@ -242,7 +242,7 @@ function seedPayloadCache({ config }) {
242
242
  return;
243
243
  _seedPromise = (async () => {
244
244
  const payload = await getPayload({ config: await config });
245
- _helpers = createCacheHelpers(payload);
245
+ _helpers = { ...createCacheHelpers(payload), payload };
246
246
  })();
247
247
  }
248
248
  async function requireCacheHelpers() {
@@ -299,6 +299,28 @@ async function queryAllDocs(args) {
299
299
  const docs = await Promise.all(ids.map((id) => findDocByID(collection, id, { locale: args.locale, overrideAccess: false, depth: 0 })));
300
300
  return docs.filter((d) => d !== null);
301
301
  }
302
+ async function queryDocs({
303
+ collectionSlug,
304
+ list,
305
+ draft = false,
306
+ depth = RENDER_DEPTH,
307
+ ...find
308
+ }) {
309
+ "use cache";
310
+ cacheLife("weeks");
311
+ const { payload, cacheIds, cacheDoc } = await requireCacheHelpers();
312
+ const collection = collectionSlug;
313
+ const result = await payload.find({ ...find, collection, draft, depth, overrideAccess: draft });
314
+ await cacheIds(result, collection, { list, draft });
315
+ for (const doc of result.docs)
316
+ await cacheDoc(doc, collection, { draft });
317
+ return {
318
+ docs: result.docs,
319
+ totalDocs: result.totalDocs,
320
+ totalPages: result.totalPages,
321
+ page: result.page ?? 1
322
+ };
323
+ }
302
324
  async function queryDoc(args) {
303
325
  if ("globalSlug" in args)
304
326
  return queryGlobal(args);
package/dist/sitemap.js CHANGED
@@ -37,7 +37,7 @@ function seedPayloadCache({ config }) {
37
37
  return;
38
38
  _seedPromise = (async () => {
39
39
  const payload = await getPayload({ config: await config });
40
- _helpers = createCacheHelpers(payload);
40
+ _helpers = { ...createCacheHelpers(payload), payload };
41
41
  })();
42
42
  }
43
43
  async function requireCacheHelpers() {
@@ -94,6 +94,28 @@ async function queryAllDocs(args) {
94
94
  const docs = await Promise.all(ids.map((id) => findDocByID(collection, id, { locale: args.locale, overrideAccess: false, depth: 0 })));
95
95
  return docs.filter((d) => d !== null);
96
96
  }
97
+ async function queryDocs({
98
+ collectionSlug,
99
+ list,
100
+ draft = false,
101
+ depth = RENDER_DEPTH,
102
+ ...find
103
+ }) {
104
+ "use cache";
105
+ cacheLife("weeks");
106
+ const { payload, cacheIds, cacheDoc } = await requireCacheHelpers();
107
+ const collection = collectionSlug;
108
+ const result = await payload.find({ ...find, collection, draft, depth, overrideAccess: draft });
109
+ await cacheIds(result, collection, { list, draft });
110
+ for (const doc of result.docs)
111
+ await cacheDoc(doc, collection, { draft });
112
+ return {
113
+ docs: result.docs,
114
+ totalDocs: result.totalDocs,
115
+ totalPages: result.totalPages,
116
+ page: result.page ?? 1
117
+ };
118
+ }
97
119
  async function queryDoc(args) {
98
120
  if ("globalSlug" in args)
99
121
  return queryGlobal(args);
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@justanarthur/payload-www",
3
3
  "description": "Reusable Payload CMS website template — config builder, collections, globals, blocks, fields, access, hooks, metadata (JSON-LD, hreflang), page renderers, and test helpers.",
4
- "version": "2.2.9",
4
+ "version": "2.3.0",
5
+ "license": "MIT",
5
6
  "type": "module",
6
7
  "private": false,
7
8
  "files": [