@payloadcms/plugin-search 3.70.0 → 3.71.0-internal.727c7a4

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import type { Config } from 'payload';
2
2
  import type { SearchPluginConfig } from './types.js';
3
- export declare const searchPlugin: (incomingPluginConfig: SearchPluginConfig) => (config: Config) => Config;
3
+ export declare const searchPlugin: <ConfigTypes = unknown>(incomingPluginConfig: SearchPluginConfig<ConfigTypes>) => (config: Config) => Config;
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA6B,MAAM,EAAE,MAAM,SAAS,CAAA;AAEhE,OAAO,KAAK,EAA+B,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAQjF,eAAO,MAAM,YAAY,yBACA,kBAAkB,cAChC,MAAM,KAAG,MA8EjB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA6B,MAAM,EAAE,MAAM,SAAS,CAAA;AAEhE,OAAO,KAAK,EAA+B,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAQjF,eAAO,MAAM,YAAY,GACtB,WAAW,kCAAkC,kBAAkB,CAAC,WAAW,CAAC,cACpE,MAAM,KAAG,MAuEjB,CAAA"}
package/dist/index.js CHANGED
@@ -8,7 +8,6 @@ export const searchPlugin = (incomingPluginConfig)=>(config)=>{
8
8
  const shouldLocalize = typeof incomingPluginConfig.localize === 'boolean' ? incomingPluginConfig.localize : Boolean(config.localization);
9
9
  incomingPluginConfig.localize = shouldLocalize;
10
10
  if (collections) {
11
- const locales = config.localization ? config.localization.locales.map((localeConfig)=>typeof localeConfig === 'string' ? localeConfig : localeConfig.code) : [];
12
11
  const labels = Object.fromEntries(collections.filter(({ slug })=>incomingPluginConfig.collections?.includes(slug)).map((collection)=>[
13
12
  collection.slug,
14
13
  collection.labels
@@ -17,7 +16,6 @@ export const searchPlugin = (incomingPluginConfig)=>(config)=>{
17
16
  // write any config defaults here
18
17
  deleteDrafts: true,
19
18
  labels,
20
- locales,
21
19
  reindexBatchSize: incomingPluginConfig?.reindexBatchSize || 50,
22
20
  syncDrafts: false,
23
21
  ...incomingPluginConfig
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CollectionAfterChangeHook, Config } from 'payload'\n\nimport type { SanitizedSearchPluginConfig, SearchPluginConfig } from './types.js'\n\nimport { deleteFromSearch } from './Search/hooks/deleteFromSearch.js'\nimport { syncWithSearch } from './Search/hooks/syncWithSearch.js'\nimport { generateSearchCollection } from './Search/index.js'\n\ntype CollectionAfterChangeHookArgs = Parameters<CollectionAfterChangeHook>[0]\n\nexport const searchPlugin =\n (incomingPluginConfig: SearchPluginConfig) =>\n (config: Config): Config => {\n const { collections } = config\n\n // If the user defines `localize` to either true or false, use that\n // Otherwise, set it based on if their config has localization enabled or disabled\n const shouldLocalize =\n typeof incomingPluginConfig.localize === 'boolean'\n ? incomingPluginConfig.localize\n : Boolean(config.localization)\n incomingPluginConfig.localize = shouldLocalize\n\n if (collections) {\n const locales = config.localization\n ? config.localization.locales.map((localeConfig) =>\n typeof localeConfig === 'string' ? localeConfig : localeConfig.code,\n )\n : []\n\n const labels = Object.fromEntries(\n collections\n .filter(({ slug }) => incomingPluginConfig.collections?.includes(slug))\n .map((collection) => [collection.slug, collection.labels]),\n )\n\n const pluginConfig: SanitizedSearchPluginConfig = {\n // write any config defaults here\n deleteDrafts: true,\n labels,\n locales,\n reindexBatchSize: incomingPluginConfig?.reindexBatchSize || 50,\n syncDrafts: false,\n ...incomingPluginConfig,\n }\n\n // add afterChange and afterDelete hooks to every search-enabled collection\n const collectionsWithSearchHooks = config?.collections\n ?.map((collection) => {\n const { hooks: existingHooks } = collection\n\n const enabledCollections = pluginConfig.collections || []\n const isEnabled = enabledCollections.indexOf(collection.slug) > -1\n if (isEnabled) {\n return {\n ...collection,\n hooks: {\n ...collection.hooks,\n afterChange: [\n ...(existingHooks?.afterChange || []),\n async (args: CollectionAfterChangeHookArgs) => {\n await syncWithSearch({\n ...args,\n collection: collection.slug,\n pluginConfig,\n })\n },\n ],\n beforeDelete: [\n ...(existingHooks?.beforeDelete || []),\n deleteFromSearch(pluginConfig),\n ],\n },\n }\n }\n\n return collection\n })\n .filter(Boolean)\n\n return {\n ...config,\n collections: [\n ...(collectionsWithSearchHooks || []),\n generateSearchCollection(pluginConfig),\n ],\n }\n }\n\n return config\n }\n"],"names":["deleteFromSearch","syncWithSearch","generateSearchCollection","searchPlugin","incomingPluginConfig","config","collections","shouldLocalize","localize","Boolean","localization","locales","map","localeConfig","code","labels","Object","fromEntries","filter","slug","includes","collection","pluginConfig","deleteDrafts","reindexBatchSize","syncDrafts","collectionsWithSearchHooks","hooks","existingHooks","enabledCollections","isEnabled","indexOf","afterChange","args","beforeDelete"],"mappings":"AAIA,SAASA,gBAAgB,QAAQ,qCAAoC;AACrE,SAASC,cAAc,QAAQ,mCAAkC;AACjE,SAASC,wBAAwB,QAAQ,oBAAmB;AAI5D,OAAO,MAAMC,eACX,CAACC,uBACD,CAACC;QACC,MAAM,EAAEC,WAAW,EAAE,GAAGD;QAExB,mEAAmE;QACnE,kFAAkF;QAClF,MAAME,iBACJ,OAAOH,qBAAqBI,QAAQ,KAAK,YACrCJ,qBAAqBI,QAAQ,GAC7BC,QAAQJ,OAAOK,YAAY;QACjCN,qBAAqBI,QAAQ,GAAGD;QAEhC,IAAID,aAAa;YACf,MAAMK,UAAUN,OAAOK,YAAY,GAC/BL,OAAOK,YAAY,CAACC,OAAO,CAACC,GAAG,CAAC,CAACC,eAC/B,OAAOA,iBAAiB,WAAWA,eAAeA,aAAaC,IAAI,IAErE,EAAE;YAEN,MAAMC,SAASC,OAAOC,WAAW,CAC/BX,YACGY,MAAM,CAAC,CAAC,EAAEC,IAAI,EAAE,GAAKf,qBAAqBE,WAAW,EAAEc,SAASD,OAChEP,GAAG,CAAC,CAACS,aAAe;oBAACA,WAAWF,IAAI;oBAAEE,WAAWN,MAAM;iBAAC;YAG7D,MAAMO,eAA4C;gBAChD,iCAAiC;gBACjCC,cAAc;gBACdR;gBACAJ;gBACAa,kBAAkBpB,sBAAsBoB,oBAAoB;gBAC5DC,YAAY;gBACZ,GAAGrB,oBAAoB;YACzB;YAEA,2EAA2E;YAC3E,MAAMsB,6BAA6BrB,QAAQC,aACvCM,IAAI,CAACS;gBACL,MAAM,EAAEM,OAAOC,aAAa,EAAE,GAAGP;gBAEjC,MAAMQ,qBAAqBP,aAAahB,WAAW,IAAI,EAAE;gBACzD,MAAMwB,YAAYD,mBAAmBE,OAAO,CAACV,WAAWF,IAAI,IAAI,CAAC;gBACjE,IAAIW,WAAW;oBACb,OAAO;wBACL,GAAGT,UAAU;wBACbM,OAAO;4BACL,GAAGN,WAAWM,KAAK;4BACnBK,aAAa;mCACPJ,eAAeI,eAAe,EAAE;gCACpC,OAAOC;oCACL,MAAMhC,eAAe;wCACnB,GAAGgC,IAAI;wCACPZ,YAAYA,WAAWF,IAAI;wCAC3BG;oCACF;gCACF;6BACD;4BACDY,cAAc;mCACRN,eAAeM,gBAAgB,EAAE;gCACrClC,iBAAiBsB;6BAClB;wBACH;oBACF;gBACF;gBAEA,OAAOD;YACT,GACCH,OAAOT;YAEV,OAAO;gBACL,GAAGJ,MAAM;gBACTC,aAAa;uBACPoB,8BAA8B,EAAE;oBACpCxB,yBAAyBoB;iBAC1B;YACH;QACF;QAEA,OAAOjB;IACT,EAAC"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CollectionAfterChangeHook, Config } from 'payload'\n\nimport type { SanitizedSearchPluginConfig, SearchPluginConfig } from './types.js'\n\nimport { deleteFromSearch } from './Search/hooks/deleteFromSearch.js'\nimport { syncWithSearch } from './Search/hooks/syncWithSearch.js'\nimport { generateSearchCollection } from './Search/index.js'\n\ntype CollectionAfterChangeHookArgs = Parameters<CollectionAfterChangeHook>[0]\n\nexport const searchPlugin =\n <ConfigTypes = unknown>(incomingPluginConfig: SearchPluginConfig<ConfigTypes>) =>\n (config: Config): Config => {\n const { collections } = config\n\n // If the user defines `localize` to either true or false, use that\n // Otherwise, set it based on if their config has localization enabled or disabled\n const shouldLocalize =\n typeof incomingPluginConfig.localize === 'boolean'\n ? incomingPluginConfig.localize\n : Boolean(config.localization)\n incomingPluginConfig.localize = shouldLocalize\n\n if (collections) {\n const labels = Object.fromEntries(\n collections\n .filter(({ slug }) => incomingPluginConfig.collections?.includes(slug))\n .map((collection) => [collection.slug, collection.labels]),\n )\n\n const pluginConfig: SanitizedSearchPluginConfig<ConfigTypes> = {\n // write any config defaults here\n deleteDrafts: true,\n labels,\n reindexBatchSize: incomingPluginConfig?.reindexBatchSize || 50,\n syncDrafts: false,\n ...incomingPluginConfig,\n }\n\n // add afterChange and afterDelete hooks to every search-enabled collection\n const collectionsWithSearchHooks = config?.collections\n ?.map((collection) => {\n const { hooks: existingHooks } = collection\n\n const enabledCollections = pluginConfig.collections || []\n const isEnabled = enabledCollections.indexOf(collection.slug) > -1\n if (isEnabled) {\n return {\n ...collection,\n hooks: {\n ...collection.hooks,\n afterChange: [\n ...(existingHooks?.afterChange || []),\n async (args: CollectionAfterChangeHookArgs) => {\n await syncWithSearch({\n ...args,\n collection: collection.slug,\n pluginConfig,\n })\n },\n ],\n beforeDelete: [\n ...(existingHooks?.beforeDelete || []),\n deleteFromSearch(pluginConfig),\n ],\n },\n }\n }\n\n return collection\n })\n .filter(Boolean)\n\n return {\n ...config,\n collections: [\n ...(collectionsWithSearchHooks || []),\n generateSearchCollection(pluginConfig),\n ],\n }\n }\n\n return config\n }\n"],"names":["deleteFromSearch","syncWithSearch","generateSearchCollection","searchPlugin","incomingPluginConfig","config","collections","shouldLocalize","localize","Boolean","localization","labels","Object","fromEntries","filter","slug","includes","map","collection","pluginConfig","deleteDrafts","reindexBatchSize","syncDrafts","collectionsWithSearchHooks","hooks","existingHooks","enabledCollections","isEnabled","indexOf","afterChange","args","beforeDelete"],"mappings":"AAIA,SAASA,gBAAgB,QAAQ,qCAAoC;AACrE,SAASC,cAAc,QAAQ,mCAAkC;AACjE,SAASC,wBAAwB,QAAQ,oBAAmB;AAI5D,OAAO,MAAMC,eACX,CAAwBC,uBACxB,CAACC;QACC,MAAM,EAAEC,WAAW,EAAE,GAAGD;QAExB,mEAAmE;QACnE,kFAAkF;QAClF,MAAME,iBACJ,OAAOH,qBAAqBI,QAAQ,KAAK,YACrCJ,qBAAqBI,QAAQ,GAC7BC,QAAQJ,OAAOK,YAAY;QACjCN,qBAAqBI,QAAQ,GAAGD;QAEhC,IAAID,aAAa;YACf,MAAMK,SAASC,OAAOC,WAAW,CAC/BP,YACGQ,MAAM,CAAC,CAAC,EAAEC,IAAI,EAAE,GAAKX,qBAAqBE,WAAW,EAAEU,SAASD,OAChEE,GAAG,CAAC,CAACC,aAAe;oBAACA,WAAWH,IAAI;oBAAEG,WAAWP,MAAM;iBAAC;YAG7D,MAAMQ,eAAyD;gBAC7D,iCAAiC;gBACjCC,cAAc;gBACdT;gBACAU,kBAAkBjB,sBAAsBiB,oBAAoB;gBAC5DC,YAAY;gBACZ,GAAGlB,oBAAoB;YACzB;YAEA,2EAA2E;YAC3E,MAAMmB,6BAA6BlB,QAAQC,aACvCW,IAAI,CAACC;gBACL,MAAM,EAAEM,OAAOC,aAAa,EAAE,GAAGP;gBAEjC,MAAMQ,qBAAqBP,aAAab,WAAW,IAAI,EAAE;gBACzD,MAAMqB,YAAYD,mBAAmBE,OAAO,CAACV,WAAWH,IAAI,IAAI,CAAC;gBACjE,IAAIY,WAAW;oBACb,OAAO;wBACL,GAAGT,UAAU;wBACbM,OAAO;4BACL,GAAGN,WAAWM,KAAK;4BACnBK,aAAa;mCACPJ,eAAeI,eAAe,EAAE;gCACpC,OAAOC;oCACL,MAAM7B,eAAe;wCACnB,GAAG6B,IAAI;wCACPZ,YAAYA,WAAWH,IAAI;wCAC3BI;oCACF;gCACF;6BACD;4BACDY,cAAc;mCACRN,eAAeM,gBAAgB,EAAE;gCACrC/B,iBAAiBmB;6BAClB;wBACH;oBACF;gBACF;gBAEA,OAAOD;YACT,GACCJ,OAAOL;YAEV,OAAO;gBACL,GAAGJ,MAAM;gBACTC,aAAa;uBACPiB,8BAA8B,EAAE;oBACpCrB,yBAAyBiB;iBAC1B;YACH;QACF;QAEA,OAAOd;IACT,EAAC"}
package/dist/types.d.ts CHANGED
@@ -18,7 +18,15 @@ export type BeforeSync = (args: {
18
18
  export type FieldsOverride = (args: {
19
19
  defaultFields: Field[];
20
20
  }) => Field[];
21
- export type SearchPluginConfig = {
21
+ export type SkipSyncFunction<ConfigTypes = unknown> = (args: {
22
+ collectionSlug: string;
23
+ doc: any;
24
+ locale: ConfigTypes extends {
25
+ locale: unknown;
26
+ } ? ConfigTypes['locale'] : string | undefined;
27
+ req: PayloadRequest;
28
+ }) => boolean | Promise<boolean>;
29
+ export type SearchPluginConfig<ConfigTypes = unknown> = {
22
30
  /**
23
31
  * @deprecated
24
32
  * This plugin gets the api route from the config directly and does not need to be passed in.
@@ -47,6 +55,27 @@ export type SearchPluginConfig = {
47
55
  searchOverrides?: {
48
56
  fields?: FieldsOverride;
49
57
  } & Partial<Omit<CollectionConfig, 'fields'>>;
58
+ /**
59
+ * Determine whether to skip syncing a document for a specific locale.
60
+ * Useful for multi-tenant applications, conditional indexing, or any scenario where
61
+ * sync behavior should vary by locale, document, or other factors.
62
+ *
63
+ * @default undefined - All configured locales will be synced
64
+ *
65
+ * @example
66
+ * // Skip syncing based on document's tenant settings
67
+ * skipSync: async ({ locale, req, doc, collectionSlug }) => {
68
+ * // For non-localized collections, locale will be undefined
69
+ * if (!locale) return false
70
+ *
71
+ * const tenant = await req.payload.findByID({
72
+ * collection: 'tenants',
73
+ * id: doc.tenant.id
74
+ * })
75
+ * return !tenant.allowedLocales.includes(locale)
76
+ * }
77
+ */
78
+ skipSync?: SkipSyncFunction<ConfigTypes>;
50
79
  /**
51
80
  * Controls whether drafts are synced to the search index
52
81
  *
@@ -60,14 +89,13 @@ export type CollectionLabels = {
60
89
  export type ResolvedCollectionLabels = {
61
90
  [collection: string]: StaticLabel;
62
91
  };
63
- export type SearchPluginConfigWithLocales = {
92
+ export type SearchPluginConfigWithLocales<ConfigTypes = unknown> = {
64
93
  labels?: CollectionLabels;
65
- locales?: string[];
66
- } & SearchPluginConfig;
67
- export type SanitizedSearchPluginConfig = {
94
+ } & SearchPluginConfig<ConfigTypes>;
95
+ export type SanitizedSearchPluginConfig<ConfigTypes = unknown> = {
68
96
  reindexBatchSize: number;
69
97
  syncDrafts: boolean;
70
- } & SearchPluginConfigWithLocales;
98
+ } & SearchPluginConfigWithLocales<ConfigTypes>;
71
99
  export type SyncWithSearchArgs = {
72
100
  collection: string;
73
101
  pluginConfig: SanitizedSearchPluginConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type {\n CollectionAfterChangeHook,\n CollectionBeforeDeleteHook,\n CollectionConfig,\n Field,\n Locale,\n Payload,\n PayloadRequest,\n StaticLabel,\n} from 'payload'\n\nexport type DocToSync = {\n [key: string]: any\n doc: {\n relationTo: string\n value: string\n }\n title: string\n}\n\nexport type BeforeSync = (args: {\n originalDoc: {\n [key: string]: any\n }\n payload: Payload\n req: PayloadRequest\n searchDoc: DocToSync\n}) => DocToSync | Promise<DocToSync>\n\nexport type FieldsOverride = (args: { defaultFields: Field[] }) => Field[]\n\nexport type SearchPluginConfig = {\n /**\n * @deprecated\n * This plugin gets the api route from the config directly and does not need to be passed in.\n * As long as you have `routes.api` set in your Payload config, the plugin will use that.\n * This property will be removed in the next major version.\n */\n apiBasePath?: string\n beforeSync?: BeforeSync\n collections?: string[]\n defaultPriorities?: {\n [collection: string]: ((doc: any) => number | Promise<number>) | number\n }\n /**\n * Controls whether drafts are deleted from the search index\n *\n * @default true\n */\n deleteDrafts?: boolean\n localize?: boolean\n /**\n * We use batching when re-indexing large collections. You can control the amount of items per batch, lower numbers should help with memory.\n *\n * @default 50\n */\n reindexBatchSize?: number\n searchOverrides?: { fields?: FieldsOverride } & Partial<Omit<CollectionConfig, 'fields'>>\n /**\n * Controls whether drafts are synced to the search index\n *\n * @default false\n */\n syncDrafts?: boolean\n}\n\nexport type CollectionLabels = {\n [collection: string]: CollectionConfig['labels']\n}\n\nexport type ResolvedCollectionLabels = {\n [collection: string]: StaticLabel\n}\n\nexport type SearchPluginConfigWithLocales = {\n labels?: CollectionLabels\n locales?: string[]\n} & SearchPluginConfig\n\nexport type SanitizedSearchPluginConfig = {\n reindexBatchSize: number\n syncDrafts: boolean\n} & SearchPluginConfigWithLocales\n\nexport type SyncWithSearchArgs = {\n collection: string\n pluginConfig: SanitizedSearchPluginConfig\n} & Omit<Parameters<CollectionAfterChangeHook>[0], 'collection'>\n\nexport type SyncDocArgs = {\n locale?: Locale['code']\n onSyncError?: () => void\n} & Omit<SyncWithSearchArgs, 'context' | 'previousDoc'>\n\n// Extend the `CollectionAfterChangeHook` with more function args\n// Convert the `collection` arg from `SanitizedCollectionConfig` to a string\nexport type SyncWithSearch = (Args: SyncWithSearchArgs) => ReturnType<CollectionAfterChangeHook>\n\nexport type DeleteFromSearch = (args: SearchPluginConfig) => CollectionBeforeDeleteHook\n"],"names":[],"mappings":"AAkGA,WAAuF"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type {\n CollectionAfterChangeHook,\n CollectionBeforeDeleteHook,\n CollectionConfig,\n Field,\n Locale,\n Payload,\n PayloadRequest,\n StaticLabel,\n} from 'payload'\n\nexport type DocToSync = {\n [key: string]: any\n doc: {\n relationTo: string\n value: string\n }\n title: string\n}\n\nexport type BeforeSync = (args: {\n originalDoc: {\n [key: string]: any\n }\n payload: Payload\n req: PayloadRequest\n searchDoc: DocToSync\n}) => DocToSync | Promise<DocToSync>\n\nexport type FieldsOverride = (args: { defaultFields: Field[] }) => Field[]\n\nexport type SkipSyncFunction<ConfigTypes = unknown> = (args: {\n collectionSlug: string\n doc: any\n locale: ConfigTypes extends { locale: unknown } ? ConfigTypes['locale'] : string | undefined\n req: PayloadRequest\n}) => boolean | Promise<boolean>\n\nexport type SearchPluginConfig<ConfigTypes = unknown> = {\n /**\n * @deprecated\n * This plugin gets the api route from the config directly and does not need to be passed in.\n * As long as you have `routes.api` set in your Payload config, the plugin will use that.\n * This property will be removed in the next major version.\n */\n apiBasePath?: string\n beforeSync?: BeforeSync\n collections?: string[]\n defaultPriorities?: {\n [collection: string]: ((doc: any) => number | Promise<number>) | number\n }\n /**\n * Controls whether drafts are deleted from the search index\n *\n * @default true\n */\n deleteDrafts?: boolean\n localize?: boolean\n /**\n * We use batching when re-indexing large collections. You can control the amount of items per batch, lower numbers should help with memory.\n *\n * @default 50\n */\n reindexBatchSize?: number\n searchOverrides?: { fields?: FieldsOverride } & Partial<Omit<CollectionConfig, 'fields'>>\n /**\n * Determine whether to skip syncing a document for a specific locale.\n * Useful for multi-tenant applications, conditional indexing, or any scenario where\n * sync behavior should vary by locale, document, or other factors.\n *\n * @default undefined - All configured locales will be synced\n *\n * @example\n * // Skip syncing based on document's tenant settings\n * skipSync: async ({ locale, req, doc, collectionSlug }) => {\n * // For non-localized collections, locale will be undefined\n * if (!locale) return false\n *\n * const tenant = await req.payload.findByID({\n * collection: 'tenants',\n * id: doc.tenant.id\n * })\n * return !tenant.allowedLocales.includes(locale)\n * }\n */\n skipSync?: SkipSyncFunction<ConfigTypes>\n /**\n * Controls whether drafts are synced to the search index\n *\n * @default false\n */\n syncDrafts?: boolean\n}\n\nexport type CollectionLabels = {\n [collection: string]: CollectionConfig['labels']\n}\n\nexport type ResolvedCollectionLabels = {\n [collection: string]: StaticLabel\n}\n\nexport type SearchPluginConfigWithLocales<ConfigTypes = unknown> = {\n labels?: CollectionLabels\n} & SearchPluginConfig<ConfigTypes>\n\nexport type SanitizedSearchPluginConfig<ConfigTypes = unknown> = {\n reindexBatchSize: number\n syncDrafts: boolean\n} & SearchPluginConfigWithLocales<ConfigTypes>\n\nexport type SyncWithSearchArgs = {\n collection: string\n pluginConfig: SanitizedSearchPluginConfig\n} & Omit<Parameters<CollectionAfterChangeHook>[0], 'collection'>\n\nexport type SyncDocArgs = {\n locale?: Locale['code']\n onSyncError?: () => void\n} & Omit<SyncWithSearchArgs, 'context' | 'previousDoc'>\n\n// Extend the `CollectionAfterChangeHook` with more function args\n// Convert the `collection` arg from `SanitizedCollectionConfig` to a string\nexport type SyncWithSearch = (Args: SyncWithSearchArgs) => ReturnType<CollectionAfterChangeHook>\n\nexport type DeleteFromSearch = (args: SearchPluginConfig) => CollectionBeforeDeleteHook\n"],"names":[],"mappings":"AA6HA,WAAuF"}
@@ -1 +1 @@
1
- {"version":3,"file":"generateReindexHandler.d.ts","sourceRoot":"","sources":["../../src/utilities/generateReindexHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;AAWpD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAA;AAS9D,eAAO,MAAM,sBAAsB,iBAClB,2BAA2B,KAAG,cAiL5C,CAAA"}
1
+ {"version":3,"file":"generateReindexHandler.d.ts","sourceRoot":"","sources":["../../src/utilities/generateReindexHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;AAWpD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAA;AAS9D,eAAO,MAAM,sBAAsB,iBAClB,2BAA2B,KAAG,cA+M5C,CAAA"}
@@ -11,10 +11,7 @@ export const generateReindexHandler = (pluginConfig)=>async (req)=>{
11
11
  const t = req.t;
12
12
  const searchSlug = pluginConfig?.searchOverrides?.slug || 'search';
13
13
  const searchCollections = pluginConfig?.collections || [];
14
- const reindexLocales = pluginConfig?.locales?.length ? pluginConfig.locales : req.locale ? [
15
- req.locale
16
- ] : [];
17
- const validatePermissions = async ()=>{
14
+ async function validatePermissions() {
18
15
  const accessResults = await getAccessResults({
19
16
  req
20
17
  });
@@ -45,8 +42,8 @@ export const generateReindexHandler = (pluginConfig)=>async (req)=>{
45
42
  isValid: false,
46
43
  message: t('error:notAllowedToPerformAction')
47
44
  };
48
- };
49
- const validateCollections = ()=>{
45
+ }
46
+ function validateCollections() {
50
47
  const collectionsAreValid = collections.every((col)=>searchCollections.includes(col));
51
48
  return collections.length && collectionsAreValid ? {
52
49
  isValid: true
@@ -56,7 +53,7 @@ export const generateReindexHandler = (pluginConfig)=>async (req)=>{
56
53
  args: `'collections'`
57
54
  })
58
55
  };
59
- };
56
+ }
60
57
  const headers = headersWithCors({
61
58
  headers: new Headers(),
62
59
  req
@@ -94,7 +91,7 @@ export const generateReindexHandler = (pluginConfig)=>async (req)=>{
94
91
  let aggregateDocsWithDrafts = 0;
95
92
  let aggregateErrors = 0;
96
93
  let aggregateDocs = 0;
97
- const countDocuments = async (collection, drafts)=>{
94
+ async function countDocuments(collection, drafts) {
98
95
  const { totalDocs } = await payload.count({
99
96
  collection,
100
97
  ...defaultLocalApiProps,
@@ -102,8 +99,8 @@ export const generateReindexHandler = (pluginConfig)=>async (req)=>{
102
99
  where: drafts ? undefined : whereStatusPublished
103
100
  });
104
101
  return totalDocs;
105
- };
106
- const deleteIndexes = async (collection)=>{
102
+ }
103
+ async function deleteIndexes(collection) {
107
104
  await payload.delete({
108
105
  collection: searchSlug,
109
106
  depth: 0,
@@ -117,29 +114,58 @@ export const generateReindexHandler = (pluginConfig)=>async (req)=>{
117
114
  },
118
115
  ...defaultLocalApiProps
119
116
  });
120
- };
121
- const reindexCollection = async (collection)=>{
117
+ }
118
+ async function reindexCollection(collection) {
122
119
  const draftsEnabled = Boolean(payload.collections[collection]?.config.versions?.drafts);
123
120
  const totalDocsWithDrafts = await countDocuments(collection, true);
124
121
  const totalDocs = syncDrafts || !draftsEnabled ? totalDocsWithDrafts : await countDocuments(collection, !draftsEnabled);
125
122
  const totalBatches = Math.ceil(totalDocs / batchSize);
126
123
  aggregateDocsWithDrafts += totalDocsWithDrafts;
127
124
  aggregateDocs += totalDocs;
128
- for(let j = 0; j < reindexLocales.length; j++){
129
- // create first index, then we update with other locales accordingly
130
- const operation = j === 0 ? 'create' : 'update';
131
- const localeToSync = reindexLocales[j];
132
- for(let i = 0; i < totalBatches; i++){
133
- const { docs } = await payload.find({
134
- collection,
135
- depth: 0,
136
- limit: batchSize,
137
- locale: localeToSync,
138
- page: i + 1,
139
- where: syncDrafts || !draftsEnabled ? undefined : whereStatusPublished,
140
- ...defaultLocalApiProps
141
- });
142
- for (const doc of docs){
125
+ // Loop through batches, then documents, then locales per document
126
+ for(let i = 0; i < totalBatches; i++){
127
+ const defaultLocale = req.payload.config.localization ? req.payload.config.localization.defaultLocale : req.locale;
128
+ const { docs } = await payload.find({
129
+ collection,
130
+ depth: 0,
131
+ limit: batchSize,
132
+ locale: defaultLocale,
133
+ page: i + 1,
134
+ where: syncDrafts || !draftsEnabled ? undefined : whereStatusPublished,
135
+ ...defaultLocalApiProps
136
+ });
137
+ for (const doc of docs){
138
+ // Get all configured locales
139
+ // If no localization, use [undefined] to sync once without a locale
140
+ const allLocales = req.payload.config.localization ? req.payload.config.localization.localeCodes : [
141
+ undefined
142
+ ];
143
+ // Loop through all locales and check each one
144
+ let firstAllowedLocale = true;
145
+ for (const localeToSync of allLocales){
146
+ // Check if we should skip this locale for this document
147
+ let shouldSkip = false;
148
+ if (typeof pluginConfig.skipSync === 'function') {
149
+ try {
150
+ shouldSkip = await pluginConfig.skipSync({
151
+ collectionSlug: collection,
152
+ doc,
153
+ locale: localeToSync,
154
+ req
155
+ });
156
+ } catch (err) {
157
+ req.payload.logger.error({
158
+ err,
159
+ msg: 'Search plugin: Error executing skipSync. Proceeding with sync.'
160
+ });
161
+ }
162
+ }
163
+ if (shouldSkip) {
164
+ continue; // Skip this locale
165
+ }
166
+ // Sync this locale (create first index, then update with other locales accordingly)
167
+ const operation = firstAllowedLocale ? 'create' : 'update';
168
+ firstAllowedLocale = false;
143
169
  await syncDocAsSearchIndex({
144
170
  collection,
145
171
  data: doc,
@@ -153,7 +179,7 @@ export const generateReindexHandler = (pluginConfig)=>async (req)=>{
153
179
  }
154
180
  }
155
181
  }
156
- };
182
+ }
157
183
  const shouldCommit = await initTransaction(req);
158
184
  try {
159
185
  const promises = collections.map(async (collection)=>{
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/generateReindexHandler.ts"],"sourcesContent":["import type { PayloadHandler, Where } from 'payload'\n\nimport {\n addLocalesToRequestFromData,\n commitTransaction,\n getAccessResults,\n headersWithCors,\n initTransaction,\n killTransaction,\n} from 'payload'\n\nimport type { SanitizedSearchPluginConfig } from '../types.js'\n\nimport { syncDocAsSearchIndex } from './syncDocAsSearchIndex.js'\n\ntype ValidationResult = {\n isValid: boolean\n message?: string\n}\n\nexport const generateReindexHandler =\n (pluginConfig: SanitizedSearchPluginConfig): PayloadHandler =>\n async (req) => {\n addLocalesToRequestFromData(req)\n if (!req.json) {\n return new Response('Req.json is undefined', { status: 400 })\n }\n const { collections = [] } = (await req.json()) as { collections: string[] }\n const t = req.t\n\n const searchSlug = pluginConfig?.searchOverrides?.slug || 'search'\n const searchCollections = pluginConfig?.collections || []\n const reindexLocales = pluginConfig?.locales?.length\n ? pluginConfig.locales\n : req.locale\n ? [req.locale]\n : []\n\n const validatePermissions = async (): Promise<ValidationResult> => {\n const accessResults = await getAccessResults({ req })\n const searchAccessResults = accessResults.collections?.[searchSlug]\n if (!searchAccessResults) {\n return { isValid: false, message: t('error:notAllowedToPerformAction') }\n }\n\n const permissions = [searchAccessResults.delete, searchAccessResults.update]\n // plugin doesn't allow create by default:\n // if user provided, then add it to check\n if (pluginConfig.searchOverrides?.access?.create) {\n permissions.push(searchAccessResults.create)\n }\n // plugin allows reads by anyone by default:\n // so if user provided, then add to check\n if (pluginConfig.searchOverrides?.access?.read) {\n permissions.push(searchAccessResults.read)\n }\n return permissions.every(Boolean)\n ? { isValid: true }\n : { isValid: false, message: t('error:notAllowedToPerformAction') }\n }\n\n const validateCollections = (): ValidationResult => {\n const collectionsAreValid = collections.every((col) => searchCollections.includes(col))\n return collections.length && collectionsAreValid\n ? { isValid: true }\n : { isValid: false, message: t('error:invalidRequestArgs', { args: `'collections'` }) }\n }\n\n const headers = headersWithCors({\n headers: new Headers(),\n req,\n })\n\n const { isValid: hasPermissions, message: permissionError } = await validatePermissions()\n if (!hasPermissions) {\n return Response.json({ message: permissionError }, { headers, status: 401 })\n }\n\n const { isValid: validCollections, message: collectionError } = validateCollections()\n if (!validCollections) {\n return Response.json({ message: collectionError }, { headers, status: 400 })\n }\n\n const payload = req.payload\n const { reindexBatchSize: batchSize, syncDrafts } = pluginConfig\n\n const defaultLocalApiProps = {\n overrideAccess: false,\n req,\n user: req.user,\n }\n const whereStatusPublished: Where = {\n _status: {\n equals: 'published',\n },\n }\n let aggregateDocsWithDrafts = 0\n let aggregateErrors = 0\n let aggregateDocs = 0\n\n const countDocuments = async (collection: string, drafts?: boolean): Promise<number> => {\n const { totalDocs } = await payload.count({\n collection,\n ...defaultLocalApiProps,\n req: undefined,\n where: drafts ? undefined : whereStatusPublished,\n })\n return totalDocs\n }\n\n const deleteIndexes = async (collection: string) => {\n await payload.delete({\n collection: searchSlug,\n depth: 0,\n select: { id: true },\n where: { 'doc.relationTo': { equals: collection } },\n ...defaultLocalApiProps,\n })\n }\n\n const reindexCollection = async (collection: string) => {\n const draftsEnabled = Boolean(payload.collections[collection]?.config.versions?.drafts)\n\n const totalDocsWithDrafts = await countDocuments(collection, true)\n const totalDocs =\n syncDrafts || !draftsEnabled\n ? totalDocsWithDrafts\n : await countDocuments(collection, !draftsEnabled)\n const totalBatches = Math.ceil(totalDocs / batchSize)\n\n aggregateDocsWithDrafts += totalDocsWithDrafts\n aggregateDocs += totalDocs\n\n for (let j = 0; j < reindexLocales.length; j++) {\n // create first index, then we update with other locales accordingly\n const operation = j === 0 ? 'create' : 'update'\n const localeToSync = reindexLocales[j]\n\n for (let i = 0; i < totalBatches; i++) {\n const { docs } = await payload.find({\n collection,\n depth: 0,\n limit: batchSize,\n locale: localeToSync,\n page: i + 1,\n where: syncDrafts || !draftsEnabled ? undefined : whereStatusPublished,\n ...defaultLocalApiProps,\n })\n\n for (const doc of docs) {\n await syncDocAsSearchIndex({\n collection,\n data: doc,\n doc,\n locale: localeToSync,\n onSyncError: () => operation === 'create' && aggregateErrors++,\n operation,\n pluginConfig,\n req,\n })\n }\n }\n }\n }\n\n const shouldCommit = await initTransaction(req)\n\n try {\n const promises = collections.map(async (collection) => {\n try {\n await deleteIndexes(collection)\n await reindexCollection(collection)\n } catch (err) {\n const message = t('error:unableToReindexCollection', { collection })\n payload.logger.error({ err, msg: message })\n }\n })\n\n await Promise.all(promises)\n } catch (err: any) {\n if (shouldCommit) {\n await killTransaction(req)\n }\n return Response.json({ message: err.message }, { headers, status: 500 })\n }\n\n const message = t('general:successfullyReindexed', {\n collections: collections.join(', '),\n count: aggregateDocs - aggregateErrors,\n skips: syncDrafts ? 0 : aggregateDocsWithDrafts - aggregateDocs,\n total: aggregateDocsWithDrafts,\n })\n\n if (shouldCommit) {\n await commitTransaction(req)\n }\n\n return Response.json({ message }, { headers, status: 200 })\n }\n"],"names":["addLocalesToRequestFromData","commitTransaction","getAccessResults","headersWithCors","initTransaction","killTransaction","syncDocAsSearchIndex","generateReindexHandler","pluginConfig","req","json","Response","status","collections","t","searchSlug","searchOverrides","slug","searchCollections","reindexLocales","locales","length","locale","validatePermissions","accessResults","searchAccessResults","isValid","message","permissions","delete","update","access","create","push","read","every","Boolean","validateCollections","collectionsAreValid","col","includes","args","headers","Headers","hasPermissions","permissionError","validCollections","collectionError","payload","reindexBatchSize","batchSize","syncDrafts","defaultLocalApiProps","overrideAccess","user","whereStatusPublished","_status","equals","aggregateDocsWithDrafts","aggregateErrors","aggregateDocs","countDocuments","collection","drafts","totalDocs","count","undefined","where","deleteIndexes","depth","select","id","reindexCollection","draftsEnabled","config","versions","totalDocsWithDrafts","totalBatches","Math","ceil","j","operation","localeToSync","i","docs","find","limit","page","doc","data","onSyncError","shouldCommit","promises","map","err","logger","error","msg","Promise","all","join","skips","total"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/utilities/generateReindexHandler.ts"],"sourcesContent":["import type { PayloadHandler, Where } from 'payload'\n\nimport {\n addLocalesToRequestFromData,\n commitTransaction,\n getAccessResults,\n headersWithCors,\n initTransaction,\n killTransaction,\n} from 'payload'\n\nimport type { SanitizedSearchPluginConfig } from '../types.js'\n\nimport { syncDocAsSearchIndex } from './syncDocAsSearchIndex.js'\n\ntype ValidationResult = {\n isValid: boolean\n message?: string\n}\n\nexport const generateReindexHandler =\n (pluginConfig: SanitizedSearchPluginConfig): PayloadHandler =>\n async (req) => {\n addLocalesToRequestFromData(req)\n if (!req.json) {\n return new Response('Req.json is undefined', { status: 400 })\n }\n const { collections = [] } = (await req.json()) as { collections: string[] }\n const t = req.t\n\n const searchSlug = pluginConfig?.searchOverrides?.slug || 'search'\n const searchCollections = pluginConfig?.collections || []\n\n async function validatePermissions(): Promise<ValidationResult> {\n const accessResults = await getAccessResults({ req })\n const searchAccessResults = accessResults.collections?.[searchSlug]\n if (!searchAccessResults) {\n return { isValid: false, message: t('error:notAllowedToPerformAction') }\n }\n\n const permissions = [searchAccessResults.delete, searchAccessResults.update]\n // plugin doesn't allow create by default:\n // if user provided, then add it to check\n if (pluginConfig.searchOverrides?.access?.create) {\n permissions.push(searchAccessResults.create)\n }\n // plugin allows reads by anyone by default:\n // so if user provided, then add to check\n if (pluginConfig.searchOverrides?.access?.read) {\n permissions.push(searchAccessResults.read)\n }\n return permissions.every(Boolean)\n ? { isValid: true }\n : { isValid: false, message: t('error:notAllowedToPerformAction') }\n }\n\n function validateCollections(): ValidationResult {\n const collectionsAreValid = collections.every((col) => searchCollections.includes(col))\n return collections.length && collectionsAreValid\n ? { isValid: true }\n : { isValid: false, message: t('error:invalidRequestArgs', { args: `'collections'` }) }\n }\n\n const headers = headersWithCors({\n headers: new Headers(),\n req,\n })\n\n const { isValid: hasPermissions, message: permissionError } = await validatePermissions()\n if (!hasPermissions) {\n return Response.json({ message: permissionError }, { headers, status: 401 })\n }\n\n const { isValid: validCollections, message: collectionError } = validateCollections()\n if (!validCollections) {\n return Response.json({ message: collectionError }, { headers, status: 400 })\n }\n\n const payload = req.payload\n const { reindexBatchSize: batchSize, syncDrafts } = pluginConfig\n\n const defaultLocalApiProps = {\n overrideAccess: false,\n req,\n user: req.user,\n }\n const whereStatusPublished: Where = {\n _status: {\n equals: 'published',\n },\n }\n let aggregateDocsWithDrafts = 0\n let aggregateErrors = 0\n let aggregateDocs = 0\n\n async function countDocuments(collection: string, drafts?: boolean): Promise<number> {\n const { totalDocs } = await payload.count({\n collection,\n ...defaultLocalApiProps,\n req: undefined,\n where: drafts ? undefined : whereStatusPublished,\n })\n return totalDocs\n }\n\n async function deleteIndexes(collection: string) {\n await payload.delete({\n collection: searchSlug,\n depth: 0,\n select: { id: true },\n where: { 'doc.relationTo': { equals: collection } },\n ...defaultLocalApiProps,\n })\n }\n\n async function reindexCollection(collection: string) {\n const draftsEnabled = Boolean(payload.collections[collection]?.config.versions?.drafts)\n\n const totalDocsWithDrafts = await countDocuments(collection, true)\n const totalDocs =\n syncDrafts || !draftsEnabled\n ? totalDocsWithDrafts\n : await countDocuments(collection, !draftsEnabled)\n const totalBatches = Math.ceil(totalDocs / batchSize)\n\n aggregateDocsWithDrafts += totalDocsWithDrafts\n aggregateDocs += totalDocs\n\n // Loop through batches, then documents, then locales per document\n for (let i = 0; i < totalBatches; i++) {\n const defaultLocale = req.payload.config.localization\n ? req.payload.config.localization.defaultLocale\n : req.locale\n\n const { docs } = await payload.find({\n collection,\n depth: 0,\n limit: batchSize,\n locale: defaultLocale,\n page: i + 1,\n where: syncDrafts || !draftsEnabled ? undefined : whereStatusPublished,\n ...defaultLocalApiProps,\n })\n\n for (const doc of docs) {\n // Get all configured locales\n // If no localization, use [undefined] to sync once without a locale\n const allLocales = req.payload.config.localization\n ? req.payload.config.localization.localeCodes\n : [undefined]\n\n // Loop through all locales and check each one\n let firstAllowedLocale = true\n for (const localeToSync of allLocales) {\n // Check if we should skip this locale for this document\n let shouldSkip = false\n if (typeof pluginConfig.skipSync === 'function') {\n try {\n shouldSkip = await pluginConfig.skipSync({\n collectionSlug: collection,\n doc,\n locale: localeToSync,\n req,\n })\n } catch (err) {\n req.payload.logger.error({\n err,\n msg: 'Search plugin: Error executing skipSync. Proceeding with sync.',\n })\n }\n }\n\n if (shouldSkip) {\n continue // Skip this locale\n }\n\n // Sync this locale (create first index, then update with other locales accordingly)\n const operation = firstAllowedLocale ? 'create' : 'update'\n firstAllowedLocale = false\n\n await syncDocAsSearchIndex({\n collection,\n data: doc,\n doc,\n locale: localeToSync,\n onSyncError: () => operation === 'create' && aggregateErrors++,\n operation,\n pluginConfig,\n req,\n })\n }\n }\n }\n }\n\n const shouldCommit = await initTransaction(req)\n\n try {\n const promises = collections.map(async (collection) => {\n try {\n await deleteIndexes(collection)\n await reindexCollection(collection)\n } catch (err) {\n const message = t('error:unableToReindexCollection', { collection })\n payload.logger.error({ err, msg: message })\n }\n })\n\n await Promise.all(promises)\n } catch (err: any) {\n if (shouldCommit) {\n await killTransaction(req)\n }\n return Response.json({ message: err.message }, { headers, status: 500 })\n }\n\n const message = t('general:successfullyReindexed', {\n collections: collections.join(', '),\n count: aggregateDocs - aggregateErrors,\n skips: syncDrafts ? 0 : aggregateDocsWithDrafts - aggregateDocs,\n total: aggregateDocsWithDrafts,\n })\n\n if (shouldCommit) {\n await commitTransaction(req)\n }\n\n return Response.json({ message }, { headers, status: 200 })\n }\n"],"names":["addLocalesToRequestFromData","commitTransaction","getAccessResults","headersWithCors","initTransaction","killTransaction","syncDocAsSearchIndex","generateReindexHandler","pluginConfig","req","json","Response","status","collections","t","searchSlug","searchOverrides","slug","searchCollections","validatePermissions","accessResults","searchAccessResults","isValid","message","permissions","delete","update","access","create","push","read","every","Boolean","validateCollections","collectionsAreValid","col","includes","length","args","headers","Headers","hasPermissions","permissionError","validCollections","collectionError","payload","reindexBatchSize","batchSize","syncDrafts","defaultLocalApiProps","overrideAccess","user","whereStatusPublished","_status","equals","aggregateDocsWithDrafts","aggregateErrors","aggregateDocs","countDocuments","collection","drafts","totalDocs","count","undefined","where","deleteIndexes","depth","select","id","reindexCollection","draftsEnabled","config","versions","totalDocsWithDrafts","totalBatches","Math","ceil","i","defaultLocale","localization","locale","docs","find","limit","page","doc","allLocales","localeCodes","firstAllowedLocale","localeToSync","shouldSkip","skipSync","collectionSlug","err","logger","error","msg","operation","data","onSyncError","shouldCommit","promises","map","Promise","all","join","skips","total"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncDocAsSearchIndex.d.ts","sourceRoot":"","sources":["../../src/utilities/syncDocAsSearchIndex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzD,eAAO,MAAM,oBAAoB,8FAS9B,WAAW,iBAyPb,CAAA"}
1
+ {"version":3,"file":"syncDocAsSearchIndex.d.ts","sourceRoot":"","sources":["../../src/utilities/syncDocAsSearchIndex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzD,eAAO,MAAM,oBAAoB,8FAS9B,WAAW,iBA+Qb,CAAA"}
@@ -2,7 +2,26 @@ export const syncDocAsSearchIndex = async ({ collection, doc, locale, onSyncErro
2
2
  const { id, _status: status, title } = doc || {};
3
3
  const { beforeSync, defaultPriorities, deleteDrafts, searchOverrides, syncDrafts } = pluginConfig;
4
4
  const searchSlug = searchOverrides?.slug || 'search';
5
- const syncLocale = locale || req.locale;
5
+ // Determine sync locale
6
+ const syncLocale = locale || req.locale || undefined;
7
+ if (typeof pluginConfig.skipSync === 'function') {
8
+ try {
9
+ const skipSync = await pluginConfig.skipSync({
10
+ collectionSlug: collection,
11
+ doc,
12
+ locale: syncLocale,
13
+ req
14
+ });
15
+ if (skipSync) {
16
+ return doc;
17
+ }
18
+ } catch (err) {
19
+ req.payload.logger.error({
20
+ err,
21
+ msg: 'Search plugin: Error executing skipSync. Proceeding with sync.'
22
+ });
23
+ }
24
+ }
6
25
  let dataToSave = {
7
26
  doc: {
8
27
  relationTo: collection,
@@ -11,7 +30,7 @@ export const syncDocAsSearchIndex = async ({ collection, doc, locale, onSyncErro
11
30
  title
12
31
  };
13
32
  const docKeyPrefix = `${collection}:${id}`;
14
- const docKey = pluginConfig.locales?.length ? `${docKeyPrefix}:${syncLocale}` : docKeyPrefix;
33
+ const docKey = req.payload.config.localization ? `${docKeyPrefix}:${syncLocale}` : docKeyPrefix;
15
34
  const syncedDocsSet = req.context?.syncedDocsSet || new Set();
16
35
  if (syncedDocsSet.has(docKey)) {
17
36
  /*
@@ -115,27 +134,6 @@ export const syncDocAsSearchIndex = async ({ collection, doc, locale, onSyncErro
115
134
  }
116
135
  if (foundDoc) {
117
136
  const { id: searchDocID } = foundDoc;
118
- if (doSync) {
119
- // update the doc normally
120
- try {
121
- await payload.update({
122
- id: searchDocID,
123
- collection: searchSlug,
124
- data: {
125
- ...dataToSave,
126
- priority: foundDoc.priority || defaultPriority
127
- },
128
- depth: 0,
129
- locale: syncLocale,
130
- req
131
- });
132
- } catch (err) {
133
- payload.logger.error({
134
- err,
135
- msg: `Error updating ${searchSlug} document.`
136
- });
137
- }
138
- }
139
137
  // Check if document is trashed and delete from search
140
138
  const isTrashDocument = doc && 'deletedAt' in doc && doc.deletedAt;
141
139
  if (isTrashDocument) {
@@ -152,49 +150,71 @@ export const syncDocAsSearchIndex = async ({ collection, doc, locale, onSyncErro
152
150
  msg: `Error deleting ${searchSlug} document for trashed doc.`
153
151
  });
154
152
  }
155
- }
156
- if (deleteDrafts && status === 'draft') {
157
- // Check to see if there's a published version of the doc
158
- // We don't want to remove the search doc if there is a published version but a new draft has been created
159
- const { docs: [docWithPublish] } = await payload.find({
160
- collection,
161
- depth: 0,
162
- draft: false,
163
- limit: 1,
164
- locale: syncLocale,
165
- pagination: false,
166
- req,
167
- where: {
168
- and: [
169
- {
170
- _status: {
171
- equals: 'published'
172
- }
173
- },
174
- {
175
- id: {
176
- equals: id
177
- }
178
- }
179
- ]
180
- }
181
- });
182
- if (!docWithPublish && !isTrashDocument) {
183
- // do not include draft docs in search results, so delete the record
153
+ } else {
154
+ if (doSync) {
155
+ // update the doc normally
184
156
  try {
185
- await payload.delete({
157
+ await payload.update({
186
158
  id: searchDocID,
187
159
  collection: searchSlug,
160
+ data: {
161
+ ...dataToSave,
162
+ priority: foundDoc.priority || defaultPriority
163
+ },
188
164
  depth: 0,
165
+ locale: syncLocale,
189
166
  req
190
167
  });
191
168
  } catch (err) {
192
169
  payload.logger.error({
193
170
  err,
194
- msg: `Error deleting ${searchSlug} document.`
171
+ msg: `Error updating ${searchSlug} document.`
195
172
  });
196
173
  }
197
174
  }
175
+ if (deleteDrafts && status === 'draft') {
176
+ // Check to see if there's a published version of the doc
177
+ // We don't want to remove the search doc if there is a published version but a new draft has been created
178
+ const { docs: [docWithPublish] } = await payload.find({
179
+ collection,
180
+ depth: 0,
181
+ draft: false,
182
+ limit: 1,
183
+ locale: syncLocale,
184
+ pagination: false,
185
+ req,
186
+ where: {
187
+ and: [
188
+ {
189
+ _status: {
190
+ equals: 'published'
191
+ }
192
+ },
193
+ {
194
+ id: {
195
+ equals: id
196
+ }
197
+ }
198
+ ]
199
+ }
200
+ });
201
+ if (!docWithPublish) {
202
+ // do not include draft docs in search results, so delete the record
203
+ try {
204
+ await payload.delete({
205
+ id: searchDocID,
206
+ collection: searchSlug,
207
+ depth: 0,
208
+ req
209
+ });
210
+ } catch (err) {
211
+ payload.logger.error({
212
+ err,
213
+ msg: `Error deleting ${searchSlug} document.`
214
+ });
215
+ }
216
+ }
217
+ }
198
218
  }
199
219
  } else if (doSync) {
200
220
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/syncDocAsSearchIndex.ts"],"sourcesContent":["import type { DocToSync, SyncDocArgs } from '../types.js'\n\nexport const syncDocAsSearchIndex = async ({\n collection,\n doc,\n locale,\n onSyncError,\n operation,\n pluginConfig,\n req: { payload },\n req,\n}: SyncDocArgs) => {\n const { id, _status: status, title } = doc || {}\n\n const { beforeSync, defaultPriorities, deleteDrafts, searchOverrides, syncDrafts } = pluginConfig\n\n const searchSlug = searchOverrides?.slug || 'search'\n const syncLocale = locale || req.locale\n\n let dataToSave: DocToSync = {\n doc: {\n relationTo: collection,\n value: id,\n },\n title,\n }\n const docKeyPrefix = `${collection}:${id}`\n const docKey = pluginConfig.locales?.length ? `${docKeyPrefix}:${syncLocale}` : docKeyPrefix\n const syncedDocsSet = (req.context?.syncedDocsSet as Set<string>) || new Set<string>()\n\n if (syncedDocsSet.has(docKey)) {\n /*\n * prevents duplicate syncing of documents in the same request\n * this can happen when hooks call `payload.update` within the create lifecycle\n * like the nested-docs plugin does\n */\n return doc\n } else {\n syncedDocsSet.add(docKey)\n }\n\n req.context.syncedDocsSet = syncedDocsSet\n\n if (typeof beforeSync === 'function') {\n let docToSyncWith = doc\n if (payload.config?.localization) {\n // Check if document is trashed (has deletedAt field)\n const isTrashDocument = doc && 'deletedAt' in doc && doc.deletedAt\n\n docToSyncWith = await payload.findByID({\n id,\n collection,\n locale: syncLocale,\n req,\n // Include trashed documents when the document being synced is trashed\n trash: isTrashDocument,\n })\n }\n dataToSave = await beforeSync({\n originalDoc: docToSyncWith,\n payload,\n req,\n searchDoc: dataToSave,\n })\n }\n\n let defaultPriority = 0\n if (defaultPriorities) {\n const { [collection]: priority } = defaultPriorities\n\n if (typeof priority === 'function') {\n try {\n defaultPriority = await priority(doc)\n } catch (err: unknown) {\n payload.logger.error(err)\n payload.logger.error(\n `Error gathering default priority for ${searchSlug} documents related to ${collection}`,\n )\n }\n } else if (priority !== undefined) {\n defaultPriority = priority\n }\n }\n\n const doSync = syncDrafts || (!syncDrafts && status !== 'draft')\n\n try {\n if (operation === 'create' && doSync) {\n await payload.create({\n collection: searchSlug,\n data: {\n ...dataToSave,\n priority: defaultPriority,\n },\n depth: 0,\n locale: syncLocale,\n req,\n })\n }\n\n if (operation === 'update') {\n try {\n // find the correct doc to sync with\n const searchDocQuery = await payload.find({\n collection: searchSlug,\n depth: 0,\n locale: syncLocale,\n req,\n where: {\n 'doc.relationTo': {\n equals: collection,\n },\n 'doc.value': {\n equals: id,\n },\n },\n })\n\n const docs: Array<{\n id: number | string\n priority?: number\n }> = searchDocQuery?.docs || []\n\n const [foundDoc, ...duplicativeDocs] = docs\n\n // delete all duplicative search docs (docs that reference the same page)\n // to ensure the same, out-of-date result does not appear twice (where only syncing the first found doc)\n if (duplicativeDocs.length > 0) {\n try {\n const duplicativeDocIDs = duplicativeDocs.map(({ id }) => id)\n await payload.delete({\n collection: searchSlug,\n depth: 0,\n req,\n where: { id: { in: duplicativeDocIDs } },\n })\n } catch (err: unknown) {\n payload.logger.error({\n err,\n msg: `Error deleting duplicative ${searchSlug} documents.`,\n })\n }\n }\n\n if (foundDoc) {\n const { id: searchDocID } = foundDoc\n\n if (doSync) {\n // update the doc normally\n try {\n await payload.update({\n id: searchDocID,\n collection: searchSlug,\n data: {\n ...dataToSave,\n priority: foundDoc.priority || defaultPriority,\n },\n depth: 0,\n locale: syncLocale,\n req,\n })\n } catch (err: unknown) {\n payload.logger.error({ err, msg: `Error updating ${searchSlug} document.` })\n }\n }\n\n // Check if document is trashed and delete from search\n const isTrashDocument = doc && 'deletedAt' in doc && doc.deletedAt\n\n if (isTrashDocument) {\n try {\n await payload.delete({\n id: searchDocID,\n collection: searchSlug,\n depth: 0,\n req,\n })\n } catch (err: unknown) {\n payload.logger.error({\n err,\n msg: `Error deleting ${searchSlug} document for trashed doc.`,\n })\n }\n }\n\n if (deleteDrafts && status === 'draft') {\n // Check to see if there's a published version of the doc\n // We don't want to remove the search doc if there is a published version but a new draft has been created\n const {\n docs: [docWithPublish],\n } = await payload.find({\n collection,\n depth: 0,\n draft: false,\n limit: 1,\n locale: syncLocale,\n pagination: false,\n req,\n where: {\n and: [\n {\n _status: {\n equals: 'published',\n },\n },\n {\n id: {\n equals: id,\n },\n },\n ],\n },\n })\n\n if (!docWithPublish && !isTrashDocument) {\n // do not include draft docs in search results, so delete the record\n try {\n await payload.delete({\n id: searchDocID,\n collection: searchSlug,\n depth: 0,\n req,\n })\n } catch (err: unknown) {\n payload.logger.error({ err, msg: `Error deleting ${searchSlug} document.` })\n }\n }\n }\n } else if (doSync) {\n try {\n await payload.create({\n collection: searchSlug,\n data: {\n ...dataToSave,\n priority: defaultPriority,\n },\n depth: 0,\n locale: syncLocale,\n req,\n })\n } catch (err: unknown) {\n payload.logger.error({ err, msg: `Error creating ${searchSlug} document.` })\n }\n }\n } catch (err: unknown) {\n payload.logger.error({ err, msg: `Error finding ${searchSlug} document.` })\n }\n }\n } catch (err: unknown) {\n payload.logger.error({\n err,\n msg: `Error syncing ${searchSlug} document related to ${collection} with id: '${id}'.`,\n })\n\n if (onSyncError) {\n onSyncError()\n }\n }\n\n return doc\n}\n"],"names":["syncDocAsSearchIndex","collection","doc","locale","onSyncError","operation","pluginConfig","req","payload","id","_status","status","title","beforeSync","defaultPriorities","deleteDrafts","searchOverrides","syncDrafts","searchSlug","slug","syncLocale","dataToSave","relationTo","value","docKeyPrefix","docKey","locales","length","syncedDocsSet","context","Set","has","add","docToSyncWith","config","localization","isTrashDocument","deletedAt","findByID","trash","originalDoc","searchDoc","defaultPriority","priority","err","logger","error","undefined","doSync","create","data","depth","searchDocQuery","find","where","equals","docs","foundDoc","duplicativeDocs","duplicativeDocIDs","map","delete","in","msg","searchDocID","update","docWithPublish","draft","limit","pagination","and"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/utilities/syncDocAsSearchIndex.ts"],"sourcesContent":["import type { DocToSync, SyncDocArgs } from '../types.js'\n\nexport const syncDocAsSearchIndex = async ({\n collection,\n doc,\n locale,\n onSyncError,\n operation,\n pluginConfig,\n req: { payload },\n req,\n}: SyncDocArgs) => {\n const { id, _status: status, title } = doc || {}\n\n const { beforeSync, defaultPriorities, deleteDrafts, searchOverrides, syncDrafts } = pluginConfig\n\n const searchSlug = searchOverrides?.slug || 'search'\n\n // Determine sync locale\n const syncLocale = locale || req.locale || undefined\n\n if (typeof pluginConfig.skipSync === 'function') {\n try {\n const skipSync = await pluginConfig.skipSync({\n collectionSlug: collection,\n doc,\n locale: syncLocale,\n req,\n })\n\n if (skipSync) {\n return doc\n }\n } catch (err) {\n req.payload.logger.error({\n err,\n msg: 'Search plugin: Error executing skipSync. Proceeding with sync.',\n })\n }\n }\n\n let dataToSave: DocToSync = {\n doc: {\n relationTo: collection,\n value: id,\n },\n title,\n }\n const docKeyPrefix = `${collection}:${id}`\n const docKey = req.payload.config.localization ? `${docKeyPrefix}:${syncLocale}` : docKeyPrefix\n const syncedDocsSet = (req.context?.syncedDocsSet as Set<string>) || new Set<string>()\n\n if (syncedDocsSet.has(docKey)) {\n /*\n * prevents duplicate syncing of documents in the same request\n * this can happen when hooks call `payload.update` within the create lifecycle\n * like the nested-docs plugin does\n */\n return doc\n } else {\n syncedDocsSet.add(docKey)\n }\n\n req.context.syncedDocsSet = syncedDocsSet\n\n if (typeof beforeSync === 'function') {\n let docToSyncWith = doc\n if (payload.config?.localization) {\n // Check if document is trashed (has deletedAt field)\n const isTrashDocument = doc && 'deletedAt' in doc && doc.deletedAt\n\n docToSyncWith = await payload.findByID({\n id,\n collection,\n locale: syncLocale,\n req,\n // Include trashed documents when the document being synced is trashed\n trash: isTrashDocument,\n })\n }\n dataToSave = await beforeSync({\n originalDoc: docToSyncWith,\n payload,\n req,\n searchDoc: dataToSave,\n })\n }\n\n let defaultPriority = 0\n if (defaultPriorities) {\n const { [collection]: priority } = defaultPriorities\n\n if (typeof priority === 'function') {\n try {\n defaultPriority = await priority(doc)\n } catch (err: unknown) {\n payload.logger.error(err)\n payload.logger.error(\n `Error gathering default priority for ${searchSlug} documents related to ${collection}`,\n )\n }\n } else if (priority !== undefined) {\n defaultPriority = priority\n }\n }\n\n const doSync = syncDrafts || (!syncDrafts && status !== 'draft')\n\n try {\n if (operation === 'create' && doSync) {\n await payload.create({\n collection: searchSlug,\n data: {\n ...dataToSave,\n priority: defaultPriority,\n },\n depth: 0,\n locale: syncLocale,\n req,\n })\n }\n\n if (operation === 'update') {\n try {\n // find the correct doc to sync with\n const searchDocQuery = await payload.find({\n collection: searchSlug,\n depth: 0,\n locale: syncLocale,\n req,\n where: {\n 'doc.relationTo': {\n equals: collection,\n },\n 'doc.value': {\n equals: id,\n },\n },\n })\n\n const docs: Array<{\n id: number | string\n priority?: number\n }> = searchDocQuery?.docs || []\n\n const [foundDoc, ...duplicativeDocs] = docs\n\n // delete all duplicative search docs (docs that reference the same page)\n // to ensure the same, out-of-date result does not appear twice (where only syncing the first found doc)\n if (duplicativeDocs.length > 0) {\n try {\n const duplicativeDocIDs = duplicativeDocs.map(({ id }) => id)\n await payload.delete({\n collection: searchSlug,\n depth: 0,\n req,\n where: { id: { in: duplicativeDocIDs } },\n })\n } catch (err: unknown) {\n payload.logger.error({\n err,\n msg: `Error deleting duplicative ${searchSlug} documents.`,\n })\n }\n }\n\n if (foundDoc) {\n const { id: searchDocID } = foundDoc\n\n // Check if document is trashed and delete from search\n const isTrashDocument = doc && 'deletedAt' in doc && doc.deletedAt\n\n if (isTrashDocument) {\n try {\n await payload.delete({\n id: searchDocID,\n collection: searchSlug,\n depth: 0,\n req,\n })\n } catch (err: unknown) {\n payload.logger.error({\n err,\n msg: `Error deleting ${searchSlug} document for trashed doc.`,\n })\n }\n } else {\n if (doSync) {\n // update the doc normally\n try {\n await payload.update({\n id: searchDocID,\n collection: searchSlug,\n data: {\n ...dataToSave,\n priority: foundDoc.priority || defaultPriority,\n },\n depth: 0,\n locale: syncLocale,\n req,\n })\n } catch (err: unknown) {\n payload.logger.error({ err, msg: `Error updating ${searchSlug} document.` })\n }\n }\n\n if (deleteDrafts && status === 'draft') {\n // Check to see if there's a published version of the doc\n // We don't want to remove the search doc if there is a published version but a new draft has been created\n const {\n docs: [docWithPublish],\n } = await payload.find({\n collection,\n depth: 0,\n draft: false,\n limit: 1,\n locale: syncLocale,\n pagination: false,\n req,\n where: {\n and: [\n {\n _status: {\n equals: 'published',\n },\n },\n {\n id: {\n equals: id,\n },\n },\n ],\n },\n })\n\n if (!docWithPublish) {\n // do not include draft docs in search results, so delete the record\n try {\n await payload.delete({\n id: searchDocID,\n collection: searchSlug,\n depth: 0,\n req,\n })\n } catch (err: unknown) {\n payload.logger.error({ err, msg: `Error deleting ${searchSlug} document.` })\n }\n }\n }\n }\n } else if (doSync) {\n try {\n await payload.create({\n collection: searchSlug,\n data: {\n ...dataToSave,\n priority: defaultPriority,\n },\n depth: 0,\n locale: syncLocale,\n req,\n })\n } catch (err: unknown) {\n payload.logger.error({ err, msg: `Error creating ${searchSlug} document.` })\n }\n }\n } catch (err: unknown) {\n payload.logger.error({ err, msg: `Error finding ${searchSlug} document.` })\n }\n }\n } catch (err: unknown) {\n payload.logger.error({\n err,\n msg: `Error syncing ${searchSlug} document related to ${collection} with id: '${id}'.`,\n })\n\n if (onSyncError) {\n onSyncError()\n }\n }\n\n return doc\n}\n"],"names":["syncDocAsSearchIndex","collection","doc","locale","onSyncError","operation","pluginConfig","req","payload","id","_status","status","title","beforeSync","defaultPriorities","deleteDrafts","searchOverrides","syncDrafts","searchSlug","slug","syncLocale","undefined","skipSync","collectionSlug","err","logger","error","msg","dataToSave","relationTo","value","docKeyPrefix","docKey","config","localization","syncedDocsSet","context","Set","has","add","docToSyncWith","isTrashDocument","deletedAt","findByID","trash","originalDoc","searchDoc","defaultPriority","priority","doSync","create","data","depth","searchDocQuery","find","where","equals","docs","foundDoc","duplicativeDocs","length","duplicativeDocIDs","map","delete","in","searchDocID","update","docWithPublish","draft","limit","pagination","and"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/plugin-search",
3
- "version": "3.70.0",
3
+ "version": "3.71.0-internal.727c7a4",
4
4
  "description": "Search plugin for Payload",
5
5
  "keywords": [
6
6
  "payload",
@@ -50,19 +50,19 @@
50
50
  "dist"
51
51
  ],
52
52
  "dependencies": {
53
- "@payloadcms/next": "3.70.0",
54
- "@payloadcms/ui": "3.70.0"
53
+ "@payloadcms/next": "3.71.0-internal.727c7a4",
54
+ "@payloadcms/ui": "3.71.0-internal.727c7a4"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/react": "19.2.1",
58
58
  "@types/react-dom": "19.2.1",
59
59
  "@payloadcms/eslint-config": "3.28.0",
60
- "payload": "3.70.0"
60
+ "payload": "3.71.0-internal.727c7a4"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "react": "^19.0.1 || ^19.1.2 || ^19.2.1",
64
64
  "react-dom": "^19.0.1 || ^19.1.2 || ^19.2.1",
65
- "payload": "3.70.0"
65
+ "payload": "3.71.0-internal.727c7a4"
66
66
  },
67
67
  "publishConfig": {
68
68
  "registry": "https://registry.npmjs.org/"