@payloadcms/plugin-multi-tenant 4.0.0-internal.c7d5de4 → 4.0.0-internal.cc18726

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 (29) hide show
  1. package/dist/components/AssignTenantFieldModal/index.client.d.ts +2 -2
  2. package/dist/components/AssignTenantFieldModal/index.client.d.ts.map +1 -1
  3. package/dist/components/AssignTenantFieldModal/index.client.js.map +1 -1
  4. package/dist/components/TenantField/index.client.d.ts.map +1 -1
  5. package/dist/components/TenantField/index.client.js +84 -74
  6. package/dist/components/TenantField/index.client.js.map +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +13 -3
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.spec.js +70 -0
  11. package/dist/index.spec.js.map +1 -0
  12. package/dist/types.d.ts +1 -0
  13. package/dist/types.d.ts.map +1 -1
  14. package/dist/types.js.map +1 -1
  15. package/dist/utilities/addCollectionAccess.d.ts +6 -8
  16. package/dist/utilities/addCollectionAccess.d.ts.map +1 -1
  17. package/dist/utilities/addCollectionAccess.js +91 -20
  18. package/dist/utilities/addCollectionAccess.js.map +1 -1
  19. package/dist/utilities/addCollectionAccess.spec.js +134 -0
  20. package/dist/utilities/addCollectionAccess.spec.js.map +1 -0
  21. package/package.json +6 -7
  22. package/dist/utilities/combineWhereConstraints.d.ts +0 -3
  23. package/dist/utilities/combineWhereConstraints.d.ts.map +0 -1
  24. package/dist/utilities/combineWhereConstraints.js +0 -19
  25. package/dist/utilities/combineWhereConstraints.js.map +0 -1
  26. package/dist/utilities/withTenantAccess.d.ts +0 -16
  27. package/dist/utilities/withTenantAccess.d.ts.map +0 -1
  28. package/dist/utilities/withTenantAccess.js +0 -76
  29. package/dist/utilities/withTenantAccess.js.map +0 -1
@@ -2,8 +2,8 @@ import React from 'react';
2
2
  export declare const assignTenantModalSlug = "assign-tenant-field-modal";
3
3
  export declare const AssignTenantFieldTrigger: React.FC;
4
4
  export declare const AssignTenantFieldModal: React.FC<{
5
- afterModalClose: () => void;
6
- afterModalOpen: () => void;
5
+ afterModalClose?: () => void;
6
+ afterModalOpen?: () => void;
7
7
  children: React.ReactNode;
8
8
  onCancel?: () => void;
9
9
  onConfirm?: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../../../src/components/AssignTenantFieldModal/index.client.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,eAAO,MAAM,qBAAqB,8BAA8B,CAAA;AAEhE,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAc5C,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC;IAC5C,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB,CAuCA,CAAA"}
1
+ {"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../../../src/components/AssignTenantFieldModal/index.client.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,eAAO,MAAM,qBAAqB,8BAA8B,CAAA;AAEhE,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAc5C,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC;IAC5C,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB,CAuCA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/AssignTenantFieldModal/index.client.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientCollectionConfig } from 'payload'\n\nimport { getTranslation } from '@payloadcms/translations'\nimport {\n DialogBody,\n DialogCancel,\n DialogConfirm,\n DialogFooter,\n DialogHeader,\n DialogModal,\n Pill,\n PopupList,\n useConfig,\n useDocumentInfo,\n useDocumentTitle,\n useModal,\n useTranslation,\n} from '@payloadcms/ui'\nimport React from 'react'\n\nimport type {\n PluginMultiTenantTranslationKeys,\n PluginMultiTenantTranslations,\n} from '../../translations/index.js'\n\nimport { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'\n\nexport const assignTenantModalSlug = 'assign-tenant-field-modal'\n\nexport const AssignTenantFieldTrigger: React.FC = () => {\n const { openModal } = useModal()\n const { t } = useTranslation<PluginMultiTenantTranslations, PluginMultiTenantTranslationKeys>()\n const { options } = useTenantSelection()\n\n if (options.length <= 1) {\n return null\n }\n\n return (\n <PopupList.Button onClick={() => openModal(assignTenantModalSlug)}>\n {t('plugin-multi-tenant:assign-tenant-button-label')}\n </PopupList.Button>\n )\n}\n\nexport const AssignTenantFieldModal: React.FC<{\n afterModalClose: () => void\n afterModalOpen: () => void\n children: React.ReactNode\n onCancel?: () => void\n onConfirm?: () => void\n}> = ({ afterModalClose, afterModalOpen, children, onCancel, onConfirm }) => {\n const { i18n, t } = useTranslation<\n PluginMultiTenantTranslations,\n PluginMultiTenantTranslationKeys\n >()\n const { collectionSlug } = useDocumentInfo()\n const { title } = useDocumentTitle()\n const { getEntityConfig } = useConfig()\n const collectionConfig = getEntityConfig({ collectionSlug }) as ClientCollectionConfig\n const { isModalOpen: isModalOpenFn } = useModal()\n const isModalOpen = isModalOpenFn(assignTenantModalSlug)\n const wasModalOpenRef = React.useRef<boolean>(isModalOpen)\n\n React.useEffect(() => {\n if (wasModalOpenRef.current && !isModalOpen) {\n afterModalClose?.()\n }\n if (!wasModalOpenRef.current && isModalOpen) {\n afterModalOpen?.()\n }\n wasModalOpenRef.current = isModalOpen\n }, [isModalOpen, afterModalClose, afterModalOpen])\n\n if (!collectionConfig) {\n return null\n }\n\n return (\n <DialogModal size=\"medium\" slug={assignTenantModalSlug}>\n <DialogHeader title={t('plugin-multi-tenant:assign-tenant-modal-title', { title })}>\n <Pill size=\"small\">{getTranslation(collectionConfig.labels.singular, i18n)}</Pill>\n </DialogHeader>\n <DialogBody>{children}</DialogBody>\n <DialogFooter>\n <DialogCancel onClick={onCancel} />\n <DialogConfirm onClick={() => onConfirm?.()} />\n </DialogFooter>\n </DialogModal>\n )\n}\n"],"names":["getTranslation","DialogBody","DialogCancel","DialogConfirm","DialogFooter","DialogHeader","DialogModal","Pill","PopupList","useConfig","useDocumentInfo","useDocumentTitle","useModal","useTranslation","React","useTenantSelection","assignTenantModalSlug","AssignTenantFieldTrigger","openModal","t","options","length","Button","onClick","AssignTenantFieldModal","afterModalClose","afterModalOpen","children","onCancel","onConfirm","i18n","collectionSlug","title","getEntityConfig","collectionConfig","isModalOpen","isModalOpenFn","wasModalOpenRef","useRef","useEffect","current","size","slug","labels","singular"],"mappings":"AAAA;;AAIA,SAASA,cAAc,QAAQ,2BAA0B;AACzD,SACEC,UAAU,EACVC,YAAY,EACZC,aAAa,EACbC,YAAY,EACZC,YAAY,EACZC,WAAW,EACXC,IAAI,EACJC,SAAS,EACTC,SAAS,EACTC,eAAe,EACfC,gBAAgB,EAChBC,QAAQ,EACRC,cAAc,QACT,iBAAgB;AACvB,OAAOC,WAAW,QAAO;AAOzB,SAASC,kBAAkB,QAAQ,0DAAyD;AAE5F,OAAO,MAAMC,wBAAwB,4BAA2B;AAEhE,OAAO,MAAMC,2BAAqC;IAChD,MAAM,EAAEC,SAAS,EAAE,GAAGN;IACtB,MAAM,EAAEO,CAAC,EAAE,GAAGN;IACd,MAAM,EAAEO,OAAO,EAAE,GAAGL;IAEpB,IAAIK,QAAQC,MAAM,IAAI,GAAG;QACvB,OAAO;IACT;IAEA,qBACE,KAACb,UAAUc,MAAM;QAACC,SAAS,IAAML,UAAUF;kBACxCG,EAAE;;AAGT,EAAC;AAED,OAAO,MAAMK,yBAMR,CAAC,EAAEC,eAAe,EAAEC,cAAc,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,SAAS,EAAE;IACtE,MAAM,EAAEC,IAAI,EAAEX,CAAC,EAAE,GAAGN;IAIpB,MAAM,EAAEkB,cAAc,EAAE,GAAGrB;IAC3B,MAAM,EAAEsB,KAAK,EAAE,GAAGrB;IAClB,MAAM,EAAEsB,eAAe,EAAE,GAAGxB;IAC5B,MAAMyB,mBAAmBD,gBAAgB;QAAEF;IAAe;IAC1D,MAAM,EAAEI,aAAaC,aAAa,EAAE,GAAGxB;IACvC,MAAMuB,cAAcC,cAAcpB;IAClC,MAAMqB,kBAAkBvB,MAAMwB,MAAM,CAAUH;IAE9CrB,MAAMyB,SAAS,CAAC;QACd,IAAIF,gBAAgBG,OAAO,IAAI,CAACL,aAAa;YAC3CV;QACF;QACA,IAAI,CAACY,gBAAgBG,OAAO,IAAIL,aAAa;YAC3CT;QACF;QACAW,gBAAgBG,OAAO,GAAGL;IAC5B,GAAG;QAACA;QAAaV;QAAiBC;KAAe;IAEjD,IAAI,CAACQ,kBAAkB;QACrB,OAAO;IACT;IAEA,qBACE,MAAC5B;QAAYmC,MAAK;QAASC,MAAM1B;;0BAC/B,KAACX;gBAAa2B,OAAOb,EAAE,iDAAiD;oBAAEa;gBAAM;0BAC9E,cAAA,KAACzB;oBAAKkC,MAAK;8BAASzC,eAAekC,iBAAiBS,MAAM,CAACC,QAAQ,EAAEd;;;0BAEvE,KAAC7B;0BAAY0B;;0BACb,MAACvB;;kCACC,KAACF;wBAAaqB,SAASK;;kCACvB,KAACzB;wBAAcoB,SAAS,IAAMM;;;;;;AAItC,EAAC"}
1
+ {"version":3,"sources":["../../../src/components/AssignTenantFieldModal/index.client.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientCollectionConfig } from 'payload'\n\nimport { getTranslation } from '@payloadcms/translations'\nimport {\n DialogBody,\n DialogCancel,\n DialogConfirm,\n DialogFooter,\n DialogHeader,\n DialogModal,\n Pill,\n PopupList,\n useConfig,\n useDocumentInfo,\n useDocumentTitle,\n useModal,\n useTranslation,\n} from '@payloadcms/ui'\nimport React from 'react'\n\nimport type {\n PluginMultiTenantTranslationKeys,\n PluginMultiTenantTranslations,\n} from '../../translations/index.js'\n\nimport { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'\n\nexport const assignTenantModalSlug = 'assign-tenant-field-modal'\n\nexport const AssignTenantFieldTrigger: React.FC = () => {\n const { openModal } = useModal()\n const { t } = useTranslation<PluginMultiTenantTranslations, PluginMultiTenantTranslationKeys>()\n const { options } = useTenantSelection()\n\n if (options.length <= 1) {\n return null\n }\n\n return (\n <PopupList.Button onClick={() => openModal(assignTenantModalSlug)}>\n {t('plugin-multi-tenant:assign-tenant-button-label')}\n </PopupList.Button>\n )\n}\n\nexport const AssignTenantFieldModal: React.FC<{\n afterModalClose?: () => void\n afterModalOpen?: () => void\n children: React.ReactNode\n onCancel?: () => void\n onConfirm?: () => void\n}> = ({ afterModalClose, afterModalOpen, children, onCancel, onConfirm }) => {\n const { i18n, t } = useTranslation<\n PluginMultiTenantTranslations,\n PluginMultiTenantTranslationKeys\n >()\n const { collectionSlug } = useDocumentInfo()\n const { title } = useDocumentTitle()\n const { getEntityConfig } = useConfig()\n const collectionConfig = getEntityConfig({ collectionSlug }) as ClientCollectionConfig\n const { isModalOpen: isModalOpenFn } = useModal()\n const isModalOpen = isModalOpenFn(assignTenantModalSlug)\n const wasModalOpenRef = React.useRef<boolean>(isModalOpen)\n\n React.useEffect(() => {\n if (wasModalOpenRef.current && !isModalOpen) {\n afterModalClose?.()\n }\n if (!wasModalOpenRef.current && isModalOpen) {\n afterModalOpen?.()\n }\n wasModalOpenRef.current = isModalOpen\n }, [isModalOpen, afterModalClose, afterModalOpen])\n\n if (!collectionConfig) {\n return null\n }\n\n return (\n <DialogModal size=\"medium\" slug={assignTenantModalSlug}>\n <DialogHeader title={t('plugin-multi-tenant:assign-tenant-modal-title', { title })}>\n <Pill size=\"small\">{getTranslation(collectionConfig.labels.singular, i18n)}</Pill>\n </DialogHeader>\n <DialogBody>{children}</DialogBody>\n <DialogFooter>\n <DialogCancel onClick={onCancel} />\n <DialogConfirm onClick={() => onConfirm?.()} />\n </DialogFooter>\n </DialogModal>\n )\n}\n"],"names":["getTranslation","DialogBody","DialogCancel","DialogConfirm","DialogFooter","DialogHeader","DialogModal","Pill","PopupList","useConfig","useDocumentInfo","useDocumentTitle","useModal","useTranslation","React","useTenantSelection","assignTenantModalSlug","AssignTenantFieldTrigger","openModal","t","options","length","Button","onClick","AssignTenantFieldModal","afterModalClose","afterModalOpen","children","onCancel","onConfirm","i18n","collectionSlug","title","getEntityConfig","collectionConfig","isModalOpen","isModalOpenFn","wasModalOpenRef","useRef","useEffect","current","size","slug","labels","singular"],"mappings":"AAAA;;AAIA,SAASA,cAAc,QAAQ,2BAA0B;AACzD,SACEC,UAAU,EACVC,YAAY,EACZC,aAAa,EACbC,YAAY,EACZC,YAAY,EACZC,WAAW,EACXC,IAAI,EACJC,SAAS,EACTC,SAAS,EACTC,eAAe,EACfC,gBAAgB,EAChBC,QAAQ,EACRC,cAAc,QACT,iBAAgB;AACvB,OAAOC,WAAW,QAAO;AAOzB,SAASC,kBAAkB,QAAQ,0DAAyD;AAE5F,OAAO,MAAMC,wBAAwB,4BAA2B;AAEhE,OAAO,MAAMC,2BAAqC;IAChD,MAAM,EAAEC,SAAS,EAAE,GAAGN;IACtB,MAAM,EAAEO,CAAC,EAAE,GAAGN;IACd,MAAM,EAAEO,OAAO,EAAE,GAAGL;IAEpB,IAAIK,QAAQC,MAAM,IAAI,GAAG;QACvB,OAAO;IACT;IAEA,qBACE,KAACb,UAAUc,MAAM;QAACC,SAAS,IAAML,UAAUF;kBACxCG,EAAE;;AAGT,EAAC;AAED,OAAO,MAAMK,yBAMR,CAAC,EAAEC,eAAe,EAAEC,cAAc,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,SAAS,EAAE;IACtE,MAAM,EAAEC,IAAI,EAAEX,CAAC,EAAE,GAAGN;IAIpB,MAAM,EAAEkB,cAAc,EAAE,GAAGrB;IAC3B,MAAM,EAAEsB,KAAK,EAAE,GAAGrB;IAClB,MAAM,EAAEsB,eAAe,EAAE,GAAGxB;IAC5B,MAAMyB,mBAAmBD,gBAAgB;QAAEF;IAAe;IAC1D,MAAM,EAAEI,aAAaC,aAAa,EAAE,GAAGxB;IACvC,MAAMuB,cAAcC,cAAcpB;IAClC,MAAMqB,kBAAkBvB,MAAMwB,MAAM,CAAUH;IAE9CrB,MAAMyB,SAAS,CAAC;QACd,IAAIF,gBAAgBG,OAAO,IAAI,CAACL,aAAa;YAC3CV;QACF;QACA,IAAI,CAACY,gBAAgBG,OAAO,IAAIL,aAAa;YAC3CT;QACF;QACAW,gBAAgBG,OAAO,GAAGL;IAC5B,GAAG;QAACA;QAAaV;QAAiBC;KAAe;IAEjD,IAAI,CAACQ,kBAAkB;QACrB,OAAO;IACT;IAEA,qBACE,MAAC5B;QAAYmC,MAAK;QAASC,MAAM1B;;0BAC/B,KAACX;gBAAa2B,OAAOb,EAAE,iDAAiD;oBAAEa;gBAAM;0BAC9E,cAAA,KAACzB;oBAAKkC,MAAK;8BAASzC,eAAekC,iBAAiBS,MAAM,CAACC,QAAQ,EAAEd;;;0BAEvE,KAAC7B;0BAAY0B;;0BACb,MAACvB;;kCACC,KAACF;wBAAaqB,SAASK;;kCACvB,KAACzB;wBAAcoB,SAAS,IAAMM;;;;;;AAItC,EAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../../../src/components/TenantField/index.client.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAA;AAW3D,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,OAAO,aAAa,CAAA;AAIpB,KAAK,KAAK,GAAG;IACX,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,GAAG,4BAA4B,CAAA;AAEhC,eAAO,MAAM,WAAW,oCAAqC,KAAK,6BA6HjE,CAAA"}
1
+ {"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../../../src/components/TenantField/index.client.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAA;AAa3D,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,aAAa,CAAA;AAIpB,KAAK,KAAK,GAAG;IACX,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,GAAG,4BAA4B,CAAA;AAEhC,eAAO,MAAM,WAAW,oCAAqC,KAAK,6BAoIjE,CAAA"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Pill, RelationshipField, useDocumentInfo, useField, useForm, useFormModified, useModal } from '@payloadcms/ui';
3
+ import { FieldContext, FieldPathContext, Pill, RelationshipField, useDocumentInfo, useField, useForm, useFormModified, useModal } from '@payloadcms/ui';
4
4
  import React from 'react';
