@payloadcms/plugin-multi-tenant 4.0.0-internal.d1cc1d3 → 4.0.0-internal.d21c039

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 (42) hide show
  1. package/dist/fields/tenantField/index.d.ts +4 -2
  2. package/dist/fields/tenantField/index.d.ts.map +1 -1
  3. package/dist/fields/tenantField/index.js +36 -9
  4. package/dist/fields/tenantField/index.js.map +1 -1
  5. package/dist/hooks/enforceTenantMembership.d.ts +15 -0
  6. package/dist/hooks/enforceTenantMembership.d.ts.map +1 -0
  7. package/dist/hooks/enforceTenantMembership.js +39 -0
  8. package/dist/hooks/enforceTenantMembership.js.map +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +26 -4
  11. package/dist/index.js.map +1 -1
  12. package/dist/providers/TenantSelectionProvider/index.client.d.ts.map +1 -1
  13. package/dist/providers/TenantSelectionProvider/index.client.js +22 -24
  14. package/dist/providers/TenantSelectionProvider/index.client.js.map +1 -1
  15. package/dist/utilities/addCollectionAccess.d.ts +6 -8
  16. package/dist/utilities/addCollectionAccess.d.ts.map +1 -1
  17. package/dist/utilities/addCollectionAccess.js +91 -20
  18. package/dist/utilities/addCollectionAccess.js.map +1 -1
  19. package/dist/utilities/getTenantIDsFromValue.d.ts +2 -0
  20. package/dist/utilities/getTenantIDsFromValue.d.ts.map +1 -0
  21. package/dist/utilities/getTenantIDsFromValue.js +14 -0
  22. package/dist/utilities/getTenantIDsFromValue.js.map +1 -0
  23. package/dist/utilities/getUserTenantIDs.d.ts.map +1 -1
  24. package/dist/utilities/getUserTenantIDs.js +3 -2
  25. package/dist/utilities/getUserTenantIDs.js.map +1 -1
  26. package/dist/utilities/isValidTenantAssignment.d.ts +13 -0
  27. package/dist/utilities/isValidTenantAssignment.d.ts.map +1 -0
  28. package/dist/utilities/isValidTenantAssignment.js +28 -0
  29. package/dist/utilities/isValidTenantAssignment.js.map +1 -0
  30. package/dist/utilities/shouldRefreshTenantSelection.d.ts +5 -0
  31. package/dist/utilities/shouldRefreshTenantSelection.d.ts.map +1 -0
  32. package/dist/utilities/shouldRefreshTenantSelection.js +3 -0
  33. package/dist/utilities/shouldRefreshTenantSelection.js.map +1 -0
  34. package/package.json +7 -7
  35. package/dist/utilities/combineWhereConstraints.d.ts +0 -3
  36. package/dist/utilities/combineWhereConstraints.d.ts.map +0 -1
  37. package/dist/utilities/combineWhereConstraints.js +0 -19
  38. package/dist/utilities/combineWhereConstraints.js.map +0 -1
  39. package/dist/utilities/withTenantAccess.d.ts +0 -16
  40. package/dist/utilities/withTenantAccess.d.ts.map +0 -1
  41. package/dist/utilities/withTenantAccess.js +0 -76
  42. package/dist/utilities/withTenantAccess.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import type { SingleRelationshipField } from 'payload';
