@payloadcms/plugin-multi-tenant 4.0.0-internal.af6aad0 → 4.0.0-internal.b8a92d2

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 (34) 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 +13 -1
  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/getTenantIDsFromValue.d.ts +2 -0
  16. package/dist/utilities/getTenantIDsFromValue.d.ts.map +1 -0
  17. package/dist/utilities/getTenantIDsFromValue.js +14 -0
  18. package/dist/utilities/getTenantIDsFromValue.js.map +1 -0
  19. package/dist/utilities/getUserTenantIDs.d.ts.map +1 -1
  20. package/dist/utilities/getUserTenantIDs.js +3 -2
  21. package/dist/utilities/getUserTenantIDs.js.map +1 -1
  22. package/dist/utilities/isValidTenantAssignment.d.ts +13 -0
  23. package/dist/utilities/isValidTenantAssignment.d.ts.map +1 -0
  24. package/dist/utilities/isValidTenantAssignment.js +28 -0
  25. package/dist/utilities/isValidTenantAssignment.js.map +1 -0
  26. package/dist/utilities/shouldRefreshTenantSelection.d.ts +5 -0
  27. package/dist/utilities/shouldRefreshTenantSelection.d.ts.map +1 -0
  28. package/dist/utilities/shouldRefreshTenantSelection.js +3 -0
  29. package/dist/utilities/shouldRefreshTenantSelection.js.map +1 -0
  30. package/package.json +7 -7
  31. package/dist/index.spec.js +0 -70
  32. package/dist/index.spec.js.map +0 -1
  33. package/dist/utilities/addCollectionAccess.spec.js +0 -134
  34. package/dist/utilities/addCollectionAccess.spec.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,gEA+a5B,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';
@@ -245,15 +246,26 @@ export const multiTenantPlugin = definePlugin({
245
246
  * Add tenant field to enabled collections
246
247
  */ collection.fields.unshift(tenantField({
247
248
  name: tenantFieldName,
249
+ adminUsersSlug: adminUsersCollection.slug,
248
250
  debug: pluginConfig.debug,
249
251
  isAutosaveEnabled: hasAutosaveEnabled(collection),
250
252
  overrides: pluginConfig.collections[collection.slug]?.tenantFieldOverrides ? pluginConfig.collections[collection.slug]?.tenantFieldOverrides : pluginConfig.tenantField || {},
251
253
  tenantsArrayFieldName,
252
254
  tenantsArrayTenantFieldName,
253
255
  tenantsCollectionSlug,
254
- unique: isGlobal
256
+ unique: isGlobal,
257
+ userHasAccessToAllTenants
255
258
  }));
256
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
+ }));
257
269
  const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {};
