payload-plugin-urls 0.9.0 → 0.9.2

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
@@ -11,60 +11,88 @@ import { payloadPluginUrls } from "payload-plugin-urls"
11
11
  export default buildConfig({
12
12
  plugins: [
13
13
  payloadPluginUrls({
14
- collections: [
15
- {
16
- slug: "pages",
14
+ collections: {
15
+ pages: {
17
16
  breadcrumbs: true,
18
17
  },
19
- {
20
- slug: "posts",
21
- rootPageField: "postsPage",
18
+ posts: {
19
+ rootPage: "postsPage",
20
+ prefixStrategy: "category",
22
21
  category: {
23
22
  collection: "post-categories",
24
23
  field: "categories",
25
- prefixStrategy: "category",
26
24
  },
27
25
  },
28
- {
29
- slug: "recipes",
30
- rootPageField: "recipesPage",
26
+ recipes: {
27
+ rootPage: "recipesPage",
28
+ prefixStrategy: "rootPage",
31
29
  category: {
32
30
  collection: "recipe-categories",
33
31
  field: "categories",
34
- prefixStrategy: "rootPage",
35
32
  },
36
33
  },
37
- ],
38
- locales: {
39
- defaultLocale: "en",
40
- locales: ["en", "it"],
41
34
  },
42
35
  rootPages: {
43
- indexPageField: "homePage",
44
- fieldOverrides: {
36
+ fields: {
45
37
  homePage: {
46
- label: "Homepage",
47
- admin: {
48
- description: "Select the page used as /",
49
- width: "100%",
38
+ isHomepage: true,
39
+ relationTo: "pages",
40
+ overrides: {
41
+ label: "Homepage",
42
+ admin: {
43
+ description: "Select the page used as /",
44
+ width: "100%",
45
+ },
50
46
  },
51
47
  },
52
48
  postsPage: {
53
- label: "Blog index",
54
- required: true,
49
+ relationTo: "pages",
50
+ overrides: {
51
+ label: "Blog index",
52
+ required: true,
53
+ },
54
+ },
55
+ recipesPage: {
56
+ relationTo: "recipes",
55
57
  },
56
58
  },
57
59
  },
60
+ overrides: {
61
+ label: "Site structure",
62
+ hooks: {
63
+ afterChange: [
64
+ async () => {
65
+ /* runs after the plugin's root-pages URL hook */
66
+ },
67
+ ],
68
+ },
69
+ },
58
70
  }),
59
71
  ],
60
72
  })
61
73
  ```
62
74
 
63
- The plugin adds a managed `root-pages` global. Its `indexPage` field defines the homepage by
64
- default, so that page resolves to `/` for the default locale and `/<locale>` for other locales. Use
65
- `rootPages.indexPageField` to rename that field, and `rootPages.fieldOverrides` to customize the
66
- generated root page relationship fields. Collection root fields such as `recipesPage` define URL
67
- prefixes for documents in that collection.
75
+ The plugin adds a managed `root-pages` global. Define every root page relationship in
76
+ `rootPages.fields`. Mark one field with `isHomepage: true` so that document resolves to `/` for the
77
+ default locale and `/<locale>` for other locales. Each field can set `relationTo` and `overrides`,
78
+ which are merged into the generated relationship field. Collection `rootPage` values such as
79
+ `recipesPage` define URL prefixes for documents in that collection.
80
+
81
+ ### Root-pages global (`overrides`)
82
+
83
+ `overrides` is an optional `Partial<GlobalConfig>` merged into the generated `root-pages` global
84
+ after the plugin's defaults. Use it for **`label`**, **`access`**, extra **`hooks`**, **`admin`**, and
85
+ other global options. Hook arrays are **concatenated**: the plugin's hooks run first, then yours.
86
+ The **`fields`** key in `overrides` is ignored so the plugin keeps control of the relationship
87
+ fields; customize those per field with `rootPages.fields[fieldName].overrides` instead.
88
+
89
+ ### Other options
90
+
91
+ - **`field`** — Rename the hidden `populatedUrl` field or pass field-level `overrides` (for example
92
+ `admin` UI tweaks).
93
+ - **`locales`** — Optional. When omitted, locales are taken from Payload `localization`. Set
94
+ `defaultLocale` and `locales` explicitly to override that, or when using exported helpers outside
95
+ the normal plugin lifecycle.
68
96
 
69
97
  Categorized collections can choose how their prefix is built:
70
98
 
@@ -77,11 +105,304 @@ when page, category, or root page relationships change.
77
105
 
78
106
  ## Exports
79
107
 
80
- - `payloadPluginUrls`
81
- - `populatedUrlField`
82
- - `resolvePopulatedUrl`
83
- - `getDocumentLink`
84
- - `getDocumentLinkBySlugs`
85
- - `getRootPageChangeSources`
86
- - `withLocalePrefix`
87
- - `withoutTrailingSlash`
108
+ The package default export is the same function as `payloadPluginUrls`. Import runtime helpers and
109
+ TypeScript types from `payload-plugin-urls`.
110
+
111
+ ### `payloadPluginUrls`
112
+
113
+ Registers the `root-pages` global, URL hooks and fields on configured collections, and the
114
+ `update-urls` job workflow.
115
+
116
+ ```ts
117
+ interface PayloadPluginUrls {
118
+ (options?: PayloadPluginUrlsOptions): PayloadPlugin
119
+ }
120
+ ```
121
+
122
+ - **`options`** — Plugin configuration; see [`PayloadPluginUrlsOptions`](#payloadpluginurlsoptions).
123
+ When omitted, the plugin is a no-op and returns the Payload config unchanged.
124
+
125
+ ### `populatedUrlField`
126
+
127
+ Builds the hidden, localized `populatedUrl` text field definition (same shape the plugin injects
128
+ into collections). Use it if you register the field manually.
129
+
130
+ ```ts
131
+ interface PopulatedUrlField {
132
+ (options?: PayloadPluginUrlsOptions): Field
133
+ }
134
+ ```
135
+
136
+ - **`options`** — Same plugin options object used for `name` / `overrides` under `field`; defaults
137
+ to `{ collections: {} }` if omitted.
138
+
139
+ ### `resolvePopulatedUrl`
140
+
141
+ Async helper that computes the public URL path for a document using your plugin `options`, locale,
142
+ merged document data, and the `root-pages` global snapshot.
143
+
144
+ ```ts
145
+ interface ResolvePopulatedUrl {
146
+ (args: ResolvePopulatedUrlArgs): Promise<string | undefined>
147
+ }
148
+ ```
149
+
150
+ `ResolvePopulatedUrlArgs`:
151
+
152
+ ```ts
153
+ interface ResolvePopulatedUrlArgs {
154
+ /** Collection slug configured under `options.collections`. */
155
+ collection: string
156
+ /** Incoming payload data for the document (merged with `originalDoc` internally). */
157
+ data: Partial<UrlDoc>
158
+ /** Locale code to resolve for. */
159
+ locale: Locale
160
+ /** Full plugin options including `collections` and `locales`. */
161
+ options: PayloadPluginUrlsOptions
162
+ /** Prior revision used to fill missing fields when resolving. */
163
+ originalDoc?: Partial<UrlDoc>
164
+ /** Optional Payload instance for loading related docs when needed. */
165
+ payload?: PayloadLike
166
+ /** Loaded `root-pages` global document for this locale. */
167
+ rootPages: RootPagesDoc
168
+ }
169
+ ```
170
+
171
+ ### `getDocumentLink`
172
+
173
+ Builds a site path from a populated relationship. Reads `populatedUrl` from the related document when
174
+ present; otherwise derives segments from plugin options and document shape. Throws if
175
+ `reference.value` is a plain id string instead of an expanded doc.
176
+
177
+ ```ts
178
+ interface GetDocumentLink {
179
+ (reference: GetDocumentLinkArgs, context: GetDocumentLinkContext): string
180
+ }
181
+ ```
182
+
183
+ - **`reference`** — Which collection the relationship targets and the related `UrlDoc` (expanded).
184
+
185
+ `GetDocumentLinkArgs`:
186
+
187
+ ```ts
188
+ interface GetDocumentLinkArgs {
189
+ relationTo: string
190
+ value: string | UrlDoc
191
+ }
192
+ ```
193
+
194
+ - **`relationTo`** — Target collection slug.
195
+ - **`value`** — Related document; must be an object with `populatedUrl` / category / slug data,
196
+ not an unresolved id string.
197
+
198
+ - **`context`** — Locale, plugin options, optional public `baseUrl`, and optional
199
+ `urlPrefixStrategy` override when building category paths.
200
+
201
+ `GetDocumentLinkContext`:
202
+
203
+ ```ts
204
+ interface GetDocumentLinkContext {
205
+ baseUrl?: string
206
+ locale: Locale
207
+ options: PayloadPluginUrlsOptions
208
+ urlPrefixStrategy?: UrlPrefixStrategy
209
+ }
210
+ ```
211
+
212
+ ### `getDocumentLinkBySlugs`
213
+
214
+ Joins URL segments with an optional locale or `baseUrl` prefix. Lower-level helper used when you
215
+ already know slug segments.
216
+
217
+ ```ts
218
+ interface GetDocumentLinkBySlugs {
219
+ (slugs: string[], context: GetDocumentLinkBySlugsContext): string
220
+ }
221
+ ```
222
+
223
+ `GetDocumentLinkBySlugsContext`:
224
+
225
+ ```ts
226
+ interface GetDocumentLinkBySlugsContext {
227
+ /** When set, prefixed before segments (can include origin or path prefix). */
228
+ baseUrl?: string
229
+ /** Declares which collection the path belongs to (for consistency with call sites). */
230
+ collection: string
231
+ locale: Locale
232
+ options: PayloadPluginUrlsOptions
233
+ }
234
+ ```
235
+
236
+ - **`slugs`** — Path segments in order (for example category path + page slug).
237
+ - **`context`** — Locale and options for default-locale handling; `collection` is part of the public
238
+ signature for naming consistency with internal routing.
239
+
240
+ ### `withLocalePrefix`
241
+
242
+ Prefixes `path` with `/<locale>` when `locale` is not the configured default locale.
243
+
244
+ ```ts
245
+ interface WithLocalePrefix {
246
+ (path: string, locale: Locale, options: PayloadPluginUrlsOptions): string
247
+ }
248
+ ```
249
+
250
+ - **`path`** — Path starting with `/` (trailing slash normalized away in the result).
251
+ - **`locale`** — Active locale.
252
+ - **`options`** — Plugin options (uses normalized `locales.defaultLocale`).
253
+
254
+ ### `withoutTrailingSlash`
255
+
256
+ Normalizes a path to start with `/` and removes a trailing slash.
257
+
258
+ ```ts
259
+ interface WithoutTrailingSlash {
260
+ (path: string): string
261
+ }
262
+ ```
263
+
264
+ - **`path`** — Raw path string.
265
+
266
+ ### `getRootPageChangeSources`
267
+
268
+ Returns job input sources describing which collections and homepage pages need URL refreshes after
269
+ the root-pages global changes. Used internally and available for custom workflows.
270
+
271
+ ```ts
272
+ interface GetRootPageChangeSources {
273
+ (current: RootPagesDoc, previous: RootPagesDoc | undefined, options: PayloadPluginUrlsOptions): UrlUpdateSource[]
274
+ }
275
+ ```
276
+
277
+ - **`current`** — New root-pages field values (ids or embedded docs).
278
+ - **`previous`** — Previous root-pages snapshot, or `undefined` on first save.
279
+ - **`options`** — Plugin options determining homepage field and `rootPage` mappings.
280
+
281
+ ### `hasUrlPathChanged`
282
+
283
+ Compares `populatedUrl` on two document-like values when both exist; otherwise compares `slug`.
284
+ Handy in hooks to detect URL-relevant edits.
285
+
286
+ ```ts
287
+ interface HasUrlPathChanged {
288
+ (doc: unknown, previousDoc: unknown): boolean
289
+ }
290
+ ```
291
+
292
+ - **`doc`** — Current document or partial.
293
+ - **`previousDoc`** — Prior revision for comparison.
294
+
295
+ ---
296
+
297
+ ### Exported types
298
+
299
+ References to `GlobalConfig`, `Field`, `LabelFunction`, `StaticLabel`, and `PayloadPlugin` use the
300
+ same names as in [`payload`](https://payloadcms.com/docs/configuration/overview). The following
301
+ interfaces are exported from `payload-plugin-urls` for use in your codebase.
302
+
303
+ #### `PayloadPluginUrlsOptions`
304
+
305
+ ```ts
306
+ interface PayloadPluginUrlsOptions {
307
+ /** Collections that participate in URL generation and how each behaves. */
308
+ collections: Record<string, UrlCollectionOptions>
309
+ /** Applied to the generated `root-pages` global only; see [Root-pages global](#root-pages-global-overrides). */
310
+ overrides?: Partial<GlobalConfig>
311
+ /** Customize the hidden URL field name or field config. */
312
+ field?: {
313
+ name?: string
314
+ overrides?: Record<string, unknown>
315
+ }
316
+ /** Override inferred localization or use helpers outside Payload. */
317
+ locales?: {
318
+ defaultLocale?: Locale
319
+ locales?: Locale[]
320
+ }
321
+ /** Root global slug, label, and relationship fields. */
322
+ rootPages?: {
323
+ slug?: string
324
+ label?: LabelFunction | StaticLabel
325
+ fields?: Record<string, RootPageFieldOptions>
326
+ }
327
+ }
328
+ ```
329
+
330
+ #### `UrlCollectionOptions`
331
+
332
+ ```ts
333
+ interface UrlCollectionOptions {
334
+ /** Use breadcrumb trail for path (typical for page trees). */
335
+ breadcrumbs?: boolean
336
+ /** How category segments appear in the path. */
337
+ prefixStrategy?: UrlPrefixStrategy
338
+ /** Key in `rootPages.fields` whose page defines this collection's URL prefix. */
339
+ rootPage?: string
340
+ category?: {
341
+ collection: string
342
+ field?: string
343
+ }
344
+ routeCollection?: string
345
+ }
346
+ ```
347
+
348
+ #### `RootPageFieldOptions`
349
+
350
+ ```ts
351
+ interface RootPageFieldOptions {
352
+ /** Marks the field whose target is the locale root `/`. */
353
+ isHomepage?: boolean
354
+ relationTo?: string
355
+ overrides?: Partial<Field>
356
+ }
357
+ ```
358
+
359
+ #### `UrlDoc`, `RootPagesDoc`, `Breadcrumb`
360
+
361
+ ```ts
362
+ interface Breadcrumb {
363
+ url?: string | null
364
+ }
365
+
366
+ interface UrlDoc {
367
+ id?: string | number | null
368
+ slug?: string | null
369
+ populatedUrl?: string | null
370
+ breadcrumbs?: Breadcrumb[] | null
371
+ categories?: unknown
372
+ _status?: "draft" | "published" | null
373
+ [key: string]: unknown
374
+ }
375
+
376
+ interface RootPagesDoc {
377
+ id?: string | null
378
+ [field: string]: string | UrlDoc | null | undefined
379
+ }
380
+ ```
381
+
382
+ #### `UrlPrefixStrategy`
383
+
384
+ ```ts
385
+ type UrlPrefixStrategy = "category" | "rootPage"
386
+ ```
387
+
388
+ #### `UrlUpdateSource`
389
+
390
+ ```ts
391
+ type UrlUpdateSource =
392
+ | {
393
+ collection: string
394
+ id?: string | null
395
+ type: "category" | "collection" | "page"
396
+ }
397
+ | {
398
+ current: RootPagesDoc
399
+ previous?: RootPagesDoc
400
+ type: "rootPages"
401
+ }
402
+ ```
403
+
404
+ #### Aliases from Payload
405
+
406
+ `PayloadPlugin`, `PayloadPluginConfig`, `CollectionConfigLike`, `GlobalConfigLike`, `FieldLike`,
407
+ `WorkflowLike`, `PayloadLike`, `Hook`, and `HookArgs` match the names exported from this package and
408
+ follow Payload’s definitions. `Locale` is `string`.
package/dist/field.d.ts CHANGED
@@ -1,11 +1,4 @@
1
1
  import type { PayloadPluginUrlsOptions } from "./types";
2
- export declare function populatedUrlField(options?: PayloadPluginUrlsOptions): {
3
- admin: {
4
- hidden: boolean;
5
- };
6
- name: string;
7
- type: string;
8
- label: boolean;
9
- localized: boolean;
10
- };
2
+ import type { Field } from "payload";
3
+ export declare function populatedUrlField(options?: PayloadPluginUrlsOptions): Field;
11
4
  //# sourceMappingURL=field.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AAEvD,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA8C;;;;;;;;EAcxF"}
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AACvD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA8C,GAAG,KAAK,CAchG"}
package/dist/hooks.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import type { Hook, PayloadPluginUrlsOptions } from "./types";
2
- export declare function createPopulateUrlHook(options: PayloadPluginUrlsOptions): Hook;
3
- export declare function createCollectionUrlUpdateHook(options: PayloadPluginUrlsOptions): Hook;
4
- export declare function createRootPagesUrlUpdateHook(options: PayloadPluginUrlsOptions): Hook;
1
+ import type { PayloadPluginUrlsOptions } from "./types";
2
+ import type { CollectionAfterChangeHook, CollectionBeforeChangeHook, GlobalAfterChangeHook } from "payload";
3
+ export declare function createPopulateUrlHook(options: PayloadPluginUrlsOptions): CollectionBeforeChangeHook;
4
+ export declare function createCollectionUrlUpdateHook(options: PayloadPluginUrlsOptions): CollectionAfterChangeHook;
5
+ export declare function createRootPagesUrlUpdateHook(options: PayloadPluginUrlsOptions): GlobalAfterChangeHook;
5
6
  //# sourceMappingURL=hooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AAE7D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAqC7E;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CA0CrF;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAqBpF"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,wBAAwB,EAAwB,MAAM,SAAS,CAAA;AAC7E,OAAO,KAAK,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,SAAS,CAAA;AAEhB,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,wBAAwB,GAChC,0BAA0B,CAqC5B;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,wBAAwB,GAChC,yBAAyB,CA0C3B;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,wBAAwB,GAChC,qBAAqB,CAuBvB"}
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var S=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Y=(e,o)=>{for(var t in o)S(e,t,{get:o[t],enumerable:!0})},Z=(e,o,t,r)=>{if(o&&typeof o=="object"||typeof o=="function")for(let n of Q(o))!X.call(e,n)&&n!==t&&S(e,n,{get:()=>o[n],enumerable:!(r=K(o,n))||r.enumerable});return e};var q=e=>Z(S({},"__esModule",{value:!0}),e);var ce={};Y(ce,{default:()=>v,getBreadcrumbsUrl:()=>p,getDocumentLink:()=>U,getDocumentLinkBySlugs:()=>T,getRootPageChangeSources:()=>I,hasUrlPathChanged:()=>w,payloadPluginUrls:()=>v,populatedUrlField:()=>x,resolvePopulatedUrl:()=>f,withLocalePrefix:()=>b,withoutTrailingSlash:()=>d});module.exports=q(ce);var d=e=>e.replace(/\/$/,"").replace(/^\/?/,"/");function p(e){return(e?.at(-1)?.url??"").replace(/(^\/)|(\/$)/g,"")}function H(e,o){return{...o||{},...oe(e)}}function F(e){let{populatedUrl:o,...t}=e;return t}function $(e){if(!(!e||typeof e!="object"||!("id"in e)))return typeof e.id=="string"?e.id:void 0}function m(e){if(typeof e=="string")return e;if(!(!e||typeof e!="object"||!("id"in e)))return typeof e.id=="string"?e.id:void 0}function h(e){return d(`/${e??""}`.replace(/\/+/g,"/"))}function y(e,o){if(!e||typeof e!="object"||!(o in e))return;let t=e;return typeof t[o]=="string"?t[o]:void 0}function O(e){return e._status==="draft"}function B(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function G(e){let o=new Set;return e.filter(t=>{let r=ee(t);return o.has(r)?!1:(o.add(r),!0)})}function ee(e){switch(e.type){case"category":case"collection":case"page":return`${e.type}:${e.collection}:${e.id??""}`;case"rootPages":return`rootPages:${JSON.stringify(e.current)}:${JSON.stringify(e.previous)}`}}function oe(e){return Object.fromEntries(Object.entries(e).filter(([,o])=>o!==void 0))}function x(e={collections:[]}){return{name:e.field?.name??"populatedUrl",type:"text",label:!1,localized:!0,...e.field?.overrides??{},admin:{hidden:!0,...B(e.field?.overrides?.admin)}}}function u(e){return{...e,field:{name:"populatedUrl",...e.field},locales:{defaultLocale:"en",locales:["en"],...e.locales},rootPages:{slug:"root-pages",indexPageField:"indexPage",...e.rootPages}}}function g(e,o){return e.collections.find(t=>t.slug===o)}function k(e,o){return e.collections.filter(t=>t.category?.collection===o).map(t=>t.slug)}function C(e){let o=u(e),t=[o.rootPages?.indexPageField??"indexPage",...o.collections.flatMap(r=>r.rootPageField?[r.rootPageField]:[])];return[...new Set(t)]}function U(e,{baseUrl:o,locale:t,options:r,urlPrefixStrategy:n}){if(typeof e.value=="string")throw new Error("Reference value is a string");if(e.value.populatedUrl)return d(e.value.populatedUrl);let l=u(r),a=g(l,e.relationTo),i=a?.routeCollection??e.relationTo,s=te(a,e.value,n??a?.category?.prefixStrategy);return T(s,{baseUrl:o,collection:i,locale:t,options:l})}function T(e,{baseUrl:o,locale:t,options:r}){let n=u(r),a=[...(o||(t===n.locales?.defaultLocale?"":t)).split("/"),...e].filter(Boolean);return d(`/${a.join("/")}`)}function b(e,o,t){let r=u(t).locales?.defaultLocale;return o===r?d(e):d(`/${o}${e}`)}function te(e,o,t){return e?.category?t==="rootPage"?[o.slug??""]:[re(o),o.slug??""]:e?.breadcrumbs?[p(o.breadcrumbs)||o.slug||""]:[o.slug??""]}function re(e){let o=e.categories;if(!Array.isArray(o)||o.length===0)return"";let r=o[0];if(!r||typeof r!="object"||typeof r=="string")return"";let n=r;return p(n.breadcrumbs)||(n.slug??"").replace(/(^\/)|(\/$)/g,"")}async function f({collection:e,data:o,locale:t,options:r,originalDoc:n,payload:l,rootPages:a}){let i=u(r),s=g(i,e);if(!s)return;let c=H(o,n),D=i.rootPages?.indexPageField??"indexPage";if(s.breadcrumbs&&!s.rootPageField){let P=await N({field:D,locale:t,payload:l,rootPages:a}),J=$(P);if(c.id&&J===c.id)return b("/",t,i);let L=h(p(c.breadcrumbs)),R=h(p(P?.breadcrumbs)),W=R&&L.startsWith(`${R}/`)?h(L.slice(R.length)):L;return b(W||`/${c.slug??""}`,t,i)}let A=await ne({collectionOptions:s,locale:t,options:i,payload:l,rootPages:a});if(s.category){let P=await le({categories:c[s.category.field??"categories"],collection:s.category.collection,locale:t,payload:l});return U({relationTo:e,value:{...F(c),[s.category.field??"categories"]:P,categories:P}},{baseUrl:A,locale:t,options:i})}return U({relationTo:e,value:F(c)},{baseUrl:A,locale:t,options:i})}async function ne({collectionOptions:e,locale:o,options:t,payload:r,rootPages:n}){if(!e.rootPageField)return;let l=await N({field:e.rootPageField,locale:o,payload:r,rootPages:n});if(l)return f({collection:"pages",data:{},locale:o,options:t,originalDoc:l,payload:r,rootPages:n})}async function N({field:e,locale:o,payload:t,rootPages:r}){let n=r[e];if(n)return typeof n!="string"?n:t?.findByID?.({collection:"pages",id:n,depth:0,locale:o,select:{id:!0,slug:!0,populatedUrl:!0,breadcrumbs:!0}})}async function le({categories:e,collection:o,locale:t,payload:r}){return Array.isArray(e)?Promise.all(e.map(n=>typeof n!="string"?Promise.resolve(n):r?.findByID?.({collection:o,id:n,depth:2,locale:t})??Promise.resolve(n))):[]}function V(e){return async({job:o,req:t})=>{let r=u(e),n=o.input?.sources??[];for(let l of r.locales?.locales??[]){let a=await t.payload.findGlobal?.({slug:r.rootPages?.slug??"root-pages",depth:2,locale:l});if(a)for(let i of ae(n,r))switch(i.type){case"category":await j({collection:i.collection,locale:l,options:r,payload:t.payload,rootPages:a});for(let s of k(r,i.collection))await j({collection:s,locale:l,options:r,payload:t.payload,rootPages:a});break;case"collection":case"page":await j({collection:i.collection,locale:l,options:r,payload:t.payload,rootPages:a});break;case"rootPages":break}}}}function w(e,o){let t=y(e,"populatedUrl"),r=y(o,"populatedUrl");return t&&r?t!==r:y(e,"slug")!==y(o,"slug")}function I(e,o,t){let r=u(t),n=[],l=r.rootPages?.indexPageField??"indexPage",a=m(e[l]),i=m(o?.[l]);a!==i&&(a&&n.push({type:"page",collection:"pages",id:a}),i&&n.push({type:"page",collection:"pages",id:i}));for(let s of r.collections){if(!s.rootPageField)continue;let c=m(e[s.rootPageField]),D=m(o?.[s.rootPageField]);c!==D&&n.push({type:"collection",collection:s.slug})}return G(n)}function z(e,o){let t={};for(let r of C(o))t[r]=e?.[r];return t}function ae(e,o){return e.flatMap(t=>t.type!=="rootPages"?[t]:I(t.current,t.previous,o))}async function j({collection:e,locale:o,options:t,payload:r,rootPages:n}){let l=1;for(;;){let a=await r.find?.({collection:e,depth:2,draft:!1,limit:100,locale:o,overrideAccess:!0,page:l,pagination:!0});if(!a)return;for(let i of a.docs){if(!i.id||O(i))continue;let s=await f({collection:e,data:{},locale:o,options:t,originalDoc:i,payload:r,rootPages:n});!s||i.populatedUrl===s||await r.update?.({collection:e,id:i.id,locale:o,overrideAccess:!0,data:{[t.field?.name??"populatedUrl"]:s},context:{disablePopulateUrl:!0,disableUrlUpdates:!0}})}if(!a.nextPage)return;l=a.nextPage}}function M(e){return async({collection:o,context:t,data:r={},originalDoc:n,req:l})=>{let a=l.locale;if(!a||a==="all"||t?.disablePopulateUrl||l.context?.disablePopulateUrl)return r;let i=await l.payload.findGlobal?.({slug:u(e).rootPages?.slug??"root-pages",depth:2,locale:a});if(!i)return r;let s=await f({collection:o?.slug??"",data:r,locale:a,options:e,originalDoc:n,payload:l.payload,rootPages:i});return s&&(r[e.field?.name??"populatedUrl"]=s),r}}function _(e){return async({collection:o,doc:t,previousDoc:r,req:n})=>{if(n.context?.disableUrlUpdates||!t||O(t)||!w(t,r))return t;let l=g(e,o?.slug??"");if(!l)return t;let a=o?.slug??"",s=k(e,a).length>0?{type:"category",collection:a,id:t.id}:{type:l.breadcrumbs&&!l.rootPageField?"page":"collection",collection:a,id:t.id};return await n.payload.jobs?.queue?.({workflow:"update-urls",input:{sources:[s]}}),t}}function E(e){return async({doc:o,previousDoc:t,req:r})=>(r.context?.disableUrlUpdates||await r.payload.jobs?.queue?.({workflow:"update-urls",input:{sources:[{type:"rootPages",current:z(o,e),previous:z(t,e)}]}}),o)}var v=e=>e?o=>{let t=u(e),r=(o.collections??[]).map(n=>ie(n,t));return{...o,collections:r,globals:[...o.globals??[],se(t)],jobs:{...o.jobs??{},workflows:[...o.jobs?.workflows??[],{slug:"update-urls",handler:V(t)}]}}}:o=>o;function ie(e,o){if(!g(o,e.slug))return e;let r=o.field?.name??"populatedUrl",n=e.fields.some(a=>a.name===r)?e.fields:[...e.fields,x(o)],l=e.hooks??{};return{...e,defaultPopulate:{...e.defaultPopulate??{},[r]:!0},fields:n,hooks:{...l,beforeChange:[...l.beforeChange??[],M(o)],afterChange:[...l.afterChange??[],_(o)]}}}function se(e){let o=u(e).rootPages;return{slug:o?.slug??"root-pages",label:o?.label??"Root pages",fields:C(e).map(t=>ue(t,e)),hooks:{afterChange:[E(e)]}}}function ue(e,o){let t=u(o).rootPages,{name:r,...n}=t?.fieldOverrides?.[e]??{};return{name:e,type:"relationship",relationTo:"pages",hasMany:!1,label:t?.fieldLabels?.[e],admin:{width:"50%"},...n}}0&&(module.exports={getBreadcrumbsUrl,getDocumentLink,getDocumentLinkBySlugs,getRootPageChangeSources,hasUrlPathChanged,payloadPluginUrls,populatedUrlField,resolvePopulatedUrl,withLocalePrefix,withoutTrailingSlash});
1
+ "use strict";var H=Object.defineProperty;var oo=Object.getOwnPropertyDescriptor;var eo=Object.getOwnPropertyNames;var to=Object.prototype.hasOwnProperty;var no=(o,e)=>{for(var t in e)H(o,t,{get:e[t],enumerable:!0})},ro=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of eo(e))!to.call(o,r)&&r!==t&&H(o,r,{get:()=>e[r],enumerable:!(n=oo(e,r))||n.enumerable});return o};var lo=o=>ro(H({},"__esModule",{value:!0}),o);var Oo={};no(Oo,{default:()=>$,getBreadcrumbsUrl:()=>g,getDocumentLink:()=>U,getDocumentLinkBySlugs:()=>G,getRootPageChangeSources:()=>z,hasUrlPathChanged:()=>F,payloadPluginUrls:()=>$,populatedUrlField:()=>L,resolvePopulatedUrl:()=>y,withLocalePrefix:()=>k,withoutTrailingSlash:()=>d});module.exports=lo(Oo);var d=o=>o.replace(/\/$/,"").replace(/^\/?/,"/");function g(o){return(o?.at(-1)?.url??"").replace(/(^\/)|(\/$)/g,"")}function B(o,e){return{...e||{},...ao(o)}}function T(o){let{populatedUrl:e,...t}=o;return t}function N(o){if(!(!o||typeof o!="object"||!("id"in o)))return typeof o.id=="string"?o.id:void 0}function O(o){if(typeof o=="string")return o;if(!(!o||typeof o!="object"||!("id"in o)))return typeof o.id=="string"?o.id:void 0}function h(o){return d(`/${o??""}`.replace(/\/+/g,"/"))}function C(o,e){if(!o||typeof o!="object"||!(e in o))return;let t=o;return typeof t[e]=="string"?t[e]:void 0}function w(o){return o._status==="draft"}function _(o){return o&&typeof o=="object"&&!Array.isArray(o)?o:{}}function E(o){let e=new Set;return o.filter(t=>{let n=io(t);return e.has(n)?!1:(e.add(n),!0)})}function io(o){switch(o.type){case"category":case"collection":case"page":return`${o.type}:${o.collection}:${o.id??""}`;case"rootPages":return`rootPages:${JSON.stringify(o.current)}:${JSON.stringify(o.previous)}`}}function ao(o){return Object.fromEntries(Object.entries(o).filter(([,e])=>e!==void 0))}function L(o={collections:{}}){return{name:o.field?.name??"populatedUrl",type:"text",label:!1,localized:!0,...o.field?.overrides??{},admin:{hidden:!0,..._(o.field?.overrides?.admin)}}}function c(o,e){let r={...so(e)??{defaultLocale:"en",locales:["en"]},...o.locales};return{...o,field:{name:"populatedUrl",...o.field},locales:{defaultLocale:r.defaultLocale??r.locales[0]??"en",locales:r.locales.length?r.locales:[r.defaultLocale??"en"]},rootPages:{slug:"root-pages",...o.rootPages,fields:o.rootPages?.fields??{indexPage:{isHomepage:!0,relationTo:"pages"}}}}}function p(o,e){return o.collections[e]}function j(o){return Object.entries(o.collections)}function R(o,e){return j(o).filter(([,t])=>t.category?.collection===e).map(([t])=>t)}function D(o){let e=c(o);return Object.keys(e.rootPages.fields)}function x(o){let e=c(o).rootPages.fields;return(Object.entries(e).find(([,n])=>n.isHomepage)??Object.entries(e).find(([n])=>n==="indexPage"))?.[0]??Object.keys(e)[0]??"indexPage"}function m(o,e){return c(o).rootPages.fields[e]}function so(o){let e=o?.localization||void 0,t=e?.locales?.map(n=>typeof n=="string"?n:n.code).filter(n=>!!n);if(t?.length)return{defaultLocale:e?.defaultLocale??t[0]??"en",locales:t}}function U(o,{baseUrl:e,locale:t,options:n,urlPrefixStrategy:r}){if(typeof o.value=="string")throw new Error("Reference value is a string");if(o.value.populatedUrl)return d(o.value.populatedUrl);let l=c(n),a=p(l,o.relationTo),i=a?.routeCollection??o.relationTo,s=co(a,o.value,r??a?.prefixStrategy);return G(s,{baseUrl:e,collection:i,locale:t,options:l})}function G(o,{baseUrl:e,locale:t,options:n}){let r=c(n),a=[...(e||(t===r.locales?.defaultLocale?"":t)).split("/"),...o].filter(Boolean);return d(`/${a.join("/")}`)}function k(o,e,t){let n=c(t).locales?.defaultLocale;return e===n?d(o):d(`/${e}${o}`)}function co(o,e,t){return o?.category?t==="rootPage"?[e.slug??""]:[uo(e),e.slug??""]:o?.breadcrumbs?[g(e.breadcrumbs)||e.slug||""]:[e.slug??""]}function uo(o){let e=o.categories;if(!Array.isArray(e)||e.length===0)return"";let n=e[0];if(!n||typeof n!="object"||typeof n=="string")return"";let r=n;return g(r.breadcrumbs)||(r.slug??"").replace(/(^\/)|(\/$)/g,"")}async function y({collection:o,data:e,locale:t,options:n,originalDoc:r,payload:l,rootPages:a}){let i=c(n),s=p(i,o);if(!s)return;let u=B(e,r),f=x(i);if(s.breadcrumbs&&!s.rootPage){let P=await W({field:f,locale:t,payload:l,rootPages:a}),Z=N(P);if(u.id&&Z===u.id)return k("/",t,i);let S=h(g(u.breadcrumbs)),A=h(g(P?.breadcrumbs)),q=A&&S.startsWith(`${A}/`)?h(S.slice(A.length)):S;return k(q||`/${u.slug??""}`,t,i)}let b=await go({collectionOptions:s,locale:t,options:i,payload:l,rootPages:a});if(s.category){let P=await po({categories:u[s.category.field??"categories"],collection:s.category.collection,locale:t,payload:l});return U({relationTo:o,value:{...T(u),[s.category.field??"categories"]:P,categories:P}},{baseUrl:b,locale:t,options:i})}return U({relationTo:o,value:T(u)},{baseUrl:b,locale:t,options:i})}async function go({collectionOptions:o,locale:e,options:t,payload:n,rootPages:r}){if(!o.rootPage)return;let l=o.rootPage,i=m(t,l)?.relationTo??"pages",s=await W({collection:i,field:l,locale:e,payload:n,rootPages:r});if(s)return typeof s.populatedUrl=="string"&&s.populatedUrl?V(s.populatedUrl,e,t):i!=="pages"?V(U({relationTo:i,value:s},{locale:e,options:t}),e,t):y({collection:i,data:{},locale:e,options:t,originalDoc:s,payload:n,rootPages:r})}function V(o,e,t){let n=c(t);return e===n.locales.defaultLocale||o===`/${e}`||o.startsWith(`/${e}/`)?o:k(o,e,n)}async function W({collection:o="pages",field:e,locale:t,payload:n,rootPages:r}){let l=r[e];return l?typeof l!="string"?l:await n?.findByID?.({collection:o,id:l,depth:0,locale:t,select:{id:!0,slug:!0,populatedUrl:!0,breadcrumbs:!0}}):void 0}async function po({categories:o,collection:e,locale:t,payload:n}){return Array.isArray(o)?Promise.all(o.map(r=>typeof r!="string"?Promise.resolve(r):n?.findByID?.({collection:e,id:r,depth:2,locale:t})??Promise.resolve(r))):[]}function M(o){return async({job:e,req:t})=>{let n=c(o),r=e.input?.sources??[];for(let l of n.locales?.locales??[]){let a=await t.payload.findGlobal?.({slug:n.rootPages?.slug??"root-pages",depth:2,locale:l});if(a)for(let i of fo(r,n))switch(i.type){case"category":await v({collection:i.collection,locale:l,options:n,payload:t.payload,rootPages:a});for(let s of R(n,i.collection))await v({collection:s,locale:l,options:n,payload:t.payload,rootPages:a});break;case"collection":case"page":await v({collection:i.collection,locale:l,options:n,payload:t.payload,rootPages:a});break;case"rootPages":break}}}}function F(o,e){let t=C(o,"populatedUrl"),n=C(e,"populatedUrl");return t&&n?t!==n:C(o,"slug")!==C(e,"slug")}function z(o,e,t){let n=c(t),r=[],l=x(n),a=m(n,l)?.relationTo??"pages",i=O(o[l]),s=O(e?.[l]);i!==s&&(i&&r.push({type:"page",collection:a,id:i}),s&&r.push({type:"page",collection:a,id:s}));for(let[u,f]of j(n)){if(!f.rootPage)continue;let b=O(o[f.rootPage]),P=O(e?.[f.rootPage]);b!==P&&r.push({type:"collection",collection:u})}return E(r)}function I(o,e){let t={};for(let n of D(e))t[n]=o?.[n];return t}function fo(o,e){return o.flatMap(t=>t.type!=="rootPages"?[t]:z(t.current,t.previous,e))}async function v({collection:o,locale:e,options:t,payload:n,rootPages:r}){let l=1;for(;;){let a=await n.find?.({collection:o,depth:2,draft:!1,limit:100,locale:e,overrideAccess:!0,page:l,pagination:!0});if(!a)return;for(let i of a.docs){let s=i;if(!s.id||w(s))continue;let u=await y({collection:o,data:{},locale:e,options:t,originalDoc:s,payload:n,rootPages:r});!u||s.populatedUrl===u||await n.update?.({collection:o,id:s.id,locale:e,overrideAccess:!0,data:{[t.field?.name??"populatedUrl"]:u},context:{disablePopulateUrl:!0,disableUrlUpdates:!0}})}if(!a.nextPage)return;l=a.nextPage}}function J(o){return async({collection:e,context:t,data:n={},originalDoc:r,req:l})=>{let a=l.locale;if(!a||a==="all"||t?.disablePopulateUrl||l.context?.disablePopulateUrl)return n;let i=await l.payload.findGlobal?.({slug:c(o).rootPages?.slug??"root-pages",depth:2,locale:a});if(!i)return n;let s=await y({collection:e?.slug??"",data:n,locale:a,options:o,originalDoc:r,payload:l.payload,rootPages:i});return s&&(n[o.field?.name??"populatedUrl"]=s),n}}function K(o){return async({collection:e,doc:t,previousDoc:n,req:r})=>{let l=t,a=n;if(r.context?.disableUrlUpdates||!l||w(l)||!F(l,a))return l;let i=p(o,e?.slug??"");if(!i)return l;let s=e?.slug??"",f=R(o,s).length>0?{type:"category",collection:s,id:l.id}:{type:i.breadcrumbs&&!i.rootPage?"page":"collection",collection:s,id:l.id};return await r.payload.jobs?.queue?.({workflow:"update-urls",input:{sources:[f]}}),l}}function Q(o){return async({doc:e,previousDoc:t,req:n})=>{let r=e,l=t;return n.context?.disableUrlUpdates||await n.payload.jobs?.queue?.({workflow:"update-urls",input:{sources:[{type:"rootPages",current:I(r,o),previous:I(l,o)}]}}),r}}var $=o=>o?e=>{let t=c(o,e),n=(e.collections??[]).map(r=>Po(r,t));return{...e,collections:n,globals:[...e.globals??[],yo(t)],jobs:{...e.jobs??{},workflows:[...e.jobs?.workflows??[],{slug:"update-urls",handler:M(t)}]}}}:e=>e;function Po(o,e){if(!p(e,o.slug))return o;let n=e.field?.name??"populatedUrl",r=o.fields.some(a=>"name"in a&&a.name===n)?o.fields:[...o.fields,L(e)],l=o.hooks??{};return{...o,defaultPopulate:{...o.defaultPopulate??{},[n]:!0},fields:r,hooks:{...l,beforeChange:[...l.beforeChange??[],J(e)],afterChange:[...l.afterChange??[],K(e)]}}}function yo(o){let e=c(o).rootPages,t={slug:e?.slug??"root-pages",label:e?.label??"Root pages",fields:D(o).map(r=>mo(r,o)),hooks:{afterChange:[Q(o)]}},n=o.overrides;return n?ko(t,n):t}function mo(o,e){let t=m(e,o),n=t?.overrides??{},{name:r,type:l,hasMany:a,relationTo:i,...s}=n;return{name:o,type:"relationship",relationTo:t?.relationTo??"pages",hasMany:!1,admin:{width:"50%"},...s}}function X(o){if(!Array.isArray(o))return[];let e=[];for(let t of o)e.push(t);return e}function Uo(o,e){if(!e)return o;if(!o)return e;let t=o,n=e,r={...t};for(let l of Object.keys(n)){let a=t[l],i=n[l];Array.isArray(a)||Array.isArray(i)?r[l]=[...X(a),...X(i)]:i!==void 0&&(r[l]=i)}return r}function Y(o,e){return e?o?{...o,...e}:e:o}function ko(o,e){let{hooks:t,access:n,admin:r,fields:l,...a}=e,i={...o,...a};return n&&(i.access=Y(o.access,n)),r&&(i.admin=Y(o.admin,r)),(t||o.hooks)&&(i.hooks=Uo(o.hooks,t)),i}0&&(module.exports={getBreadcrumbsUrl,getDocumentLink,getDocumentLinkBySlugs,getRootPageChangeSources,hasUrlPathChanged,payloadPluginUrls,populatedUrlField,resolvePopulatedUrl,withLocalePrefix,withoutTrailingSlash});
2
2
  //# sourceMappingURL=index.cjs.map