5
5
  import { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js';
6
6
  import { AssignTenantFieldModal, assignTenantModalSlug } from '../AssignTenantFieldModal/index.client.js';
@@ -8,84 +8,83 @@ import './index.css';
8
8
  const baseClass = 'tenantField';
9
9
  export const TenantField = ({ debug, unique, ...fieldArgs })=>{
10
10
  const { entityType, options, selectedTenantID, setEntityType, setTenant } = useTenantSelection();
11
- const { setValue, showError, value } = useField();
12
- const modified = useFormModified();
13
- const { isValid: isFormValid, setModified } = useForm();
11
+ const tenantField = useField();
12
+ const { setValue, showError, value } = tenantField;
13
+ const { isValid: isFormValid } = useForm();
14
14
  const { id: docID, collectionSlug } = useDocumentInfo();
15
15
  const { isModalOpen, openModal } = useModal();
16
16
  const isEditManyModalOpen = collectionSlug ? isModalOpen(`edit-${collectionSlug}`) || isModalOpen(`edit-${collectionSlug}-bulk-uploads`) : false;
17
- const isConfirmingRef = React.useRef(false);
18
- const prevModified = React.useRef(modified);
19
- const prevValue = React.useRef(value);
20
- const showField = options.length > 1 && !fieldArgs.field.admin?.hidden && !fieldArgs.field.hidden || debug;
21
- const onConfirm = React.useCallback(()=>{
22
- isConfirmingRef.current = true;
23
- }, []);
24
- const afterModalOpen = React.useCallback(()=>{
25
- prevModified.current = modified;
26
- prevValue.current = value;
27
- }, [
28
- modified,
29
- value
17
+ const [modalValue, setModalValue] = React.useState(value);
18
+ const localTenantField = React.useMemo(()=>({
19
+ ...tenantField,
20
+ initialValue: modalValue,
21
+ setValue: (nextValue)=>{
22
+ setModalValue(nextValue);
23
+ },
24
+ value: modalValue
25
+ }), [
26
+ modalValue,
27
+ tenantField
30
28
  ]);
31
- const afterModalClose = React.useCallback(()=>{
32
- let didChange = true;
33
- if (isConfirmingRef.current) {
34
- // did the values actually change?
35
- if (fieldArgs.field.hasMany) {
36
- const prev = prevValue.current || [];
37
- const newValue = value || [];
38
- if (prev.length !== newValue.length) {
39
- didChange = true;
40
- } else {
41
- const allMatch = newValue.every((val)=>prev.includes(val));
42
- if (allMatch) {
43
- didChange = false;
44
- }
45
- }
46
- } else if (value === prevValue.current) {
47
- didChange = false;
29
+ const showField = options.length > 1 && !fieldArgs.field.admin?.hidden && !fieldArgs.field.hidden || debug;
30
+ function getTenantIDToSelect({ selectedTenantID, value }) {
31
+ if (Array.isArray(value)) {
32
+ if (!value.length) {
33
+ return undefined;
48
34
  }
49
- if (didChange) {
50
- prevModified.current = true;
51
- prevValue.current = value;
35
+ if (!selectedTenantID || !value.includes(selectedTenantID)) {
36
+ return value[0];
52
37
  }
38
+ return undefined;
39
+ }
40
+ if (value && selectedTenantID !== value) {
41
+ return value;
42
+ }
43
+ return undefined;
44
+ }
45
+ const syncTenantSelectionFromValue = React.useCallback((tenantValue)=>{
46
+ const tenantID = getTenantIDToSelect({
47
+ selectedTenantID,
48
+ value: tenantValue
49
+ });
50
+ if (tenantID) {
51
+ setTenant({
52
+ id: tenantID,
53
+ refresh: false
54
+ });
55
+ }
56
+ }, [
57
+ selectedTenantID,
58
+ setTenant
59
+ ]);
60
+ const onConfirm = React.useCallback(()=>{
61
+ if (hasTenantValueChanged({
62
+ hasMany: fieldArgs.field.hasMany,
63
+ modalValue,
64
+ value
65
+ })) {
66
+ setValue(modalValue);
67
+ syncTenantSelectionFromValue(modalValue);
53
68
  }
54
- setValue(prevValue.current, true);
55
- setModified(prevModified.current);
56
- isConfirmingRef.current = false;
57
69
  }, [
70
+ fieldArgs.field.hasMany,
71
+ modalValue,
58
72
  setValue,
59
- setModified,
60
- value,
61
- fieldArgs.field.hasMany
73
+ syncTenantSelectionFromValue,
74
+ value
75
+ ]);
76
+ const afterModalOpen = React.useCallback(()=>{
77
+ setModalValue(value);
78
+ }, [
79
+ value
62
80
  ]);
63
81
  React.useEffect(()=>{
64
82
  if (!entityType) {
65
83
  setEntityType(unique ? 'global' : 'document');
66
84
  } else {
67
85
  // unique documents are controlled from the global TenantSelector
68
- if (!unique && value) {
69
- if (Array.isArray(value)) {
70
- if (value.length) {
71
- if (!selectedTenantID) {
72
- setTenant({
73
- id: value[0],
74
- refresh: false
75
- });
76
- } else if (!value.includes(selectedTenantID)) {
77
- setTenant({
78
- id: value[0],
79
- refresh: false
80
- });
81
- }
82
- }
83
- } else if (selectedTenantID !== value) {
84
- setTenant({
85
- id: value,
86
- refresh: false
87
- });
88
- }
86
+ if (!unique) {
87
+ syncTenantSelectionFromValue(value);
89
88
  }
90
89
  }
91
90
  return ()=>{
@@ -94,13 +93,11 @@ export const TenantField = ({ debug, unique, ...fieldArgs })=>{
94
93
  }
95
94
  };
96
95
  }, [
97
- unique,
98
- options,
99
- selectedTenantID,
100
- setTenant,
101
- value,
96
+ entityType,
102
97
  setEntityType,
103
- entityType
98
+ syncTenantSelectionFromValue,
99
+ unique,
100
+ value
104
101
  ]);
105
102
  React.useEffect(()=>{
106
103
  if (unique || debug || isEditManyModalOpen) {
@@ -131,12 +128,17 @@ export const TenantField = ({ debug, unique, ...fieldArgs })=>{
131
128
  }
132
129
  if (!unique) {
133
130
  /** Editing a non-global tenant document */ return /*#__PURE__*/ _jsx(AssignTenantFieldModal, {
134
- afterModalClose: afterModalClose,
135
131
  afterModalOpen: afterModalOpen,
136
132
  onConfirm: onConfirm,
137
- children: /*#__PURE__*/ _jsx(TenantRelationshipField, {
138
- fieldArgs: fieldArgs,
139
- unique: unique
133
+ children: /*#__PURE__*/ _jsx(FieldPathContext, {
134
+ value: tenantField.path,
135
+ children: /*#__PURE__*/ _jsx(FieldContext, {
136
+ value: localTenantField,
137
+ children: /*#__PURE__*/ _jsx(TenantRelationshipField, {
138
+ fieldArgs: fieldArgs,
139
+ unique: unique
140
+ })
141
+ })
140
142
  })
141
143
  });
142
144
  }
@@ -144,6 +146,14 @@ export const TenantField = ({ debug, unique, ...fieldArgs })=>{
144
146
  }
145
147
  return null;
146
148
  };
149
+ const hasTenantValueChanged = ({ hasMany, modalValue, value })=>{
150
+ if (hasMany) {
151
+ const currentValue = value || [];
152
+ const nextValue = modalValue || [];
153
+ return currentValue.length !== nextValue.length || nextValue.some((nextValueItem)=>!currentValue.includes(nextValueItem));
154
+ }
155
+ return value !== modalValue;
156
+ };
147
157
  const TenantRelationshipField = ({ debug, fieldArgs, unique })=>{
148
158
  return /*#__PURE__*/ _jsx("div", {
149
159
  className: baseClass,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/TenantField/index.client.tsx"],"sourcesContent":["'use client'\n\nimport type { RelationshipFieldClientProps } from 'payload'\n\nimport {\n Pill,\n RelationshipField,\n useDocumentInfo,\n useField,\n useForm,\n useFormModified,\n useModal,\n} from '@payloadcms/ui'\nimport React from 'react'\n\nimport { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'\nimport {\n AssignTenantFieldModal,\n assignTenantModalSlug,\n} from '../AssignTenantFieldModal/index.client.js'\nimport './index.css'\n\nconst baseClass = 'tenantField'\n\ntype Props = {\n debug?: boolean\n unique?: boolean\n} & RelationshipFieldClientProps\n\nexport const TenantField = ({ debug, unique, ...fieldArgs }: Props) => {\n const { entityType, options, selectedTenantID, setEntityType, setTenant } = useTenantSelection()\n const { setValue, showError, value } = useField<(number | string)[] | (number | string)>()\n const modified = useFormModified()\n const { isValid: isFormValid, setModified } = useForm()\n const { id: docID, collectionSlug } = useDocumentInfo()\n const { isModalOpen, openModal } = useModal()\n const isEditManyModalOpen = collectionSlug\n ? isModalOpen(`edit-${collectionSlug}`) || isModalOpen(`edit-${collectionSlug}-bulk-uploads`)\n : false\n const isConfirmingRef = React.useRef<boolean>(false)\n const prevModified = React.useRef(modified)\n const prevValue = React.useRef<typeof value>(value)\n const showField =\n (options.length > 1 && !fieldArgs.field.admin?.hidden && !fieldArgs.field.hidden) || debug\n\n const onConfirm = React.useCallback(() => {\n isConfirmingRef.current = true\n }, [])\n\n const afterModalOpen = React.useCallback(() => {\n prevModified.current = modified\n prevValue.current = value\n }, [modified, value])\n\n const afterModalClose = React.useCallback(() => {\n let didChange = true\n if (isConfirmingRef.current) {\n // did the values actually change?\n if (fieldArgs.field.hasMany) {\n const prev = (prevValue.current || []) as (number | string)[]\n const newValue = (value || []) as (number | string)[]\n if (prev.length !== newValue.length) {\n didChange = true\n } else {\n const allMatch = newValue.every((val) => prev.includes(val))\n if (allMatch) {\n didChange = false\n }\n }\n } else if (value === prevValue.current) {\n didChange = false\n }\n\n if (didChange) {\n prevModified.current = true\n prevValue.current = value\n }\n }\n\n setValue(prevValue.current, true)\n setModified(prevModified.current)\n\n isConfirmingRef.current = false\n }, [setValue, setModified, value, fieldArgs.field.hasMany])\n\n React.useEffect(() => {\n if (!entityType) {\n setEntityType(unique ? 'global' : 'document')\n } else {\n // unique documents are controlled from the global TenantSelector\n if (!unique && value) {\n if (Array.isArray(value)) {\n if (value.length) {\n if (!selectedTenantID) {\n setTenant({ id: value[0], refresh: false })\n } else if (!value.includes(selectedTenantID)) {\n setTenant({ id: value[0], refresh: false })\n }\n }\n } else if (selectedTenantID !== value) {\n setTenant({ id: value, refresh: false })\n }\n }\n }\n\n return () => {\n if (entityType) {\n setEntityType(undefined)\n }\n }\n }, [unique, options, selectedTenantID, setTenant, value, setEntityType, entityType])\n\n React.useEffect(() => {\n if (unique || debug || isEditManyModalOpen) {\n return\n }\n if (showField && ((!isFormValid && showError) || (!value && !selectedTenantID))) {\n openModal(assignTenantModalSlug)\n }\n }, [\n debug,\n isEditManyModalOpen,\n isFormValid,\n showError,\n showField,\n openModal,\n value,\n docID,\n selectedTenantID,\n unique,\n ])\n\n if (showField) {\n if (debug || isEditManyModalOpen) {\n return <TenantRelationshipField debug={debug} fieldArgs={fieldArgs} unique={unique} />\n }\n\n if (!unique) {\n /** Editing a non-global tenant document */\n return (\n <AssignTenantFieldModal\n afterModalClose={afterModalClose}\n afterModalOpen={afterModalOpen}\n onConfirm={onConfirm}\n >\n <TenantRelationshipField fieldArgs={fieldArgs} unique={unique} />\n </AssignTenantFieldModal>\n )\n }\n\n return <SyncFormModified />\n }\n\n return null\n}\n\nconst TenantRelationshipField: React.FC<{\n debug?: boolean\n fieldArgs: RelationshipFieldClientProps\n unique?: boolean\n}> = ({ debug, fieldArgs, unique }) => {\n return (\n <div className={baseClass}>\n <div className={`${baseClass}__wrapper`}>\n {debug && (\n <Pill className={`${baseClass}__debug-pill`} pillStyle=\"success\" size=\"small\">\n Multi-Tenant Debug Enabled\n </Pill>\n )}\n <RelationshipField\n {...fieldArgs}\n field={{\n ...fieldArgs.field,\n required: true,\n }}\n readOnly={fieldArgs.readOnly || fieldArgs.field.admin?.readOnly || unique}\n />\n </div>\n </div>\n )\n}\n\n/**\n * Tells the global selector when the form has been modified\n * so it can display the \"Leave without saving\" confirmation modal\n * if modified and attempting to change the tenant\n */\nconst SyncFormModified = () => {\n const modified = useFormModified()\n const { setModified } = useTenantSelection()\n\n React.useEffect(() => {\n setModified(modified)\n }, [modified, setModified])\n\n return null\n}\n"],"names":["Pill","RelationshipField","useDocumentInfo","useField","useForm","useFormModified","useModal","React","useTenantSelection","AssignTenantFieldModal","assignTenantModalSlug","baseClass","TenantField","debug","unique","fieldArgs","entityType","options","selectedTenantID","setEntityType","setTenant","setValue","showError","value","modified","isValid","isFormValid","setModified","id","docID","collectionSlug","isModalOpen","openModal","isEditManyModalOpen","isConfirmingRef","useRef","prevModified","prevValue","showField","length","field","admin","hidden","onConfirm","useCallback","current","afterModalOpen","afterModalClose","didChange","hasMany","prev","newValue","allMatch","every","val","includes","useEffect","Array","isArray","refresh","undefined","TenantRelationshipField","SyncFormModified","div","className","pillStyle","size","required","readOnly"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/TenantField/index.client.tsx"],"sourcesContent":["'use client'\n\nimport type { FieldType } from '@payloadcms/ui'\nimport type { RelationshipFieldClientProps } from 'payload'\n\nimport {\n FieldContext,\n FieldPathContext,\n Pill,\n RelationshipField,\n useDocumentInfo,\n useField,\n useForm,\n useFormModified,\n useModal,\n} from '@payloadcms/ui'\nimport React from 'react'\n\nimport type { TenantValue } from '../../types.js'\n\nimport { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'\nimport {\n AssignTenantFieldModal,\n assignTenantModalSlug,\n} from '../AssignTenantFieldModal/index.client.js'\nimport './index.css'\n\nconst baseClass = 'tenantField'\n\ntype Props = {\n debug?: boolean\n unique?: boolean\n} & RelationshipFieldClientProps\n\nexport const TenantField = ({ debug, unique, ...fieldArgs }: Props) => {\n const { entityType, options, selectedTenantID, setEntityType, setTenant } = useTenantSelection()\n const tenantField = useField<TenantValue>()\n const { setValue, showError, value } = tenantField\n const { isValid: isFormValid } = useForm()\n const { id: docID, collectionSlug } = useDocumentInfo()\n const { isModalOpen, openModal } = useModal()\n const isEditManyModalOpen = collectionSlug\n ? isModalOpen(`edit-${collectionSlug}`) || isModalOpen(`edit-${collectionSlug}-bulk-uploads`)\n : false\n const [modalValue, setModalValue] = React.useState<TenantValue | undefined>(value)\n const localTenantField = React.useMemo<FieldType<TenantValue>>(\n () => ({\n ...tenantField,\n initialValue: modalValue,\n setValue: (nextValue) => {\n setModalValue(nextValue as TenantValue | undefined)\n },\n value: modalValue as TenantValue,\n }),\n [modalValue, tenantField],\n )\n const showField =\n (options.length > 1 && !fieldArgs.field.admin?.hidden && !fieldArgs.field.hidden) || debug\n\n function getTenantIDToSelect({\n selectedTenantID,\n value,\n }: {\n selectedTenantID: number | string | undefined\n value: TenantValue | undefined\n }): number | string | undefined {\n if (Array.isArray(value)) {\n if (!value.length) {\n return undefined\n }\n\n if (!selectedTenantID || !value.includes(selectedTenantID)) {\n return value[0]\n }\n\n return undefined\n }\n\n if (value && selectedTenantID !== value) {\n return value\n }\n\n return undefined\n }\n\n const syncTenantSelectionFromValue = React.useCallback(\n (tenantValue: TenantValue | undefined) => {\n const tenantID = getTenantIDToSelect({ selectedTenantID, value: tenantValue })\n\n if (tenantID) {\n setTenant({ id: tenantID, refresh: false })\n }\n },\n [selectedTenantID, setTenant],\n )\n\n const onConfirm = React.useCallback(() => {\n if (hasTenantValueChanged({ hasMany: fieldArgs.field.hasMany, modalValue, value })) {\n setValue(modalValue)\n syncTenantSelectionFromValue(modalValue)\n }\n }, [fieldArgs.field.hasMany, modalValue, setValue, syncTenantSelectionFromValue, value])\n\n const afterModalOpen = React.useCallback(() => {\n setModalValue(value)\n }, [value])\n\n React.useEffect(() => {\n if (!entityType) {\n setEntityType(unique ? 'global' : 'document')\n } else {\n // unique documents are controlled from the global TenantSelector\n if (!unique) {\n syncTenantSelectionFromValue(value)\n }\n }\n\n return () => {\n if (entityType) {\n setEntityType(undefined)\n }\n }\n }, [entityType, setEntityType, syncTenantSelectionFromValue, unique, value])\n\n React.useEffect(() => {\n if (unique || debug || isEditManyModalOpen) {\n return\n }\n if (showField && ((!isFormValid && showError) || (!value && !selectedTenantID))) {\n openModal(assignTenantModalSlug)\n }\n }, [\n debug,\n isEditManyModalOpen,\n isFormValid,\n showError,\n showField,\n openModal,\n value,\n docID,\n selectedTenantID,\n unique,\n ])\n\n if (showField) {\n if (debug || isEditManyModalOpen) {\n return <TenantRelationshipField debug={debug} fieldArgs={fieldArgs} unique={unique} />\n }\n\n if (!unique) {\n /** Editing a non-global tenant document */\n return (\n <AssignTenantFieldModal afterModalOpen={afterModalOpen} onConfirm={onConfirm}>\n <FieldPathContext value={tenantField.path}>\n <FieldContext value={localTenantField}>\n <TenantRelationshipField fieldArgs={fieldArgs} unique={unique} />\n </FieldContext>\n </FieldPathContext>\n </AssignTenantFieldModal>\n )\n }\n\n return <SyncFormModified />\n }\n\n return null\n}\n\nconst hasTenantValueChanged = ({\n hasMany,\n modalValue,\n value,\n}: {\n hasMany?: boolean\n modalValue: TenantValue | undefined\n value: TenantValue | undefined\n}): boolean => {\n if (hasMany) {\n const currentValue = (value || []) as (number | string)[]\n const nextValue = (modalValue || []) as (number | string)[]\n\n return (\n currentValue.length !== nextValue.length ||\n nextValue.some((nextValueItem) => !currentValue.includes(nextValueItem))\n )\n }\n\n return value !== modalValue\n}\n\nconst TenantRelationshipField: React.FC<{\n debug?: boolean\n fieldArgs: RelationshipFieldClientProps\n unique?: boolean\n}> = ({ debug, fieldArgs, unique }) => {\n return (\n <div className={baseClass}>\n <div className={`${baseClass}__wrapper`}>\n {debug && (\n <Pill className={`${baseClass}__debug-pill`} pillStyle=\"success\" size=\"small\">\n Multi-Tenant Debug Enabled\n </Pill>\n )}\n <RelationshipField\n {...fieldArgs}\n field={{\n ...fieldArgs.field,\n required: true,\n }}\n readOnly={fieldArgs.readOnly || fieldArgs.field.admin?.readOnly || unique}\n />\n </div>\n </div>\n )\n}\n\n/**\n * Tells the global selector when the form has been modified\n * so it can display the \"Leave without saving\" confirmation modal\n * if modified and attempting to change the tenant\n */\nconst SyncFormModified = () => {\n const modified = useFormModified()\n const { setModified } = useTenantSelection()\n\n React.useEffect(() => {\n setModified(modified)\n }, [modified, setModified])\n\n return null\n}\n"],"names":["FieldContext","FieldPathContext","Pill","RelationshipField","useDocumentInfo","useField","useForm","useFormModified","useModal","React","useTenantSelection","AssignTenantFieldModal","assignTenantModalSlug","baseClass","TenantField","debug","unique","fieldArgs","entityType","options","selectedTenantID","setEntityType","setTenant","tenantField","setValue","showError","value","isValid","isFormValid","id","docID","collectionSlug","isModalOpen","openModal","isEditManyModalOpen","modalValue","setModalValue","useState","localTenantField","useMemo","initialValue","nextValue","showField","length","field","admin","hidden","getTenantIDToSelect","Array","isArray","undefined","includes","syncTenantSelectionFromValue","useCallback","tenantValue","tenantID","refresh","onConfirm","hasTenantValueChanged","hasMany","afterModalOpen","useEffect","TenantRelationshipField","path","SyncFormModified","currentValue","some","nextValueItem","div","className","pillStyle","size","required","readOnly","modified","setModified"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAczD,eAAO,MAAM,iBAAiB,gEAua5B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAczD,eAAO,MAAM,iBAAiB,gEA+a5B,CAAA"}
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ export const multiTenantPlugin = definePlugin({
24
24
  const tenantFieldName = pluginConfig?.tenantField?.name || defaults.tenantFieldName;
25
25
  const tenantsArrayFieldName = pluginConfig?.tenantsArrayField?.arrayFieldName || defaults.tenantsArrayFieldName;
26
26
  const tenantsArrayTenantFieldName = pluginConfig?.tenantsArrayField?.arrayTenantFieldName || defaults.tenantsArrayTenantFieldName;
27
+ const tenantAccessScopes = [];
27
28
  /**
28
29
  * Add defaults for admin properties
29
30
  */ if (!incomingConfig.admin) {
@@ -65,12 +66,17 @@ export const multiTenantPlugin = definePlugin({
65
66
  */ if (pluginConfig?.tenantsArrayField?.includeDefaultField !== false) {
66
67
  adminUsersCollection.fields.push(tenantsArrayField({
67
68
  ...pluginConfig?.tenantsArrayField || {},
69
+ arrayFieldAccess: {
70
+ create: ({ req })=>Boolean(req.user && userHasAccessToAllTenants(req.user)),
71
+ update: ({ req })=>Boolean(req.user && userHasAccessToAllTenants(req.user)),
72
+ ...pluginConfig?.tenantsArrayField?.arrayFieldAccess || {}
73
+ },
68
74
  tenantsArrayFieldName,
69
75
  tenantsArrayTenantFieldName,
70
76
  tenantsCollectionSlug
71
77
  }));
72
78
  }
73
- addCollectionAccess({
79
+ tenantAccessScopes.push({
74
80
  accessResultCallback: pluginConfig.usersAccessResultOverride,
75
81
  adminUsersSlug: adminUsersCollection.slug,
76
82
  collection: adminUsersCollection,
@@ -126,7 +132,7 @@ export const multiTenantPlugin = definePlugin({
126
132
  /**
127
133
  * Add access control constraint to tenants collection
128
134
  * - constrains access a users assigned tenants
129
- */ addCollectionAccess({
135
+ */ tenantAccessScopes.push({
130
136
  adminUsersSlug: adminUsersCollection.slug,
131
137
  collection,
132
138
  fieldName: 'id',
@@ -269,7 +275,7 @@ export const multiTenantPlugin = definePlugin({
269
275
  if (pluginConfig.collections[collection.slug]?.useTenantAccess !== false) {
270
276
  /**
271
277
  * Add access control constraint to tenant enabled collection
272
- */ addCollectionAccess({
278
+ */ tenantAccessScopes.push({
273
279
  accessResultCallback: pluginConfig.collections[collection.slug]?.accessResultOverride,
274
280
  adminUsersSlug: adminUsersCollection.slug,
275
281
  collection,
@@ -292,6 +298,10 @@ export const multiTenantPlugin = definePlugin({
292
298
  // eslint-disable-next-line no-console
293
299
  console.error(miniChalk.yellowBold('WARNING (plugin-multi-tenant)'), 'missing collections', missingSlugs, 'try placing the multi-tenant plugin after other plugins.');
294
300
  }
301
+ addCollectionAccess({
302
+ config: incomingConfig,
303
+ scopes: tenantAccessScopes
304
+ });
295
305
  /**
296
306
  * Add TenantSelectionProvider to admin providers
297
307
  */ incomingConfig.admin.components.providers.push({
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { AcceptedLanguages } from '@payloadcms/translations'\nimport type { CollectionConfig } from 'payload'\n\nimport { definePlugin } from 'payload'\nimport { hasAutosaveEnabled } from 'payload/shared'\n\nimport type { PluginDefaultTranslationsObject } from './translations/types.js'\nimport type { MultiTenantPluginConfig } from './types.js'\n\nimport { defaults } from './defaults.js'\nimport { getTenantOptionsEndpoint } from './endpoints/getTenantOptionsEndpoint.js'\nimport { tenantField } from './fields/tenantField/index.js'\nimport { tenantsArrayField } from './fields/tenantsArrayField/index.js'\nimport { filterDocumentsByTenants } from './filters/filterDocumentsByTenants.js'\nimport { addTenantCleanup } from './hooks/afterTenantDelete.js'\nimport { translations } from './translations/index.js'\nimport { addCollectionAccess } from './utilities/addCollectionAccess.js'\nimport { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'\nimport { combineFilters } from './utilities/combineFilters.js'\nimport { miniChalk } from './utilities/miniChalk.js'\n\nexport const multiTenantPlugin = definePlugin<MultiTenantPluginConfig>({\n slug: '@payloadcms/plugin-multi-tenant',\n plugin: ({ config: incomingConfig, options: pluginConfig }) => {\n if (pluginConfig.enabled === false) {\n return incomingConfig\n }\n\n /**\n * Set defaults\n */\n const userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'] =\n typeof pluginConfig.userHasAccessToAllTenants === 'function'\n ? pluginConfig.userHasAccessToAllTenants\n : () => false\n const tenantsCollectionSlug = (pluginConfig.tenantsSlug =\n pluginConfig.tenantsSlug || defaults.tenantCollectionSlug)\n const tenantFieldName = pluginConfig?.tenantField?.name || defaults.tenantFieldName\n const tenantsArrayFieldName =\n pluginConfig?.tenantsArrayField?.arrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n pluginConfig?.tenantsArrayField?.arrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n\n /**\n * Add defaults for admin properties\n */\n if (!incomingConfig.admin) {\n incomingConfig.admin = {}\n }\n if (!incomingConfig.admin?.components) {\n incomingConfig.admin.components = {\n actions: [],\n beforeNav: [],\n providers: [],\n }\n }\n if (!incomingConfig.admin.components?.providers) {\n incomingConfig.admin.components.providers = []\n }\n if (!incomingConfig.admin.components?.actions) {\n incomingConfig.admin.components.actions = []\n }\n if (!incomingConfig.admin.components?.beforeNav) {\n incomingConfig.admin.components.beforeNav = []\n }\n if (!incomingConfig.collections) {\n incomingConfig.collections = []\n }\n\n /**\n * Add tenants array field to users collection\n */\n const adminUsersCollection = incomingConfig.collections.find(({ slug, auth }) => {\n if (incomingConfig.admin?.user) {\n return slug === incomingConfig.admin.user\n } else if (auth) {\n return true\n }\n })\n\n if (!adminUsersCollection) {\n throw Error('An auth enabled collection was not found')\n }\n\n /**\n * Add tenants array field to users collection\n */\n if (pluginConfig?.tenantsArrayField?.includeDefaultField !== false) {\n adminUsersCollection.fields.push(\n tenantsArrayField({\n ...(pluginConfig?.tenantsArrayField || {}),\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n }),\n )\n }\n\n addCollectionAccess({\n accessResultCallback: pluginConfig.usersAccessResultOverride,\n adminUsersSlug: adminUsersCollection.slug,\n collection: adminUsersCollection,\n fieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.useUsersTenantFilter !== false) {\n if (!adminUsersCollection.admin) {\n adminUsersCollection.admin = {}\n }\n\n const baseFilter =\n adminUsersCollection.admin?.baseFilter ?? adminUsersCollection.admin?.baseListFilter\n adminUsersCollection.admin.baseFilter = combineFilters({\n baseFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n let tenantCollection: CollectionConfig | undefined\n\n const [collectionSlugs, globalCollectionSlugs] = Object.keys(pluginConfig.collections).reduce<\n [string[], string[]]\n >(\n (acc, slug) => {\n if (slug === adminUsersCollection.slug) {\n return acc\n }\n if (pluginConfig?.collections?.[slug]?.isGlobal) {\n acc[1].push(slug)\n } else {\n acc[0].push(slug)\n }\n\n return acc\n },\n [[], []],\n )\n\n // used to track and not duplicate filterOptions on referenced blocks\n const blockReferencesWithFilters: string[] = []\n\n // used to validate enabled collection slugs\n const multiTenantCollectionsFound: string[] = []\n\n /**\n * Modify collections\n */\n incomingConfig.collections.forEach((collection) => {\n /**\n * Modify tenants collection\n */\n if (collection.slug === tenantsCollectionSlug) {\n tenantCollection = collection\n\n if (pluginConfig.useTenantsCollectionAccess !== false) {\n /**\n * Add access control constraint to tenants collection\n * - constrains access a users assigned tenants\n */\n addCollectionAccess({\n adminUsersSlug: adminUsersCollection.slug,\n collection,\n fieldName: 'id',\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n\n if (pluginConfig.useTenantsListFilter !== false) {\n /**\n * Add list filter to tenants collection\n * - filter by selected tenant\n */\n if (!collection.admin) {\n collection.admin = {}\n }\n\n const baseFilter = collection.admin?.baseFilter ?? collection.admin?.baseListFilter\n collection.admin.baseFilter = combineFilters({\n baseFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: 'id',\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n if (pluginConfig.cleanupAfterTenantDelete !== false) {\n /**\n * Add cleanup logic when tenant is deleted\n * - delete documents related to tenant\n * - remove tenant from users\n */\n addTenantCleanup({\n collection,\n enabledSlugs: [...collectionSlugs, ...globalCollectionSlugs],\n tenantFieldName,\n tenantsCollectionSlug,\n usersSlug: adminUsersCollection.slug,\n usersTenantsArrayFieldName: tenantsArrayFieldName,\n usersTenantsArrayTenantFieldName: tenantsArrayTenantFieldName,\n })\n }\n\n /**\n * Add custom tenant field that watches and dispatches updates to the selector\n */\n collection.fields.push({\n name: '_watchTenant',\n type: 'ui',\n admin: {\n components: {\n Field: {\n path: '@payloadcms/plugin-multi-tenant/client#WatchTenantCollection',\n },\n },\n disabled: { bulkEdit: true, column: true },\n },\n })\n\n collection.endpoints = [\n ...(collection.endpoints || []),\n getTenantOptionsEndpoint({\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n }),\n ]\n } else if (pluginConfig.collections?.[collection.slug]) {\n if (collection.slug === adminUsersCollection.slug) {\n // eslint-disable-next-line no-console\n console.warn(\n `[plugin-multi-tenant] The admin users collection \"${collection.slug}\" should not be listed in pluginConfig.collections — it is already handled by the plugin. Skipping tenant-field processing for this collection to avoid double access control and validation errors.`,\n )\n return\n }\n multiTenantCollectionsFound.push(collection.slug)\n const isGlobal = Boolean(pluginConfig.collections[collection.slug]?.isGlobal)\n\n if (isGlobal) {\n collection.disableDuplicate = true\n }\n\n if (!pluginConfig.debug && !isGlobal) {\n collection.admin ??= {}\n collection.admin.components ??= {}\n collection.admin.components.edit ??= {}\n collection.admin.components.edit.editMenuItems ??= []\n collection.admin.components.edit.editMenuItems.push({\n path: '@payloadcms/plugin-multi-tenant/client#AssignTenantFieldTrigger',\n })\n }\n\n /**\n * Add filter options to all relationship fields\n */\n collection.fields = addFilterOptionsToFields({\n blockReferencesWithFilters,\n config: incomingConfig,\n fields: collection.fields,\n tenantEnabledCollectionSlugs: collectionSlugs,\n tenantEnabledGlobalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.collections[collection.slug]?.customTenantField !== true) {\n /**\n * Add tenant field to enabled collections\n */\n collection.fields.unshift(\n tenantField({\n name: tenantFieldName,\n debug: pluginConfig.debug,\n isAutosaveEnabled: hasAutosaveEnabled(collection),\n overrides: pluginConfig.collections[collection.slug]?.tenantFieldOverrides\n ? pluginConfig.collections[collection.slug]?.tenantFieldOverrides\n : pluginConfig.tenantField || {},\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n unique: isGlobal,\n }),\n )\n }\n\n const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {}\n if (useBaseFilter ?? useBaseListFilter ?? true) {\n /**\n * Add list filter to enabled collections\n * - filters results by selected tenant\n */\n collection.admin = collection.admin || {}\n collection.admin.baseFilter = combineFilters({\n baseFilter: collection.admin?.baseFilter ?? collection.admin?.baseListFilter,\n customFilter: (args) =>\n filterDocumentsByTenants({\n filterFieldName: tenantFieldName,\n req: args.req,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n userHasAccessToAllTenants,\n }),\n })\n }\n\n if (pluginConfig.collections[collection.slug]?.useTenantAccess !== false) {\n /**\n * Add access control constraint to tenant enabled collection\n */\n addCollectionAccess({\n accessResultCallback: pluginConfig.collections[collection.slug]?.accessResultOverride,\n adminUsersSlug: adminUsersCollection.slug,\n collection,\n fieldName: tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n }\n })\n\n if (!tenantCollection) {\n throw new Error(`Tenants collection not found with slug: ${tenantsCollectionSlug}`)\n }\n\n if (\n multiTenantCollectionsFound.length !==\n collectionSlugs.length + globalCollectionSlugs.length\n ) {\n const missingSlugs = [...collectionSlugs, ...globalCollectionSlugs].filter(\n (slug) => !multiTenantCollectionsFound.includes(slug),\n )\n // eslint-disable-next-line no-console\n console.error(\n miniChalk.yellowBold('WARNING (plugin-multi-tenant)'),\n 'missing collections',\n missingSlugs,\n 'try placing the multi-tenant plugin after other plugins.',\n )\n }\n\n /**\n * Add TenantSelectionProvider to admin providers\n */\n incomingConfig.admin.components.providers.push({\n clientProps: {\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug: tenantCollection.slug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelectionProvider',\n })\n\n /**\n * Add global redirect action\n */\n if (globalCollectionSlugs.length) {\n incomingConfig.admin.components.actions.push({\n path: '@payloadcms/plugin-multi-tenant/rsc#GlobalViewRedirect',\n serverProps: {\n globalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n userHasAccessToAllTenants,\n },\n })\n }\n\n /**\n * Add tenant selector to admin UI\n */\n incomingConfig.admin.components.beforeNav.push({\n clientProps: {\n enabledSlugs: [\n ...collectionSlugs,\n ...globalCollectionSlugs,\n adminUsersCollection.slug,\n tenantCollection.slug,\n ],\n label: pluginConfig.tenantSelectorLabel || undefined,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelector',\n })\n\n /**\n * Merge plugin translations\n */\n if (!incomingConfig.i18n) {\n incomingConfig.i18n = {}\n }\n Object.entries(translations).forEach(([locale, pluginI18nObject]) => {\n const typedLocale = locale as AcceptedLanguages\n if (!incomingConfig.i18n!.translations) {\n incomingConfig.i18n!.translations = {}\n }\n if (!(typedLocale in incomingConfig.i18n!.translations)) {\n incomingConfig.i18n!.translations[typedLocale] = {}\n }\n if (!('plugin-multi-tenant' in incomingConfig.i18n!.translations[typedLocale]!)) {\n ;(incomingConfig.i18n!.translations[typedLocale] as PluginDefaultTranslationsObject)[\n 'plugin-multi-tenant'\n ] = {} as PluginDefaultTranslationsObject['plugin-multi-tenant']\n }\n\n ;(incomingConfig.i18n!.translations[typedLocale] as PluginDefaultTranslationsObject)[\n 'plugin-multi-tenant'\n ] = {\n ...pluginI18nObject.translations['plugin-multi-tenant'],\n ...(pluginConfig.i18n?.translations?.[typedLocale] || {}),\n }\n })\n\n return incomingConfig\n },\n})\n"],"names":["definePlugin","hasAutosaveEnabled","defaults","getTenantOptionsEndpoint","tenantField","tenantsArrayField","filterDocumentsByTenants","addTenantCleanup","translations","addCollectionAccess","addFilterOptionsToFields","combineFilters","miniChalk","multiTenantPlugin","slug","plugin","config","incomingConfig","options","pluginConfig","enabled","userHasAccessToAllTenants","tenantsCollectionSlug","tenantsSlug","tenantCollectionSlug","tenantFieldName","name","tenantsArrayFieldName","arrayFieldName","tenantsArrayTenantFieldName","arrayTenantFieldName","admin","components","actions","beforeNav","providers","collections","adminUsersCollection","find","auth","user","Error","includeDefaultField","fields","push","accessResultCallback","usersAccessResultOverride","adminUsersSlug","collection","fieldName","useUsersTenantFilter","baseFilter","baseListFilter","customFilter","args","filterFieldName","req","tenantCollection","collectionSlugs","globalCollectionSlugs","Object","keys","reduce","acc","isGlobal","blockReferencesWithFilters","multiTenantCollectionsFound","forEach","useTenantsCollectionAccess","useTenantsListFilter","cleanupAfterTenantDelete","enabledSlugs","usersSlug","usersTenantsArrayFieldName","usersTenantsArrayTenantFieldName","type","Field","path","disabled","bulkEdit","column","endpoints","useAsTitle","console","warn","Boolean","disableDuplicate","debug","edit","editMenuItems","tenantEnabledCollectionSlugs","tenantEnabledGlobalSlugs","customTenantField","unshift","isAutosaveEnabled","overrides","tenantFieldOverrides","unique","useBaseFilter","useBaseListFilter","useTenantAccess","accessResultOverride","length","missingSlugs","filter","includes","error","yellowBold","clientProps","serverProps","globalSlugs","label","tenantSelectorLabel","undefined","i18n","entries","locale","pluginI18nObject","typedLocale"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { AcceptedLanguages } from '@payloadcms/translations'\nimport type { CollectionConfig } from 'payload'\n\nimport { definePlugin } from 'payload'\nimport { hasAutosaveEnabled } from 'payload/shared'\n\nimport type { PluginDefaultTranslationsObject } from './translations/types.js'\nimport type { MultiTenantPluginConfig } from './types.js'\n\nimport { defaults } from './defaults.js'\nimport { getTenantOptionsEndpoint } from './endpoints/getTenantOptionsEndpoint.js'\nimport { tenantField } from './fields/tenantField/index.js'\nimport { tenantsArrayField } from './fields/tenantsArrayField/index.js'\nimport { filterDocumentsByTenants } from './filters/filterDocumentsByTenants.js'\nimport { addTenantCleanup } from './hooks/afterTenantDelete.js'\nimport { 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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"}
@@ -0,0 +1,70 @@
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
@@ -0,0 +1 @@
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"}
package/dist/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { AcceptedLanguages } from '@payloadcms/translations';
2
2
  import type { AccessArgs, AccessResult, ArrayField, CollectionConfig, CollectionSlug, Field, RelationshipField, SingleRelationshipField, User } from 'payload';
3
+ export type TenantValue = (number | string)[] | (number | string);
3
4
  export type MultiTenantPluginConfig = {
4
5
  /**
5
6
  * After a tenant is deleted, the plugin will attempt to clean up related documents
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { AcceptedLanguages } from '@payloadcms/translations'\nimport type {\n AccessArgs,\n AccessResult,\n ArrayField,\n CollectionConfig,\n CollectionSlug,\n Field,\n RelationshipField,\n SingleRelationshipField,\n User,\n} from 'payload'\n\nexport type MultiTenantPluginConfig = {\n /**\n * After a tenant is deleted, the plugin will attempt to clean up related documents\n * - removing documents with the tenant ID\n * - removing the tenant from users\n *\n * @default true\n */\n cleanupAfterTenantDelete?: boolean\n /**\n * Automatically\n */\n collections: {\n [key in CollectionSlug]?: {\n /**\n * Override the access result from the collection access control functions\n *\n * The function receives:\n * - accessResult: the original result from the access control function\n * - accessKey: 'read', 'create', 'update', 'delete', 'readVersions', or 'unlock'\n * - ...restOfAccessArgs: the original arguments passed to the access control function\n */\n accessResultOverride?: CollectionAccessResultOverride\n /**\n * Opt out of adding the tenant field and place\n * it manually using the `tenantField` export from the plugin\n */\n customTenantField?: boolean\n /**\n * Set to `true` if you want the collection to behave as a global\n *\n * @default false\n */\n isGlobal?: boolean\n /**\n * Overrides for the tenant field, will override the entire tenantField configuration\n */\n tenantFieldOverrides?: CollectionTenantFieldConfigOverrides\n /**\n * Set to `false` if you want to manually apply the baseListFilter\n * Set to `false` if you want to manually apply the baseFilter\n *\n * @default true\n */\n useBaseFilter?: boolean\n /**\n * @deprecated Use `useBaseFilter` instead. If both are defined,\n * `useBaseFilter` will take precedence. This property remains only\n * for backward compatibility and may be removed in a future version.\n *\n * Originally, `baseListFilter` was intended to filter only the List View\n * in the admin panel. However, base filtering is often required in other areas\n * such as internal link relationships in the Lexical editor.\n *\n * @default true\n */\n useBaseListFilter?: boolean\n /**\n * Set to `false` if you want to handle collection access manually without the multi-tenant constraints applied\n *\n * @default true\n */\n useTenantAccess?: boolean\n }\n }\n /**\n * Enables debug mode\n * - Makes the tenant field visible in the admin UI within applicable collections\n *\n * @default false\n */\n debug?: boolean\n /**\n * Enables the multi-tenant plugin\n *\n * @default true\n */\n enabled?: boolean\n /**\n * Localization for the plugin\n */\n i18n?: {\n translations: {\n [key in AcceptedLanguages]?: {\n /**\n * Shown inside 3 dot menu on edit document view\n *\n * @default 'Assign Tenant'\n */\n 'assign-tenant-button-label'?: string\n /**\n * Shown as the title of the assign tenant modal\n *\n * @default 'Assign \"{{title}}\"'\n */\n 'assign-tenant-modal-title'?: string\n /**\n * Shown as the label for the assigned tenant field in the assign tenant modal\n *\n * @default 'Assigned Tenant'\n */\n 'field-assignedTenant-label'?: string\n /**\n * Shown as the label for the global tenant selector in the admin UI\n *\n * @default 'Filter by Tenant'\n */\n 'nav-tenantSelector-label'?: string\n }\n }\n }\n /**\n * Field configuration for the field added to all tenant enabled collections\n */\n tenantField?: RootTenantFieldConfigOverrides\n /**\n * Field configuration for the field added to the users collection\n *\n * If `includeDefaultField` is `false`, you must include the field on your users collection manually\n * This is useful if you want to customize the field or place the field in a specific location\n */\n tenantsArrayField?:\n | {\n /**\n * Access configuration for the array field\n */\n arrayFieldAccess?: ArrayField['access']\n /**\n * Name of the array field\n *\n * @default 'tenants'\n */\n arrayFieldName?: string\n /**\n * Name of the tenant field\n *\n * @default 'tenant'\n */\n arrayTenantFieldName?: string\n /**\n * When `includeDefaultField` is `true`, the field will be added to the users collection automatically\n */\n includeDefaultField?: true\n /**\n * Additional fields to include on the tenants array field\n */\n rowFields?: Field[]\n /**\n * Access configuration for the tenant field\n */\n tenantFieldAccess?: RelationshipField['access']\n }\n | {\n arrayFieldAccess?: never\n arrayFieldName?: string\n arrayTenantFieldName?: string\n /**\n * When `includeDefaultField` is `false`, you must include the field on your users collection manually\n */\n includeDefaultField?: false\n rowFields?: never\n tenantFieldAccess?: never\n }\n /**\n * Customize tenant selector label\n *\n * Either a string or an object where the keys are i18n codes and the values are the string labels\n *\n * @deprecated Use `i18n.translations` instead.\n */\n tenantSelectorLabel?:\n | Partial<{\n [key in AcceptedLanguages]?: string\n }>\n | string\n /**\n * The slug for the tenant collection\n *\n * @default 'tenants'\n */\n tenantsSlug?: string\n /**\n * Function that determines if a user has access to _all_ tenants\n *\n * Useful for super-admin type users\n */\n userHasAccessToAllTenants?: (user: User) => boolean\n /**\n * Override the access result on the users collection access control functions\n *\n * The function receives:\n * - accessResult: the original result from the access control function\n * - accessKey: 'read', 'create', 'update', 'delete', 'readVersions', or 'unlock'\n * - ...restOfAccessArgs: the original arguments passed to the access control function\n */\n usersAccessResultOverride?: CollectionAccessResultOverride\n /**\n * Opt out of adding access constraints to the tenants collection\n */\n useTenantsCollectionAccess?: boolean\n /**\n * Opt out including the baseListFilter to filter tenants by selected tenant\n */\n useTenantsListFilter?: boolean\n\n /**\n * Opt out including the baseListFilter to filter users by selected tenant\n */\n useUsersTenantFilter?: boolean\n}\n\nexport type RootTenantFieldConfigOverrides = Partial<\n Omit<\n SingleRelationshipField,\n | '_sanitized'\n | 'hidden'\n | 'index'\n | 'localized'\n | 'max'\n | 'maxRows'\n | 'min'\n | 'minRows'\n | 'relationTo'\n | 'required'\n | 'type'\n | 'unique'\n | 'virtual'\n >\n>\n\nexport type CollectionTenantFieldConfigOverrides = Partial<\n Omit<RootTenantFieldConfigOverrides, 'name'>\n>\n\nexport type Tenant<IDType = number | string> = {\n id: IDType\n name: string\n}\n\nexport type UserWithTenantsField = {\n tenants?:\n | {\n tenant: number | string | Tenant\n }[]\n | null\n} & User\n\ntype AllAccessKeysT<T extends readonly string[]> = T[number] extends keyof Omit<\n Required<CollectionConfig>['access'],\n 'admin'\n>\n ? keyof Omit<Required<CollectionConfig>['access'], 'admin'> extends T[number]\n ? T\n : never\n : never\n\nexport type AllAccessKeys = AllAccessKeysT<\n ['create', 'read', 'update', 'delete', 'readVersions', 'unlock']\n>\n\nexport type CollectionAccessResultOverride = ({\n accessKey,\n accessResult,\n}: {\n accessKey: AllAccessKeys[number]\n accessResult: AccessResult\n} & AccessArgs) => AccessResult | Promise<AccessResult>\n"],"names":[],"mappings":"AAiRA,WAMuD"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { AcceptedLanguages } from '@payloadcms/translations'\nimport type {\n AccessArgs,\n AccessResult,\n ArrayField,\n CollectionConfig,\n CollectionSlug,\n Field,\n RelationshipField,\n SingleRelationshipField,\n User,\n} from 'payload'\n\nexport type TenantValue = (number | string)[] | (number | string)\n\nexport type MultiTenantPluginConfig = {\n /**\n * After a tenant is deleted, the plugin will attempt to clean up related documents\n * - removing documents with the tenant ID\n * - removing the tenant from users\n *\n * @default true\n */\n cleanupAfterTenantDelete?: boolean\n /**\n * Automatically\n */\n collections: {\n [key in CollectionSlug]?: {\n /**\n * Override the access result from the collection access control functions\n *\n * The function receives:\n * - accessResult: the original result from the access control function\n * - accessKey: 'read', 'create', 'update', 'delete', 'readVersions', or 'unlock'\n * - ...restOfAccessArgs: the original arguments passed to the access control function\n */\n accessResultOverride?: CollectionAccessResultOverride\n /**\n * Opt out of adding the tenant field and place\n * it manually using the `tenantField` export from the plugin\n */\n customTenantField?: boolean\n /**\n * Set to `true` if you want the collection to behave as a global\n *\n * @default false\n */\n isGlobal?: boolean\n /**\n * Overrides for the tenant field, will override the entire tenantField configuration\n */\n tenantFieldOverrides?: CollectionTenantFieldConfigOverrides\n /**\n * Set to `false` if you want to manually apply the baseListFilter\n * Set to `false` if you want to manually apply the baseFilter\n *\n * @default true\n */\n useBaseFilter?: boolean\n /**\n * @deprecated Use `useBaseFilter` instead. If both are defined,\n * `useBaseFilter` will take precedence. This property remains only\n * for backward compatibility and may be removed in a future version.\n *\n * Originally, `baseListFilter` was intended to filter only the List View\n * in the admin panel. However, base filtering is often required in other areas\n * such as internal link relationships in the Lexical editor.\n *\n * @default true\n */\n useBaseListFilter?: boolean\n /**\n * Set to `false` if you want to handle collection access manually without the multi-tenant constraints applied\n *\n * @default true\n */\n useTenantAccess?: boolean\n }\n }\n /**\n * Enables debug mode\n * - Makes the tenant field visible in the admin UI within applicable collections\n *\n * @default false\n */\n debug?: boolean\n /**\n * Enables the multi-tenant plugin\n *\n * @default true\n */\n enabled?: boolean\n /**\n * Localization for the plugin\n */\n i18n?: {\n translations: {\n [key in AcceptedLanguages]?: {\n /**\n * Shown inside 3 dot menu on edit document view\n *\n * @default 'Assign Tenant'\n */\n 'assign-tenant-button-label'?: string\n /**\n * Shown as the title of the assign tenant modal\n *\n * @default 'Assign \"{{title}}\"'\n */\n 'assign-tenant-modal-title'?: string\n /**\n * Shown as the label for the assigned tenant field in the assign tenant modal\n *\n * @default 'Assigned Tenant'\n */\n 'field-assignedTenant-label'?: string\n /**\n * Shown as the label for the global tenant selector in the admin UI\n *\n * @default 'Filter by Tenant'\n */\n 'nav-tenantSelector-label'?: string\n }\n }\n }\n /**\n * Field configuration for the field added to all tenant enabled collections\n */\n tenantField?: RootTenantFieldConfigOverrides\n /**\n * Field configuration for the field added to the users collection\n *\n * If `includeDefaultField` is `false`, you must include the field on your users collection manually\n * This is useful if you want to customize the field or place the field in a specific location\n */\n tenantsArrayField?:\n | {\n /**\n * Access configuration for the array field\n */\n arrayFieldAccess?: ArrayField['access']\n /**\n * Name of the array field\n *\n * @default 'tenants'\n */\n arrayFieldName?: string\n /**\n * Name of the tenant field\n *\n * @default 'tenant'\n */\n arrayTenantFieldName?: string\n /**\n * When `includeDefaultField` is `true`, the field will be added to the users collection automatically\n */\n includeDefaultField?: true\n /**\n * Additional fields to include on the tenants array field\n */\n rowFields?: Field[]\n /**\n * Access configuration for the tenant field\n */\n tenantFieldAccess?: RelationshipField['access']\n }\n | {\n arrayFieldAccess?: never\n arrayFieldName?: string\n arrayTenantFieldName?: string\n /**\n * When `includeDefaultField` is `false`, you must include the field on your users collection manually\n */\n includeDefaultField?: false\n rowFields?: never\n tenantFieldAccess?: never\n }\n /**\n * Customize tenant selector label\n *\n * Either a string or an object where the keys are i18n codes and the values are the string labels\n *\n * @deprecated Use `i18n.translations` instead.\n */\n tenantSelectorLabel?:\n | Partial<{\n [key in AcceptedLanguages]?: string\n }>\n | string\n /**\n * The slug for the tenant collection\n *\n * @default 'tenants'\n */\n tenantsSlug?: string\n /**\n * Function that determines if a user has access to _all_ tenants\n *\n * Useful for super-admin type users\n */\n userHasAccessToAllTenants?: (user: User) => boolean\n /**\n * Override the access result on the users collection access control functions\n *\n * The function receives:\n * - accessResult: the original result from the access control function\n * - accessKey: 'read', 'create', 'update', 'delete', 'readVersions', or 'unlock'\n * - ...restOfAccessArgs: the original arguments passed to the access control function\n */\n usersAccessResultOverride?: CollectionAccessResultOverride\n /**\n * Opt out of adding access constraints to the tenants collection\n */\n useTenantsCollectionAccess?: boolean\n /**\n * Opt out including the baseListFilter to filter tenants by selected tenant\n */\n useTenantsListFilter?: boolean\n\n /**\n * Opt out including the baseListFilter to filter users by selected tenant\n */\n useUsersTenantFilter?: boolean\n}\n\nexport type RootTenantFieldConfigOverrides = Partial<\n Omit<\n SingleRelationshipField,\n | '_sanitized'\n | 'hidden'\n | 'index'\n | 'localized'\n | 'max'\n | 'maxRows'\n | 'min'\n | 'minRows'\n | 'relationTo'\n | 'required'\n | 'type'\n | 'unique'\n | 'virtual'\n >\n>\n\nexport type CollectionTenantFieldConfigOverrides = Partial<\n Omit<RootTenantFieldConfigOverrides, 'name'>\n>\n\nexport type Tenant<IDType = number | string> = {\n id: IDType\n name: string\n}\n\nexport type UserWithTenantsField = {\n tenants?:\n | {\n tenant: number | string | Tenant\n }[]\n | null\n} & User\n\ntype AllAccessKeysT<T extends readonly string[]> = T[number] extends keyof Omit<\n Required<CollectionConfig>['access'],\n 'admin'\n>\n ? keyof Omit<Required<CollectionConfig>['access'], 'admin'> extends T[number]\n ? T\n : never\n : never\n\nexport type AllAccessKeys = AllAccessKeysT<\n ['create', 'read', 'update', 'delete', 'readVersions', 'unlock']\n>\n\nexport type CollectionAccessResultOverride = ({\n accessKey,\n accessResult,\n}: {\n accessKey: AllAccessKeys[number]\n accessResult: AccessResult\n} & AccessArgs) => AccessResult | Promise<AccessResult>\n"],"names":[],"mappings":"AAmRA,WAMuD"}
@@ -1,7 +1,7 @@
1
- import type { CollectionConfig } from 'payload';
1
+ import type { CollectionConfig, Config } from 'payload';
2
2
  import type { AllAccessKeys, MultiTenantPluginConfig } from '../types.js';
3
3
  export declare const collectionAccessKeys: AllAccessKeys;
4
- type Args = {
4
+ export type TenantAccessConfig = {
5
5
  accessResultCallback?: MultiTenantPluginConfig['usersAccessResultOverride'];
6
6
  adminUsersSlug: string;
7
7
  collection: CollectionConfig;
@@ -10,10 +10,8 @@ type Args = {
10
10
  tenantsArrayTenantFieldName?: string;
11
11
  userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'];
12
12
  };
13
- /**
14
- * Adds tenant access constraint to collection
15
- * - constrains access a users assigned tenants
16
- */
17
- export declare const addCollectionAccess: ({ accessResultCallback, adminUsersSlug, collection, fieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, }: Args) => void;
18
- export {};
13
+ export declare const addCollectionAccess: ({ config, scopes, }: {
14
+ config: Config;
15
+ scopes: TenantAccessConfig[];
16
+ }) => void;
19
17
  //# sourceMappingURL=addCollectionAccess.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"addCollectionAccess.d.ts","sourceRoot":"","sources":["../../src/utilities/addCollectionAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAIzE,eAAO,MAAM,oBAAoB,EAAE,aAOzB,CAAA;AAEV,KAAK,IAAI,GAAG;IACV,oBAAoB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;IAC3E,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,yBAAyB,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,2BAA2B,CAAC,CAAA;CAC1F,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,oJAQ7B,IAAI,KAAG,IAiBT,CAAA"}
1
+ {"version":3,"file":"addCollectionAccess.d.ts","sourceRoot":"","sources":["../../src/utilities/addCollectionAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,gBAAgB,EAAE,MAAM,EAAe,MAAM,SAAS,CAAA;AAE9F,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAIzE,eAAO,MAAM,oBAAoB,EAAE,aAOzB,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG;IAC/B,oBAAoB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;IAC3E,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,yBAAyB,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,2BAA2B,CAAC,CAAA;CAC1F,CAAA;AAsED,eAAO,MAAM,mBAAmB,wBAG7B;IACD,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,kBAAkB,EAAE,CAAA;CAC7B,KAAG,IA4CH,CAAA"}
@@ -1,4 +1,4 @@
1
- import { withTenantAccess } from './withTenantAccess.js';
1
+ import { getTenantAccess } from './getTenantAccess.js';
2
2
  export const collectionAccessKeys = [
3
3
  'create',
4
4
  'read',
@@ -7,26 +7,97 @@ export const collectionAccessKeys = [
7
7
  'readVersions',
8
8
  'unlock'
9
9
  ];
10
- /**
11
- * Adds tenant access constraint to collection
12
- * - constrains access a users assigned tenants
13
- */ export const addCollectionAccess = ({ accessResultCallback, adminUsersSlug, collection, fieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants })=>{
14
- collectionAccessKeys.forEach((key)=>{
15
- if (!collection.access) {
16
- collection.access = {};
17
- }
18
- collection.access[key] = withTenantAccess({
19
- accessFunction: collection.access?.[key],
20
- accessKey: key,
21
- accessResultCallback,
22
- adminUsersSlug,
23
- collection,
24
- fieldName: key === 'readVersions' ? `version.${fieldName}` : fieldName,
25
- tenantsArrayFieldName,
26
- tenantsArrayTenantFieldName,
27
- userHasAccessToAllTenants
28
- });
10
+ const getTenantAccessResult = ({ accessKey, accessResult, accessResultCallback, adminUsersSlug, args, collection, fieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants })=>{
11
+ const finish = (result)=>accessResultCallback ? accessResultCallback({
12
+ accessKey,
13
+ accessResult: result,
14
+ ...args
15
+ }) : result;
16
+ if (!accessResult) {
17
+ return finish(false);
18
+ }
19
+ const user = args.req.user;
20
+ if (!user || user.collection !== adminUsersSlug || userHasAccessToAllTenants(user)) {
21
+ return finish(accessResult);
22
+ }
23
+ const tenantConstraint = getTenantAccess({
24
+ fieldName,
25
+ tenantsArrayFieldName,
26
+ tenantsArrayTenantFieldName,
27
+ user
28
+ });
29
+ if (tenantConstraint[fieldName]?.in.length === 0) {
30
+ return finish(collection.slug === user.collection ? {
31
+ id: {
32
+ equals: user.id
33
+ }
34
+ } : false);
35
+ }
36
+ const tenantResult = collection.slug === user.collection ? {
37
+ or: [
38
+ {
39
+ id: {
40
+ equals: user.id
41
+ }
42
+ },
43
+ tenantConstraint
44
+ ]
45
+ } : tenantConstraint;
46
+ return finish(accessResult === true ? tenantResult : {
47
+ and: [
48
+ accessResult,
49
+ tenantResult
50
+ ]
29
51
  });
30
52
  };
53
+ const wrapCollectionAccess = (scope)=>{
54
+ scope.collection.access ??= {};
55
+ for (const accessKey of collectionAccessKeys){
56
+ const accessFunction = scope.collection.access[accessKey] ?? (({ req })=>Boolean(req.user));
57
+ scope.collection.access[accessKey] = async (args)=>getTenantAccessResult({
58
+ ...scope,
59
+ accessKey,
60
+ accessResult: await accessFunction(args),
61
+ args,
62
+ fieldName: accessKey === 'readVersions' ? `version.${scope.fieldName}` : scope.fieldName
63
+ });
64
+ }
65
+ };
66
+ export const addCollectionAccess = ({ config, scopes })=>{
67
+ const baseAccessScopes = new Map();
68
+ for (const scope of scopes){
69
+ if (scope.accessResultCallback) {
70
+ wrapCollectionAccess(scope);
71
+ } else {
72
+ baseAccessScopes.set(scope.collection.slug, scope);
73
+ }
74
+ }
75
+ if (!baseAccessScopes.size) {
76
+ return;
77
+ }
78
+ config.baseAccess ??= {};
79
+ config.baseAccess.collections ??= {};
80
+ for (const accessKey of collectionAccessKeys){
81
+ const baseAccessFunction = config.baseAccess.collections[accessKey];
82
+ config.baseAccess.collections[accessKey] = async (args)=>{
83
+ const baseResult = baseAccessFunction ? await baseAccessFunction(args) : true;
84
+ const scope = baseAccessScopes.get(args.slug);
85
+ if (!baseResult || !scope) {
86
+ return baseResult;
87
+ }
88
+ if (accessKey === 'create' && typeof baseResult === 'object') {
89
+ return baseResult;
90
+ }
91
+ const tenantResult = await getTenantAccessResult({
92
+ ...scope,
93
+ accessKey,
94
+ accessResult: baseResult,
95
+ args,
96
+ fieldName: accessKey === 'readVersions' ? `version.${scope.fieldName}` : scope.fieldName
97
+ });
98
+ return accessKey === 'create' ? Boolean(tenantResult) : tenantResult;
99
+ };
100
+ }
101
+ };
31
102
 
32
103
  //# sourceMappingURL=addCollectionAccess.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/addCollectionAccess.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { AllAccessKeys, MultiTenantPluginConfig } from '../types.js'\n\nimport { withTenantAccess } from './withTenantAccess.js'\n\nexport const collectionAccessKeys: AllAccessKeys = [\n 'create',\n 'read',\n 'update',\n 'delete',\n 'readVersions',\n 'unlock',\n] as const\n\ntype Args = {\n accessResultCallback?: MultiTenantPluginConfig['usersAccessResultOverride']\n adminUsersSlug: string\n collection: CollectionConfig\n fieldName: string\n tenantsArrayFieldName?: string\n tenantsArrayTenantFieldName?: string\n userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants']\n}\n\n/**\n * Adds tenant access constraint to collection\n * - constrains access a users assigned tenants\n */\nexport const addCollectionAccess = ({\n accessResultCallback,\n adminUsersSlug,\n collection,\n fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n}: Args): void => {\n collectionAccessKeys.forEach((key) => {\n if (!collection.access) {\n collection.access = {}\n }\n collection.access[key] = withTenantAccess({\n accessFunction: collection.access?.[key],\n accessKey: key,\n accessResultCallback,\n adminUsersSlug,\n collection,\n fieldName: key === 'readVersions' ? `version.${fieldName}` : fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n })\n })\n}\n"],"names":["withTenantAccess","collectionAccessKeys","addCollectionAccess","accessResultCallback","adminUsersSlug","collection","fieldName","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","forEach","key","access","accessFunction","accessKey"],"mappings":"AAIA,SAASA,gBAAgB,QAAQ,wBAAuB;AAExD,OAAO,MAAMC,uBAAsC;IACjD;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAYV;;;CAGC,GACD,OAAO,MAAMC,sBAAsB,CAAC,EAClCC,oBAAoB,EACpBC,cAAc,EACdC,UAAU,EACVC,SAAS,EACTC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EACpB;IACLR,qBAAqBS,OAAO,CAAC,CAACC;QAC5B,IAAI,CAACN,WAAWO,MAAM,EAAE;YACtBP,WAAWO,MAAM,GAAG,CAAC;QACvB;QACAP,WAAWO,MAAM,CAACD,IAAI,GAAGX,iBAAiB;YACxCa,gBAAgBR,WAAWO,MAAM,EAAE,CAACD,IAAI;YACxCG,WAAWH;YACXR;YACAC;YACAC;YACAC,WAAWK,QAAQ,iBAAiB,CAAC,QAAQ,EAAEL,WAAW,GAAGA;YAC7DC;YACAC;YACAC;QACF;IACF;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/utilities/addCollectionAccess.ts"],"sourcesContent":["import type { AccessArgs, AccessResult, CollectionConfig, Config, User, Where } from 'payload'\n\nimport type { AllAccessKeys, MultiTenantPluginConfig } from '../types.js'\n\nimport { getTenantAccess } from './getTenantAccess.js'\n\nexport const collectionAccessKeys: AllAccessKeys = [\n 'create',\n 'read',\n 'update',\n 'delete',\n 'readVersions',\n 'unlock',\n] as const\n\nexport type TenantAccessConfig = {\n accessResultCallback?: MultiTenantPluginConfig['usersAccessResultOverride']\n adminUsersSlug: string\n collection: CollectionConfig\n fieldName: string\n tenantsArrayFieldName?: string\n tenantsArrayTenantFieldName?: string\n userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants']\n}\n\nconst getTenantAccessResult = ({\n accessKey,\n accessResult,\n accessResultCallback,\n adminUsersSlug,\n args,\n collection,\n fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n}: {\n accessKey: AllAccessKeys[number]\n accessResult: AccessResult\n args: AccessArgs\n} & TenantAccessConfig): AccessResult | Promise<AccessResult> => {\n const finish = (result: AccessResult): AccessResult | Promise<AccessResult> =>\n accessResultCallback\n ? accessResultCallback({ accessKey, accessResult: result, ...args })\n : result\n\n if (!accessResult) {\n return finish(false)\n }\n\n const user = args.req.user\n\n if (!user || user.collection !== adminUsersSlug || userHasAccessToAllTenants(user as User)) {\n return finish(accessResult)\n }\n\n const tenantConstraint = getTenantAccess({\n fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n user,\n })\n\n if (tenantConstraint[fieldName]?.in.length === 0) {\n return finish(collection.slug === user.collection ? { id: { equals: user.id } } : false)\n }\n\n const tenantResult: Where =\n collection.slug === user.collection\n ? { or: [{ id: { equals: user.id } }, tenantConstraint] }\n : tenantConstraint\n\n return finish(accessResult === true ? tenantResult : { and: [accessResult, tenantResult] })\n}\n\nconst wrapCollectionAccess = (scope: TenantAccessConfig): void => {\n scope.collection.access ??= {}\n\n for (const accessKey of collectionAccessKeys) {\n const accessFunction =\n scope.collection.access[accessKey] ?? (({ req }: AccessArgs) => Boolean(req.user))\n\n scope.collection.access[accessKey] = async (args) =>\n getTenantAccessResult({\n ...scope,\n accessKey,\n accessResult: await accessFunction(args),\n args,\n fieldName: accessKey === 'readVersions' ? `version.${scope.fieldName}` : scope.fieldName,\n })\n }\n}\n\nexport const addCollectionAccess = ({\n config,\n scopes,\n}: {\n config: Config\n scopes: TenantAccessConfig[]\n}): void => {\n const baseAccessScopes = new Map<string, TenantAccessConfig>()\n\n for (const scope of scopes) {\n if (scope.accessResultCallback) {\n wrapCollectionAccess(scope)\n } else {\n baseAccessScopes.set(scope.collection.slug, scope)\n }\n }\n\n if (!baseAccessScopes.size) {\n return\n }\n\n config.baseAccess ??= {}\n config.baseAccess.collections ??= {}\n\n for (const accessKey of collectionAccessKeys) {\n const baseAccessFunction = config.baseAccess.collections[accessKey]\n\n config.baseAccess.collections[accessKey] = async (args) => {\n const baseResult = baseAccessFunction ? await baseAccessFunction(args) : true\n const scope = baseAccessScopes.get(args.slug)\n\n if (!baseResult || !scope) {\n return baseResult\n }\n\n if (accessKey === 'create' && typeof baseResult === 'object') {\n return baseResult\n }\n\n const tenantResult = await getTenantAccessResult({\n ...scope,\n accessKey,\n accessResult: baseResult,\n args,\n fieldName: accessKey === 'readVersions' ? `version.${scope.fieldName}` : scope.fieldName,\n })\n\n return accessKey === 'create' ? Boolean(tenantResult) : tenantResult\n }\n }\n}\n"],"names":["getTenantAccess","collectionAccessKeys","getTenantAccessResult","accessKey","accessResult","accessResultCallback","adminUsersSlug","args","collection","fieldName","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","finish","result","user","req","tenantConstraint","in","length","slug","id","equals","tenantResult","or","and","wrapCollectionAccess","scope","access","accessFunction","Boolean","addCollectionAccess","config","scopes","baseAccessScopes","Map","set","size","baseAccess","collections","baseAccessFunction","baseResult","get"],"mappings":"AAIA,SAASA,eAAe,QAAQ,uBAAsB;AAEtD,OAAO,MAAMC,uBAAsC;IACjD;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAYV,MAAMC,wBAAwB,CAAC,EAC7BC,SAAS,EACTC,YAAY,EACZC,oBAAoB,EACpBC,cAAc,EACdC,IAAI,EACJC,UAAU,EACVC,SAAS,EACTC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EAKL;IACpB,MAAMC,SAAS,CAACC,SACdT,uBACIA,qBAAqB;YAAEF;YAAWC,cAAcU;YAAQ,GAAGP,IAAI;QAAC,KAChEO;IAEN,IAAI,CAACV,cAAc;QACjB,OAAOS,OAAO;IAChB;IAEA,MAAME,OAAOR,KAAKS,GAAG,CAACD,IAAI;IAE1B,IAAI,CAACA,QAAQA,KAAKP,UAAU,KAAKF,kBAAkBM,0BAA0BG,OAAe;QAC1F,OAAOF,OAAOT;IAChB;IAEA,MAAMa,mBAAmBjB,gBAAgB;QACvCS;QACAC;QACAC;QACAI;IACF;IAEA,IAAIE,gBAAgB,CAACR,UAAU,EAAES,GAAGC,WAAW,GAAG;QAChD,OAAON,OAAOL,WAAWY,IAAI,KAAKL,KAAKP,UAAU,GAAG;YAAEa,IAAI;gBAAEC,QAAQP,KAAKM,EAAE;YAAC;QAAE,IAAI;IACpF;IAEA,MAAME,eACJf,WAAWY,IAAI,KAAKL,KAAKP,UAAU,GAC/B;QAAEgB,IAAI;YAAC;gBAAEH,IAAI;oBAAEC,QAAQP,KAAKM,EAAE;gBAAC;YAAE;YAAGJ;SAAiB;IAAC,IACtDA;IAEN,OAAOJ,OAAOT,iBAAiB,OAAOmB,eAAe;QAAEE,KAAK;YAACrB;YAAcmB;SAAa;IAAC;AAC3F;AAEA,MAAMG,uBAAuB,CAACC;IAC5BA,MAAMnB,UAAU,CAACoB,MAAM,KAAK,CAAC;IAE7B,KAAK,MAAMzB,aAAaF,qBAAsB;QAC5C,MAAM4B,iBACJF,MAAMnB,UAAU,CAACoB,MAAM,CAACzB,UAAU,IAAK,CAAA,CAAC,EAAEa,GAAG,EAAc,GAAKc,QAAQd,IAAID,IAAI,CAAA;QAElFY,MAAMnB,UAAU,CAACoB,MAAM,CAACzB,UAAU,GAAG,OAAOI,OAC1CL,sBAAsB;gBACpB,GAAGyB,KAAK;gBACRxB;gBACAC,cAAc,MAAMyB,eAAetB;gBACnCA;gBACAE,WAAWN,cAAc,iBAAiB,CAAC,QAAQ,EAAEwB,MAAMlB,SAAS,EAAE,GAAGkB,MAAMlB,SAAS;YAC1F;IACJ;AACF;AAEA,OAAO,MAAMsB,sBAAsB,CAAC,EAClCC,MAAM,EACNC,MAAM,EAIP;IACC,MAAMC,mBAAmB,IAAIC;IAE7B,KAAK,MAAMR,SAASM,OAAQ;QAC1B,IAAIN,MAAMtB,oBAAoB,EAAE;YAC9BqB,qBAAqBC;QACvB,OAAO;YACLO,iBAAiBE,GAAG,CAACT,MAAMnB,UAAU,CAACY,IAAI,EAAEO;QAC9C;IACF;IAEA,IAAI,CAACO,iBAAiBG,IAAI,EAAE;QAC1B;IACF;IAEAL,OAAOM,UAAU,KAAK,CAAC;IACvBN,OAAOM,UAAU,CAACC,WAAW,KAAK,CAAC;IAEnC,KAAK,MAAMpC,aAAaF,qBAAsB;QAC5C,MAAMuC,qBAAqBR,OAAOM,UAAU,CAACC,WAAW,CAACpC,UAAU;QAEnE6B,OAAOM,UAAU,CAACC,WAAW,CAACpC,UAAU,GAAG,OAAOI;YAChD,MAAMkC,aAAaD,qBAAqB,MAAMA,mBAAmBjC,QAAQ;YACzE,MAAMoB,QAAQO,iBAAiBQ,GAAG,CAACnC,KAAKa,IAAI;YAE5C,IAAI,CAACqB,cAAc,CAACd,OAAO;gBACzB,OAAOc;YACT;YAEA,IAAItC,cAAc,YAAY,OAAOsC,eAAe,UAAU;gBAC5D,OAAOA;YACT;YAEA,MAAMlB,eAAe,MAAMrB,sBAAsB;gBAC/C,GAAGyB,KAAK;gBACRxB;gBACAC,cAAcqC;gBACdlC;gBACAE,WAAWN,cAAc,iBAAiB,CAAC,QAAQ,EAAEwB,MAAMlB,SAAS,EAAE,GAAGkB,MAAMlB,SAAS;YAC1F;YAEA,OAAON,cAAc,WAAW2B,QAAQP,gBAAgBA;QAC1D;IACF;AACF,EAAC"}
@@ -0,0 +1,134 @@
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
@@ -0,0 +1 @@
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/plugin-multi-tenant",
3
- "version": "4.0.0-internal.c7d5de4",
3
+ "version": "4.0.0-internal.cc18726",
4
4
  "description": "Multi Tenant plugin for Payload",
5
5
  "keywords": [
6
6
  "payload",
@@ -26,7 +26,6 @@
26
26
  }
27
27
  ],
28
28
  "sideEffects": [
29
- "*.scss",
30
29
  "*.css"
31
30
  ],
32
31
  "type": "module",
@@ -81,13 +80,13 @@
81
80
  ],
82
81
  "devDependencies": {
83
82
  "@payloadcms/eslint-config": "3.28.0",
84
- "@payloadcms/ui": "4.0.0-internal.c7d5de4",
85
- "@payloadcms/translations": "4.0.0-internal.c7d5de4",
86
- "payload": "4.0.0-internal.c7d5de4"
83
+ "@payloadcms/translations": "4.0.0-internal.cc18726",
84
+ "@payloadcms/ui": "4.0.0-internal.cc18726",
85
+ "payload": "4.0.0-internal.cc18726"
87
86
  },
88
87
  "peerDependencies": {
89
- "@payloadcms/ui": "4.0.0-internal.c7d5de4",
90
- "payload": "4.0.0-internal.c7d5de4"
88
+ "@payloadcms/ui": "4.0.0-internal.cc18726",
89
+ "payload": "4.0.0-internal.cc18726"
91
90
  },
92
91
  "homepage:": "https://payloadcms.com",
93
92
  "scripts": {
@@ -1,3 +0,0 @@
1
- import type { Where } from 'payload';
2
- export declare function combineWhereConstraints(constraints: Array<Where>): Where;
3
- //# sourceMappingURL=combineWhereConstraints.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"combineWhereConstraints.d.ts","sourceRoot":"","sources":["../../src/utilities/combineWhereConstraints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,CAgBxE"}
@@ -1,19 +0,0 @@
1
- export function combineWhereConstraints(constraints) {
2
- if (constraints.length === 0) {
3
- return {};
4
- }
5
- if (constraints.length === 1 && constraints[0]) {
6
- return constraints[0];
7
- }
8
- const andConstraint = {
9
- and: []
10
- };
11
- constraints.forEach((constraint)=>{
12
- if (andConstraint.and && constraint && typeof constraint === 'object') {
13
- andConstraint.and.push(constraint);
14
- }
15
- });
16
- return andConstraint;
17
- }
18
-
19
- //# sourceMappingURL=combineWhereConstraints.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utilities/combineWhereConstraints.ts"],"sourcesContent":["import type { Where } from 'payload'\n\nexport function combineWhereConstraints(constraints: Array<Where>): Where {\n if (constraints.length === 0) {\n return {}\n }\n if (constraints.length === 1 && constraints[0]) {\n return constraints[0]\n }\n const andConstraint: Where = {\n and: [],\n }\n constraints.forEach((constraint) => {\n if (andConstraint.and && constraint && typeof constraint === 'object') {\n andConstraint.and.push(constraint)\n }\n })\n return andConstraint\n}\n"],"names":["combineWhereConstraints","constraints","length","andConstraint","and","forEach","constraint","push"],"mappings":"AAEA,OAAO,SAASA,wBAAwBC,WAAyB;IAC/D,IAAIA,YAAYC,MAAM,KAAK,GAAG;QAC5B,OAAO,CAAC;IACV;IACA,IAAID,YAAYC,MAAM,KAAK,KAAKD,WAAW,CAAC,EAAE,EAAE;QAC9C,OAAOA,WAAW,CAAC,EAAE;IACvB;IACA,MAAME,gBAAuB;QAC3BC,KAAK,EAAE;IACT;IACAH,YAAYI,OAAO,CAAC,CAACC;QACnB,IAAIH,cAAcC,GAAG,IAAIE,cAAc,OAAOA,eAAe,UAAU;YACrEH,cAAcC,GAAG,CAACG,IAAI,CAACD;QACzB;IACF;IACA,OAAOH;AACT"}
@@ -1,16 +0,0 @@
1
- import type { Access, AccessArgs, AccessResult, CollectionConfig } from 'payload';
2
- import type { AllAccessKeys, MultiTenantPluginConfig } from '../types.js';
3
- type Args = {
4
- accessFunction?: Access;
5
- accessKey: AllAccessKeys[number];
6
- accessResultCallback?: MultiTenantPluginConfig['usersAccessResultOverride'];
7
- adminUsersSlug: string;
8
- collection: CollectionConfig;
9
- fieldName: string;
10
- tenantsArrayFieldName?: string;
11
- tenantsArrayTenantFieldName?: string;
12
- userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants'];
13
- };
14
- export declare const withTenantAccess: ({ accessFunction, accessKey, accessResultCallback, adminUsersSlug, collection, fieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants, }: Args) => (args: AccessArgs) => Promise<AccessResult>;
15
- export {};
16
- //# sourceMappingURL=withTenantAccess.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"withTenantAccess.d.ts","sourceRoot":"","sources":["../../src/utilities/withTenantAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAA;AAE9F,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAwB,MAAM,aAAa,CAAA;AAK/F,KAAK,IAAI,GAAG;IACV,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAChC,oBAAoB,CAAC,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;IAC3E,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,yBAAyB,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,2BAA2B,CAAC,CAAA;CAC1F,CAAA;AACD,eAAO,MAAM,gBAAgB,+KAWxB,IAAI,YACM,UAAU,KAAG,OAAO,CAAC,YAAY,CAkF7C,CAAA"}
@@ -1,76 +0,0 @@
1
- import { combineWhereConstraints } from './combineWhereConstraints.js';
2
- import { getTenantAccess } from './getTenantAccess.js';
3
- export const withTenantAccess = ({ accessFunction, accessKey, accessResultCallback, adminUsersSlug, collection, fieldName, tenantsArrayFieldName, tenantsArrayTenantFieldName, userHasAccessToAllTenants })=>async (args)=>{
4
- const constraints = [];
5
- const accessFn = typeof accessFunction === 'function' ? accessFunction : ({ req })=>Boolean(req.user);
6
- const accessResult = await accessFn(args);
7
- if (accessResult === false) {
8
- if (accessResultCallback) {
9
- return accessResultCallback({
10
- accessKey,
11
- accessResult: false,
12
- ...args
13
- });
14
- } else {
15
- return false;
16
- }
17
- } else if (accessResult && typeof accessResult === 'object') {
18
- constraints.push(accessResult);
19
- }
20
- if (args.req.user && args.req.user.collection === adminUsersSlug && !userHasAccessToAllTenants(args.req.user)) {
21
- const tenantConstraint = getTenantAccess({
22
- fieldName,
23
- tenantsArrayFieldName,
24
- tenantsArrayTenantFieldName,
25
- user: args.req.user
26
- });
27
- // User with no tenants should have no access to tenant-scoped documents
28
- // except for their own user document
29
- if (tenantConstraint[fieldName]?.in.length === 0) {
30
- const result = collection.slug === args.req.user.collection ? {
31
- id: {
32
- equals: args.req.user.id
33
- }
34
- } : false;
35
- return accessResultCallback ? accessResultCallback({
36
- accessKey,
37
- accessResult: result,
38
- ...args
39
- }) : result;
40
- }
41
- if (collection.slug === args.req.user.collection) {
42
- constraints.push({
43
- or: [
44
- {
45
- id: {
46
- equals: args.req.user.id
47
- }
48
- },
49
- tenantConstraint
50
- ]
51
- });
52
- } else {
53
- constraints.push(tenantConstraint);
54
- }
55
- if (accessResultCallback) {
56
- return accessResultCallback({
57
- accessKey,
58
- accessResult: combineWhereConstraints(constraints),
59
- ...args
60
- });
61
- } else {
62
- return combineWhereConstraints(constraints);
63
- }
64
- }
65
- if (accessResultCallback) {
66
- return accessResultCallback({
67
- accessKey,
68
- accessResult,
69
- ...args
70
- });
71
- } else {
72
- return accessResult;
73
- }
74
- };
75
-
76
- //# sourceMappingURL=withTenantAccess.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utilities/withTenantAccess.ts"],"sourcesContent":["import type { Access, AccessArgs, AccessResult, CollectionConfig, User, Where } from 'payload'\n\nimport type { AllAccessKeys, MultiTenantPluginConfig, UserWithTenantsField } from '../types.js'\n\nimport { combineWhereConstraints } from './combineWhereConstraints.js'\nimport { getTenantAccess } from './getTenantAccess.js'\n\ntype Args = {\n accessFunction?: Access\n accessKey: AllAccessKeys[number]\n accessResultCallback?: MultiTenantPluginConfig['usersAccessResultOverride']\n adminUsersSlug: string\n collection: CollectionConfig\n fieldName: string\n tenantsArrayFieldName?: string\n tenantsArrayTenantFieldName?: string\n userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants']\n}\nexport const withTenantAccess =\n ({\n accessFunction,\n accessKey,\n accessResultCallback,\n adminUsersSlug,\n collection,\n fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n userHasAccessToAllTenants,\n }: Args) =>\n async (args: AccessArgs): Promise<AccessResult> => {\n const constraints: Where[] = []\n const accessFn =\n typeof accessFunction === 'function'\n ? accessFunction\n : ({ req }: AccessArgs): AccessResult => Boolean(req.user)\n const accessResult: AccessResult = await accessFn(args)\n\n if (accessResult === false) {\n if (accessResultCallback) {\n return accessResultCallback({\n accessKey,\n accessResult: false,\n ...args,\n })\n } else {\n return false\n }\n } else if (accessResult && typeof accessResult === 'object') {\n constraints.push(accessResult)\n }\n\n if (\n args.req.user &&\n args.req.user.collection === adminUsersSlug &&\n !userHasAccessToAllTenants(args.req.user as User)\n ) {\n const tenantConstraint = getTenantAccess({\n fieldName,\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n user: args.req.user as UserWithTenantsField,\n })\n\n // User with no tenants should have no access to tenant-scoped documents\n // except for their own user document\n if (tenantConstraint[fieldName]?.in.length === 0) {\n const result: AccessResult =\n collection.slug === args.req.user.collection\n ? { id: { equals: args.req.user.id } }\n : false\n\n return accessResultCallback\n ? accessResultCallback({ accessKey, accessResult: result, ...args })\n : result\n }\n\n if (collection.slug === args.req.user.collection) {\n constraints.push({\n or: [\n {\n id: {\n equals: args.req.user.id,\n },\n },\n tenantConstraint,\n ],\n })\n } else {\n constraints.push(tenantConstraint)\n }\n\n if (accessResultCallback) {\n return accessResultCallback({\n accessKey,\n accessResult: combineWhereConstraints(constraints),\n ...args,\n })\n } else {\n return combineWhereConstraints(constraints)\n }\n }\n\n if (accessResultCallback) {\n return accessResultCallback({\n accessKey,\n accessResult,\n ...args,\n })\n } else {\n return accessResult\n }\n }\n"],"names":["combineWhereConstraints","getTenantAccess","withTenantAccess","accessFunction","accessKey","accessResultCallback","adminUsersSlug","collection","fieldName","tenantsArrayFieldName","tenantsArrayTenantFieldName","userHasAccessToAllTenants","args","constraints","accessFn","req","Boolean","user","accessResult","push","tenantConstraint","in","length","result","slug","id","equals","or"],"mappings":"AAIA,SAASA,uBAAuB,QAAQ,+BAA8B;AACtE,SAASC,eAAe,QAAQ,uBAAsB;AAatD,OAAO,MAAMC,mBACX,CAAC,EACCC,cAAc,EACdC,SAAS,EACTC,oBAAoB,EACpBC,cAAc,EACdC,UAAU,EACVC,SAAS,EACTC,qBAAqB,EACrBC,2BAA2B,EAC3BC,yBAAyB,EACpB,GACP,OAAOC;QACL,MAAMC,cAAuB,EAAE;QAC/B,MAAMC,WACJ,OAAOX,mBAAmB,aACtBA,iBACA,CAAC,EAAEY,GAAG,EAAc,GAAmBC,QAAQD,IAAIE,IAAI;QAC7D,MAAMC,eAA6B,MAAMJ,SAASF;QAElD,IAAIM,iBAAiB,OAAO;YAC1B,IAAIb,sBAAsB;gBACxB,OAAOA,qBAAqB;oBAC1BD;oBACAc,cAAc;oBACd,GAAGN,IAAI;gBACT;YACF,OAAO;gBACL,OAAO;YACT;QACF,OAAO,IAAIM,gBAAgB,OAAOA,iBAAiB,UAAU;YAC3DL,YAAYM,IAAI,CAACD;QACnB;QAEA,IACEN,KAAKG,GAAG,CAACE,IAAI,IACbL,KAAKG,GAAG,CAACE,IAAI,CAACV,UAAU,KAAKD,kBAC7B,CAACK,0BAA0BC,KAAKG,GAAG,CAACE,IAAI,GACxC;YACA,MAAMG,mBAAmBnB,gBAAgB;gBACvCO;gBACAC;gBACAC;gBACAO,MAAML,KAAKG,GAAG,CAACE,IAAI;YACrB;YAEA,wEAAwE;YACxE,qCAAqC;YACrC,IAAIG,gBAAgB,CAACZ,UAAU,EAAEa,GAAGC,WAAW,GAAG;gBAChD,MAAMC,SACJhB,WAAWiB,IAAI,KAAKZ,KAAKG,GAAG,CAACE,IAAI,CAACV,UAAU,GACxC;oBAAEkB,IAAI;wBAAEC,QAAQd,KAAKG,GAAG,CAACE,IAAI,CAACQ,EAAE;oBAAC;gBAAE,IACnC;gBAEN,OAAOpB,uBACHA,qBAAqB;oBAAED;oBAAWc,cAAcK;oBAAQ,GAAGX,IAAI;gBAAC,KAChEW;YACN;YAEA,IAAIhB,WAAWiB,IAAI,KAAKZ,KAAKG,GAAG,CAACE,IAAI,CAACV,UAAU,EAAE;gBAChDM,YAAYM,IAAI,CAAC;oBACfQ,IAAI;wBACF;4BACEF,IAAI;gCACFC,QAAQd,KAAKG,GAAG,CAACE,IAAI,CAACQ,EAAE;4BAC1B;wBACF;wBACAL;qBACD;gBACH;YACF,OAAO;gBACLP,YAAYM,IAAI,CAACC;YACnB;YAEA,IAAIf,sBAAsB;gBACxB,OAAOA,qBAAqB;oBAC1BD;oBACAc,cAAclB,wBAAwBa;oBACtC,GAAGD,IAAI;gBACT;YACF,OAAO;gBACL,OAAOZ,wBAAwBa;YACjC;QACF;QAEA,IAAIR,sBAAsB;YACxB,OAAOA,qBAAqB;gBAC1BD;gBACAc;gBACA,GAAGN,IAAI;YACT;QACF,OAAO;YACL,OAAOM;QACT;IACF,EAAC"}