@abinnovision/payloadcms-wayfinder 1.0.0-beta.2 → 1.0.0-beta.4

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.
Files changed (52) hide show
  1. package/README.md +22 -19
  2. package/dist/admin/LinkLabelFeature.client.d.mts +2 -3
  3. package/dist/config/collection-string-field.d.mts +3 -4
  4. package/dist/config/has-duplicates.d.mts +2 -3
  5. package/dist/config/link-field.d.mts +4 -5
  6. package/dist/config/load-mappings.d.mts +21 -6
  7. package/dist/config/load-mappings.mjs +16 -4
  8. package/dist/config/mapping-global.d.mts +18 -10
  9. package/dist/config/mapping-global.mjs +2 -1
  10. package/dist/config/plugin.d.mts +9 -7
  11. package/dist/config/plugin.mjs +5 -2
  12. package/dist/config/translations.d.mts +2 -3
  13. package/dist/index.d.mts +10 -14
  14. package/dist/index.mjs +4 -13
  15. package/dist/internal.d.mts +11 -0
  16. package/dist/internal.mjs +11 -0
  17. package/dist/lexical/index.d.mts +12 -27
  18. package/dist/lexical/index.mjs +11 -25
  19. package/dist/montage/index.d.mts +33 -12
  20. package/dist/montage/index.mjs +25 -11
  21. package/dist/pattern/define-links.d.mts +31 -20
  22. package/dist/pattern/define-mappings.d.mts +6 -3
  23. package/dist/pattern/define-mappings.mjs +3 -1
  24. package/dist/pattern/derive-link-label.d.mts +2 -3
  25. package/dist/pattern/derive-link-label.mjs +2 -1
  26. package/dist/pattern/link-node.d.mts +20 -0
  27. package/dist/pattern/link-node.mjs +34 -0
  28. package/dist/pattern/matcher.d.mts +3 -4
  29. package/dist/pattern/param-query-path.d.mts +10 -9
  30. package/dist/pattern/param-query-path.mjs +6 -7
  31. package/dist/pattern/resolver.d.mts +6 -5
  32. package/dist/pattern/resolver.mjs +5 -2
  33. package/dist/pattern/types.d.mts +45 -19
  34. package/dist/pattern/types.mjs +2 -1
  35. package/dist/runtime/build-href.d.mts +5 -12
  36. package/dist/runtime/build-href.mjs +1 -2
  37. package/dist/runtime/build-path.d.mts +3 -4
  38. package/dist/runtime/build-path.mjs +9 -0
  39. package/dist/runtime/create-router.d.mts +71 -0
  40. package/dist/runtime/create-router.mjs +64 -0
  41. package/dist/runtime/diagnostics.d.mts +7 -8
  42. package/dist/runtime/resolve-link.d.mts +17 -13
  43. package/dist/runtime/resolve-link.mjs +10 -10
  44. package/dist/runtime/resolve-path.d.mts +26 -12
  45. package/dist/runtime/resolve-path.mjs +2 -4
  46. package/dist/runtime/resolve-relationship-slug.d.mts +3 -5
  47. package/dist/runtime/resolve-relationship-slug.mjs +16 -8
  48. package/package.json +13 -9
  49. package/dist/pattern/index.d.mts +0 -8
  50. package/dist/pattern/index.mjs +0 -8
  51. package/dist/runtime/index.d.mts +0 -7
  52. package/dist/runtime/index.mjs +0 -6