2
- import type { RootTenantFieldConfigOverrides } from '../../types.js';
2
+ import type { MultiTenantPluginConfig, RootTenantFieldConfigOverrides } from '../../types.js';
3
3
  type Args = {
4
+ adminUsersSlug?: string;
4
5
  debug?: boolean;
5
6
  isAutosaveEnabled?: boolean;
6
7
  name: string;
@@ -9,7 +10,8 @@ type Args = {
9
10
  tenantsArrayTenantFieldName: string;
10
11
  tenantsCollectionSlug: string;
11
12
  unique: boolean;
13
+ userHasAccessToAllTenants?: MultiTenantPluginConfig['userHasAccessToAllTenants'];
12
14
  };
13
- export declare const tenantField: ({ name, debug, isAutosaveEnabled, overrides: _overrides, tenantsArrayFieldName, tenantsArrayTenantFieldName, tenantsCollectionSlug, unique, }: Args) => SingleRelationshipField;
15
+ export declare const tenantField: ({ name, adminUsersSlug, debug, isAutosaveEnabled, overrides: _overrides, tenantsArrayFieldName, tenantsArrayTenantFieldName, tenantsCollectionSlug, unique, userHasAccessToAllTenants, }: Args) => SingleRelationshipField;
14
16
  export {};
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fields/tenantField/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA+B,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAEnF,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAA;AA8BpE,KAAK,IAAI,GAAG;IACV,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,8BAA8B,CAAA;IAC1C,qBAAqB,EAAE,MAAM,CAAA;IAC7B,2BAA2B,EAAE,MAAM,CAAA;IACnC,qBAAqB,EAAE,MAAM,CAAA;IAC7B,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AACD,eAAO,MAAM,WAAW,kJASrB,IAAI,KAAG,uBAiGT,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fields/tenantField/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA+B,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAEnF,OAAO,KAAK,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAA;AAsD7F,KAAK,IAAI,GAAG;IACV,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,8BAA8B,CAAA;IAC1C,qBAAqB,EAAE,MAAM,CAAA;IAC7B,2BAA2B,EAAE,MAAM,CAAA;IACnC,qBAAqB,EAAE,MAAM,CAAA;IAC7B,MAAM,EAAE,OAAO,CAAA;IACf,yBAAyB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;CACjF,CAAA;AACD,eAAO,MAAM,WAAW,6LAWrB,IAAI,KAAG,uBAmHT,CAAA"}
@@ -2,9 +2,10 @@ import { defaults } from '../../defaults.js';
2
2
  import { getCollectionIDType } from '../../utilities/getCollectionIDType.js';
3
3
  import { getTenantFromCookie } from '../../utilities/getTenantFromCookie.js';
4
4
  import { getUserTenantIDs } from '../../utilities/getUserTenantIDs.js';
5
- const fieldValidation = (validateFunction)=>(value, options)=>{
5
+ import { isValidTenantAssignment } from '../../utilities/isValidTenantAssignment.js';
6
+ const fieldValidation = ({ tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, validateFunction })=>async (value, options)=>{
6
7
  if (validateFunction) {
7
- const result = validateFunction(value, options);
8
+ const result = await validateFunction(value, options);
8
9
  if (result !== true) {
9
10
  return result;
10
11
  }
@@ -18,15 +19,31 @@ const fieldValidation = (validateFunction)=>(value, options)=>{
18
19
  return options.req.t('validation:required');
19
20
  }
20
21
  }
21
- return true;
22
+ if (isValidTenantAssignment({
23
+ previousValue: options.previousValue,
24
+ req: options.req,
25
+ tenantsArrayFieldName,
26
+ tenantsArrayTenantFieldName,
27
+ userHasAccessToAllTenants,
28
+ value
29
+ })) {
30
+ return true;
31
+ }
32
+ return options.req.t('validation:invalidSelection');
22
33
  };
23
- export const tenantField = ({ name = defaults.tenantFieldName, debug, isAutosaveEnabled, overrides: _overrides = {}, tenantsArrayFieldName = defaults.tenantsArrayFieldName, tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName, tenantsCollectionSlug = defaults.tenantCollectionSlug, unique })=>{
34
+ export const tenantField = ({ name = defaults.tenantFieldName, adminUsersSlug, debug, isAutosaveEnabled, overrides: _overrides = {}, tenantsArrayFieldName = defaults.tenantsArrayFieldName, tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName, tenantsCollectionSlug = defaults.tenantCollectionSlug, unique, userHasAccessToAllTenants })=>{
24
35
  const { hasMany = false, validate, ...overrides } = _overrides || {};
25
36
  return {
26
37
  ...overrides || {},
27
38
  name,
28
39
  type: 'relationship',
29
- access: overrides.access || {},
40
+ access: {
41
+ ...adminUsersSlug ? {
42
+ create: ({ req })=>Boolean(req.user && req.user.collection === adminUsersSlug),
43
+ update: ({ req })=>Boolean(req.user && req.user.collection === adminUsersSlug)
44
+ } : {},
45
+ ...overrides.access || {}
46
+ },
30
47
  admin: {
31
48
  allowCreate: false,
32
49
  allowEdit: false,
@@ -57,7 +74,7 @@ export const tenantField = ({ name = defaults.tenantFieldName, debug, isAutosave
57
74
  });
58
75
  const tenantFromCookie = getTenantFromCookie(req.headers, idType);
59
76
  if (tenantFromCookie) {
60
- const isValidTenant = await req.payload.count({
77
+ const { totalDocs } = await req.payload.count({
61
78
  collection: tenantsCollectionSlug,
62
79
  overrideAccess: false,
63
80
  req,
@@ -70,7 +87,7 @@ export const tenantField = ({ name = defaults.tenantFieldName, debug, isAutosave
70
87
  }
71
88
  }
72
89
  });
73
- return isValidTenant ? tenantFromCookie : null;
90
+ return totalDocs > 0 ? tenantFromCookie : null;
74
91
  }
75
92
  if (req.user && isAutosaveEnabled) {
76
93
  const userTenants = getUserTenantIDs(req.user, {
@@ -103,11 +120,21 @@ export const tenantField = ({ name = defaults.tenantFieldName, debug, isAutosave
103
120
  ...hasMany ? {
104
121
  hasMany: true,
105
122
  // TODO: V4 - replace validation with required: true
106
- validate: fieldValidation(validate)
123
+ validate: fieldValidation({
124
+ tenantsArrayFieldName,
125
+ tenantsArrayTenantFieldName,
126
+ userHasAccessToAllTenants,
127
+ validateFunction: validate
128
+ })
107
129
  } : {
108
130
  hasMany: false,
109
131
  // TODO: V4 - replace validation with required: true
110
- validate: fieldValidation(validate)
132
+ validate: fieldValidation({
133
+ tenantsArrayFieldName,
134
+ tenantsArrayTenantFieldName,
135
+ userHasAccessToAllTenants,
136
+ validateFunction: validate
137
+ })
111
138
  },
112
139
  // @ts-expect-error translations are not typed for this plugin
113
140
  label: overrides.label || (({ t })=>t('plugin-multi-tenant:field-assignedTenant-label'))
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/fields/tenantField/index.ts"],"sourcesContent":["import type { RelationshipFieldValidation, SingleRelationshipField } from 'payload'\n\nimport type { RootTenantFieldConfigOverrides } from '../../types.js'\n\nimport { defaults } from '../../defaults.js'\nimport { getCollectionIDType } from '../../utilities/getCollectionIDType.js'\nimport { getTenantFromCookie } from '../../utilities/getTenantFromCookie.js'\nimport { getUserTenantIDs } from '../../utilities/getUserTenantIDs.js'\n\nconst fieldValidation =\n (validateFunction?: RelationshipFieldValidation): RelationshipFieldValidation =>\n (value, options) => {\n if (validateFunction) {\n const result = validateFunction(value, options)\n if (result !== true) {\n return result\n }\n }\n\n if (options.hasMany) {\n if (!value || (Array.isArray(value) && value.length === 0)) {\n return options.req.t('validation:required')\n }\n } else {\n if (!value) {\n return options.req.t('validation:required')\n }\n }\n\n return true\n }\n\ntype Args = {\n debug?: boolean\n isAutosaveEnabled?: boolean\n name: string\n overrides?: RootTenantFieldConfigOverrides\n tenantsArrayFieldName: string\n tenantsArrayTenantFieldName: string\n tenantsCollectionSlug: string\n unique: boolean\n}\nexport const tenantField = ({\n name = defaults.tenantFieldName,\n debug,\n isAutosaveEnabled,\n overrides: _overrides = {},\n tenantsArrayFieldName = defaults.tenantsArrayFieldName,\n tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName,\n tenantsCollectionSlug = defaults.tenantCollectionSlug,\n unique,\n}: Args): SingleRelationshipField => {\n const { hasMany = false, validate, ...overrides } = _overrides || {}\n return {\n ...(overrides || {}),\n name,\n type: 'relationship',\n access: overrides.access || {},\n admin: {\n allowCreate: false,\n allowEdit: false,\n disabled: { column: true, filter: true, groupBy: true },\n position: 'sidebar',\n ...(overrides.admin || {}),\n components: {\n ...(overrides.admin?.components || {}),\n Field: {\n path: '@payloadcms/plugin-multi-tenant/client#TenantField',\n ...(typeof overrides.admin?.components?.Field !== 'string'\n ? overrides.admin?.components?.Field || {}\n : {}),\n clientProps: {\n ...(typeof overrides.admin?.components?.Field !== 'string'\n ? (overrides.admin?.components?.Field || {})?.clientProps\n : {}),\n debug,\n unique,\n },\n },\n },\n },\n defaultValue:\n overrides.defaultValue ||\n (async ({ req }) => {\n const idType = getCollectionIDType({\n collectionSlug: tenantsCollectionSlug,\n payload: req.payload,\n })\n const tenantFromCookie = getTenantFromCookie(req.headers, idType)\n if (tenantFromCookie) {\n const isValidTenant = await req.payload.count({\n collection: tenantsCollectionSlug,\n overrideAccess: false,\n req,\n user: req.user,\n where: {\n id: {\n in: [tenantFromCookie],\n },\n },\n })\n return isValidTenant ? tenantFromCookie : null\n }\n if (req.user && isAutosaveEnabled) {\n const userTenants = getUserTenantIDs(req.user, {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n })\n if (userTenants.length > 0) {\n return userTenants[0]\n }\n }\n return null\n }),\n filterOptions:\n overrides.filterOptions ||\n (({ req }) => {\n const userAssignedTenants = getUserTenantIDs(req.user, {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n })\n if (userAssignedTenants.length > 0) {\n return {\n id: {\n in: userAssignedTenants,\n },\n }\n }\n\n return true\n }),\n index: true,\n relationTo: tenantsCollectionSlug,\n unique,\n ...(hasMany\n ? {\n hasMany: true,\n // TODO: V4 - replace validation with required: true\n validate: fieldValidation(validate as RelationshipFieldValidation),\n }\n : {\n hasMany: false,\n // TODO: V4 - replace validation with required: true\n validate: fieldValidation(validate as RelationshipFieldValidation),\n }),\n // @ts-expect-error translations are not typed for this plugin\n label: overrides.label || (({ t }) => t('plugin-multi-tenant:field-assignedTenant-label')),\n }\n}\n"],"names":["defaults","getCollectionIDType","getTenantFromCookie","getUserTenantIDs","fieldValidation","validateFunction","value","options","result","hasMany","Array","isArray","length","req","t","tenantField","name","tenantFieldName","debug","isAutosaveEnabled","overrides","_overrides","tenantsArrayFieldName","tenantsArrayTenantFieldName","tenantsCollectionSlug","tenantCollectionSlug","unique","validate","type","access","admin","allowCreate","allowEdit","disabled","column","filter","groupBy","position","components","Field","path","clientProps","defaultValue","idType","collectionSlug","payload","tenantFromCookie","headers","isValidTenant","count","collection","overrideAccess","user","where","id","in","userTenants","filterOptions","userAssignedTenants","index","relationTo","label"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/fields/tenantField/index.ts"],"sourcesContent":["import type { RelationshipFieldValidation, SingleRelationshipField } from 'payload'\n\nimport type { MultiTenantPluginConfig, RootTenantFieldConfigOverrides } from '../../types.js'\n\nimport { defaults } from '../../defaults.js'\nimport { getCollectionIDType } from '../../utilities/getCollectionIDType.js'\nimport { getTenantFromCookie } from '../../utilities/getTenantFromCookie.js'\nimport { getUserTenantIDs } from '../../utilities/getUserTenantIDs.js'\nimport { isValidTenantAssignment } from '../../utilities/isValidTenantAssignment.js'\n\nconst fieldValidation =\n ({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n validateFunction,\n }: {\n tenantsArrayFieldName: string\n tenantsArrayTenantFieldName: string\n userHasAccessToAllTenants?: MultiTenantPluginConfig['userHasAccessToAllTenants']\n validateFunction?: RelationshipFieldValidation\n }): RelationshipFieldValidation =>\n async (value, options) => {\n if (validateFunction) {\n const result = await validateFunction(value, options)\n if (result !== true) {\n return result\n }\n }\n\n if (options.hasMany) {\n if (!value || (Array.isArray(value) && value.length === 0)) {\n return options.req.t('validation:required')\n }\n } else {\n if (!value) {\n return options.req.t('validation:required')\n }\n }\n\n if (\n isValidTenantAssignment({\n previousValue: options.previousValue,\n req: options.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n value,\n })\n ) {\n return true\n }\n\n return options.req.t('validation:invalidSelection')\n }\n\ntype Args = {\n adminUsersSlug?: string\n debug?: boolean\n isAutosaveEnabled?: boolean\n name: string\n overrides?: RootTenantFieldConfigOverrides\n tenantsArrayFieldName: string\n tenantsArrayTenantFieldName: string\n tenantsCollectionSlug: string\n unique: boolean\n userHasAccessToAllTenants?: MultiTenantPluginConfig['userHasAccessToAllTenants']\n}\nexport const tenantField = ({\n name = defaults.tenantFieldName,\n adminUsersSlug,\n debug,\n isAutosaveEnabled,\n overrides: _overrides = {},\n tenantsArrayFieldName = defaults.tenantsArrayFieldName,\n tenantsArrayTenantFieldName = defaults.tenantsArrayTenantFieldName,\n tenantsCollectionSlug = defaults.tenantCollectionSlug,\n unique,\n userHasAccessToAllTenants,\n}: Args): SingleRelationshipField => {\n const { hasMany = false, validate, ...overrides } = _overrides || {}\n return {\n ...(overrides || {}),\n name,\n type: 'relationship',\n access: {\n ...(adminUsersSlug\n ? {\n create: ({ req }) => Boolean(req.user && req.user.collection === adminUsersSlug),\n update: ({ req }) => Boolean(req.user && req.user.collection === adminUsersSlug),\n }\n : {}),\n ...(overrides.access || {}),\n },\n admin: {\n allowCreate: false,\n allowEdit: false,\n disabled: { column: true, filter: true, groupBy: true },\n position: 'sidebar',\n ...(overrides.admin || {}),\n components: {\n ...(overrides.admin?.components || {}),\n Field: {\n path: '@payloadcms/plugin-multi-tenant/client#TenantField',\n ...(typeof overrides.admin?.components?.Field !== 'string'\n ? overrides.admin?.components?.Field || {}\n : {}),\n clientProps: {\n ...(typeof overrides.admin?.components?.Field !== 'string'\n ? (overrides.admin?.components?.Field || {})?.clientProps\n : {}),\n debug,\n unique,\n },\n },\n },\n },\n defaultValue:\n overrides.defaultValue ||\n (async ({ req }) => {\n const idType = getCollectionIDType({\n collectionSlug: tenantsCollectionSlug,\n payload: req.payload,\n })\n const tenantFromCookie = getTenantFromCookie(req.headers, idType)\n if (tenantFromCookie) {\n const { totalDocs } = await req.payload.count({\n collection: tenantsCollectionSlug,\n overrideAccess: false,\n req,\n user: req.user,\n where: {\n id: {\n in: [tenantFromCookie],\n },\n },\n })\n return totalDocs > 0 ? tenantFromCookie : null\n }\n if (req.user && isAutosaveEnabled) {\n const userTenants = getUserTenantIDs(req.user, {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n })\n if (userTenants.length > 0) {\n return userTenants[0]\n }\n }\n return null\n }),\n filterOptions:\n overrides.filterOptions ||\n (({ req }) => {\n const userAssignedTenants = getUserTenantIDs(req.user, {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n })\n if (userAssignedTenants.length > 0) {\n return {\n id: {\n in: userAssignedTenants,\n },\n }\n }\n\n return true\n }),\n index: true,\n relationTo: tenantsCollectionSlug,\n unique,\n ...(hasMany\n ? {\n hasMany: true,\n // TODO: V4 - replace validation with required: true\n validate: fieldValidation({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n validateFunction: validate as RelationshipFieldValidation,\n }),\n }\n : {\n hasMany: false,\n // TODO: V4 - replace validation with required: true\n validate: fieldValidation({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n validateFunction: validate as RelationshipFieldValidation,\n }),\n }),\n // @ts-expect-error translations are not typed for this plugin\n label: overrides.label || (({ t }) => t('plugin-multi-tenant:field-assignedTenant-label')),\n }\n}\n"],"names":["defaults","getCollectionIDType","getTenantFromCookie","getUserTenantIDs","isValidTenantAssignment","fieldValidation","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","validateFunction","value","options","result","hasMany","Array","isArray","length","req","t","previousValue","tenantField","name","tenantFieldName","adminUsersSlug","debug","isAutosaveEnabled","overrides","_overrides","tenantsCollectionSlug","tenantCollectionSlug","unique","validate","type","access","create","Boolean","user","collection","update","admin","allowCreate","allowEdit","disabled","column","filter","groupBy","position","components","Field","path","clientProps","defaultValue","idType","collectionSlug","payload","tenantFromCookie","headers","totalDocs","count","overrideAccess","where","id","in","userTenants","filterOptions","userAssignedTenants","index","relationTo","label"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { CollectionBeforeValidateHook, CollectionConfig } from 'payload';
2
+ import type { MultiTenantPluginConfig } from '../types.js';
3
+ type Args = {
4
+ collection: CollectionConfig;
5
+ tenantFieldName: string;
6
+ tenantsArrayFieldName: string;
7
+ tenantsArrayTenantFieldName: string;
8
+ userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'];
9
+ };
10
+ export declare const addCollectionBeforeValidateHook: ({ collection, tenantFieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, }: Args) => void;
11
+ export declare const enforceTenantMembership: ({ collectionSlug, tenantFieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, }: {
12
+ collectionSlug: string;
13
+ } & Omit<Args, 'collection'>) => CollectionBeforeValidateHook;
14
+ export {};
15
+ //# sourceMappingURL=enforceTenantMembership.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforceTenantMembership.d.ts","sourceRoot":"","sources":["../../src/hooks/enforceTenantMembership.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAI7E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAI1D,KAAK,IAAI,GAAG;IACV,UAAU,EAAE,gBAAgB,CAAA;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,2BAA2B,EAAE,MAAM,CAAA;IACnC,yBAAyB,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,2BAA2B,CAAC,CAAA;CAC1F,CAAA;AAED,eAAO,MAAM,+BAA+B,oHAMzC,IAAI,SAYN,CAAA;AAED,eAAO,MAAM,uBAAuB,wHAO/B;IAAE,cAAc,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,KAAG,4BA0B1D,CAAA"}
@@ -0,0 +1,39 @@
1
+ import { ValidationError } from 'payload';
2
+ import { isValidTenantAssignment } from '../utilities/isValidTenantAssignment.js';
3
+ export const addCollectionBeforeValidateHook = ({ collection, tenantFieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants })=>{
4
+ collection.hooks ??= {};
5
+ collection.hooks.beforeValidate ??= [];
6
+ collection.hooks.beforeValidate.push(enforceTenantMembership({
7
+ collectionSlug: collection.slug,
8
+ tenantFieldName,
9
+ tenantsArrayFieldName,
10
+ tenantsArrayTenantFieldName,
11
+ userHasAccessToAllTenants
12
+ }));
13
+ };
14
+ export const enforceTenantMembership = ({ collectionSlug, tenantFieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants })=>({ data, originalDoc, req })=>{
15
+ if (!data || !(tenantFieldName in data)) {
16
+ return data;
17
+ }
18
+ if (!isValidTenantAssignment({
19
+ previousValue: originalDoc?.[tenantFieldName],
20
+ req,
21
+ tenantsArrayFieldName,
22
+ tenantsArrayTenantFieldName,
23
+ userHasAccessToAllTenants,
24
+ value: data[tenantFieldName]
25
+ })) {
26
+ throw new ValidationError({
27
+ collection: collectionSlug,
28
+ errors: [
29
+ {
30
+ message: req.t('validation:invalidSelection'),
31
+ path: tenantFieldName
32
+ }
33
+ ]
34
+ }, req.t);
35
+ }
36
+ return data;
37
+ };
38
+
39
+ //# sourceMappingURL=enforceTenantMembership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/enforceTenantMembership.ts"],"sourcesContent":["import type { CollectionBeforeValidateHook, CollectionConfig } from 'payload'\n\nimport { ValidationError } from 'payload'\n\nimport type { MultiTenantPluginConfig } from '../types.js'\n\nimport { isValidTenantAssignment } from '../utilities/isValidTenantAssignment.js'\n\ntype Args = {\n collection: CollectionConfig\n tenantFieldName: string\n tenantsArrayFieldName: string\n tenantsArrayTenantFieldName: string\n userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants']\n}\n\nexport const addCollectionBeforeValidateHook = ({\n collection,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n}: Args) => {\n collection.hooks ??= {}\n collection.hooks.beforeValidate ??= []\n collection.hooks.beforeValidate.push(\n enforceTenantMembership({\n collectionSlug: collection.slug,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n }),\n )\n}\n\nexport const enforceTenantMembership =\n ({\n collectionSlug,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n }: { collectionSlug: string } & Omit<Args, 'collection'>): CollectionBeforeValidateHook =>\n ({ data, originalDoc, req }) => {\n if (!data || !(tenantFieldName in data)) {\n return data\n }\n\n if (\n !isValidTenantAssignment({\n previousValue: originalDoc?.[tenantFieldName],\n req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n value: (data as Record<string, unknown>)[tenantFieldName],\n })\n ) {\n throw new ValidationError(\n {\n collection: collectionSlug,\n errors: [{ message: req.t('validation:invalidSelection'), path: tenantFieldName }],\n },\n req.t,\n )\n }\n\n return data\n }\n"],"names":["ValidationError","isValidTenantAssignment","addCollectionBeforeValidateHook","collection","tenantFieldName","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","hooks","beforeValidate","push","enforceTenantMembership","collectionSlug","slug","data","originalDoc","req","previousValue","value","errors","message","t","path"],"mappings":"AAEA,SAASA,eAAe,QAAQ,UAAS;AAIzC,SAASC,uBAAuB,QAAQ,0CAAyC;AAUjF,OAAO,MAAMC,kCAAkC,CAAC,EAC9CC,UAAU,EACVC,eAAe,EACfC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EACpB;IACLJ,WAAWK,KAAK,KAAK,CAAC;IACtBL,WAAWK,KAAK,CAACC,cAAc,KAAK,EAAE;IACtCN,WAAWK,KAAK,CAACC,cAAc,CAACC,IAAI,CAClCC,wBAAwB;QACtBC,gBAAgBT,WAAWU,IAAI;QAC/BT;QACAC;QACAC;QACAC;IACF;AAEJ,EAAC;AAED,OAAO,MAAMI,0BACX,CAAC,EACCC,cAAc,EACdR,eAAe,EACfC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EAC6B,GACxD,CAAC,EAAEO,IAAI,EAAEC,WAAW,EAAEC,GAAG,EAAE;QACzB,IAAI,CAACF,QAAQ,CAAEV,CAAAA,mBAAmBU,IAAG,GAAI;YACvC,OAAOA;QACT;QAEA,IACE,CAACb,wBAAwB;YACvBgB,eAAeF,aAAa,CAACX,gBAAgB;YAC7CY;YACAX;YACAC;YACAC;YACAW,OAAO,AAACJ,IAAgC,CAACV,gBAAgB;QAC3D,IACA;YACA,MAAM,IAAIJ,gBACR;gBACEG,YAAYS;gBACZO,QAAQ;oBAAC;wBAAEC,SAASJ,IAAIK,CAAC,CAAC;wBAAgCC,MAAMlB;oBAAgB;iBAAE;YACpF,GACAY,IAAIK,CAAC;QAET;QAEA,OAAOP;IACT,EAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAczD,eAAO,MAAM,iBAAiB,gEAua5B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAezD,eAAO,MAAM,iBAAiB,gEA6b5B,CAAA"}
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { tenantField } from './fields/tenantField/index.js';
6
6
  import { tenantsArrayField } from './fields/tenantsArrayField/index.js';
7
7
  import { filterDocumentsByTenants } from './filters/filterDocumentsByTenants.js';
8
8
  import { addTenantCleanup } from './hooks/afterTenantDelete.js';
9
+ import { enforceTenantMembership } from './hooks/enforceTenantMembership.js';
9
10
  import { translations } from './translations/index.js';
10
11
  import { addCollectionAccess } from './utilities/addCollectionAccess.js';
11
12
  import { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js';
@@ -24,6 +25,7 @@ export const multiTenantPlugin = definePlugin({
24
25
  const tenantFieldName = pluginConfig?.tenantField?.name || defaults.tenantFieldName;
25
26
  const tenantsArrayFieldName = pluginConfig?.tenantsArrayField?.arrayFieldName || defaults.tenantsArrayFieldName;
26
27
  const tenantsArrayTenantFieldName = pluginConfig?.tenantsArrayField?.arrayTenantFieldName || defaults.tenantsArrayTenantFieldName;
28
+ const tenantAccessScopes = [];
27
29
  /**
28
30
  * Add defaults for admin properties
29
31
  */ if (!incomingConfig.admin) {
@@ -65,12 +67,17 @@ export const multiTenantPlugin = definePlugin({
65
67
  */ if (pluginConfig?.tenantsArrayField?.includeDefaultField !== false) {
66
68
  adminUsersCollection.fields.push(tenantsArrayField({
67
69
  ...pluginConfig?.tenantsArrayField || {},
70
+ arrayFieldAccess: {
71
+ create: ({ req })=>Boolean(req.user && userHasAccessToAllTenants(req.user)),
72
+ update: ({ req })=>Boolean(req.user && userHasAccessToAllTenants(req.user)),
73
+ ...pluginConfig?.tenantsArrayField?.arrayFieldAccess || {}
74
+ },
68
75
  tenantsArrayFieldName,
69
76
  tenantsArrayTenantFieldName,
70
77
  tenantsCollectionSlug
71
78
  }));
72
79
  }
73
- addCollectionAccess({
80
+ tenantAccessScopes.push({
74
81
  accessResultCallback: pluginConfig.usersAccessResultOverride,
75
82
  adminUsersSlug: adminUsersCollection.slug,
76
83
  collection: adminUsersCollection,
@@ -126,7 +133,7 @@ export const multiTenantPlugin = definePlugin({
126
133
  /**
127
134
  * Add access control constraint to tenants collection
128
135
  * - constrains access a users assigned tenants
129
- */ addCollectionAccess({
136
+ */ tenantAccessScopes.push({
130
137
  adminUsersSlug: adminUsersCollection.slug,
131
138
  collection,
132
139
  fieldName: 'id',
@@ -239,15 +246,26 @@ export const multiTenantPlugin = definePlugin({
239
246
  * Add tenant field to enabled collections
240
247
  */ collection.fields.unshift(tenantField({
241
248
  name: tenantFieldName,
249
+ adminUsersSlug: adminUsersCollection.slug,
242
250
  debug: pluginConfig.debug,
243
251
  isAutosaveEnabled: hasAutosaveEnabled(collection),
244
252
  overrides: pluginConfig.collections[collection.slug]?.tenantFieldOverrides ? pluginConfig.collections[collection.slug]?.tenantFieldOverrides : pluginConfig.tenantField || {},
245
253
  tenantsArrayFieldName,
246
254
  tenantsArrayTenantFieldName,
247
255
  tenantsCollectionSlug,
248
- unique: isGlobal
256
+ unique: isGlobal,
257
+ userHasAccessToAllTenants
249
258
  }));
250
259
  }
260
+ collection.hooks ??= {};
261
+ collection.hooks.beforeValidate ??= [];
262
+ collection.hooks.beforeValidate.push(enforceTenantMembership({
263
+ collectionSlug: collection.slug,
264
+ tenantFieldName,
265
+ tenantsArrayFieldName,
266
+ tenantsArrayTenantFieldName,
267
+ userHasAccessToAllTenants
268
+ }));
251
269
  const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {};
252
270
  if (useBaseFilter ?? useBaseListFilter ?? true) {
253
271
  /**
@@ -269,7 +287,7 @@ export const multiTenantPlugin = definePlugin({
269
287
  if (pluginConfig.collections[collection.slug]?.useTenantAccess !== false) {
270
288
  /**
271
289
  * Add access control constraint to tenant enabled collection
272
- */ addCollectionAccess({
290
+ */ tenantAccessScopes.push({
273
291
  accessResultCallback: pluginConfig.collections[collection.slug]?.accessResultOverride,
274
292
  adminUsersSlug: adminUsersCollection.slug,
275
293
  collection,
@@ -292,6 +310,10 @@ export const multiTenantPlugin = definePlugin({
292
310
  // eslint-disable-next-line no-console
293
311
  console.error(miniChalk.yellowBold('WARNING (plugin-multi-tenant)'), 'missing collections', missingSlugs, 'try placing the multi-tenant plugin after other plugins.');
294
312
  }
313
+ addCollectionAccess({
314
+ config: incomingConfig,
315
+ scopes: tenantAccessScopes
316
+ });
295
317
  /**
296
318
  * Add TenantSelectionProvider to admin providers
297
319
  */ incomingConfig.admin.components.providers.push({
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { AcceptedLanguages } from '@payloadcms/translations'\nimport type { CollectionConfig } from 'payload'\n\nimport { definePlugin } from 'payload'\nimport { hasAutosaveEnabled } from 'payload/shared'\n\nimport type { PluginDefaultTranslationsObject } from './translations/types.js'\nimport type { MultiTenantPluginConfig } from './types.js'\n\nimport { defaults } from './defaults.js'\nimport { getTenantOptionsEndpoint } from './endpoints/getTenantOptionsEndpoint.js'\nimport { tenantField } from './fields/tenantField/index.js'\nimport { tenantsArrayField } from './fields/tenantsArrayField/index.js'\nimport { filterDocumentsByTenants } from './filters/filterDocumentsByTenants.js'\nimport { addTenantCleanup } from './hooks/afterTenantDelete.js'\nimport { translations } from './translations/index.js'\nimport { addCollectionAccess } from './utilities/addCollectionAccess.js'\nimport { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'\nimport { combineFilters } from './utilities/combineFilters.js'\nimport { miniChalk } from './utilities/miniChalk.js'\n\nexport const multiTenantPlugin = definePlugin<MultiTenantPluginConfig>({\n slug: '@payloadcms/plugin-multi-tenant',\n plugin: ({ config: incomingConfig, options: pluginConfig }) => {\n if (pluginConfig.enabled === false) {\n return incomingConfig\n }\n\n /**\n * Set defaults\n */\n const userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'] =\n typeof pluginConfig.userHasAccessToAllTenants === 'function'\n ? pluginConfig.userHasAccessToAllTenants\n : () => false\n const tenantsCollectionSlug = (pluginConfig.tenantsSlug =\n pluginConfig.tenantsSlug || defaults.tenantCollectionSlug)\n const tenantFieldName = pluginConfig?.tenantField?.name || defaults.tenantFieldName\n const tenantsArrayFieldName =\n pluginConfig?.tenantsArrayField?.arrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n pluginConfig?.tenantsArrayField?.arrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n\n /**\n * Add defaults for admin properties\n */\n if (!incomingConfig.admin) {\n incomingConfig.admin = {}\n }\n if (!incomingConfig.admin?.components) {\n incomingConfig.admin.components = {\n actions: [],\n beforeNav: [],\n providers: [],\n }\n }\n if (!incomingConfig.admin.components?.providers) {\n incomingConfig.admin.components.providers = []\n }\n if (!incomingConfig.admin.components?.actions) {\n incomingConfig.admin.components.actions = []\n }\n if (!incomingConfig.admin.components?.beforeNav) {\n incomingConfig.admin.components.beforeNav = []\n }\n if (!incomingConfig.collections) {\n incomingConfig.collections = []\n }\n\n /**\n * Add tenants array field to users collection\n */\n const adminUsersCollection = incomingConfig.collections.find(({ slug, auth }) => {\n if (incomingConfig.admin?.user) {\n return slug === incomingConfig.admin.user\n } else if (auth) {\n return true\n }\n })\n\n if (!adminUsersCollection) {\n throw Error('An auth enabled collection was not found')\n }\n\n /**\n * Add tenants array field to users collection\n */\n if (pluginConfig?.tenantsArrayField?.includeDefaultField !== false) {\n adminUsersCollection.fields.push(\n tenantsArrayField({\n ...(pluginConfig?.tenantsArrayField || {}),\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n }),\n )\n }\n\n addCollectionAccess({\n accessResultCallback: pluginConfig.usersAccessResultOverride,\n adminUsersSlug: adminUsersCollection.slug,\n collection: adminUsersCollection,\n fieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.useUsersTenantFilter !== false) {\n if (!adminUsersCollection.admin) {\n adminUsersCollection.admin = {}\n }\n\n const baseFilter =\n adminUsersCollection.admin?.baseFilter ?? adminUsersCollection.admin?.baseListFilter\n adminUsersCollection.admin.baseFilter = combineFilters({\n baseFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n let tenantCollection: CollectionConfig | undefined\n\n const [collectionSlugs, globalCollectionSlugs] = Object.keys(pluginConfig.collections).reduce<\n [string[], string[]]\n >(\n (acc, slug) => {\n if (slug === adminUsersCollection.slug) {\n return acc\n }\n if (pluginConfig?.collections?.[slug]?.isGlobal) {\n acc[1].push(slug)\n } else {\n acc[0].push(slug)\n }\n\n return acc\n },\n [[], []],\n )\n\n // used to track and not duplicate filterOptions on referenced blocks\n const blockReferencesWithFilters: string[] = []\n\n // used to validate enabled collection slugs\n const multiTenantCollectionsFound: string[] = []\n\n /**\n * Modify collections\n */\n incomingConfig.collections.forEach((collection) => {\n /**\n * Modify tenants collection\n */\n if (collection.slug === tenantsCollectionSlug) {\n tenantCollection = collection\n\n if (pluginConfig.useTenantsCollectionAccess !== false) {\n /**\n * Add access control constraint to tenants collection\n * - constrains access a users assigned tenants\n */\n addCollectionAccess({\n adminUsersSlug: adminUsersCollection.slug,\n collection,\n fieldName: 'id',\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n\n if (pluginConfig.useTenantsListFilter !== false) {\n /**\n * Add list filter to tenants collection\n * - filter by selected tenant\n */\n if (!collection.admin) {\n collection.admin = {}\n }\n\n const baseFilter = collection.admin?.baseFilter ?? collection.admin?.baseListFilter\n collection.admin.baseFilter = combineFilters({\n baseFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: 'id',\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n if (pluginConfig.cleanupAfterTenantDelete !== false) {\n /**\n * Add cleanup logic when tenant is deleted\n * - delete documents related to tenant\n * - remove tenant from users\n */\n addTenantCleanup({\n collection,\n enabledSlugs: [...collectionSlugs, ...globalCollectionSlugs],\n tenantFieldName,\n tenantsCollectionSlug,\n usersSlug: adminUsersCollection.slug,\n usersTenantsArrayFieldName: tenantsArrayFieldName,\n usersTenantsArrayTenantFieldName: tenantsArrayTenantFieldName,\n })\n }\n\n /**\n * Add custom tenant field that watches and dispatches updates to the selector\n */\n collection.fields.push({\n name: '_watchTenant',\n type: 'ui',\n admin: {\n components: {\n Field: {\n path: '@payloadcms/plugin-multi-tenant/client#WatchTenantCollection',\n },\n },\n disabled: { bulkEdit: true, column: true },\n },\n })\n\n collection.endpoints = [\n ...(collection.endpoints || []),\n getTenantOptionsEndpoint({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n }),\n ]\n } else if (pluginConfig.collections?.[collection.slug]) {\n if (collection.slug === adminUsersCollection.slug) {\n // eslint-disable-next-line no-console\n console.warn(\n `[plugin-multi-tenant] The admin users collection \"${collection.slug}\" should not be listed in pluginConfig.collections — it is already handled by the plugin. Skipping tenant-field processing for this collection to avoid double access control and validation errors.`,\n )\n return\n }\n multiTenantCollectionsFound.push(collection.slug)\n const isGlobal = Boolean(pluginConfig.collections[collection.slug]?.isGlobal)\n\n if (isGlobal) {\n collection.disableDuplicate = true\n }\n\n if (!pluginConfig.debug && !isGlobal) {\n collection.admin ??= {}\n collection.admin.components ??= {}\n collection.admin.components.edit ??= {}\n collection.admin.components.edit.editMenuItems ??= []\n collection.admin.components.edit.editMenuItems.push({\n path: '@payloadcms/plugin-multi-tenant/client#AssignTenantFieldTrigger',\n })\n }\n\n /**\n * Add filter options to all relationship fields\n */\n collection.fields = addFilterOptionsToFields({\n blockReferencesWithFilters,\n config: incomingConfig,\n fields: collection.fields,\n tenantEnabledCollectionSlugs: collectionSlugs,\n tenantEnabledGlobalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.collections[collection.slug]?.customTenantField !== true) {\n /**\n * Add tenant field to enabled collections\n */\n collection.fields.unshift(\n tenantField({\n name: tenantFieldName,\n debug: pluginConfig.debug,\n isAutosaveEnabled: hasAutosaveEnabled(collection),\n overrides: pluginConfig.collections[collection.slug]?.tenantFieldOverrides\n ? pluginConfig.collections[collection.slug]?.tenantFieldOverrides\n : pluginConfig.tenantField || {},\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n unique: isGlobal,\n }),\n )\n }\n\n const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {}\n if (useBaseFilter ?? useBaseListFilter ?? true) {\n /**\n * Add list filter to enabled collections\n * - filters results by selected tenant\n */\n collection.admin = collection.admin || {}\n collection.admin.baseFilter = combineFilters({\n baseFilter: collection.admin?.baseFilter ?? collection.admin?.baseListFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: tenantFieldName,\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n if (pluginConfig.collections[collection.slug]?.useTenantAccess !== false) {\n /**\n * Add access control constraint to tenant enabled collection\n */\n addCollectionAccess({\n accessResultCallback: pluginConfig.collections[collection.slug]?.accessResultOverride,\n adminUsersSlug: adminUsersCollection.slug,\n collection,\n fieldName: tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n }\n })\n\n if (!tenantCollection) {\n throw new Error(`Tenants collection not found with slug: ${tenantsCollectionSlug}`)\n }\n\n if (\n multiTenantCollectionsFound.length !==\n collectionSlugs.length + globalCollectionSlugs.length\n ) {\n const missingSlugs = [...collectionSlugs, ...globalCollectionSlugs].filter(\n (slug) => !multiTenantCollectionsFound.includes(slug),\n )\n // eslint-disable-next-line no-console\n console.error(\n miniChalk.yellowBold('WARNING (plugin-multi-tenant)'),\n 'missing collections',\n missingSlugs,\n 'try placing the multi-tenant plugin after other plugins.',\n )\n }\n\n /**\n * Add TenantSelectionProvider to admin providers\n */\n incomingConfig.admin.components.providers.push({\n clientProps: {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug: tenantCollection.slug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelectionProvider',\n })\n\n /**\n * Add global redirect action\n */\n if (globalCollectionSlugs.length) {\n incomingConfig.admin.components.actions.push({\n path: '@payloadcms/plugin-multi-tenant/rsc#GlobalViewRedirect',\n serverProps: {\n globalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n },\n })\n }\n\n /**\n * Add tenant selector to admin UI\n */\n incomingConfig.admin.components.beforeNav.push({\n clientProps: {\n enabledSlugs: [\n ...collectionSlugs,\n ...globalCollectionSlugs,\n adminUsersCollection.slug,\n tenantCollection.slug,\n ],\n label: pluginConfig.tenantSelectorLabel || undefined,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelector',\n })\n\n /**\n * Merge plugin translations\n */\n if (!incomingConfig.i18n) {\n incomingConfig.i18n = {}\n }\n Object.entries(translations).forEach(([locale, pluginI18nObject]) => {\n const typedLocale = locale as AcceptedLanguages\n if (!incomingConfig.i18n!.translations) {\n incomingConfig.i18n!.translations = {}\n }\n if (!(typedLocale in incomingConfig.i18n!.translations)) {\n incomingConfig.i18n!.translations[typedLocale] = {}\n }\n if (!('plugin-multi-tenant' in incomingConfig.i18n!.translations[typedLocale]!)) {\n ;(incomingConfig.i18n!.translations[typedLocale] as PluginDefaultTranslationsObject)[\n 'plugin-multi-tenant'\n ] = {} as PluginDefaultTranslationsObject['plugin-multi-tenant']\n }\n\n ;(incomingConfig.i18n!.translations[typedLocale] as PluginDefaultTranslationsObject)[\n 'plugin-multi-tenant'\n ] = {\n ...pluginI18nObject.translations['plugin-multi-tenant'],\n ...(pluginConfig.i18n?.translations?.[typedLocale] || {}),\n }\n })\n\n return incomingConfig\n },\n})\n"],"names":["definePlugin","hasAutosaveEnabled","defaults","getTenantOptionsEndpoint","tenantField","tenantsArrayField","filterDocumentsByTenants","addTenantCleanup","translations","addCollectionAccess","addFilterOptionsToFields","combineFilters","miniChalk","multiTenantPlugin","slug","plugin","config","incomingConfig","options","pluginConfig","enabled","userHasAccessToAllTenants","tenantsCollectionSlug","tenantsSlug","tenantCollectionSlug","tenantFieldName","name","tenantsArrayFieldName","arrayFieldName","tenantsArrayTenantFieldName","arrayTenantFieldName","admin","components","actions","beforeNav","providers","collections","adminUsersCollection","find","auth","user","Error","includeDefaultField","fields","push","accessResultCallback","usersAccessResultOverride","adminUsersSlug","collection","fieldName","useUsersTenantFilter","baseFilter","baseListFilter","customFilter","args","filterFieldName","req","tenantCollection","collectionSlugs","globalCollectionSlugs","Object","keys","reduce","acc","isGlobal","blockReferencesWithFilters","multiTenantCollectionsFound","forEach","useTenantsCollectionAccess","useTenantsListFilter","cleanupAfterTenantDelete","enabledSlugs","usersSlug","usersTenantsArrayFieldName","usersTenantsArrayTenantFieldName","type","Field","path","disabled","bulkEdit","column","endpoints","useAsTitle","console","warn","Boolean","disableDuplicate","debug","edit","editMenuItems","tenantEnabledCollectionSlugs","tenantEnabledGlobalSlugs","customTenantField","unshift","isAutosaveEnabled","overrides","tenantFieldOverrides","unique","useBaseFilter","useBaseListFilter","useTenantAccess","accessResultOverride","length","missingSlugs","filter","includes","error","yellowBold","clientProps","serverProps","globalSlugs","label","tenantSelectorLabel","undefined","i18n","entries","locale","pluginI18nObject","typedLocale"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { AcceptedLanguages } from '@payloadcms/translations'\nimport type { CollectionConfig } from 'payload'\n\nimport { definePlugin } from 'payload'\nimport { hasAutosaveEnabled } from 'payload/shared'\n\nimport type { PluginDefaultTranslationsObject } from './translations/types.js'\nimport type { MultiTenantPluginConfig } from './types.js'\n\nimport { defaults } from './defaults.js'\nimport { getTenantOptionsEndpoint } from './endpoints/getTenantOptionsEndpoint.js'\nimport { tenantField } from './fields/tenantField/index.js'\nimport { tenantsArrayField } from './fields/tenantsArrayField/index.js'\nimport { filterDocumentsByTenants } from './filters/filterDocumentsByTenants.js'\nimport { addTenantCleanup } from './hooks/afterTenantDelete.js'\nimport { enforceTenantMembership } from './hooks/enforceTenantMembership.js'\nimport { translations } from './translations/index.js'\nimport { addCollectionAccess, type TenantAccessConfig } from './utilities/addCollectionAccess.js'\nimport { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'\nimport { combineFilters } from './utilities/combineFilters.js'\nimport { miniChalk } from './utilities/miniChalk.js'\n\nexport const multiTenantPlugin = definePlugin<MultiTenantPluginConfig>({\n slug: '@payloadcms/plugin-multi-tenant',\n plugin: ({ config: incomingConfig, options: pluginConfig }) => {\n if (pluginConfig.enabled === false) {\n return incomingConfig\n }\n\n /**\n * Set defaults\n */\n const userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'] =\n typeof pluginConfig.userHasAccessToAllTenants === 'function'\n ? pluginConfig.userHasAccessToAllTenants\n : () => false\n const tenantsCollectionSlug = (pluginConfig.tenantsSlug =\n pluginConfig.tenantsSlug || defaults.tenantCollectionSlug)\n const tenantFieldName = pluginConfig?.tenantField?.name || defaults.tenantFieldName\n const tenantsArrayFieldName =\n pluginConfig?.tenantsArrayField?.arrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n pluginConfig?.tenantsArrayField?.arrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n const tenantAccessScopes: TenantAccessConfig[] = []\n\n /**\n * Add defaults for admin properties\n */\n if (!incomingConfig.admin) {\n incomingConfig.admin = {}\n }\n if (!incomingConfig.admin?.components) {\n incomingConfig.admin.components = {\n actions: [],\n beforeNav: [],\n providers: [],\n }\n }\n if (!incomingConfig.admin.components?.providers) {\n incomingConfig.admin.components.providers = []\n }\n if (!incomingConfig.admin.components?.actions) {\n incomingConfig.admin.components.actions = []\n }\n if (!incomingConfig.admin.components?.beforeNav) {\n incomingConfig.admin.components.beforeNav = []\n }\n if (!incomingConfig.collections) {\n incomingConfig.collections = []\n }\n\n /**\n * Add tenants array field to users collection\n */\n const adminUsersCollection = incomingConfig.collections.find(({ slug, auth }) => {\n if (incomingConfig.admin?.user) {\n return slug === incomingConfig.admin.user\n } else if (auth) {\n return true\n }\n })\n\n if (!adminUsersCollection) {\n throw Error('An auth enabled collection was not found')\n }\n\n /**\n * Add tenants array field to users collection\n */\n if (pluginConfig?.tenantsArrayField?.includeDefaultField !== false) {\n adminUsersCollection.fields.push(\n tenantsArrayField({\n ...(pluginConfig?.tenantsArrayField || {}),\n arrayFieldAccess: {\n create: ({ req }) => Boolean(req.user && userHasAccessToAllTenants(req.user)),\n update: ({ req }) => Boolean(req.user && userHasAccessToAllTenants(req.user)),\n ...(pluginConfig?.tenantsArrayField?.arrayFieldAccess || {}),\n },\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n }),\n )\n }\n\n tenantAccessScopes.push({\n accessResultCallback: pluginConfig.usersAccessResultOverride,\n adminUsersSlug: adminUsersCollection.slug,\n collection: adminUsersCollection,\n fieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.useUsersTenantFilter !== false) {\n if (!adminUsersCollection.admin) {\n adminUsersCollection.admin = {}\n }\n\n const baseFilter =\n adminUsersCollection.admin?.baseFilter ?? adminUsersCollection.admin?.baseListFilter\n adminUsersCollection.admin.baseFilter = combineFilters({\n baseFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n let tenantCollection: CollectionConfig | undefined\n\n const [collectionSlugs, globalCollectionSlugs] = Object.keys(pluginConfig.collections).reduce<\n [string[], string[]]\n >(\n (acc, slug) => {\n if (slug === adminUsersCollection.slug) {\n return acc\n }\n if (pluginConfig?.collections?.[slug]?.isGlobal) {\n acc[1].push(slug)\n } else {\n acc[0].push(slug)\n }\n\n return acc\n },\n [[], []],\n )\n\n // used to track and not duplicate filterOptions on referenced blocks\n const blockReferencesWithFilters: string[] = []\n\n // used to validate enabled collection slugs\n const multiTenantCollectionsFound: string[] = []\n\n /**\n * Modify collections\n */\n incomingConfig.collections.forEach((collection) => {\n /**\n * Modify tenants collection\n */\n if (collection.slug === tenantsCollectionSlug) {\n tenantCollection = collection\n\n if (pluginConfig.useTenantsCollectionAccess !== false) {\n /**\n * Add access control constraint to tenants collection\n * - constrains access a users assigned tenants\n */\n tenantAccessScopes.push({\n adminUsersSlug: adminUsersCollection.slug,\n collection,\n fieldName: 'id',\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n\n if (pluginConfig.useTenantsListFilter !== false) {\n /**\n * Add list filter to tenants collection\n * - filter by selected tenant\n */\n if (!collection.admin) {\n collection.admin = {}\n }\n\n const baseFilter = collection.admin?.baseFilter ?? collection.admin?.baseListFilter\n collection.admin.baseFilter = combineFilters({\n baseFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: 'id',\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n if (pluginConfig.cleanupAfterTenantDelete !== false) {\n /**\n * Add cleanup logic when tenant is deleted\n * - delete documents related to tenant\n * - remove tenant from users\n */\n addTenantCleanup({\n collection,\n enabledSlugs: [...collectionSlugs, ...globalCollectionSlugs],\n tenantFieldName,\n tenantsCollectionSlug,\n usersSlug: adminUsersCollection.slug,\n usersTenantsArrayFieldName: tenantsArrayFieldName,\n usersTenantsArrayTenantFieldName: tenantsArrayTenantFieldName,\n })\n }\n\n /**\n * Add custom tenant field that watches and dispatches updates to the selector\n */\n collection.fields.push({\n name: '_watchTenant',\n type: 'ui',\n admin: {\n components: {\n Field: {\n path: '@payloadcms/plugin-multi-tenant/client#WatchTenantCollection',\n },\n },\n disabled: { bulkEdit: true, column: true },\n },\n })\n\n collection.endpoints = [\n ...(collection.endpoints || []),\n getTenantOptionsEndpoint({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n }),\n ]\n } else if (pluginConfig.collections?.[collection.slug]) {\n if (collection.slug === adminUsersCollection.slug) {\n // eslint-disable-next-line no-console\n console.warn(\n `[plugin-multi-tenant] The admin users collection \"${collection.slug}\" should not be listed in pluginConfig.collections — it is already handled by the plugin. Skipping tenant-field processing for this collection to avoid double access control and validation errors.`,\n )\n return\n }\n multiTenantCollectionsFound.push(collection.slug)\n const isGlobal = Boolean(pluginConfig.collections[collection.slug]?.isGlobal)\n\n if (isGlobal) {\n collection.disableDuplicate = true\n }\n\n if (!pluginConfig.debug && !isGlobal) {\n collection.admin ??= {}\n collection.admin.components ??= {}\n collection.admin.components.edit ??= {}\n collection.admin.components.edit.editMenuItems ??= []\n collection.admin.components.edit.editMenuItems.push({\n path: '@payloadcms/plugin-multi-tenant/client#AssignTenantFieldTrigger',\n })\n }\n\n /**\n * Add filter options to all relationship fields\n */\n collection.fields = addFilterOptionsToFields({\n blockReferencesWithFilters,\n config: incomingConfig,\n fields: collection.fields,\n tenantEnabledCollectionSlugs: collectionSlugs,\n tenantEnabledGlobalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.collections[collection.slug]?.customTenantField !== true) {\n /**\n * Add tenant field to enabled collections\n */\n collection.fields.unshift(\n tenantField({\n name: tenantFieldName,\n adminUsersSlug: adminUsersCollection.slug,\n debug: pluginConfig.debug,\n isAutosaveEnabled: hasAutosaveEnabled(collection),\n overrides: pluginConfig.collections[collection.slug]?.tenantFieldOverrides\n ? pluginConfig.collections[collection.slug]?.tenantFieldOverrides\n : pluginConfig.tenantField || {},\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n unique: isGlobal,\n userHasAccessToAllTenants,\n }),\n )\n }\n\n collection.hooks ??= {}\n collection.hooks.beforeValidate ??= []\n collection.hooks.beforeValidate.push(\n enforceTenantMembership({\n collectionSlug: collection.slug,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n }),\n )\n\n const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {}\n if (useBaseFilter ?? useBaseListFilter ?? true) {\n /**\n * Add list filter to enabled collections\n * - filters results by selected tenant\n */\n collection.admin = collection.admin || {}\n collection.admin.baseFilter = combineFilters({\n baseFilter: collection.admin?.baseFilter ?? collection.admin?.baseListFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: tenantFieldName,\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n if (pluginConfig.collections[collection.slug]?.useTenantAccess !== false) {\n /**\n * Add access control constraint to tenant enabled collection\n */\n tenantAccessScopes.push({\n accessResultCallback: pluginConfig.collections[collection.slug]?.accessResultOverride,\n adminUsersSlug: adminUsersCollection.slug,\n collection,\n fieldName: tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n }\n })\n\n if (!tenantCollection) {\n throw new Error(`Tenants collection not found with slug: ${tenantsCollectionSlug}`)\n }\n\n if (\n multiTenantCollectionsFound.length !==\n collectionSlugs.length + globalCollectionSlugs.length\n ) {\n const missingSlugs = [...collectionSlugs, ...globalCollectionSlugs].filter(\n (slug) => !multiTenantCollectionsFound.includes(slug),\n )\n // eslint-disable-next-line no-console\n console.error(\n miniChalk.yellowBold('WARNING (plugin-multi-tenant)'),\n 'missing collections',\n missingSlugs,\n 'try placing the multi-tenant plugin after other plugins.',\n )\n }\n\n addCollectionAccess({ config: incomingConfig, scopes: tenantAccessScopes })\n\n /**\n * Add TenantSelectionProvider to admin providers\n */\n incomingConfig.admin.components.providers.push({\n clientProps: {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug: tenantCollection.slug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelectionProvider',\n })\n\n /**\n * Add global redirect action\n */\n if (globalCollectionSlugs.length) {\n incomingConfig.admin.components.actions.push({\n path: '@payloadcms/plugin-multi-tenant/rsc#GlobalViewRedirect',\n serverProps: {\n globalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n },\n })\n }\n\n /**\n * Add tenant selector to admin UI\n */\n incomingConfig.admin.components.beforeNav.push({\n clientProps: {\n enabledSlugs: [\n ...collectionSlugs,\n ...globalCollectionSlugs,\n adminUsersCollection.slug,\n tenantCollection.slug,\n ],\n label: pluginConfig.tenantSelectorLabel || undefined,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelector',\n })\n\n /**\n * Merge plugin translations\n */\n if (!incomingConfig.i18n) {\n incomingConfig.i18n = {}\n }\n Object.entries(translations).forEach(([locale, pluginI18nObject]) => {\n const typedLocale = locale as AcceptedLanguages\n if (!incomingConfig.i18n!.translations) {\n incomingConfig.i18n!.translations = {}\n }\n if (!(typedLocale in incomingConfig.i18n!.translations)) {\n incomingConfig.i18n!.translations[typedLocale] = {}\n }\n if (!('plugin-multi-tenant' in incomingConfig.i18n!.translations[typedLocale]!)) {\n ;(incomingConfig.i18n!.translations[typedLocale] as PluginDefaultTranslationsObject)[\n 'plugin-multi-tenant'\n ] = {} as PluginDefaultTranslationsObject['plugin-multi-tenant']\n }\n\n ;(incomingConfig.i18n!.translations[typedLocale] as PluginDefaultTranslationsObject)[\n 'plugin-multi-tenant'\n ] = {\n ...pluginI18nObject.translations['plugin-multi-tenant'],\n ...(pluginConfig.i18n?.translations?.[typedLocale] || {}),\n }\n })\n\n return incomingConfig\n },\n})\n"],"names":["definePlugin","hasAutosaveEnabled","defaults","getTenantOptionsEndpoint","tenantField","tenantsArrayField","filterDocumentsByTenants","addTenantCleanup","enforceTenantMembership","translations","addCollectionAccess","addFilterOptionsToFields","combineFilters","miniChalk","multiTenantPlugin","slug","plugin","config","incomingConfig","options","pluginConfig","enabled","userHasAccessToAllTenants","tenantsCollectionSlug","tenantsSlug","tenantCollectionSlug","tenantFieldName","name","tenantsArrayFieldName","arrayFieldName","tenantsArrayTenantFieldName","arrayTenantFieldName","tenantAccessScopes","admin","components","actions","beforeNav","providers","collections","adminUsersCollection","find","auth","user","Error","includeDefaultField","fields","push","arrayFieldAccess","create","req","Boolean","update","accessResultCallback","usersAccessResultOverride","adminUsersSlug","collection","fieldName","useUsersTenantFilter","baseFilter","baseListFilter","customFilter","args","filterFieldName","tenantCollection","collectionSlugs","globalCollectionSlugs","Object","keys","reduce","acc","isGlobal","blockReferencesWithFilters","multiTenantCollectionsFound","forEach","useTenantsCollectionAccess","useTenantsListFilter","cleanupAfterTenantDelete","enabledSlugs","usersSlug","usersTenantsArrayFieldName","usersTenantsArrayTenantFieldName","type","Field","path","disabled","bulkEdit","column","endpoints","useAsTitle","console","warn","disableDuplicate","debug","edit","editMenuItems","tenantEnabledCollectionSlugs","tenantEnabledGlobalSlugs","customTenantField","unshift","isAutosaveEnabled","overrides","tenantFieldOverrides","unique","hooks","beforeValidate","collectionSlug","useBaseFilter","useBaseListFilter","useTenantAccess","accessResultOverride","length","missingSlugs","filter","includes","error","yellowBold","scopes","clientProps","serverProps","globalSlugs","label","tenantSelectorLabel","undefined","i18n","entries","locale","pluginI18nObject","typedLocale"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../../../src/providers/TenantSelectionProvider/index.client.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAI3C,OAAO,KAAwB,MAAM,OAAO,CAAA;AAI5C,KAAK,WAAW,GAAG;IACjB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAA;IAClC;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB;;OAEG;IACH,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAC7C;;OAEG;IACH,aAAa,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAA;IACtF;;OAEG;IACH,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAA;IAC1D;;;;;OAKG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAA;IACjF;;OAEG;IACH,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAChC;;OAEG;IACH,aAAa,EAAE,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACtE,CAAA;AA+CD,eAAO,MAAM,6BAA6B,6EAKvC;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,oBAAoB,EAAE,YAAY,EAAE,CAAA;IACpC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,qBAAqB,EAAE,MAAM,CAAA;CAC9B,sBAiOA,CAAA;AAED,eAAO,MAAM,kBAAkB,mBAA2B,CAAA"}
1
+ {"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../../../src/providers/TenantSelectionProvider/index.client.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAI3C,OAAO,KAAwB,MAAM,OAAO,CAAA;AAK5C,KAAK,WAAW,GAAG;IACjB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAA;IAClC;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB;;OAEG;IACH,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAC7C;;OAEG;IACH,aAAa,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAA;IACtF;;OAEG;IACH,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAA;IAC1D;;;;;OAKG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAA;IACjF;;OAEG;IACH,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAChC;;OAEG;IACH,aAAa,EAAE,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACtE,CAAA;AA+CD,eAAO,MAAM,6BAA6B,6EAKvC;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,oBAAoB,EAAE,YAAY,EAAE,CAAA;IACpC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,qBAAqB,EAAE,MAAM,CAAA;CAC9B,sBAkOA,CAAA;AAED,eAAO,MAAM,kBAAkB,mBAA2B,CAAA"}
@@ -4,6 +4,7 @@ import { toast, useAuth, useConfig, useRouter } from '@payloadcms/ui';
4
4
  import { formatAdminURL } from 'payload/shared';
5
5
  import React, { createContext } from 'react';
6
6
  import { generateCookie } from '../../utilities/generateCookie.js';
7
+ import { shouldRefreshTenantSelection } from '../../utilities/shouldRefreshTenantSelection.js';
7
8
  const Context = /*#__PURE__*/ createContext({
8
9
  entityType: undefined,
9
10
  options: [],
@@ -176,17 +177,18 @@ export const TenantSelectionProviderClient = ({ children, initialTenantOptions,
176
177
  initialTenantOptions
177
178
  ]);
178
179
  React.useEffect(()=>{
179
- if (userChanged || initialValue && String(initialValue) !== getTenantCookie()) {
180
- if (userID) {
181
- // user logging in
182
- void syncTenants();
183
- } else {
184
- // user logging out
185
- setSelectedTenantID(undefined);
186
- deleteTenantCookie();
187
- setTenantOptions((prev)=>prev.length > 0 ? [] : prev);
188
- router.refresh();
189
- }
180
+ const hasTenantSelectionMismatch = initialValue && String(initialValue) !== getTenantCookie();
181
+ if (userID && (userChanged || tenantOptions.length === 0 || hasTenantSelectionMismatch)) {
182
+ // Sync on login, and when the provider remounts after login without tenant options.
183
+ void syncTenants();
184
+ } else if (!userID && (userChanged || hasTenantSelectionMismatch)) {
185
+ // user logging out
186
+ setSelectedTenantID(undefined);
187
+ deleteTenantCookie();
188
+ setTenantOptions((prev)=>prev.length > 0 ? [] : prev);
189
+ router.refresh();
190
+ }
191
+ if (userChanged) {
190
192
  prevUserID.current = userID;
191
193
  }
192
194
  }, [
@@ -194,7 +196,8 @@ export const TenantSelectionProviderClient = ({ children, initialTenantOptions,
194
196
  userChanged,
195
197
  syncTenants,
196
198
  initialValue,
197
- router
199
+ router,
200
+ tenantOptions.length
198
201
  ]);
199
202
  /**
200
203
  * Populate tenant options when the provider mounts already-authenticated but
@@ -219,21 +222,16 @@ export const TenantSelectionProviderClient = ({ children, initialTenantOptions,
219
222
  syncTenants
220
223
  ]);
221
224
  /**
222
- * If there is no initial value, clear the tenant and refresh the router.
223
- * Needed for stale tenantIDs set as a cookie.
224
- *
225
- * Only refresh when there is actually a stale cookie to clear: when no cookie
226
- * is present the server already rendered tenant-less, so a refresh is
227
- * redundant. This also avoids an infinite loop on framework adapters whose
228
- * `router.refresh()` remounts this provider (e.g. TanStack Start) — an
229
- * unconditional refresh would re-fire this mount effect and refresh again,
230
- * whereas guarding on the (persistent) cookie self-terminates after the one
231
- * refresh that clears it. The cookie is cleared either way via `setTenant`.
225
+ * If there is no initial value and a stale tenant cookie exists, clear the
226
+ * tenant and refresh the router.
232
227
  */ React.useEffect(()=>{
233
- if (!initialValue) {
228
+ if (shouldRefreshTenantSelection({
229
+ initialValue,
230
+ tenantCookie: getTenantCookie()
231
+ })) {
234
232
  setTenant({
235
233
  id: undefined,
236
- refresh: Boolean(getTenantCookie())
234
+ refresh: true
237
235
  });
238
236
  }
239
237
  }, [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/providers/TenantSelectionProvider/index.client.tsx"],"sourcesContent":["'use client'\n\nimport type { OptionObject } from 'payload'\n\nimport { toast, useAuth, useConfig, useRouter } from '@payloadcms/ui'\nimport { formatAdminURL } from 'payload/shared'\nimport React, { createContext } from 'react'\n\nimport { generateCookie } from '../../utilities/generateCookie.js'\n\ntype ContextType = {\n /**\n * What is the context of the selector? It is either 'document' | 'global' | undefined.\n *\n * - 'document' means you are viewing a document in the context of a tenant\n * - 'global' means you are viewing a \"global\" (globals are collection documents but prevent you from viewing the list view) document in the context of a tenant\n * - undefined means you are not viewing a document at all\n */\n entityType?: 'document' | 'global'\n /**\n * Hoists the forms modified state\n */\n modified?: boolean\n /**\n * Array of options to select from\n */\n options: OptionObject[]\n /**\n * The currently selected tenant ID\n */\n selectedTenantID: number | string | undefined\n /**\n * Sets the entityType when a document is loaded and sets it to undefined when the document unmounts.\n */\n setEntityType: React.Dispatch<React.SetStateAction<'document' | 'global' | undefined>>\n /**\n * Sets the modified state\n */\n setModified: React.Dispatch<React.SetStateAction<boolean>>\n /**\n * Sets the selected tenant ID\n *\n * @param args.id - The ID of the tenant to select\n * @param args.refresh - Whether to refresh the page after changing the tenant\n */\n setTenant: (args: { id: number | string | undefined; refresh?: boolean }) => void\n /**\n * Used to sync tenants displayed in the tenant selector when updates are made to the tenants collection.\n */\n syncTenants: () => Promise<void>\n /**\n *\n */\n updateTenants: (args: { id: number | string; label: string }) => void\n}\n\nconst Context = createContext<ContextType>({\n entityType: undefined,\n options: [],\n selectedTenantID: undefined,\n setEntityType: () => undefined,\n setModified: () => undefined,\n setTenant: () => null,\n syncTenants: () => Promise.resolve(),\n updateTenants: () => null,\n})\n\nconst DEFAULT_COOKIE_NAME = 'payload-tenant'\n\nconst setTenantCookie = (args: { cookieName?: string; value: string }) => {\n const { cookieName = DEFAULT_COOKIE_NAME, value } = args\n document.cookie = generateCookie<string>({\n name: cookieName,\n maxAge: 60 * 60 * 24 * 365, // 1 year in seconds\n path: '/',\n returnCookieAsObject: false,\n value: value || '',\n })\n}\n\nconst deleteTenantCookie = (args: { cookieName?: string } = {}) => {\n const { cookieName = DEFAULT_COOKIE_NAME } = args\n document.cookie = generateCookie<string>({\n name: cookieName,\n maxAge: -1,\n path: '/',\n returnCookieAsObject: false,\n value: '',\n })\n}\n\nconst getTenantCookie = (args: { cookieName?: string } = {}): string | undefined => {\n const { cookieName = DEFAULT_COOKIE_NAME } = args\n const value = `; ${document.cookie}`\n const parts = value.split(`; ${cookieName}=`)\n if (parts.length === 2) {\n return parts.pop()?.split(';').shift()\n }\n return undefined\n}\n\nexport const TenantSelectionProviderClient = ({\n children,\n initialTenantOptions,\n initialValue,\n tenantsCollectionSlug,\n}: {\n children: React.ReactNode\n initialTenantOptions: OptionObject[]\n initialValue?: number | string\n tenantsCollectionSlug: string\n}) => {\n const [selectedTenantID, setSelectedTenantID] = React.useState<number | string | undefined>(\n initialValue,\n )\n const [modified, setModified] = React.useState<boolean>(false)\n const [entityType, setEntityType] = React.useState<'document' | 'global' | undefined>(undefined)\n const { user } = useAuth()\n const { config } = useConfig()\n const router = useRouter()\n const userID = React.useMemo(() => user?.id, [user?.id])\n const prevUserID = React.useRef(userID)\n const userChanged = userID !== prevUserID.current\n const [tenantOptions, setTenantOptions] = React.useState<OptionObject[]>(\n () => initialTenantOptions,\n )\n const selectedTenantLabel = React.useMemo(\n () => tenantOptions.find((option) => option.value === selectedTenantID)?.label,\n [selectedTenantID, tenantOptions],\n )\n\n const setTenantAndCookie = React.useCallback(\n ({ id, refresh }: { id: number | string | undefined; refresh?: boolean }) => {\n setSelectedTenantID(id)\n if (id !== undefined) {\n setTenantCookie({ value: String(id) })\n } else {\n deleteTenantCookie()\n }\n if (refresh) {\n router.refresh()\n }\n },\n [router],\n )\n\n const setTenant = React.useCallback<ContextType['setTenant']>(\n ({ id, refresh }) => {\n if (id === undefined) {\n if (tenantOptions.length > 1 || tenantOptions.length === 0) {\n // users with multiple tenants can clear the tenant selection\n setTenantAndCookie({ id: undefined, refresh })\n } else if (tenantOptions[0]) {\n // if there is only one tenant, auto-select that tenant\n setTenantAndCookie({ id: tenantOptions[0].value, refresh: true })\n }\n } else if (!tenantOptions.find((option) => option.value === id)) {\n // if the tenant is invalid, set the first tenant as selected\n setTenantAndCookie({\n id: tenantOptions[0]?.value,\n refresh,\n })\n } else {\n // if the tenant is in the options, set it as selected\n setTenantAndCookie({ id, refresh })\n }\n },\n [tenantOptions, setTenantAndCookie],\n )\n\n const syncTenants = React.useCallback(async () => {\n try {\n const req = await fetch(\n formatAdminURL({\n apiRoute: config.routes.api,\n path: `/${tenantsCollectionSlug}/populate-tenant-options`,\n }),\n {\n credentials: 'include',\n method: 'GET',\n },\n )\n\n const result = await req.json()\n\n if (result.tenantOptions && userID) {\n setTenantOptions(result.tenantOptions)\n\n if (result.tenantOptions.length === 1) {\n setSelectedTenantID(result.tenantOptions[0].value)\n setTenantCookie({ value: String(result.tenantOptions[0].value) })\n }\n }\n } catch (e) {\n toast.error(`Error fetching tenants`)\n }\n }, [config.routes.api, tenantsCollectionSlug, userID])\n\n const updateTenants = React.useCallback<ContextType['updateTenants']>(\n ({ id, label }) => {\n setTenantOptions((prev) => {\n return prev.map((currentTenant) => {\n if (id === currentTenant.value) {\n return {\n label,\n value: id,\n }\n }\n return currentTenant\n })\n })\n\n void syncTenants()\n },\n [syncTenants],\n )\n\n /**\n * Sync server-provided tenant options into client state.\n * When the server component re-renders (e.g., after navigation post-login),\n * it provides updated initialTenantOptions. Since useState() ignores new\n * initial values on re-renders, and re-initializes with stale props on\n * remounts, we sync them explicitly via this effect.\n */\n React.useEffect(() => {\n if (initialTenantOptions.length > 0) {\n setTenantOptions((prev) => {\n if (\n prev.length === initialTenantOptions.length &&\n prev.every((opt, i) => opt.value === initialTenantOptions[i]?.value)\n ) {\n return prev\n }\n return initialTenantOptions\n })\n\n if (initialTenantOptions.length === 1 && initialTenantOptions[0]) {\n setSelectedTenantID(initialTenantOptions[0].value)\n setTenantCookie({ value: String(initialTenantOptions[0].value) })\n }\n }\n }, [initialTenantOptions])\n\n React.useEffect(() => {\n if (userChanged || (initialValue && String(initialValue) !== getTenantCookie())) {\n if (userID) {\n // user logging in\n void syncTenants()\n } else {\n // user logging out\n setSelectedTenantID(undefined)\n deleteTenantCookie()\n setTenantOptions((prev) => (prev.length > 0 ? [] : prev))\n router.refresh()\n }\n prevUserID.current = userID\n }\n }, [userID, userChanged, syncTenants, initialValue, router])\n\n /**\n * Populate tenant options when the provider mounts already-authenticated but\n * without server-provided options. This happens when a provider setup remounts\n * this subtree on login (see `config.conditionalProvider`): the remounted\n * instance initializes `prevUserID` to the current user, so `userChanged` above\n * is `false` and never fires `syncTenants`, while the server still rendered the\n * logged-out tree so `initialTenantOptions` is empty — leaving the selector\n * unrendered. Unlike `router.refresh()`, `syncTenants()` is a plain client fetch\n * that cannot trigger a remount loop, so this is safe on every framework adapter.\n */\n const didInitialSync = React.useRef(false)\n React.useEffect(() => {\n if (\n !didInitialSync.current &&\n !userChanged &&\n userID &&\n tenantOptions.length === 0 &&\n initialTenantOptions.length === 0\n ) {\n didInitialSync.current = true\n void syncTenants()\n }\n }, [userChanged, userID, tenantOptions.length, initialTenantOptions, syncTenants])\n\n /**\n * If there is no initial value, clear the tenant and refresh the router.\n * Needed for stale tenantIDs set as a cookie.\n *\n * Only refresh when there is actually a stale cookie to clear: when no cookie\n * is present the server already rendered tenant-less, so a refresh is\n * redundant. This also avoids an infinite loop on framework adapters whose\n * `router.refresh()` remounts this provider (e.g. TanStack Start) — an\n * unconditional refresh would re-fire this mount effect and refresh again,\n * whereas guarding on the (persistent) cookie self-terminates after the one\n * refresh that clears it. The cookie is cleared either way via `setTenant`.\n */\n React.useEffect(() => {\n if (!initialValue) {\n setTenant({ id: undefined, refresh: Boolean(getTenantCookie()) })\n }\n }, [initialValue, setTenant])\n\n /**\n * If there is no selected tenant ID and the entity type is 'global', set the first tenant as selected.\n * This ensures that the global tenant is always set when the component mounts.\n */\n React.useEffect(() => {\n if (!selectedTenantID && tenantOptions.length > 0 && entityType === 'global') {\n setTenant({\n id: tenantOptions[0]?.value,\n refresh: true,\n })\n }\n }, [selectedTenantID, tenantOptions, entityType, setTenant])\n\n return (\n <span\n data-selected-tenant-id={selectedTenantID}\n data-selected-tenant-title={selectedTenantLabel}\n >\n <Context\n value={{\n entityType,\n modified,\n options: tenantOptions,\n selectedTenantID,\n setEntityType,\n setModified,\n setTenant,\n syncTenants,\n updateTenants,\n }}\n >\n {children}\n </Context>\n </span>\n )\n}\n\nexport const useTenantSelection = () => React.use(Context)\n"],"names":["toast","useAuth","useConfig","useRouter","formatAdminURL","React","createContext","generateCookie","Context","entityType","undefined","options","selectedTenantID","setEntityType","setModified","setTenant","syncTenants","Promise","resolve","updateTenants","DEFAULT_COOKIE_NAME","setTenantCookie","args","cookieName","value","document","cookie","name","maxAge","path","returnCookieAsObject","deleteTenantCookie","getTenantCookie","parts","split","length","pop","shift","TenantSelectionProviderClient","children","initialTenantOptions","initialValue","tenantsCollectionSlug","setSelectedTenantID","useState","modified","user","config","router","userID","useMemo","id","prevUserID","useRef","userChanged","current","tenantOptions","setTenantOptions","selectedTenantLabel","find","option","label","setTenantAndCookie","useCallback","refresh","String","req","fetch","apiRoute","routes","api","credentials","method","result","json","e","error","prev","map","currentTenant","useEffect","every","opt","i","didInitialSync","Boolean","span","data-selected-tenant-id","data-selected-tenant-title","useTenantSelection","use"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/providers/TenantSelectionProvider/index.client.tsx"],"sourcesContent":["'use client'\n\nimport type { OptionObject } from 'payload'\n\nimport { toast, useAuth, useConfig, useRouter } from '@payloadcms/ui'\nimport { formatAdminURL } from 'payload/shared'\nimport React, { createContext } from 'react'\n\nimport { generateCookie } from '../../utilities/generateCookie.js'\nimport { shouldRefreshTenantSelection } from '../../utilities/shouldRefreshTenantSelection.js'\n\ntype ContextType = {\n /**\n * What is the context of the selector? It is either 'document' | 'global' | undefined.\n *\n * - 'document' means you are viewing a document in the context of a tenant\n * - 'global' means you are viewing a \"global\" (globals are collection documents but prevent you from viewing the list view) document in the context of a tenant\n * - undefined means you are not viewing a document at all\n */\n entityType?: 'document' | 'global'\n /**\n * Hoists the forms modified state\n */\n modified?: boolean\n /**\n * Array of options to select from\n */\n options: OptionObject[]\n /**\n * The currently selected tenant ID\n */\n selectedTenantID: number | string | undefined\n /**\n * Sets the entityType when a document is loaded and sets it to undefined when the document unmounts.\n */\n setEntityType: React.Dispatch<React.SetStateAction<'document' | 'global' | undefined>>\n /**\n * Sets the modified state\n */\n setModified: React.Dispatch<React.SetStateAction<boolean>>\n /**\n * Sets the selected tenant ID\n *\n * @param args.id - The ID of the tenant to select\n * @param args.refresh - Whether to refresh the page after changing the tenant\n */\n setTenant: (args: { id: number | string | undefined; refresh?: boolean }) => void\n /**\n * Used to sync tenants displayed in the tenant selector when updates are made to the tenants collection.\n */\n syncTenants: () => Promise<void>\n /**\n *\n */\n updateTenants: (args: { id: number | string; label: string }) => void\n}\n\nconst Context = createContext<ContextType>({\n entityType: undefined,\n options: [],\n selectedTenantID: undefined,\n setEntityType: () => undefined,\n setModified: () => undefined,\n setTenant: () => null,\n syncTenants: () => Promise.resolve(),\n updateTenants: () => null,\n})\n\nconst DEFAULT_COOKIE_NAME = 'payload-tenant'\n\nconst setTenantCookie = (args: { cookieName?: string; value: string }) => {\n const { cookieName = DEFAULT_COOKIE_NAME, value } = args\n document.cookie = generateCookie<string>({\n name: cookieName,\n maxAge: 60 * 60 * 24 * 365, // 1 year in seconds\n path: '/',\n returnCookieAsObject: false,\n value: value || '',\n })\n}\n\nconst deleteTenantCookie = (args: { cookieName?: string } = {}) => {\n const { cookieName = DEFAULT_COOKIE_NAME } = args\n document.cookie = generateCookie<string>({\n name: cookieName,\n maxAge: -1,\n path: '/',\n returnCookieAsObject: false,\n value: '',\n })\n}\n\nconst getTenantCookie = (args: { cookieName?: string } = {}): string | undefined => {\n const { cookieName = DEFAULT_COOKIE_NAME } = args\n const value = `; ${document.cookie}`\n const parts = value.split(`; ${cookieName}=`)\n if (parts.length === 2) {\n return parts.pop()?.split(';').shift()\n }\n return undefined\n}\n\nexport const TenantSelectionProviderClient = ({\n children,\n initialTenantOptions,\n initialValue,\n tenantsCollectionSlug,\n}: {\n children: React.ReactNode\n initialTenantOptions: OptionObject[]\n initialValue?: number | string\n tenantsCollectionSlug: string\n}) => {\n const [selectedTenantID, setSelectedTenantID] = React.useState<number | string | undefined>(\n initialValue,\n )\n const [modified, setModified] = React.useState<boolean>(false)\n const [entityType, setEntityType] = React.useState<'document' | 'global' | undefined>(undefined)\n const { user } = useAuth()\n const { config } = useConfig()\n const router = useRouter()\n const userID = React.useMemo(() => user?.id, [user?.id])\n const prevUserID = React.useRef(userID)\n const userChanged = userID !== prevUserID.current\n const [tenantOptions, setTenantOptions] = React.useState<OptionObject[]>(\n () => initialTenantOptions,\n )\n const selectedTenantLabel = React.useMemo(\n () => tenantOptions.find((option) => option.value === selectedTenantID)?.label,\n [selectedTenantID, tenantOptions],\n )\n\n const setTenantAndCookie = React.useCallback(\n ({ id, refresh }: { id: number | string | undefined; refresh?: boolean }) => {\n setSelectedTenantID(id)\n if (id !== undefined) {\n setTenantCookie({ value: String(id) })\n } else {\n deleteTenantCookie()\n }\n if (refresh) {\n router.refresh()\n }\n },\n [router],\n )\n\n const setTenant = React.useCallback<ContextType['setTenant']>(\n ({ id, refresh }) => {\n if (id === undefined) {\n if (tenantOptions.length > 1 || tenantOptions.length === 0) {\n // users with multiple tenants can clear the tenant selection\n setTenantAndCookie({ id: undefined, refresh })\n } else if (tenantOptions[0]) {\n // if there is only one tenant, auto-select that tenant\n setTenantAndCookie({ id: tenantOptions[0].value, refresh: true })\n }\n } else if (!tenantOptions.find((option) => option.value === id)) {\n // if the tenant is invalid, set the first tenant as selected\n setTenantAndCookie({\n id: tenantOptions[0]?.value,\n refresh,\n })\n } else {\n // if the tenant is in the options, set it as selected\n setTenantAndCookie({ id, refresh })\n }\n },\n [tenantOptions, setTenantAndCookie],\n )\n\n const syncTenants = React.useCallback(async () => {\n try {\n const req = await fetch(\n formatAdminURL({\n apiRoute: config.routes.api,\n path: `/${tenantsCollectionSlug}/populate-tenant-options`,\n }),\n {\n credentials: 'include',\n method: 'GET',\n },\n )\n\n const result = await req.json()\n\n if (result.tenantOptions && userID) {\n setTenantOptions(result.tenantOptions)\n\n if (result.tenantOptions.length === 1) {\n setSelectedTenantID(result.tenantOptions[0].value)\n setTenantCookie({ value: String(result.tenantOptions[0].value) })\n }\n }\n } catch (e) {\n toast.error(`Error fetching tenants`)\n }\n }, [config.routes.api, tenantsCollectionSlug, userID])\n\n const updateTenants = React.useCallback<ContextType['updateTenants']>(\n ({ id, label }) => {\n setTenantOptions((prev) => {\n return prev.map((currentTenant) => {\n if (id === currentTenant.value) {\n return {\n label,\n value: id,\n }\n }\n return currentTenant\n })\n })\n\n void syncTenants()\n },\n [syncTenants],\n )\n\n /**\n * Sync server-provided tenant options into client state.\n * When the server component re-renders (e.g., after navigation post-login),\n * it provides updated initialTenantOptions. Since useState() ignores new\n * initial values on re-renders, and re-initializes with stale props on\n * remounts, we sync them explicitly via this effect.\n */\n React.useEffect(() => {\n if (initialTenantOptions.length > 0) {\n setTenantOptions((prev) => {\n if (\n prev.length === initialTenantOptions.length &&\n prev.every((opt, i) => opt.value === initialTenantOptions[i]?.value)\n ) {\n return prev\n }\n return initialTenantOptions\n })\n\n if (initialTenantOptions.length === 1 && initialTenantOptions[0]) {\n setSelectedTenantID(initialTenantOptions[0].value)\n setTenantCookie({ value: String(initialTenantOptions[0].value) })\n }\n }\n }, [initialTenantOptions])\n\n React.useEffect(() => {\n const hasTenantSelectionMismatch =\n initialValue && String(initialValue) !== getTenantCookie()\n\n if (userID && (userChanged || tenantOptions.length === 0 || hasTenantSelectionMismatch)) {\n // Sync on login, and when the provider remounts after login without tenant options.\n void syncTenants()\n } else if (!userID && (userChanged || hasTenantSelectionMismatch)) {\n // user logging out\n setSelectedTenantID(undefined)\n deleteTenantCookie()\n setTenantOptions((prev) => (prev.length > 0 ? [] : prev))\n router.refresh()\n }\n\n if (userChanged) {\n prevUserID.current = userID\n }\n }, [userID, userChanged, syncTenants, initialValue, router, tenantOptions.length])\n\n /**\n * Populate tenant options when the provider mounts already-authenticated but\n * without server-provided options. This happens when a provider setup remounts\n * this subtree on login (see `config.conditionalProvider`): the remounted\n * instance initializes `prevUserID` to the current user, so `userChanged` above\n * is `false` and never fires `syncTenants`, while the server still rendered the\n * logged-out tree so `initialTenantOptions` is empty — leaving the selector\n * unrendered. Unlike `router.refresh()`, `syncTenants()` is a plain client fetch\n * that cannot trigger a remount loop, so this is safe on every framework adapter.\n */\n const didInitialSync = React.useRef(false)\n React.useEffect(() => {\n if (\n !didInitialSync.current &&\n !userChanged &&\n userID &&\n tenantOptions.length === 0 &&\n initialTenantOptions.length === 0\n ) {\n didInitialSync.current = true\n void syncTenants()\n }\n }, [userChanged, userID, tenantOptions.length, initialTenantOptions, syncTenants])\n\n /**\n * If there is no initial value and a stale tenant cookie exists, clear the\n * tenant and refresh the router.\n */\n React.useEffect(() => {\n if (\n shouldRefreshTenantSelection({\n initialValue,\n tenantCookie: getTenantCookie(),\n })\n ) {\n setTenant({ id: undefined, refresh: true })\n }\n }, [initialValue, setTenant])\n\n /**\n * If there is no selected tenant ID and the entity type is 'global', set the first tenant as selected.\n * This ensures that the global tenant is always set when the component mounts.\n */\n React.useEffect(() => {\n if (!selectedTenantID && tenantOptions.length > 0 && entityType === 'global') {\n setTenant({\n id: tenantOptions[0]?.value,\n refresh: true,\n })\n }\n }, [selectedTenantID, tenantOptions, entityType, setTenant])\n\n return (\n <span\n data-selected-tenant-id={selectedTenantID}\n data-selected-tenant-title={selectedTenantLabel}\n >\n <Context\n value={{\n entityType,\n modified,\n options: tenantOptions,\n selectedTenantID,\n setEntityType,\n setModified,\n setTenant,\n syncTenants,\n updateTenants,\n }}\n >\n {children}\n </Context>\n </span>\n )\n}\n\nexport const useTenantSelection = () => React.use(Context)\n"],"names":["toast","useAuth","useConfig","useRouter","formatAdminURL","React","createContext","generateCookie","shouldRefreshTenantSelection","Context","entityType","undefined","options","selectedTenantID","setEntityType","setModified","setTenant","syncTenants","Promise","resolve","updateTenants","DEFAULT_COOKIE_NAME","setTenantCookie","args","cookieName","value","document","cookie","name","maxAge","path","returnCookieAsObject","deleteTenantCookie","getTenantCookie","parts","split","length","pop","shift","TenantSelectionProviderClient","children","initialTenantOptions","initialValue","tenantsCollectionSlug","setSelectedTenantID","useState","modified","user","config","router","userID","useMemo","id","prevUserID","useRef","userChanged","current","tenantOptions","setTenantOptions","selectedTenantLabel","find","option","label","setTenantAndCookie","useCallback","refresh","String","req","fetch","apiRoute","routes","api","credentials","method","result","json","e","error","prev","map","currentTenant","useEffect","every","opt","i","hasTenantSelectionMismatch","didInitialSync","tenantCookie","span","data-selected-tenant-id","data-selected-tenant-title","useTenantSelection","use"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import type { CollectionConfig } from 'payload';
1
+ import type { CollectionConfig, Config } from 'payload';
2
2
  import type { AllAccessKeys, MultiTenantPluginConfig } from '../types.js';
3
3
  export declare const collectionAccessKeys: AllAccessKeys;
4
- type Args = {
4
+ export type TenantAccessConfig = {
5
5
  accessResultCallback?: MultiTenantPluginConfig['usersAccessResultOverride'];
6
6
  adminUsersSlug: string;
7
7
  collection: CollectionConfig;
@@ -10,10 +10,8 @@ type Args = {
10
10
  tenantsArrayTenantFieldName?: string;
11
11
  userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'];
12
12
  };
13
- /**
14
- * Adds tenant access constraint to collection
15
- * - constrains access a users assigned tenants
16
- */
17
- export declare const addCollectionAccess: ({ accessResultCallback, adminUsersSlug, collection, fieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, }: Args) => void;
18
- export {};
13
+ export declare const addCollectionAccess: ({ config, scopes, }: {
14
+ config: Config;
15
+ scopes: TenantAccessConfig[];
16
+ }) => void;
19
17
  //# sourceMappingURL=addCollectionAccess.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"addCollectionAccess.d.ts","sourceRoot":"","sources":["../../src/utilities/addCollectionAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAIzE,eAAO,MAAM,oBAAoB,EAAE,aAOzB,CAAA;AAEV,KAAK,IAAI,GAAG;IACV,oBAAoB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;IAC3E,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,yBAAyB,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,2BAA2B,CAAC,CAAA;CAC1F,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,oJAQ7B,IAAI,KAAG,IAiBT,CAAA"}
1
+ {"version":3,"file":"addCollectionAccess.d.ts","sourceRoot":"","sources":["../../src/utilities/addCollectionAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,gBAAgB,EAAE,MAAM,EAAe,MAAM,SAAS,CAAA;AAE9F,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAIzE,eAAO,MAAM,oBAAoB,EAAE,aAOzB,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG;IAC/B,oBAAoB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;IAC3E,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,yBAAyB,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,2BAA2B,CAAC,CAAA;CAC1F,CAAA;AAsED,eAAO,MAAM,mBAAmB,wBAG7B;IACD,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,kBAAkB,EAAE,CAAA;CAC7B,KAAG,IA4CH,CAAA"}
@@ -1,4 +1,4 @@
1
- import { withTenantAccess } from './withTenantAccess.js';
1
+ import { getTenantAccess } from './getTenantAccess.js';
2
2
  export const collectionAccessKeys = [
3
3
  'create',
4
4
  'read',
@@ -7,26 +7,97 @@ export const collectionAccessKeys = [
7
7
  'readVersions',
8
8
  'unlock'
9
9
  ];
10
- /**
11
- * Adds tenant access constraint to collection
12
- * - constrains access a users assigned tenants
13
- */ export const addCollectionAccess = ({ accessResultCallback, adminUsersSlug, collection, fieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants })=>{
14
- collectionAccessKeys.forEach((key)=>{
15
- if (!collection.access) {
16
- collection.access = {};
17
- }
18
- collection.access[key] = withTenantAccess({
19
- accessFunction: collection.access?.[key],
20
- accessKey: key,
21
- accessResultCallback,
22
- adminUsersSlug,
23
- collection,
24
- fieldName: key === 'readVersions' ? `version.${fieldName}` : fieldName,
25
- tenantsArrayFieldName,
26
- tenantsArrayTenantFieldName,
27
- userHasAccessToAllTenants
28
- });
10
+ const getTenantAccessResult = ({ accessKey, accessResult, accessResultCallback, adminUsersSlug, args, collection, fieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants })=>{
11
+ const finish = (result)=>accessResultCallback ? accessResultCallback({
12
+ accessKey,
13
+ accessResult: result,
14
+ ...args
15
+ }) : result;
16
+ if (!accessResult) {
17
+ return finish(false);
18
+ }
19
+ const user = args.req.user;
20
+ if (!user || user.collection !== adminUsersSlug || userHasAccessToAllTenants(user)) {
21
+ return finish(accessResult);
22
+ }
23
+ const tenantConstraint = getTenantAccess({
24
+ fieldName,
25
+ tenantsArrayFieldName,
26
+ tenantsArrayTenantFieldName,
27
+ user
28
+ });
29
+ if (tenantConstraint[fieldName]?.in.length === 0) {
30
+ return finish(collection.slug === user.collection ? {
31
+ id: {
32
+ equals: user.id
33
+ }
34
+ } : false);
35
+ }
36
+ const tenantResult = collection.slug === user.collection ? {
37
+ or: [
38
+ {
39
+ id: {
40
+ equals: user.id
41
+ }
42
+ },
43
+ tenantConstraint
44
+ ]
45
+ } : tenantConstraint;
46
+ return finish(accessResult === true ? tenantResult : {
47
+ and: [
48
+ accessResult,
49
+ tenantResult
50
+ ]
29
51
  });
30
52
  };
53
+ const wrapCollectionAccess = (scope)=>{
54
+ scope.collection.access ??= {};
55
+ for (const accessKey of collectionAccessKeys){
56
+ const accessFunction = scope.collection.access[accessKey] ?? (({ req })=>Boolean(req.user));
57
+ scope.collection.access[accessKey] = async (args)=>getTenantAccessResult({
58
+ ...scope,
59
+ accessKey,
60
+ accessResult: await accessFunction(args),
61
+ args,
62
+ fieldName: accessKey === 'readVersions' ? `version.${scope.fieldName}` : scope.fieldName
63
+ });
64
+ }
65
+ };
66
+ export const addCollectionAccess = ({ config, scopes })=>{
67
+ const baseAccessScopes = new Map();
68
+ for (const scope of scopes){
69
+ if (scope.accessResultCallback) {
70
+ wrapCollectionAccess(scope);
71
+ } else {
72
+ baseAccessScopes.set(scope.collection.slug, scope);
73
+ }
74
+ }
75
+ if (!baseAccessScopes.size) {
76
+ return;
77
+ }
78
+ config.baseAccess ??= {};
79
+ config.baseAccess.collections ??= {};
80
+ for (const accessKey of collectionAccessKeys){
81
+ const baseAccessFunction = config.baseAccess.collections[accessKey];
82
+ config.baseAccess.collections[accessKey] = async (args)=>{
83
+ const baseResult = baseAccessFunction ? await baseAccessFunction(args) : true;
84
+ const scope = baseAccessScopes.get(args.slug);
85
+ if (!baseResult || !scope) {
86
+ return baseResult;
87
+ }
88
+ if (accessKey === 'create' && typeof baseResult === 'object') {
89
+ return baseResult;
90
+ }
91
+ const tenantResult = await getTenantAccessResult({
92
+ ...scope,
93
+ accessKey,
94
+ accessResult: baseResult,
95
+ args,
96
+ fieldName: accessKey === 'readVersions' ? `version.${scope.fieldName}` : scope.fieldName
97
+ });
98
+ return accessKey === 'create' ? Boolean(tenantResult) : tenantResult;
99
+ };
100
+ }
101
+ };
31
102
 
32
103
  //# sourceMappingURL=addCollectionAccess.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/addCollectionAccess.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { AllAccessKeys, MultiTenantPluginConfig } from '../types.js'\n\nimport { withTenantAccess } from './withTenantAccess.js'\n\nexport const collectionAccessKeys: AllAccessKeys = [\n 'create',\n 'read',\n 'update',\n 'delete',\n 'readVersions',\n 'unlock',\n] as const\n\ntype Args = {\n accessResultCallback?: MultiTenantPluginConfig['usersAccessResultOverride']\n adminUsersSlug: string\n collection: CollectionConfig\n fieldName: string\n tenantsArrayFieldName?: string\n tenantsArrayTenantFieldName?: string\n userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants']\n}\n\n/**\n * Adds tenant access constraint to collection\n * - constrains access a users assigned tenants\n */\nexport const addCollectionAccess = ({\n accessResultCallback,\n adminUsersSlug,\n collection,\n fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n}: Args): void => {\n collectionAccessKeys.forEach((key) => {\n if (!collection.access) {\n collection.access = {}\n }\n collection.access[key] = withTenantAccess({\n accessFunction: collection.access?.[key],\n accessKey: key,\n accessResultCallback,\n adminUsersSlug,\n collection,\n fieldName: key === 'readVersions' ? `version.${fieldName}` : fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n })\n}\n"],"names":["withTenantAccess","collectionAccessKeys","addCollectionAccess","accessResultCallback","adminUsersSlug","collection","fieldName","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","forEach","key","access","accessFunction","accessKey"],"mappings":"AAIA,SAASA,gBAAgB,QAAQ,wBAAuB;AAExD,OAAO,MAAMC,uBAAsC;IACjD;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAYV;;;CAGC,GACD,OAAO,MAAMC,sBAAsB,CAAC,EAClCC,oBAAoB,EACpBC,cAAc,EACdC,UAAU,EACVC,SAAS,EACTC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EACpB;IACLR,qBAAqBS,OAAO,CAAC,CAACC;QAC5B,IAAI,CAACN,WAAWO,MAAM,EAAE;YACtBP,WAAWO,MAAM,GAAG,CAAC;QACvB;QACAP,WAAWO,MAAM,CAACD,IAAI,GAAGX,iBAAiB;YACxCa,gBAAgBR,WAAWO,MAAM,EAAE,CAACD,IAAI;YACxCG,WAAWH;YACXR;YACAC;YACAC;YACAC,WAAWK,QAAQ,iBAAiB,CAAC,QAAQ,EAAEL,WAAW,GAAGA;YAC7DC;YACAC;YACAC;QACF;IACF;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/utilities/addCollectionAccess.ts"],"sourcesContent":["import type { AccessArgs, AccessResult, CollectionConfig, Config, User, Where } from 'payload'\n\nimport type { AllAccessKeys, MultiTenantPluginConfig } from '../types.js'\n\nimport { getTenantAccess } from './getTenantAccess.js'\n\nexport const collectionAccessKeys: AllAccessKeys = [\n 'create',\n 'read',\n 'update',\n 'delete',\n 'readVersions',\n 'unlock',\n] as const\n\nexport type TenantAccessConfig = {\n accessResultCallback?: MultiTenantPluginConfig['usersAccessResultOverride']\n adminUsersSlug: string\n collection: CollectionConfig\n fieldName: string\n tenantsArrayFieldName?: string\n tenantsArrayTenantFieldName?: string\n userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants']\n}\n\nconst getTenantAccessResult = ({\n accessKey,\n accessResult,\n accessResultCallback,\n adminUsersSlug,\n args,\n collection,\n fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n}: {\n accessKey: AllAccessKeys[number]\n accessResult: AccessResult\n args: AccessArgs\n} & TenantAccessConfig): AccessResult | Promise<AccessResult> => {\n const finish = (result: AccessResult): AccessResult | Promise<AccessResult> =>\n accessResultCallback\n ? accessResultCallback({ accessKey, accessResult: result, ...args })\n : result\n\n if (!accessResult) {\n return finish(false)\n }\n\n const user = args.req.user\n\n if (!user || user.collection !== adminUsersSlug || userHasAccessToAllTenants(user as User)) {\n return finish(accessResult)\n }\n\n const tenantConstraint = getTenantAccess({\n fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n user,\n })\n\n if (tenantConstraint[fieldName]?.in.length === 0) {\n return finish(collection.slug === user.collection ? { id: { equals: user.id } } : false)\n }\n\n const tenantResult: Where =\n collection.slug === user.collection\n ? { or: [{ id: { equals: user.id } }, tenantConstraint] }\n : tenantConstraint\n\n return finish(accessResult === true ? tenantResult : { and: [accessResult, tenantResult] })\n}\n\nconst wrapCollectionAccess = (scope: TenantAccessConfig): void => {\n scope.collection.access ??= {}\n\n for (const accessKey of collectionAccessKeys) {\n const accessFunction =\n scope.collection.access[accessKey] ?? (({ req }: AccessArgs) => Boolean(req.user))\n\n scope.collection.access[accessKey] = async (args) =>\n getTenantAccessResult({\n ...scope,\n accessKey,\n accessResult: await accessFunction(args),\n args,\n fieldName: accessKey === 'readVersions' ? `version.${scope.fieldName}` : scope.fieldName,\n })\n }\n}\n\nexport const addCollectionAccess = ({\n config,\n scopes,\n}: {\n config: Config\n scopes: TenantAccessConfig[]\n}): void => {\n const baseAccessScopes = new Map<string, TenantAccessConfig>()\n\n for (const scope of scopes) {\n if (scope.accessResultCallback) {\n wrapCollectionAccess(scope)\n } else {\n baseAccessScopes.set(scope.collection.slug, scope)\n }\n }\n\n if (!baseAccessScopes.size) {\n return\n }\n\n config.baseAccess ??= {}\n config.baseAccess.collections ??= {}\n\n for (const accessKey of collectionAccessKeys) {\n const baseAccessFunction = config.baseAccess.collections[accessKey]\n\n config.baseAccess.collections[accessKey] = async (args) => {\n const baseResult = baseAccessFunction ? await baseAccessFunction(args) : true\n const scope = baseAccessScopes.get(args.slug)\n\n if (!baseResult || !scope) {\n return baseResult\n }\n\n if (accessKey === 'create' && typeof baseResult === 'object') {\n return baseResult\n }\n\n const tenantResult = await getTenantAccessResult({\n ...scope,\n accessKey,\n accessResult: baseResult,\n args,\n fieldName: accessKey === 'readVersions' ? `version.${scope.fieldName}` : scope.fieldName,\n })\n\n return accessKey === 'create' ? Boolean(tenantResult) : tenantResult\n }\n }\n}\n"],"names":["getTenantAccess","collectionAccessKeys","getTenantAccessResult","accessKey","accessResult","accessResultCallback","adminUsersSlug","args","collection","fieldName","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","finish","result","user","req","tenantConstraint","in","length","slug","id","equals","tenantResult","or","and","wrapCollectionAccess","scope","access","accessFunction","Boolean","addCollectionAccess","config","scopes","baseAccessScopes","Map","set","size","baseAccess","collections","baseAccessFunction","baseResult","get"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const getTenantIDsFromValue: (value: unknown) => string[];
2
+ //# sourceMappingURL=getTenantIDsFromValue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getTenantIDsFromValue.d.ts","sourceRoot":"","sources":["../../src/utilities/getTenantIDsFromValue.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,UAAW,OAAO,KAAG,MAAM,EAU5D,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { extractID } from './extractID.js';
2
+ export const getTenantIDsFromValue = (value)=>{
3
+ if (value === undefined || value === null) {
4
+ return [];
5
+ }
6
+ return (Array.isArray(value) ? value : [
7
+ value
8
+ ]).map((entry)=>{
9
+ const id = entry === null || entry === undefined ? undefined : extractID(entry);
10
+ return typeof id === 'number' || typeof id === 'string' ? String(id) : '';
11
+ });
12
+ };
13
+
14
+ //# sourceMappingURL=getTenantIDsFromValue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/getTenantIDsFromValue.ts"],"sourcesContent":["import type { Tenant } from '../types.js'\n\nimport { extractID } from './extractID.js'\n\nexport const getTenantIDsFromValue = (value: unknown): string[] => {\n if (value === undefined || value === null) {\n return []\n }\n\n return (Array.isArray(value) ? value : [value]).map((entry) => {\n const id = entry === null || entry === undefined ? undefined : extractID(entry as Tenant)\n\n return typeof id === 'number' || typeof id === 'string' ? String(id) : ''\n })\n}\n"],"names":["extractID","getTenantIDsFromValue","value","undefined","Array","isArray","map","entry","id","String"],"mappings":"AAEA,SAASA,SAAS,QAAQ,iBAAgB;AAE1C,OAAO,MAAMC,wBAAwB,CAACC;IACpC,IAAIA,UAAUC,aAAaD,UAAU,MAAM;QACzC,OAAO,EAAE;IACX;IAEA,OAAO,AAACE,CAAAA,MAAMC,OAAO,CAACH,SAASA,QAAQ;QAACA;KAAM,AAAD,EAAGI,GAAG,CAAC,CAACC;QACnD,MAAMC,KAAKD,UAAU,QAAQA,UAAUJ,YAAYA,YAAYH,UAAUO;QAEzE,OAAO,OAAOC,OAAO,YAAY,OAAOA,OAAO,WAAWC,OAAOD,MAAM;IACzE;AACF,EAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"getUserTenantIDs.d.ts","sourceRoot":"","sources":["../../src/utilities/getUserTenantIDs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAK/D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,MAAM,GAAG,MAAM,QACvD,IAAI,GAAG,oBAAoB,YACvB;IACR,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2BAA2B,CAAC,EAAE,MAAM,CAAA;CACrC,KACA,MAAM,EAoBR,CAAA"}
1
+ {"version":3,"file":"getUserTenantIDs.d.ts","sourceRoot":"","sources":["../../src/utilities/getUserTenantIDs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAK/D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,MAAM,GAAG,MAAM,QACvD,IAAI,GAAG,oBAAoB,YACvB;IACR,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2BAA2B,CAAC,EAAE,MAAM,CAAA;CACrC,KACA,MAAM,EAqBR,CAAA"}
@@ -12,8 +12,9 @@ import { extractID } from './extractID.js';
12
12
  const tenantsArrayTenantFieldName = options?.tenantsArrayTenantFieldName || defaults.tenantsArrayTenantFieldName;
13
13
  const tenantsArray = user[tenantsArrayFieldName];
14
14
  return (Array.isArray(tenantsArray) ? tenantsArray : [])?.reduce((acc, row)=>{
15
- if (row[tenantsArrayTenantFieldName]) {
16
- acc.push(extractID(row[tenantsArrayTenantFieldName]));
15
+ const tenantRow = row[tenantsArrayTenantFieldName];
16
+ if (tenantRow !== undefined && tenantRow !== null) {
17
+ acc.push(extractID(tenantRow));
17
18
  }
18
19
  return acc;
19
20
  }, []) || [];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/getUserTenantIDs.ts"],"sourcesContent":["import type { Tenant, UserWithTenantsField } from '../types.js'\n\nimport { defaults } from '../defaults.js'\nimport { extractID } from './extractID.js'\n\n/**\n * Returns array of all tenant IDs assigned to a user\n *\n * @param user - User object with tenants field\n */\nexport const getUserTenantIDs = <IDType extends number | string>(\n user: null | UserWithTenantsField,\n options?: {\n tenantsArrayFieldName?: string\n tenantsArrayTenantFieldName?: string\n },\n): IDType[] => {\n if (!user) {\n return []\n }\n\n const tenantsArrayFieldName = options?.tenantsArrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n options?.tenantsArrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n\n const tenantsArray = (user as Record<string, unknown>)[tenantsArrayFieldName]\n\n return (\n (Array.isArray(tenantsArray) ? tenantsArray : [])?.reduce<IDType[]>((acc, row) => {\n if (row[tenantsArrayTenantFieldName]) {\n acc.push(extractID<IDType>(row[tenantsArrayTenantFieldName] as Tenant<IDType>))\n }\n\n return acc\n }, []) || []\n )\n}\n"],"names":["defaults","extractID","getUserTenantIDs","user","options","tenantsArrayFieldName","tenantsArrayTenantFieldName","tenantsArray","Array","isArray","reduce","acc","row","push"],"mappings":"AAEA,SAASA,QAAQ,QAAQ,iBAAgB;AACzC,SAASC,SAAS,QAAQ,iBAAgB;AAE1C;;;;CAIC,GACD,OAAO,MAAMC,mBAAmB,CAC9BC,MACAC;IAKA,IAAI,CAACD,MAAM;QACT,OAAO,EAAE;IACX;IAEA,MAAME,wBAAwBD,SAASC,yBAAyBL,SAASK,qBAAqB;IAC9F,MAAMC,8BACJF,SAASE,+BAA+BN,SAASM,2BAA2B;IAE9E,MAAMC,eAAe,AAACJ,IAAgC,CAACE,sBAAsB;IAE7E,OACE,AAACG,CAAAA,MAAMC,OAAO,CAACF,gBAAgBA,eAAe,EAAE,AAAD,GAAIG,OAAiB,CAACC,KAAKC;QACxE,IAAIA,GAAG,CAACN,4BAA4B,EAAE;YACpCK,IAAIE,IAAI,CAACZ,UAAkBW,GAAG,CAACN,4BAA4B;QAC7D;QAEA,OAAOK;IACT,GAAG,EAAE,KAAK,EAAE;AAEhB,EAAC"}
1
+ {"version":3,"sources":["../../src/utilities/getUserTenantIDs.ts"],"sourcesContent":["import type { Tenant, UserWithTenantsField } from '../types.js'\n\nimport { defaults } from '../defaults.js'\nimport { extractID } from './extractID.js'\n\n/**\n * Returns array of all tenant IDs assigned to a user\n *\n * @param user - User object with tenants field\n */\nexport const getUserTenantIDs = <IDType extends number | string>(\n user: null | UserWithTenantsField,\n options?: {\n tenantsArrayFieldName?: string\n tenantsArrayTenantFieldName?: string\n },\n): IDType[] => {\n if (!user) {\n return []\n }\n\n const tenantsArrayFieldName = options?.tenantsArrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n options?.tenantsArrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n\n const tenantsArray = (user as Record<string, unknown>)[tenantsArrayFieldName]\n\n return (\n (Array.isArray(tenantsArray) ? tenantsArray : [])?.reduce<IDType[]>((acc, row) => {\n const tenantRow = row[tenantsArrayTenantFieldName] as Tenant<IDType>\n if (tenantRow !== undefined && tenantRow !== null) {\n acc.push(extractID(tenantRow))\n }\n\n return acc\n }, []) || []\n )\n}\n"],"names":["defaults","extractID","getUserTenantIDs","user","options","tenantsArrayFieldName","tenantsArrayTenantFieldName","tenantsArray","Array","isArray","reduce","acc","row","tenantRow","undefined","push"],"mappings":"AAEA,SAASA,QAAQ,QAAQ,iBAAgB;AACzC,SAASC,SAAS,QAAQ,iBAAgB;AAE1C;;;;CAIC,GACD,OAAO,MAAMC,mBAAmB,CAC9BC,MACAC;IAKA,IAAI,CAACD,MAAM;QACT,OAAO,EAAE;IACX;IAEA,MAAME,wBAAwBD,SAASC,yBAAyBL,SAASK,qBAAqB;IAC9F,MAAMC,8BACJF,SAASE,+BAA+BN,SAASM,2BAA2B;IAE9E,MAAMC,eAAe,AAACJ,IAAgC,CAACE,sBAAsB;IAE7E,OACE,AAACG,CAAAA,MAAMC,OAAO,CAACF,gBAAgBA,eAAe,EAAE,AAAD,GAAIG,OAAiB,CAACC,KAAKC;QACxE,MAAMC,YAAYD,GAAG,CAACN,4BAA4B;QAClD,IAAIO,cAAcC,aAAaD,cAAc,MAAM;YACjDF,IAAII,IAAI,CAACd,UAAUY;QACrB;QAEA,OAAOF;IACT,GAAG,EAAE,KAAK,EAAE;AAEhB,EAAC"}
@@ -0,0 +1,13 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { MultiTenantPluginConfig } from '../types.js';
3
+ type Args = {
4
+ previousValue?: unknown;
5
+ req: PayloadRequest;
6
+ tenantsArrayFieldName: string;
7
+ tenantsArrayTenantFieldName: string;
8
+ userHasAccessToAllTenants?: MultiTenantPluginConfig['userHasAccessToAllTenants'];
9
+ value: unknown;
10
+ };
11
+ export declare const isValidTenantAssignment: ({ previousValue, req, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, value, }: Args) => boolean;
12
+ export {};
13
+ //# sourceMappingURL=isValidTenantAssignment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isValidTenantAssignment.d.ts","sourceRoot":"","sources":["../../src/utilities/isValidTenantAssignment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAK1D,KAAK,IAAI,GAAG;IACV,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,GAAG,EAAE,cAAc,CAAA;IACnB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,2BAA2B,EAAE,MAAM,CAAA;IACnC,yBAAyB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;IAChF,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,eAAO,MAAM,uBAAuB,kHAOjC,IAAI,KAAG,OAgCT,CAAA"}
@@ -0,0 +1,28 @@
1
+ import { getTenantIDsFromValue } from './getTenantIDsFromValue.js';
2
+ import { getUserTenantIDs } from './getUserTenantIDs.js';
3
+ export const isValidTenantAssignment = ({ previousValue, req, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, value })=>{
4
+ const { user } = req;
5
+ if (user) {
6
+ if (userHasAccessToAllTenants?.(user)) {
7
+ return true;
8
+ }
9
+ } else if (req.payloadAPI === 'local') {
10
+ return true;
11
+ }
12
+ const submittedDocumentTenantIDs = getTenantIDsFromValue(value);
13
+ const storedDocumentTenantIDs = getTenantIDsFromValue(previousValue);
14
+ if (!hasTenantValueChanged(submittedDocumentTenantIDs, storedDocumentTenantIDs)) {
15
+ return true;
16
+ }
17
+ if (submittedDocumentTenantIDs.length === 0) {
18
+ return false;
19
+ }
20
+ const userAssignedTenantIDs = getUserTenantIDs(user, {
21
+ tenantsArrayFieldName,
22
+ tenantsArrayTenantFieldName
23
+ });
24
+ return submittedDocumentTenantIDs.every((tenantID)=>userAssignedTenantIDs.some((userAssignedTenantID)=>String(userAssignedTenantID) === String(tenantID)));
25
+ };
26
+ const hasTenantValueChanged = (submittedTenantIDs, storedTenantIDs)=>submittedTenantIDs.length !== storedTenantIDs.length || submittedTenantIDs.some((tenantID)=>!storedTenantIDs.includes(tenantID));
27
+
28
+ //# sourceMappingURL=isValidTenantAssignment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/isValidTenantAssignment.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport type { MultiTenantPluginConfig } from '../types.js'\n\nimport { getTenantIDsFromValue } from './getTenantIDsFromValue.js'\nimport { getUserTenantIDs } from './getUserTenantIDs.js'\n\ntype Args = {\n previousValue?: unknown\n req: PayloadRequest\n tenantsArrayFieldName: string\n tenantsArrayTenantFieldName: string\n userHasAccessToAllTenants?: MultiTenantPluginConfig['userHasAccessToAllTenants']\n value: unknown\n}\n\nexport const isValidTenantAssignment = ({\n previousValue,\n req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n value,\n}: Args): boolean => {\n const { user } = req\n\n if (user) {\n if (userHasAccessToAllTenants?.(user)) {\n return true\n }\n } else if (req.payloadAPI === 'local') {\n return true\n }\n\n const submittedDocumentTenantIDs = getTenantIDsFromValue(value)\n const storedDocumentTenantIDs = getTenantIDsFromValue(previousValue)\n\n if (!hasTenantValueChanged(submittedDocumentTenantIDs, storedDocumentTenantIDs)) {\n return true\n }\n\n if (submittedDocumentTenantIDs.length === 0) {\n return false\n }\n\n const userAssignedTenantIDs = getUserTenantIDs(user, {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n })\n\n return submittedDocumentTenantIDs.every((tenantID) =>\n userAssignedTenantIDs.some(\n (userAssignedTenantID) => String(userAssignedTenantID) === String(tenantID),\n ),\n )\n}\n\nconst hasTenantValueChanged = (\n submittedTenantIDs: (number | string)[],\n storedTenantIDs: (number | string)[],\n): boolean =>\n submittedTenantIDs.length !== storedTenantIDs.length ||\n submittedTenantIDs.some((tenantID) => !storedTenantIDs.includes(tenantID))\n"],"names":["getTenantIDsFromValue","getUserTenantIDs","isValidTenantAssignment","previousValue","req","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","value","user","payloadAPI","submittedDocumentTenantIDs","storedDocumentTenantIDs","hasTenantValueChanged","length","userAssignedTenantIDs","every","tenantID","some","userAssignedTenantID","String","submittedTenantIDs","storedTenantIDs","includes"],"mappings":"AAIA,SAASA,qBAAqB,QAAQ,6BAA4B;AAClE,SAASC,gBAAgB,QAAQ,wBAAuB;AAWxD,OAAO,MAAMC,0BAA0B,CAAC,EACtCC,aAAa,EACbC,GAAG,EACHC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EACzBC,KAAK,EACA;IACL,MAAM,EAAEC,IAAI,EAAE,GAAGL;IAEjB,IAAIK,MAAM;QACR,IAAIF,4BAA4BE,OAAO;YACrC,OAAO;QACT;IACF,OAAO,IAAIL,IAAIM,UAAU,KAAK,SAAS;QACrC,OAAO;IACT;IAEA,MAAMC,6BAA6BX,sBAAsBQ;IACzD,MAAMI,0BAA0BZ,sBAAsBG;IAEtD,IAAI,CAACU,sBAAsBF,4BAA4BC,0BAA0B;QAC/E,OAAO;IACT;IAEA,IAAID,2BAA2BG,MAAM,KAAK,GAAG;QAC3C,OAAO;IACT;IAEA,MAAMC,wBAAwBd,iBAAiBQ,MAAM;QACnDJ;QACAC;IACF;IAEA,OAAOK,2BAA2BK,KAAK,CAAC,CAACC,WACvCF,sBAAsBG,IAAI,CACxB,CAACC,uBAAyBC,OAAOD,0BAA0BC,OAAOH;AAGxE,EAAC;AAED,MAAMJ,wBAAwB,CAC5BQ,oBACAC,kBAEAD,mBAAmBP,MAAM,KAAKQ,gBAAgBR,MAAM,IACpDO,mBAAmBH,IAAI,CAAC,CAACD,WAAa,CAACK,gBAAgBC,QAAQ,CAACN"}
@@ -0,0 +1,5 @@
1
+ export declare const shouldRefreshTenantSelection: ({ initialValue, tenantCookie, }: {
2
+ initialValue?: number | string;
3
+ tenantCookie?: string;
4
+ }) => boolean;
5
+ //# sourceMappingURL=shouldRefreshTenantSelection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shouldRefreshTenantSelection.d.ts","sourceRoot":"","sources":["../../src/utilities/shouldRefreshTenantSelection.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,oCAGtC;IACD,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,KAAG,OAAiD,CAAA"}
@@ -0,0 +1,3 @@
1
+ export const shouldRefreshTenantSelection = ({ initialValue, tenantCookie })=>!initialValue && Boolean(tenantCookie);
2
+
3
+ //# sourceMappingURL=shouldRefreshTenantSelection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/shouldRefreshTenantSelection.ts"],"sourcesContent":["export const shouldRefreshTenantSelection = ({\n initialValue,\n tenantCookie,\n}: {\n initialValue?: number | string\n tenantCookie?: string\n}): boolean => !initialValue && Boolean(tenantCookie)\n"],"names":["shouldRefreshTenantSelection","initialValue","tenantCookie","Boolean"],"mappings":"AAAA,OAAO,MAAMA,+BAA+B,CAAC,EAC3CC,YAAY,EACZC,YAAY,EAIb,GAAc,CAACD,gBAAgBE,QAAQD,cAAa"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/plugin-multi-tenant",
3
- "version": "4.0.0-internal.d1cc1d3",
3
+ "version": "4.0.0-internal.d21c039",
4
4
  "description": "Multi Tenant plugin for Payload",
5
5
  "keywords": [
6
6
  "payload",
@@ -80,13 +80,13 @@
80
80
  ],
81
81
  "devDependencies": {
82
82
  "@payloadcms/eslint-config": "3.28.0",
83
- "@payloadcms/ui": "4.0.0-internal.d1cc1d3",
84
- "payload": "4.0.0-internal.d1cc1d3",
85
- "@payloadcms/translations": "4.0.0-internal.d1cc1d3"
83
+ "@payloadcms/translations": "4.0.0-internal.d21c039",
84
+ "payload": "4.0.0-internal.d21c039",
85
+ "@payloadcms/ui": "4.0.0-internal.d21c039"
86
86
  },
87
87
  "peerDependencies": {
88
- "@payloadcms/ui": "4.0.0-internal.d1cc1d3",
89
- "payload": "4.0.0-internal.d1cc1d3"
88
+ "@payloadcms/ui": "4.0.0-internal.d21c039",
89
+ "payload": "4.0.0-internal.d21c039"
90
90
  },
91
91
  "homepage:": "https://payloadcms.com",
92
92
  "scripts": {
@@ -95,7 +95,7 @@
95
95
  "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
96
96
  "build:types": "tsc --emitDeclarationOnly --outDir dist",
97
97
  "clean": "rimraf {dist,*.tsbuildinfo}",
98
- "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
98
+ "copyfiles": "copyfiles -u 1 -e \"**/*.spec.*\" \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
99
99
  "lint": "eslint .",
100
100
  "lint:fix": "eslint . --fix",
101
101
  "pack:plugin": "pnpm build && pnpm pack"
@@ -1,3 +0,0 @@
1
- import type { Where } from 'payload';
2
- export declare function combineWhereConstraints(constraints: Array<Where>): Where;
3
- //# sourceMappingURL=combineWhereConstraints.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"combineWhereConstraints.d.ts","sourceRoot":"","sources":["../../src/utilities/combineWhereConstraints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,CAgBxE"}
@@ -1,19 +0,0 @@
1
- export function combineWhereConstraints(constraints) {
2
- if (constraints.length === 0) {
3
- return {};
4
- }
5
- if (constraints.length === 1 && constraints[0]) {
6
- return constraints[0];
7
- }
8
- const andConstraint = {
9
- and: []
10
- };
11
- constraints.forEach((constraint)=>{
12
- if (andConstraint.and && constraint && typeof constraint === 'object') {
13
- andConstraint.and.push(constraint);
14
- }
15
- });
16
- return andConstraint;
17
- }
18
-
19
- //# sourceMappingURL=combineWhereConstraints.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utilities/combineWhereConstraints.ts"],"sourcesContent":["import type { Where } from 'payload'\n\nexport function combineWhereConstraints(constraints: Array<Where>): Where {\n if (constraints.length === 0) {\n return {}\n }\n if (constraints.length === 1 && constraints[0]) {\n return constraints[0]\n }\n const andConstraint: Where = {\n and: [],\n }\n constraints.forEach((constraint) => {\n if (andConstraint.and && constraint && typeof constraint === 'object') {\n andConstraint.and.push(constraint)\n }\n })\n return andConstraint\n}\n"],"names":["combineWhereConstraints","constraints","length","andConstraint","and","forEach","constraint","push"],"mappings":"AAEA,OAAO,SAASA,wBAAwBC,WAAyB;IAC/D,IAAIA,YAAYC,MAAM,KAAK,GAAG;QAC5B,OAAO,CAAC;IACV;IACA,IAAID,YAAYC,MAAM,KAAK,KAAKD,WAAW,CAAC,EAAE,EAAE;QAC9C,OAAOA,WAAW,CAAC,EAAE;IACvB;IACA,MAAME,gBAAuB;QAC3BC,KAAK,EAAE;IACT;IACAH,YAAYI,OAAO,CAAC,CAACC;QACnB,IAAIH,cAAcC,GAAG,IAAIE,cAAc,OAAOA,eAAe,UAAU;YACrEH,cAAcC,GAAG,CAACG,IAAI,CAACD;QACzB;IACF;IACA,OAAOH;AACT"}
@@ -1,16 +0,0 @@
1
- import type { Access, AccessArgs, AccessResult, CollectionConfig } from 'payload';
2
- import type { AllAccessKeys, MultiTenantPluginConfig } from '../types.js';
3
- type Args = {
4
- accessFunction?: Access;
5
- accessKey: AllAccessKeys[number];
6
- accessResultCallback?: MultiTenantPluginConfig['usersAccessResultOverride'];
7
- adminUsersSlug: string;
8
- collection: CollectionConfig;
9
- fieldName: string;
10
- tenantsArrayFieldName?: string;
11
- tenantsArrayTenantFieldName?: string;
12
- userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'];
13
- };
14
- export declare const withTenantAccess: ({ accessFunction, accessKey, accessResultCallback, adminUsersSlug, collection, fieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, }: Args) => (args: AccessArgs) => Promise<AccessResult>;
15
- export {};
16
- //# sourceMappingURL=withTenantAccess.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"withTenantAccess.d.ts","sourceRoot":"","sources":["../../src/utilities/withTenantAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAA;AAE9F,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAwB,MAAM,aAAa,CAAA;AAK/F,KAAK,IAAI,GAAG;IACV,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAChC,oBAAoB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;IAC3E,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,yBAAyB,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,2BAA2B,CAAC,CAAA;CAC1F,CAAA;AACD,eAAO,MAAM,gBAAgB,+KAWxB,IAAI,YACM,UAAU,KAAG,OAAO,CAAC,YAAY,CAkF7C,CAAA"}
@@ -1,76 +0,0 @@
1
- import { combineWhereConstraints } from './combineWhereConstraints.js';
2
- import { getTenantAccess } from './getTenantAccess.js';
3
- export const withTenantAccess = ({ accessFunction, accessKey, accessResultCallback, adminUsersSlug, collection, fieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants })=>async (args)=>{
4
- const constraints = [];
5
- const accessFn = typeof accessFunction === 'function' ? accessFunction : ({ req })=>Boolean(req.user);
6
- const accessResult = await accessFn(args);
7
- if (accessResult === false) {
8
- if (accessResultCallback) {
9
- return accessResultCallback({
10
- accessKey,
11
- accessResult: false,
12
- ...args
13
- });
14
- } else {
15
- return false;
16
- }
17
- } else if (accessResult && typeof accessResult === 'object') {
18
- constraints.push(accessResult);
19
- }
20
- if (args.req.user && args.req.user.collection === adminUsersSlug && !userHasAccessToAllTenants(args.req.user)) {
21
- const tenantConstraint = getTenantAccess({
22
- fieldName,
23
- tenantsArrayFieldName,
24
- tenantsArrayTenantFieldName,
25
- user: args.req.user
26
- });
27
- // User with no tenants should have no access to tenant-scoped documents
28
- // except for their own user document
29
- if (tenantConstraint[fieldName]?.in.length === 0) {
30
- const result = collection.slug === args.req.user.collection ? {
31
- id: {
32
- equals: args.req.user.id
33
- }
34
- } : false;
35
- return accessResultCallback ? accessResultCallback({
36
- accessKey,
37
- accessResult: result,
38
- ...args
39
- }) : result;
40
- }
41
- if (collection.slug === args.req.user.collection) {
42
- constraints.push({
43
- or: [
44
- {
45
- id: {
46
- equals: args.req.user.id
47
- }
48
- },
49
- tenantConstraint
50
- ]
51
- });
52
- } else {
53
- constraints.push(tenantConstraint);
54
- }
55
- if (accessResultCallback) {
56
- return accessResultCallback({
57
- accessKey,
58
- accessResult: combineWhereConstraints(constraints),
59
- ...args
60
- });
61
- } else {
62
- return combineWhereConstraints(constraints);
63
- }
64
- }
65
- if (accessResultCallback) {
66
- return accessResultCallback({
67
- accessKey,
68
- accessResult,
69
- ...args
70
- });
71
- } else {
72
- return accessResult;
73
- }
74
- };
75
-
76
- //# sourceMappingURL=withTenantAccess.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utilities/withTenantAccess.ts"],"sourcesContent":["import type { Access, AccessArgs, AccessResult, CollectionConfig, User, Where } from 'payload'\n\nimport type { AllAccessKeys, MultiTenantPluginConfig, UserWithTenantsField } from '../types.js'\n\nimport { combineWhereConstraints } from './combineWhereConstraints.js'\nimport { getTenantAccess } from './getTenantAccess.js'\n\ntype Args = {\n accessFunction?: Access\n accessKey: AllAccessKeys[number]\n accessResultCallback?: MultiTenantPluginConfig['usersAccessResultOverride']\n adminUsersSlug: string\n collection: CollectionConfig\n fieldName: string\n tenantsArrayFieldName?: string\n tenantsArrayTenantFieldName?: string\n userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants']\n}\nexport const withTenantAccess =\n ({\n accessFunction,\n accessKey,\n accessResultCallback,\n adminUsersSlug,\n collection,\n fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n }: Args) =>\n async (args: AccessArgs): Promise<AccessResult> => {\n const constraints: Where[] = []\n const accessFn =\n typeof accessFunction === 'function'\n ? accessFunction\n : ({ req }: AccessArgs): AccessResult => Boolean(req.user)\n const accessResult: AccessResult = await accessFn(args)\n\n if (accessResult === false) {\n if (accessResultCallback) {\n return accessResultCallback({\n accessKey,\n accessResult: false,\n ...args,\n })\n } else {\n return false\n }\n } else if (accessResult && typeof accessResult === 'object') {\n constraints.push(accessResult)\n }\n\n if (\n args.req.user &&\n args.req.user.collection === adminUsersSlug &&\n !userHasAccessToAllTenants(args.req.user as User)\n ) {\n const tenantConstraint = getTenantAccess({\n fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n user: args.req.user as UserWithTenantsField,\n })\n\n // User with no tenants should have no access to tenant-scoped documents\n // except for their own user document\n if (tenantConstraint[fieldName]?.in.length === 0) {\n const result: AccessResult =\n collection.slug === args.req.user.collection\n ? { id: { equals: args.req.user.id } }\n : false\n\n return accessResultCallback\n ? accessResultCallback({ accessKey, accessResult: result, ...args })\n : result\n }\n\n if (collection.slug === args.req.user.collection) {\n constraints.push({\n or: [\n {\n id: {\n equals: args.req.user.id,\n },\n },\n tenantConstraint,\n ],\n })\n } else {\n constraints.push(tenantConstraint)\n }\n\n if (accessResultCallback) {\n return accessResultCallback({\n accessKey,\n accessResult: combineWhereConstraints(constraints),\n ...args,\n })\n } else {\n return combineWhereConstraints(constraints)\n }\n }\n\n if (accessResultCallback) {\n return accessResultCallback({\n accessKey,\n accessResult,\n ...args,\n })\n } else {\n return accessResult\n }\n }\n"],"names":["combineWhereConstraints","getTenantAccess","withTenantAccess","accessFunction","accessKey","accessResultCallback","adminUsersSlug","collection","fieldName","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","args","constraints","accessFn","req","Boolean","user","accessResult","push","tenantConstraint","in","length","result","slug","id","equals","or"],"mappings":"AAIA,SAASA,uBAAuB,QAAQ,+BAA8B;AACtE,SAASC,eAAe,QAAQ,uBAAsB;AAatD,OAAO,MAAMC,mBACX,CAAC,EACCC,cAAc,EACdC,SAAS,EACTC,oBAAoB,EACpBC,cAAc,EACdC,UAAU,EACVC,SAAS,EACTC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EACpB,GACP,OAAOC;QACL,MAAMC,cAAuB,EAAE;QAC/B,MAAMC,WACJ,OAAOX,mBAAmB,aACtBA,iBACA,CAAC,EAAEY,GAAG,EAAc,GAAmBC,QAAQD,IAAIE,IAAI;QAC7D,MAAMC,eAA6B,MAAMJ,SAASF;QAElD,IAAIM,iBAAiB,OAAO;YAC1B,IAAIb,sBAAsB;gBACxB,OAAOA,qBAAqB;oBAC1BD;oBACAc,cAAc;oBACd,GAAGN,IAAI;gBACT;YACF,OAAO;gBACL,OAAO;YACT;QACF,OAAO,IAAIM,gBAAgB,OAAOA,iBAAiB,UAAU;YAC3DL,YAAYM,IAAI,CAACD;QACnB;QAEA,IACEN,KAAKG,GAAG,CAACE,IAAI,IACbL,KAAKG,GAAG,CAACE,IAAI,CAACV,UAAU,KAAKD,kBAC7B,CAACK,0BAA0BC,KAAKG,GAAG,CAACE,IAAI,GACxC;YACA,MAAMG,mBAAmBnB,gBAAgB;gBACvCO;gBACAC;gBACAC;gBACAO,MAAML,KAAKG,GAAG,CAACE,IAAI;YACrB;YAEA,wEAAwE;YACxE,qCAAqC;YACrC,IAAIG,gBAAgB,CAACZ,UAAU,EAAEa,GAAGC,WAAW,GAAG;gBAChD,MAAMC,SACJhB,WAAWiB,IAAI,KAAKZ,KAAKG,GAAG,CAACE,IAAI,CAACV,UAAU,GACxC;oBAAEkB,IAAI;wBAAEC,QAAQd,KAAKG,GAAG,CAACE,IAAI,CAACQ,EAAE;oBAAC;gBAAE,IACnC;gBAEN,OAAOpB,uBACHA,qBAAqB;oBAAED;oBAAWc,cAAcK;oBAAQ,GAAGX,IAAI;gBAAC,KAChEW;YACN;YAEA,IAAIhB,WAAWiB,IAAI,KAAKZ,KAAKG,GAAG,CAACE,IAAI,CAACV,UAAU,EAAE;gBAChDM,YAAYM,IAAI,CAAC;oBACfQ,IAAI;wBACF;4BACEF,IAAI;gCACFC,QAAQd,KAAKG,GAAG,CAACE,IAAI,CAACQ,EAAE;4BAC1B;wBACF;wBACAL;qBACD;gBACH;YACF,OAAO;gBACLP,YAAYM,IAAI,CAACC;YACnB;YAEA,IAAIf,sBAAsB;gBACxB,OAAOA,qBAAqB;oBAC1BD;oBACAc,cAAclB,wBAAwBa;oBACtC,GAAGD,IAAI;gBACT;YACF,OAAO;gBACL,OAAOZ,wBAAwBa;YACjC;QACF;QAEA,IAAIR,sBAAsB;YACxB,OAAOA,qBAAqB;gBAC1BD;gBACAc;gBACA,GAAGN,IAAI;YACT;QACF,OAAO;YACL,OAAOM;QACT;IACF,EAAC"}