258
270
  if (useBaseFilter ?? useBaseListFilter ?? true) {
259
271
  /**
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, 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 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 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","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","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
+ {"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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"}
@@ -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.af6aad0",
3
+ "version": "4.0.0-internal.b8a92d2",
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.af6aad0",
84
- "@payloadcms/translations": "4.0.0-internal.af6aad0",
85
- "payload": "4.0.0-internal.af6aad0"
83
+ "@payloadcms/translations": "4.0.0-internal.b8a92d2",
84
+ "@payloadcms/ui": "4.0.0-internal.b8a92d2",
85
+ "payload": "4.0.0-internal.b8a92d2"
86
86
  },
87
87
  "peerDependencies": {
88
- "@payloadcms/ui": "4.0.0-internal.af6aad0",
89
- "payload": "4.0.0-internal.af6aad0"
88
+ "@payloadcms/ui": "4.0.0-internal.b8a92d2",
89
+ "payload": "4.0.0-internal.b8a92d2"
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,70 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { multiTenantPlugin } from './index.js';
3
- describe('multiTenantPlugin', ()=>{
4
- it('applies tenant assignment access defaults', async ()=>{
5
- let hasAllTenantAccess = false;
6
- const plugin = multiTenantPlugin({
7
- collections: {},
8
- userHasAccessToAllTenants: ()=>hasAllTenantAccess
9
- });
10
- const config = await plugin({
11
- collections: [
12
- {
13
- slug: 'users',
14
- auth: true,
15
- fields: []
16
- },
17
- {
18
- slug: 'tenants',
19
- fields: []
20
- }
21
- ]
22
- });
23
- const usersCollection = config.collections?.find(({ slug })=>slug === 'users');
24
- const tenantsField = usersCollection?.fields.find((field)=>'name' in field && field.name === 'tenants');
25
- const accessArgs = {
26
- req: {
27
- user: {
28
- id: 'test-user'
29
- }
30
- }
31
- };
32
- expect(await tenantsField?.access?.create?.(accessArgs)).toBe(false);
33
- expect(await tenantsField?.access?.update?.(accessArgs)).toBe(false);
34
- hasAllTenantAccess = true;
35
- expect(await tenantsField?.access?.create?.(accessArgs)).toBe(true);
36
- expect(await tenantsField?.access?.update?.(accessArgs)).toBe(true);
37
- });
38
- it('retains configured tenant assignment access callbacks', async ()=>{
39
- const createAccess = ()=>false;
40
- const updateAccess = ()=>true;
41
- const plugin = multiTenantPlugin({
42
- collections: {},
43
- tenantsArrayField: {
44
- arrayFieldAccess: {
45
- create: createAccess,
46
- update: updateAccess
47
- }
48
- }
49
- });
50
- const config = await plugin({
51
- collections: [
52
- {
53
- slug: 'users',
54
- auth: true,
55
- fields: []
56
- },
57
- {
58
- slug: 'tenants',
59
- fields: []
60
- }
61
- ]
62
- });
63
- const usersCollection = config.collections?.find(({ slug })=>slug === 'users');
64
- const tenantsField = usersCollection?.fields.find((field)=>'name' in field && field.name === 'tenants');
65
- expect(tenantsField?.access?.create).toBe(createAccess);
66
- expect(tenantsField?.access?.update).toBe(updateAccess);
67
- });
68
- });
69
-
70
- //# sourceMappingURL=index.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.spec.ts"],"sourcesContent":["import type { ArrayField, Config } from 'payload'\n\nimport { describe, expect, it } from 'vitest'\n\nimport { multiTenantPlugin } from './index.js'\n\ndescribe('multiTenantPlugin', () => {\n it('applies tenant assignment access defaults', async () => {\n let hasAllTenantAccess = false\n const plugin = multiTenantPlugin({\n collections: {},\n userHasAccessToAllTenants: () => hasAllTenantAccess,\n })\n const config = await plugin({\n collections: [\n { slug: 'users', auth: true, fields: [] },\n { slug: 'tenants', fields: [] },\n ],\n } as Config)\n const usersCollection = config.collections?.find(({ slug }) => slug === 'users')\n const tenantsField = usersCollection?.fields.find(\n (field): field is ArrayField => 'name' in field && field.name === 'tenants',\n )\n const accessArgs = { req: { user: { id: 'test-user' } } } as never\n\n expect(await tenantsField?.access?.create?.(accessArgs)).toBe(false)\n expect(await tenantsField?.access?.update?.(accessArgs)).toBe(false)\n\n hasAllTenantAccess = true\n\n expect(await tenantsField?.access?.create?.(accessArgs)).toBe(true)\n expect(await tenantsField?.access?.update?.(accessArgs)).toBe(true)\n })\n\n it('retains configured tenant assignment access callbacks', async () => {\n const createAccess = () => false\n const updateAccess = () => true\n const plugin = multiTenantPlugin({\n collections: {},\n tenantsArrayField: {\n arrayFieldAccess: {\n create: createAccess,\n update: updateAccess,\n },\n },\n })\n const config = await plugin({\n collections: [\n { slug: 'users', auth: true, fields: [] },\n { slug: 'tenants', fields: [] },\n ],\n } as Config)\n const usersCollection = config.collections?.find(({ slug }) => slug === 'users')\n const tenantsField = usersCollection?.fields.find(\n (field): field is ArrayField => 'name' in field && field.name === 'tenants',\n )\n\n expect(tenantsField?.access?.create).toBe(createAccess)\n expect(tenantsField?.access?.update).toBe(updateAccess)\n })\n})\n"],"names":["describe","expect","it","multiTenantPlugin","hasAllTenantAccess","plugin","collections","userHasAccessToAllTenants","config","slug","auth","fields","usersCollection","find","tenantsField","field","name","accessArgs","req","user","id","access","create","toBe","update","createAccess","updateAccess","tenantsArrayField","arrayFieldAccess"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,iBAAiB,QAAQ,aAAY;AAE9CH,SAAS,qBAAqB;IAC5BE,GAAG,6CAA6C;QAC9C,IAAIE,qBAAqB;QACzB,MAAMC,SAASF,kBAAkB;YAC/BG,aAAa,CAAC;YACdC,2BAA2B,IAAMH;QACnC;QACA,MAAMI,SAAS,MAAMH,OAAO;YAC1BC,aAAa;gBACX;oBAAEG,MAAM;oBAASC,MAAM;oBAAMC,QAAQ,EAAE;gBAAC;gBACxC;oBAAEF,MAAM;oBAAWE,QAAQ,EAAE;gBAAC;aAC/B;QACH;QACA,MAAMC,kBAAkBJ,OAAOF,WAAW,EAAEO,KAAK,CAAC,EAAEJ,IAAI,EAAE,GAAKA,SAAS;QACxE,MAAMK,eAAeF,iBAAiBD,OAAOE,KAC3C,CAACE,QAA+B,UAAUA,SAASA,MAAMC,IAAI,KAAK;QAEpE,MAAMC,aAAa;YAAEC,KAAK;gBAAEC,MAAM;oBAAEC,IAAI;gBAAY;YAAE;QAAE;QAExDnB,OAAO,MAAMa,cAAcO,QAAQC,SAASL,aAAaM,IAAI,CAAC;QAC9DtB,OAAO,MAAMa,cAAcO,QAAQG,SAASP,aAAaM,IAAI,CAAC;QAE9DnB,qBAAqB;QAErBH,OAAO,MAAMa,cAAcO,QAAQC,SAASL,aAAaM,IAAI,CAAC;QAC9DtB,OAAO,MAAMa,cAAcO,QAAQG,SAASP,aAAaM,IAAI,CAAC;IAChE;IAEArB,GAAG,yDAAyD;QAC1D,MAAMuB,eAAe,IAAM;QAC3B,MAAMC,eAAe,IAAM;QAC3B,MAAMrB,SAASF,kBAAkB;YAC/BG,aAAa,CAAC;YACdqB,mBAAmB;gBACjBC,kBAAkB;oBAChBN,QAAQG;oBACRD,QAAQE;gBACV;YACF;QACF;QACA,MAAMlB,SAAS,MAAMH,OAAO;YAC1BC,aAAa;gBACX;oBAAEG,MAAM;oBAASC,MAAM;oBAAMC,QAAQ,EAAE;gBAAC;gBACxC;oBAAEF,MAAM;oBAAWE,QAAQ,EAAE;gBAAC;aAC/B;QACH;QACA,MAAMC,kBAAkBJ,OAAOF,WAAW,EAAEO,KAAK,CAAC,EAAEJ,IAAI,EAAE,GAAKA,SAAS;QACxE,MAAMK,eAAeF,iBAAiBD,OAAOE,KAC3C,CAACE,QAA+B,UAAUA,SAASA,MAAMC,IAAI,KAAK;QAGpEf,OAAOa,cAAcO,QAAQC,QAAQC,IAAI,CAACE;QAC1CxB,OAAOa,cAAcO,QAAQG,QAAQD,IAAI,CAACG;IAC5C;AACF"}
@@ -1,134 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { addCollectionAccess } from './addCollectionAccess.js';
3
- const createScope = (collection, accessResultCallback)=>({
4
- accessResultCallback,
5
- adminUsersSlug: 'users',
6
- collection,
7
- fieldName: 'tenant',
8
- userHasAccessToAllTenants: ()=>false
9
- });
10
- const createArgs = (slug = 'posts')=>({
11
- req: {
12
- user: {
13
- collection: 'users',
14
- id: 'user-1',
15
- tenants: [
16
- {
17
- tenant: 'tenant-1'
18
- }
19
- ]
20
- }
21
- },
22
- slug
23
- });
24
- describe('addCollectionAccess', ()=>{
25
- it('adds tenant constraints through base access without replacing collection access', async ()=>{
26
- const documentRead = vi.fn(()=>({
27
- published: {
28
- equals: true
29
- }
30
- }));
31
- const collection = {
32
- slug: 'posts',
33
- access: {
34
- read: documentRead
35
- },
36
- fields: []
37
- };
38
- const config = {
39
- baseAccess: {
40
- collections: {
41
- read: ()=>({
42
- archived: {
43
- not_equals: true
44
- }
45
- })
46
- }
47
- }
48
- };
49
- addCollectionAccess({
50
- config,
51
- scopes: [
52
- createScope(collection)
53
- ]
54
- });
55
- expect(collection.access?.read).toBe(documentRead);
56
- await expect(config.baseAccess?.collections?.read?.(createArgs())).resolves.toEqual({
57
- and: [
58
- {
59
- archived: {
60
- not_equals: true
61
- }
62
- },
63
- {
64
- tenant: {
65
- in: [
66
- 'tenant-1'
67
- ]
68
- }
69
- }
70
- ]
71
- });
72
- await expect(config.baseAccess?.collections?.read?.(createArgs('pages'))).resolves.toEqual({
73
- archived: {
74
- not_equals: true
75
- }
76
- });
77
- expect(documentRead).not.toHaveBeenCalled();
78
- });
79
- it('returns a boolean tenant result for collection create access', async ()=>{
80
- const collection = {
81
- slug: 'posts',
82
- fields: []
83
- };
84
- const config = {};
85
- addCollectionAccess({
86
- config,
87
- scopes: [
88
- createScope(collection)
89
- ]
90
- });
91
- await expect(config.baseAccess?.collections?.create?.(createArgs())).resolves.toBe(true);
92
- });
93
- it('keeps callback wrapping when an access result override is configured', async ()=>{
94
- const documentResult = {
95
- published: {
96
- equals: true
97
- }
98
- };
99
- const documentRead = vi.fn(()=>documentResult);
100
- const accessResultCallback = vi.fn(({ accessResult })=>accessResult);
101
- const collection = {
102
- slug: 'posts',
103
- access: {
104
- read: documentRead
105
- },
106
- fields: []
107
- };
108
- const config = {};
109
- addCollectionAccess({
110
- config,
111
- scopes: [
112
- createScope(collection, accessResultCallback)
113
- ]
114
- });
115
- expect(config.baseAccess).toBeUndefined();
116
- await expect(collection.access?.read?.(createArgs())).resolves.toEqual({
117
- and: [
118
- documentResult,
119
- {
120
- tenant: {
121
- in: [
122
- 'tenant-1'
123
- ]
124
- }
125
- }
126
- ]
127
- });
128
- expect(accessResultCallback).toHaveBeenCalledWith(expect.objectContaining({
129
- accessKey: 'read'
130
- }));
131
- });
132
- });
133
-
134
- //# sourceMappingURL=addCollectionAccess.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utilities/addCollectionAccess.spec.ts"],"sourcesContent":["import type { AccessArgs, CollectionConfig, Config } from 'payload'\n\nimport { describe, expect, it, vi } from 'vitest'\n\nimport type { TenantAccessConfig } from './addCollectionAccess.js'\n\nimport { addCollectionAccess } from './addCollectionAccess.js'\n\nconst createScope = (\n collection: CollectionConfig,\n accessResultCallback?: TenantAccessConfig['accessResultCallback'],\n): TenantAccessConfig => ({\n accessResultCallback,\n adminUsersSlug: 'users',\n collection,\n fieldName: 'tenant',\n userHasAccessToAllTenants: () => false,\n})\n\nconst createArgs = (slug = 'posts'): AccessArgs => ({\n req: {\n user: {\n collection: 'users',\n id: 'user-1',\n tenants: [{ tenant: 'tenant-1' }],\n },\n } as unknown as AccessArgs['req'],\n slug,\n})\n\ndescribe('addCollectionAccess', () => {\n it('adds tenant constraints through base access without replacing collection access', async () => {\n const documentRead = vi.fn(() => ({ published: { equals: true } }))\n const collection: CollectionConfig = {\n slug: 'posts',\n access: { read: documentRead },\n fields: [],\n }\n const config = {\n baseAccess: {\n collections: {\n read: () => ({ archived: { not_equals: true } }),\n },\n },\n } as Config\n\n addCollectionAccess({ config, scopes: [createScope(collection)] })\n\n expect(collection.access?.read).toBe(documentRead)\n await expect(config.baseAccess?.collections?.read?.(createArgs())).resolves.toEqual({\n and: [{ archived: { not_equals: true } }, { tenant: { in: ['tenant-1'] } }],\n })\n await expect(config.baseAccess?.collections?.read?.(createArgs('pages'))).resolves.toEqual({\n archived: { not_equals: true },\n })\n expect(documentRead).not.toHaveBeenCalled()\n })\n\n it('returns a boolean tenant result for collection create access', async () => {\n const collection: CollectionConfig = { slug: 'posts', fields: [] }\n const config = {} as Config\n\n addCollectionAccess({ config, scopes: [createScope(collection)] })\n\n await expect(config.baseAccess?.collections?.create?.(createArgs())).resolves.toBe(true)\n })\n\n it('keeps callback wrapping when an access result override is configured', async () => {\n const documentResult = { published: { equals: true } }\n const documentRead = vi.fn(() => documentResult)\n const accessResultCallback = vi.fn(({ accessResult }) => accessResult)\n const collection: CollectionConfig = {\n slug: 'posts',\n access: { read: documentRead },\n fields: [],\n }\n const config = {} as Config\n\n addCollectionAccess({\n config,\n scopes: [createScope(collection, accessResultCallback)],\n })\n\n expect(config.baseAccess).toBeUndefined()\n await expect(collection.access?.read?.(createArgs())).resolves.toEqual({\n and: [documentResult, { tenant: { in: ['tenant-1'] } }],\n })\n expect(accessResultCallback).toHaveBeenCalledWith(\n expect.objectContaining({ accessKey: 'read' }),\n )\n })\n})\n"],"names":["describe","expect","it","vi","addCollectionAccess","createScope","collection","accessResultCallback","adminUsersSlug","fieldName","userHasAccessToAllTenants","createArgs","slug","req","user","id","tenants","tenant","documentRead","fn","published","equals","access","read","fields","config","baseAccess","collections","archived","not_equals","scopes","toBe","resolves","toEqual","and","in","not","toHaveBeenCalled","create","documentResult","accessResult","toBeUndefined","toHaveBeenCalledWith","objectContaining","accessKey"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAIjD,SAASC,mBAAmB,QAAQ,2BAA0B;AAE9D,MAAMC,cAAc,CAClBC,YACAC,uBACwB,CAAA;QACxBA;QACAC,gBAAgB;QAChBF;QACAG,WAAW;QACXC,2BAA2B,IAAM;IACnC,CAAA;AAEA,MAAMC,aAAa,CAACC,OAAO,OAAO,GAAkB,CAAA;QAClDC,KAAK;YACHC,MAAM;gBACJR,YAAY;gBACZS,IAAI;gBACJC,SAAS;oBAAC;wBAAEC,QAAQ;oBAAW;iBAAE;YACnC;QACF;QACAL;IACF,CAAA;AAEAZ,SAAS,uBAAuB;IAC9BE,GAAG,mFAAmF;QACpF,MAAMgB,eAAef,GAAGgB,EAAE,CAAC,IAAO,CAAA;gBAAEC,WAAW;oBAAEC,QAAQ;gBAAK;YAAE,CAAA;QAChE,MAAMf,aAA+B;YACnCM,MAAM;YACNU,QAAQ;gBAAEC,MAAML;YAAa;YAC7BM,QAAQ,EAAE;QACZ;QACA,MAAMC,SAAS;YACbC,YAAY;gBACVC,aAAa;oBACXJ,MAAM,IAAO,CAAA;4BAAEK,UAAU;gCAAEC,YAAY;4BAAK;wBAAE,CAAA;gBAChD;YACF;QACF;QAEAzB,oBAAoB;YAAEqB;YAAQK,QAAQ;gBAACzB,YAAYC;aAAY;QAAC;QAEhEL,OAAOK,WAAWgB,MAAM,EAAEC,MAAMQ,IAAI,CAACb;QACrC,MAAMjB,OAAOwB,OAAOC,UAAU,EAAEC,aAAaJ,OAAOZ,eAAeqB,QAAQ,CAACC,OAAO,CAAC;YAClFC,KAAK;gBAAC;oBAAEN,UAAU;wBAAEC,YAAY;oBAAK;gBAAE;gBAAG;oBAAEZ,QAAQ;wBAAEkB,IAAI;4BAAC;yBAAW;oBAAC;gBAAE;aAAE;QAC7E;QACA,MAAMlC,OAAOwB,OAAOC,UAAU,EAAEC,aAAaJ,OAAOZ,WAAW,WAAWqB,QAAQ,CAACC,OAAO,CAAC;YACzFL,UAAU;gBAAEC,YAAY;YAAK;QAC/B;QACA5B,OAAOiB,cAAckB,GAAG,CAACC,gBAAgB;IAC3C;IAEAnC,GAAG,gEAAgE;QACjE,MAAMI,aAA+B;YAAEM,MAAM;YAASY,QAAQ,EAAE;QAAC;QACjE,MAAMC,SAAS,CAAC;QAEhBrB,oBAAoB;YAAEqB;YAAQK,QAAQ;gBAACzB,YAAYC;aAAY;QAAC;QAEhE,MAAML,OAAOwB,OAAOC,UAAU,EAAEC,aAAaW,SAAS3B,eAAeqB,QAAQ,CAACD,IAAI,CAAC;IACrF;IAEA7B,GAAG,wEAAwE;QACzE,MAAMqC,iBAAiB;YAAEnB,WAAW;gBAAEC,QAAQ;YAAK;QAAE;QACrD,MAAMH,eAAef,GAAGgB,EAAE,CAAC,IAAMoB;QACjC,MAAMhC,uBAAuBJ,GAAGgB,EAAE,CAAC,CAAC,EAAEqB,YAAY,EAAE,GAAKA;QACzD,MAAMlC,aAA+B;YACnCM,MAAM;YACNU,QAAQ;gBAAEC,MAAML;YAAa;YAC7BM,QAAQ,EAAE;QACZ;QACA,MAAMC,SAAS,CAAC;QAEhBrB,oBAAoB;YAClBqB;YACAK,QAAQ;gBAACzB,YAAYC,YAAYC;aAAsB;QACzD;QAEAN,OAAOwB,OAAOC,UAAU,EAAEe,aAAa;QACvC,MAAMxC,OAAOK,WAAWgB,MAAM,EAAEC,OAAOZ,eAAeqB,QAAQ,CAACC,OAAO,CAAC;YACrEC,KAAK;gBAACK;gBAAgB;oBAAEtB,QAAQ;wBAAEkB,IAAI;4BAAC;yBAAW;oBAAC;gBAAE;aAAE;QACzD;QACAlC,OAAOM,sBAAsBmC,oBAAoB,CAC/CzC,OAAO0C,gBAAgB,CAAC;YAAEC,WAAW;QAAO;IAEhD;AACF"}