package/README.md CHANGED
@@ -6,10 +6,11 @@ Editor-authored URL routing for [Payload CMS](https://payloadcms.com/).
6
6
 
7
7
  Wayfinder owns one thing: the map from collections to the URL patterns their documents are served
8
8
  at. That map is authored in the CMS, so adding a page type is an editorial act rather than a code
9
- change. Everything else in the package falls out of it. `buildHref` turns a document into an href,
10
- `buildPath` builds a path from parameter values alone for sitemaps and feeds,
11
- `resolvePathToDocument` turns a request path back into a document for a catch-all route, and
12
- `resolveLink` plus `linkField` give editors a link that follows its target when that collection's
9
+ change. Everything else in the package falls out of it. `createRouter` binds the mapping, the
10
+ locale and the href formatter once per request and hands back a router: `router.href` turns a
11
+ document into an href, `router.path` builds a path from parameter values alone for sitemaps and
12
+ feeds, `router.resolve` turns a request path back into a document for a catch-all route, and
13
+ `router.link` plus `linkField` give editors a link that follows its target when that collection's
13
14
  pattern changes. [`docs/concepts.md`](./docs/concepts.md) describes the model;
14
15
  [`docs/limitations.md`](./docs/limitations.md) states what is out of scope and why.
15
16
 
@@ -42,7 +43,7 @@ import { buildConfig } from "payload";
42
43
 
43
44
  export default buildConfig({
44
45
  // ...
45
- plugins: [wayfinderPlugin({ linkableCollections: ["pages", "articles"] })],
46
+ plugins: [wayfinderPlugin({ checkDefaultPopulateOn: ["pages", "articles"] })],
46
47
  });
47
48
  ```
48
49
 
@@ -63,7 +64,7 @@ Read the mapping and hand it to a catch-all route:
63
64
 
64
65
  ```tsx
65
66
  // app/[[...path]]/page.tsx
66
- import { resolvePathToDocument } from "@abinnovision/payloadcms-wayfinder";
67
+ import { createRouter } from "@abinnovision/payloadcms-wayfinder";
67
68
  import { loadMappings } from "@abinnovision/payloadcms-wayfinder/config";
68
69
  import { notFound } from "next/navigation";
69
70
  import { getPayload } from "payload";
@@ -75,11 +76,10 @@ const Page = async ({ params }: { params: Promise<{ path?: string[] }> }) => {
75
76
  const mappings = await loadMappings({ payload });
76
77
  const { path } = await params;
77
78
 
78
- const resolved = await resolvePathToDocument({
79
+ const wayfinder = createRouter({ mappings, locale: "en" });
80
+
81
+ const resolved = await wayfinder.resolve(`/${(path ?? []).join("/")}`, {
79
82
  payload,
80
- mappings,
81
- path: `/${(path ?? []).join("/")}`,
82
- locale: "en",
83
83
  });
84
84
 
85
85
  if (!resolved) {
@@ -98,17 +98,20 @@ export default Page;
98
98
  ## Entrypoints
99
99
 
100
100
  ```
101
- "." buildHref, buildPath, resolvePathToDocument, resolveLink, defineMappings, defineLinks, types
102
- "./config" wayfinderPlugin, createMappingGlobal, loadMappings, linkField
103
- "./lexical" wayfinderLinkFeature, linkLabelFeature, resolveLinkNode
104
- "./admin" LinkLabelFeatureClient, mounted by linkLabelFeature through the import map
105
- "./montage" initWayfinder, getMappings, wayfinderExtension
101
+ "." createRouter, defineMappings, defineLinks, resolveRelationshipSlug, deriveLinkLabel, types
102
+ "./internal" the unbound functions the router is built out of, plus the pattern internals
103
+ "./config" wayfinderPlugin, createMappingGlobal, loadMappings, linkField
104
+ "./lexical" wayfinderLinkFeature, linkLabelFeature, resolveLinkNode
105
+ "./admin" LinkLabelFeatureClient, mounted by linkLabelFeature through the import map
106
+ "./montage" initWayfinder, wayfinderFrom, wayfinderExtension
106
107
  ```
107
108
 
108
109
  `.` is the runtime half. It takes mappings as plain data and never reads the CMS, so it runs in a
109
- route handler, a sitemap, a script or a test alike. `./config` is loaded by the CLI, by migrations
110
- and by `payload generate:types`, so it must stay React-free. `./lexical`, `./admin` and
111
- `./montage` each pull in an optional peer and are separate for that reason.
110
+ route handler, a sitemap, a script or a test alike. `./internal` is the escape hatch behind it,
111
+ for a caller that holds no request or wants a different set of arguments per call; nothing there
112
+ carries a compatibility guarantee. `./config` is loaded by the CLI, by migrations and by
113
+ `payload generate:types`, so it must stay React-free. `./lexical`, `./admin` and `./montage` each
114
+ pull in an optional peer and are separate for that reason.
112
115
  [`docs/layers.md`](./docs/layers.md) explains the split.
113
116
 
114
117
  ## Documentation
@@ -119,7 +122,7 @@ and by `payload generate:types`, so it must stay React-free. `./lexical`, `./adm
119
122
  works without.
120
123
  - [`docs/integration.md`](./docs/integration.md): plugin setup, caching, catch-all routes,
121
124
  sitemaps and `defaultPopulate`.
122
- - [`docs/linking.md`](./docs/linking.md): `linkField`, `defineLinks`, `resolveLink` and the Lexical
125
+ - [`docs/linking.md`](./docs/linking.md): `linkField`, `defineLinks`, `router.link` and the Lexical
123
126
  feature.
124
127
  - [`docs/code-defined-mappings.md`](./docs/code-defined-mappings.md): `defineMappings`, with no CMS
125
128
  global at all.
@@ -1,5 +1,4 @@
1
1
  //#region src/admin/LinkLabelFeature.client.d.ts
2
2
  /** Client feature registering {@link LinkLabelPlugin} on the editor. */
3
- declare const LinkLabelFeatureClient: import("@payloadcms/richtext-lexical").FeatureProviderProviderClient<undefined, undefined>;
4
- //#endregion
5
- export { LinkLabelFeatureClient };
3
+ export declare const LinkLabelFeatureClient: import("@payloadcms/richtext-lexical").FeatureProviderProviderClient<undefined, undefined>;
4
+ //#endregion
@@ -1,6 +1,6 @@
1
1
  import { Field, TextField } from "payload";
2
2
  //#region src/config/collection-string-field.d.ts
3
- interface CreateCollectionStringFieldArgs {
3
+ export interface CreateCollectionStringFieldArgs {
4
4
  name: string;
5
5
  required?: boolean;
6
6
  label?: TextField["label"];
@@ -15,6 +15,5 @@ interface CreateCollectionStringFieldArgs {
15
15
  *
16
16
  * @param args Field name and presentation overrides.
17
17
  */
18
- declare const createCollectionStringField: (args: CreateCollectionStringFieldArgs) => Field;
19
- //#endregion
20
- export { CreateCollectionStringFieldArgs, createCollectionStringField };
18
+ export declare const createCollectionStringField: (args: CreateCollectionStringFieldArgs) => Field;
19
+ //#endregion
@@ -10,6 +10,5 @@
10
10
  *
11
11
  * @param keys The authored keys, in row order.
12
12
  */
13
- declare const hasDuplicates: (keys: (string | undefined)[]) => boolean;
14
- //#endregion
15
- export { hasDuplicates };
13
+ export declare const hasDuplicates: (keys: (string | undefined)[]) => boolean;
14
+ //#endregion
@@ -3,7 +3,7 @@ import { LinkDeclaration, LinkVariantSource } from "../pattern/define-links.mjs"
3
3
  import { Field } from "payload";
4
4
  //#region src/config/link-field.d.ts
5
5
  /** Admin labels for the built-in link types. */
6
- interface LinkFieldLabels {
6
+ export interface LinkFieldLabels {
7
7
  label?: LabelLike;
8
8
  none?: LabelLike;
9
9
  reference?: LabelLike;
@@ -14,7 +14,7 @@ interface LinkFieldLabels {
14
14
  urlField?: LabelLike;
15
15
  samePageField?: LabelLike;
16
16
  }
17
- interface LinkFieldArgs<TDeclaration extends LinkDeclaration = LinkDeclaration> extends LinkVariantSource<TDeclaration> {
17
+ export interface LinkFieldArgs<TDeclaration extends LinkDeclaration = LinkDeclaration> extends LinkVariantSource<TDeclaration> {
18
18
  /**
19
19
  * Every collection that has a URL. A target missing here cannot be linked
20
20
  * to from the admin panel even if its mapping exists.
@@ -30,6 +30,5 @@ interface LinkFieldArgs<TDeclaration extends LinkDeclaration = LinkDeclaration>
30
30
  */
31
31
  interfaceName?: string;
32
32
  }
33
- declare const linkField: <TDeclaration extends LinkDeclaration = LinkDeclaration>(args: LinkFieldArgs<TDeclaration>) => Field;
34
- //#endregion
35
- export { LinkFieldArgs, LinkFieldLabels, linkField };
33
+ export declare const linkField: <TDeclaration extends LinkDeclaration = LinkDeclaration>(args: LinkFieldArgs<TDeclaration>) => Field;
34
+ //#endregion
@@ -10,15 +10,31 @@ import { Payload } from "payload";
10
10
  * `loadMappings` call in your own framework cache and invalidate it from
11
11
  * `createMappingGlobal({ onChange })`.
12
12
  */
13
- interface MappingCache {
13
+ export interface MappingCache {
14
14
  get: (key: string) => PayloadCollectionMappingResolved[] | undefined;
15
15
  set: (key: string, value: PayloadCollectionMappingResolved[]) => void;
16
16
  }
17
- interface LoadMappingsArgs {
17
+ export interface LoadMappingsArgs {
18
18
  payload: Payload;
19
19
  globalSlug?: string;
20
- /** Must match what {@link createMappingGlobal} was given. */
20
+ /**
21
+ * Whether patterns are per-locale.
22
+ *
23
+ * Derived from the instance's own `localization` config, which is the same
24
+ * authority `wayfinderPlugin` derives it from, so the two sides cannot
25
+ * disagree. Set it only to override that, and then on both sides.
26
+ */
21
27
  localized?: boolean;
28
+ /**
29
+ * The field a relationship parameter falls back to when the target
30
+ * collection's own pattern cannot name one.
31
+ *
32
+ * Read off the mapping global's own config when the plugin was given it,
33
+ * so a project states it once, where the global is declared, and both the
34
+ * save-time validation and the compiled mappings get the same answer. Set
35
+ * it here only to override that.
36
+ */
37
+ fallbackIdentifierField?: string;
22
38
  /** Reuses compiled patterns across reads. @see MappingCache */
23
39
  cache?: MappingCache;
24
40
  }
@@ -33,6 +49,5 @@ interface LoadMappingsArgs {
33
49
  *
34
50
  * @param args The Payload instance and mapping-global settings.
35
51
  */
36
- declare const loadMappings: (args: LoadMappingsArgs) => Promise<PayloadCollectionMappingResolved[]>;
37
- //#endregion
38
- export { LoadMappingsArgs, MappingCache, loadMappings };
52
+ export declare const loadMappings: (args: LoadMappingsArgs) => Promise<PayloadCollectionMappingResolved[]>;
53
+ //#endregion
@@ -10,6 +10,16 @@ import "./mapping-global.mjs";
10
10
  };
11
11
  const defaultCache = createMemoryCache();
12
12
  /**
13
+ * Reads what {@link createMappingGlobal} was told, off the global's own config.
14
+ *
15
+ * The write side validates patterns against this and the read side compiles
16
+ * mappings with it, so the two have to agree. Carrying it on the config means
17
+ * a project states it where it declares the global and nowhere else.
18
+ */ const declaredIdentifierField = (payload, slug) => {
19
+ const declared = (payload.config.globals.find((it) => it.slug === slug)?.custom)?.wayfinder?.fallbackIdentifierField;
20
+ return typeof declared === "string" ? declared : void 0;
21
+ };
22
+ /**
13
23
  * Reads the mapping global and compiles it.
14
24
  *
15
25
  * Returns an empty list rather than throwing when the global has never been
@@ -20,10 +30,12 @@ const defaultCache = createMemoryCache();
20
30
  *
21
31
  * @param args The Payload instance and mapping-global settings.
22
32
  */ const loadMappings = async (args) => {
23
- const localized = args.localized ?? true;
33
+ const slug = args.globalSlug ?? "collections-mapping";
34
+ const localized = args.localized ?? Boolean(args.payload.config.localization);
24
35
  const cache = args.cache ?? defaultCache;
36
+ const fallbackIdentifierField = args.fallbackIdentifierField ?? declaredIdentifierField(args.payload, slug);
25
37
  const global = await args.payload.findGlobal({
26
- slug: args.globalSlug ?? "collections-mapping",
38
+ slug,
27
39
  depth: 0,
28
40
  overrideAccess: true,
29
41
  ...localized ? { locale: "all" } : {}
@@ -43,12 +55,12 @@ const defaultCache = createMemoryCache();
43
55
  path: patterns
44
56
  }] : [];
45
57
  });
46
- const key = JSON.stringify(usable);
58
+ const key = JSON.stringify([usable, fallbackIdentifierField]);
47
59
  const cached = cache.get(key);
48
60
  if (cached) return cached;
49
61
  const compiled = usable.flatMap((mapping) => {
50
62
  try {
51
- return [resolveCollectionMapping(mapping)];
63
+ return [resolveCollectionMapping(mapping, fallbackIdentifierField)];
52
64
  } catch {
53
65
  return [];
54
66
  }
@@ -1,18 +1,27 @@
1
1
  import { GlobalConfig } from "payload";
2
2
  //#region src/config/mapping-global.d.ts
3
3
  /** The global the mapping is authored in, unless overridden. */
4
- declare const DEFAULT_MAPPING_GLOBAL_SLUG = "collections-mapping";
5
- interface CreateMappingGlobalArgs {
4
+ export declare const DEFAULT_MAPPING_GLOBAL_SLUG = "collections-mapping";
5
+ export interface CreateMappingGlobalArgs {
6
6
  /** Defaults to {@link DEFAULT_MAPPING_GLOBAL_SLUG}. */
7
7
  globalSlug?: string;
8
8
  /**
9
- * Whether path patterns differ per locale. Turn it off for a project with
10
- * no `localization` block — Payload would otherwise return a scalar where
11
- * a per-locale record is expected.
9
+ * Whether path patterns differ per locale.
10
+ *
11
+ * Derived from the config's `localization` block by `wayfinderPlugin`, and
12
+ * from the running instance by `loadMappings`, so a project normally never
13
+ * sets it. Pass it only to override that — and then to both sides, because
14
+ * Payload returns a scalar for an unlocalized field and a per-locale record
15
+ * for a localized one, and the read has to expect the shape the write
16
+ * produced.
12
17
  */
13
18
  localized?: boolean;
14
- /** Fallback identifier field for relationship parameters. */
15
- identifierField?: string;
19
+ /**
20
+ * The field a relationship parameter falls back to when the target
21
+ * collection's pattern cannot name one. Used here to validate a pattern at
22
+ * save time, before any mapping has been compiled to carry it.
23
+ */
24
+ fallbackIdentifierField?: string;
16
25
  label?: GlobalConfig["label"];
17
26
  adminGroup?: string;
18
27
  access?: GlobalConfig["access"];
@@ -29,6 +38,5 @@ interface CreateMappingGlobalArgs {
29
38
  *
30
39
  * @param args Slug, localization and presentation overrides.
31
40
  */
32
- declare const createMappingGlobal: (args?: CreateMappingGlobalArgs) => GlobalConfig;
33
- //#endregion
34
- export { CreateMappingGlobalArgs, DEFAULT_MAPPING_GLOBAL_SLUG, createMappingGlobal };
41
+ export declare const createMappingGlobal: (args?: CreateMappingGlobalArgs) => GlobalConfig;
42
+ //#endregion
@@ -41,7 +41,7 @@ import { text } from "payload/shared";
41
41
  config: collection.config,
42
42
  param: key.name,
43
43
  collections: opts.req.payload.collections,
44
- ...args.identifierField ? { identifierField: args.identifierField } : {}
44
+ ...args.fallbackIdentifierField ? { fallbackIdentifierField: args.fallbackIdentifierField } : {}
45
45
  });
46
46
  if ("error" in resolved) return resolved.error;
47
47
  }
@@ -51,6 +51,7 @@ import { text } from "payload/shared";
51
51
  return {
52
52
  slug: args.globalSlug ?? "collections-mapping",
53
53
  label: args.label ?? "Collections Mapping",
54
+ ...args.fallbackIdentifierField ? { custom: { wayfinder: { fallbackIdentifierField: args.fallbackIdentifierField } } } : {},
54
55
  ...args.access ? { access: args.access } : {},
55
56
  admin: {
56
57
  group: args.adminGroup ?? "Settings",
@@ -1,12 +1,15 @@
1
1
  import { CreateMappingGlobalArgs } from "./mapping-global.mjs";
2
2
  import { Plugin } from "payload";
3
3
  //#region src/config/plugin.d.ts
4
- interface WayfinderPluginArgs extends CreateMappingGlobalArgs {
4
+ export interface WayfinderPluginArgs extends CreateMappingGlobalArgs {
5
5
  /**
6
- * Collections that can be linked to. Only used to warn about missing
7
- * `defaultPopulate`; linking itself is governed by the link field.
6
+ * Collections to check for `defaultPopulate` at boot.
7
+ *
8
+ * Named for what it does. It grants nothing: which collections can be
9
+ * linked to is decided by the link field's own `relationTo`, and an
10
+ * earlier name implied this list governed that.
8
11
  */
9
- linkableCollections?: string[];
12
+ checkDefaultPopulateOn?: string[];
10
13
  /**
11
14
  * Set when the project resolves references through its own index rather
12
15
  * than a populated document. Suppresses the `defaultPopulate` warning,
@@ -26,6 +29,5 @@ interface WayfinderPluginArgs extends CreateMappingGlobalArgs {
26
29
  *
27
30
  * @param args Mapping-global settings and startup-check inputs.
28
31
  */
29
- declare const wayfinderPlugin: (args?: WayfinderPluginArgs) => Plugin;
30
- //#endregion
31
- export { WayfinderPluginArgs, wayfinderPlugin };
32
+ export declare const wayfinderPlugin: (args?: WayfinderPluginArgs) => Plugin;
33
+ //#endregion
@@ -13,7 +13,10 @@ import { createMappingGlobal } from "./mapping-global.mjs";
13
13
  */ const wayfinderPlugin = (args = {}) => (incoming) => {
14
14
  const config = {
15
15
  ...incoming,
16
- globals: [...incoming.globals ?? [], createMappingGlobal(args)],
16
+ globals: [...incoming.globals ?? [], createMappingGlobal({
17
+ localized: Boolean(incoming.localization),
18
+ ...args
19
+ })],
17
20
  i18n: {
18
21
  ...incoming.i18n,
19
22
  translations: {
@@ -24,7 +27,7 @@ import { createMappingGlobal } from "./mapping-global.mjs";
24
27
  };
25
28
  if (args.quiet) return config;
26
29
  if (!args.resolvesReferencesExternally) {
27
- const missing = (args.linkableCollections ?? []).filter((slug) => {
30
+ const missing = (args.checkDefaultPopulateOn ?? []).filter((slug) => {
28
31
  const collection = config.collections?.find((it) => it.slug === slug);
29
32
  return collection && !collection.defaultPopulate;
30
33
  });
@@ -6,7 +6,7 @@
6
6
  * without the plugin still works — `req.t` returns the key's fallback — but
7
7
  * sees these in English regardless of the admin locale.
8
8
  */
9
- declare const wayfinderTranslations: {
9
+ export declare const wayfinderTranslations: {
10
10
  readonly en: {
11
11
  readonly wayfinder: {
12
12
  readonly duplicateCollection: "Each collection may only be mapped once";
@@ -30,5 +30,4 @@ declare const wayfinderTranslations: {
30
30
  };
31
31
  };
32
32
  };
33
- //#endregion
34
- export { wayfinderTranslations };
33
+ //#endregion
package/dist/index.d.mts CHANGED
@@ -1,16 +1,12 @@
1
- import { BaseResolvedLink, BuiltinLinkVariant, Contributed, DEFAULT_LOCALE_KEY, DeclaredLinkVariant, FormatHref, LabelLike, LinkFieldData, PayloadCollectionMapping, PayloadCollectionMappingMatch, PayloadCollectionMappingResolved, PayloadCollectionMappingResolvers, PayloadCollectionMappingSpecificity, ResolvedLink } from "./pattern/types.mjs";
2
- import { AnyLinkVariantDefinition, DataOfFields, LinkDataOf, LinkDeclaration, LinkVariantDefinition, LinkVariantSource, LinkVariantSpec, ResolvedLinkOf, VariantBuilder, defineLinks, variantsOf } from "./pattern/define-links.mjs";
3
- import { defineMappings } from "./pattern/define-mappings.mjs";
4
- import { deriveLinkLabel } from "./pattern/derive-link-label.mjs";
5
- import { PayloadCollectionMatch, matchCollectionMappings } from "./pattern/matcher.mjs";
6
- import { DEFAULT_IDENTIFIER_FIELD, RegisteredCollections, ResolveParamQueryPathArgs, resolveParamQueryPath } from "./pattern/param-query-path.mjs";
7
- import { isRootWildcard, resolveCollectionMapping, resolversFor } from "./pattern/resolver.mjs";
8
- import "./pattern/index.mjs";
1
+ import { BaseResolvedLink, BuiltinLinkVariant, DocumentId, FormatHref, LabelLike, LinkFieldData, PayloadCollectionMapping, PayloadCollectionMappingResolved } from "./pattern/types.mjs";
2
+ import { LinkContextOf, LinkDataOf, LinkDeclaration, ResolvedLinkOf, defineLinks } from "./pattern/define-links.mjs";
9
3
  import { BuildDiagnosticReason, Diagnostic, DiagnosticReason, OnDiagnostic, ResolveLinkDiagnosticReason, ResolvePathDiagnosticReason } from "./runtime/diagnostics.mjs";
10
- import { BuildHrefArgs, LinkableDocument, buildHref, identityFormatHref } from "./runtime/build-href.mjs";
11
- import { BuildPathArgs, buildPath } from "./runtime/build-path.mjs";
12
- import { ResolveLinkArgs, ResolveReference, isAvailableLink, resolveLink } from "./runtime/resolve-link.mjs";
13
- import { PayloadDocument, ResolvePathToDocumentArgs, ResolvePathWhere, ResolvedPath, resolvePathToDocument } from "./runtime/resolve-path.mjs";
4
+ import { LinkableDocument } from "./runtime/build-href.mjs";
5
+ import { ResolveReference } from "./runtime/resolve-link.mjs";
6
+ import { PayloadCollectionMatch } from "./pattern/matcher.mjs";
7
+ import { PayloadDocument, ResolvePathWhere, ResolvedPath } from "./runtime/resolve-path.mjs";
8
+ import { CreateRouterArgs, Router, createRouter } from "./runtime/create-router.mjs";
14
9
  import { ResolveRelationshipSlugArgs, resolveRelationshipSlug } from "./runtime/resolve-relationship-slug.mjs";
15
- import "./runtime/index.mjs";
16
- export { type AnyLinkVariantDefinition, type BaseResolvedLink, type BuildDiagnosticReason, type BuildHrefArgs, type BuildPathArgs, type BuiltinLinkVariant, type Contributed, DEFAULT_IDENTIFIER_FIELD, DEFAULT_LOCALE_KEY, type DataOfFields, type DeclaredLinkVariant, type Diagnostic, type DiagnosticReason, type FormatHref, type LabelLike, type LinkDataOf, type LinkDeclaration, type LinkFieldData, type LinkVariantDefinition, type LinkVariantSource, type LinkVariantSpec, type LinkableDocument, type OnDiagnostic, type PayloadCollectionMapping, type PayloadCollectionMappingMatch, type PayloadCollectionMappingResolved, type PayloadCollectionMappingResolvers, type PayloadCollectionMappingSpecificity, type PayloadCollectionMatch, type PayloadDocument, type RegisteredCollections, type ResolveLinkArgs, type ResolveLinkDiagnosticReason, type ResolveParamQueryPathArgs, type ResolvePathDiagnosticReason, type ResolvePathToDocumentArgs, type ResolvePathWhere, type ResolveReference, type ResolveRelationshipSlugArgs, type ResolvedLink, type ResolvedLinkOf, type ResolvedPath, type VariantBuilder, buildHref, buildPath, defineLinks, defineMappings, deriveLinkLabel, identityFormatHref, isAvailableLink, isRootWildcard, matchCollectionMappings, resolveCollectionMapping, resolveLink, resolveParamQueryPath, resolvePathToDocument, resolveRelationshipSlug, resolversFor, variantsOf };
10
+ import { defineMappings } from "./pattern/define-mappings.mjs";
11
+ import { deriveLinkLabel } from "./pattern/derive-link-label.mjs";
12
+ export { type BaseResolvedLink, type BuildDiagnosticReason, type BuiltinLinkVariant, type CreateRouterArgs, type Diagnostic, type DiagnosticReason, type DocumentId, type FormatHref, type LabelLike, type LinkContextOf, type LinkDataOf, type LinkDeclaration, type LinkFieldData, type LinkableDocument, type OnDiagnostic, type PayloadCollectionMapping, type PayloadCollectionMappingResolved, type PayloadCollectionMatch, type PayloadDocument, type ResolveLinkDiagnosticReason, type ResolvePathDiagnosticReason, type ResolvePathWhere, type ResolveReference, type ResolveRelationshipSlugArgs, type ResolvedLinkOf, type ResolvedPath, type Router, createRouter, defineLinks, defineMappings, deriveLinkLabel, resolveRelationshipSlug };
package/dist/index.mjs CHANGED
@@ -1,15 +1,6 @@
1
- import { defineLinks, variantsOf } from "./pattern/define-links.mjs";
2
- import { DEFAULT_LOCALE_KEY } from "./pattern/types.mjs";
3
- import { isRootWildcard, resolveCollectionMapping, resolversFor } from "./pattern/resolver.mjs";
1
+ import { defineLinks } from "./pattern/define-links.mjs";
2
+ import { createRouter } from "./runtime/create-router.mjs";
3
+ import { resolveRelationshipSlug } from "./runtime/resolve-relationship-slug.mjs";
4
4
  import { defineMappings } from "./pattern/define-mappings.mjs";
5
5
  import { deriveLinkLabel } from "./pattern/derive-link-label.mjs";
6
- import { matchCollectionMappings } from "./pattern/matcher.mjs";
7
- import { DEFAULT_IDENTIFIER_FIELD, resolveParamQueryPath } from "./pattern/param-query-path.mjs";
8
- import "./pattern/index.mjs";
9
- import { buildHref, identityFormatHref } from "./runtime/build-href.mjs";
10
- import { buildPath } from "./runtime/build-path.mjs";
11
- import { isAvailableLink, resolveLink } from "./runtime/resolve-link.mjs";
12
- import { resolvePathToDocument } from "./runtime/resolve-path.mjs";
13
- import { resolveRelationshipSlug } from "./runtime/resolve-relationship-slug.mjs";
14
- import "./runtime/index.mjs";
15
- export { DEFAULT_IDENTIFIER_FIELD, DEFAULT_LOCALE_KEY, buildHref, buildPath, defineLinks, defineMappings, deriveLinkLabel, identityFormatHref, isAvailableLink, isRootWildcard, matchCollectionMappings, resolveCollectionMapping, resolveLink, resolveParamQueryPath, resolvePathToDocument, resolveRelationshipSlug, resolversFor, variantsOf };
6
+ export { createRouter, defineLinks, defineMappings, deriveLinkLabel, resolveRelationshipSlug };
@@ -0,0 +1,11 @@
1
+ import { Contributed, DEFAULT_IDENTIFIER_FIELD, DEFAULT_LOCALE_KEY, DeclaredLinkVariant, PayloadCollectionMappingMatch, PayloadCollectionMappingResolvers, PayloadCollectionMappingSpecificity, ResolvedLink } from "./pattern/types.mjs";
2
+ import { AnyLinkVariantDefinition, DataOfFields, LinkVariantDefinition, LinkVariantSource, LinkVariantSpec, VariantBuilder, variantsOf } from "./pattern/define-links.mjs";
3
+ import { BuildHrefArgs, buildHref, identityFormatHref } from "./runtime/build-href.mjs";
4
+ import { ResolveLinkArgs, isAvailableLink, resolveLink } from "./runtime/resolve-link.mjs";
5
+ import { matchCollectionMappings } from "./pattern/matcher.mjs";
6
+ import { ResolvePathToDocumentArgs, resolvePathToDocument } from "./runtime/resolve-path.mjs";
7
+ import { BuildPathArgs, buildPath } from "./runtime/build-path.mjs";
8
+ import { normaliseLinkNodeFields } from "./pattern/link-node.mjs";
9
+ import { RegisteredCollections, ResolveParamQueryPathArgs, resolveParamQueryPath } from "./pattern/param-query-path.mjs";
10
+ import { isRootWildcard, resolveCollectionMapping, resolversFor } from "./pattern/resolver.mjs";
11
+ export { type AnyLinkVariantDefinition, type BuildHrefArgs, type BuildPathArgs, type Contributed, DEFAULT_IDENTIFIER_FIELD, DEFAULT_LOCALE_KEY, type DataOfFields, type DeclaredLinkVariant, type LinkVariantDefinition, type LinkVariantSource, type LinkVariantSpec, type PayloadCollectionMappingMatch, type PayloadCollectionMappingResolvers, type PayloadCollectionMappingSpecificity, type RegisteredCollections, type ResolveLinkArgs, type ResolveParamQueryPathArgs, type ResolvePathToDocumentArgs, type ResolvedLink, type VariantBuilder, buildHref, buildPath, identityFormatHref, isAvailableLink, isRootWildcard, matchCollectionMappings, normaliseLinkNodeFields, resolveCollectionMapping, resolveLink, resolveParamQueryPath, resolvePathToDocument, resolversFor, variantsOf };
@@ -0,0 +1,11 @@
1
+ import { DEFAULT_IDENTIFIER_FIELD, DEFAULT_LOCALE_KEY } from "./pattern/types.mjs";
2
+ import { isRootWildcard, resolveCollectionMapping, resolversFor } from "./pattern/resolver.mjs";
3
+ import { buildHref, identityFormatHref } from "./runtime/build-href.mjs";
4
+ import { buildPath } from "./runtime/build-path.mjs";
5
+ import { variantsOf } from "./pattern/define-links.mjs";
6
+ import { isAvailableLink, resolveLink } from "./runtime/resolve-link.mjs";
7
+ import { matchCollectionMappings } from "./pattern/matcher.mjs";
8
+ import { resolveParamQueryPath } from "./pattern/param-query-path.mjs";
9
+ import { resolvePathToDocument } from "./runtime/resolve-path.mjs";
10
+ import { normaliseLinkNodeFields } from "./pattern/link-node.mjs";
11
+ export { DEFAULT_IDENTIFIER_FIELD, DEFAULT_LOCALE_KEY, buildHref, buildPath, identityFormatHref, isAvailableLink, isRootWildcard, matchCollectionMappings, normaliseLinkNodeFields, resolveCollectionMapping, resolveLink, resolveParamQueryPath, resolvePathToDocument, resolversFor, variantsOf };
@@ -1,4 +1,3 @@
1
- import { BaseResolvedLink, LinkFieldData, ResolvedLink } from "../pattern/types.mjs";
2
1
  import { LinkDeclaration, ResolvedLinkOf } from "../pattern/define-links.mjs";
3
2
  import { LinkFieldArgs } from "../config/link-field.mjs";
4
3
  import { ResolveLinkArgs } from "../runtime/resolve-link.mjs";
@@ -9,7 +8,7 @@ import { LinkFeature } from "@payloadcms/richtext-lexical";
9
8
  * destination, so the floating link editor shows a useful hover preview
10
9
  * instead of a blank one.
11
10
  */
12
- declare const linkLabelFeature: import("@payloadcms/richtext-lexical").FeatureProviderProviderServer<undefined, undefined, undefined>;
11
+ export declare const linkLabelFeature: import("@payloadcms/richtext-lexical").FeatureProviderProviderServer<undefined, undefined, undefined>;
13
12
  /**
14
13
  * Replaces Lexical's link fields with the wayfinder link field, so links
15
14
  * written in rich text route through the collection mapping exactly like links
@@ -17,36 +16,22 @@ declare const linkLabelFeature: import("@payloadcms/richtext-lexical").FeaturePr
17
16
  *
18
17
  * @param args The same arguments as the standalone link field.
19
18
  */
20
- declare const wayfinderLinkFeature: <TDeclaration extends LinkDeclaration = LinkDeclaration>(args: LinkFieldArgs<TDeclaration>) => ReturnType<typeof LinkFeature>;
21
- /**
22
- * The two shapes a link node's fields arrive in.
23
- *
24
- * A node written by {@link wayfinderLinkFeature} nests the group under `link`.
25
- * A node written by Lexical's stock link feature stores `linkType` and `doc`
26
- * at the top level, which existing content will still hold.
27
- */
28
- interface SerializedLinkFields {
29
- link?: LinkFieldData;
30
- linkType?: "custom" | "internal";
31
- url?: string | null;
32
- newTab?: boolean | null;
33
- doc?: {
34
- relationTo: string;
35
- value: string | {
36
- id: string;
37
- };
38
- } | null;
39
- }
19
+ export declare const wayfinderLinkFeature: <TDeclaration extends LinkDeclaration = LinkDeclaration>(args: LinkFieldArgs<TDeclaration>) => ReturnType<typeof LinkFeature>;
40
20
  /**
41
21
  * Resolves a rich-text link node to an href.
42
22
  *
23
+ * Takes the node's `fields` as `unknown`, because Lexical types them as an
24
+ * open record and a narrower parameter would make every converter cast. The
25
+ * two shapes a node can hold are unwrapped by
26
+ * {@link normaliseLinkNodeFields}, so a link written in rich text and a link
27
+ * authored in a block resolve through exactly the same call.
28
+ *
43
29
  * Returns null when the node points nowhere resolvable, so a converter can
44
30
  * render the text without an anchor rather than emitting a dead one.
45
31
  *
46
32
  * @param args The node's fields plus the usual link-resolution arguments.
47
33
  */
48
- declare const resolveLinkNode: <TExtra = object, TDeclaration extends LinkDeclaration = LinkDeclaration>(args: Omit<ResolveLinkArgs<TExtra, TDeclaration>, "link"> & {
49
- fields: SerializedLinkFields | undefined;
50
- }) => BaseResolvedLink | ResolvedLink<object> | ResolvedLinkOf<TDeclaration> | null;
51
- //#endregion
52
- export { linkLabelFeature, resolveLinkNode, wayfinderLinkFeature };
34
+ export declare const resolveLinkNode: <TDeclaration extends LinkDeclaration = LinkDeclaration>(args: Omit<ResolveLinkArgs<TDeclaration>, "link"> & {
35
+ fields: unknown;
36
+ }) => ResolvedLinkOf<TDeclaration> | null;
37
+ //#endregion
@@ -1,4 +1,5 @@
1
1
  import { resolveLink } from "../runtime/resolve-link.mjs";
2
+ import { normaliseLinkNodeFields } from "../pattern/link-node.mjs";
2
3
  import { linkField } from "../config/link-field.mjs";
3
4
  import { LinkFeature, createServerFeature } from "@payloadcms/richtext-lexical";
4
5
  //#region src/lexical/index.ts
@@ -22,36 +23,21 @@ import { LinkFeature, createServerFeature } from "@payloadcms/richtext-lexical";
22
23
  * @param args The same arguments as the standalone link field.
23
24
  */ const wayfinderLinkFeature = (args) => LinkFeature({ fields: () => [linkField(args)] });
24
25
  /**
25
- * Normalises a link node's fields into the link field's own shape.
26
- *
27
- * @param fields The node's `fields` object.
28
- */ const normaliseNodeFields = (fields) => {
29
- if (!fields) return;
30
- if (fields.link) return fields.link;
31
- if (fields.linkType === "internal" && fields.doc) return {
32
- type: "reference",
33
- reference: fields.doc,
34
- newTab: fields.newTab ?? null
35
- };
36
- if (fields.url) return {
37
- type: "custom",
38
- url: fields.url,
39
- newTab: fields.newTab ?? null
40
- };
41
- };
42
- /**
43
26
  * Resolves a rich-text link node to an href.
44
27
  *
28
+ * Takes the node's `fields` as `unknown`, because Lexical types them as an
29
+ * open record and a narrower parameter would make every converter cast. The
30
+ * two shapes a node can hold are unwrapped by
31
+ * {@link normaliseLinkNodeFields}, so a link written in rich text and a link
32
+ * authored in a block resolve through exactly the same call.
33
+ *
45
34
  * Returns null when the node points nowhere resolvable, so a converter can
46
35
  * render the text without an anchor rather than emitting a dead one.
47
36
  *
48
37
  * @param args The node's fields plus the usual link-resolution arguments.
49
- */ const resolveLinkNode = (args) => {
50
- const link = normaliseNodeFields(args.fields);
51
- return resolveLink({
52
- ...args,
53
- link
54
- });
55
- };
38
+ */ const resolveLinkNode = (args) => resolveLink({
39
+ ...args,
40
+ link: normaliseLinkNodeFields(args.fields)
41
+ });
56
42
  //#endregion
57
43
  export { linkLabelFeature, resolveLinkNode, wayfinderLinkFeature };