@object-ui/tenant 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/TenantContext.d.ts +18 -0
- package/dist/TenantContext.d.ts.map +1 -0
- package/dist/TenantContext.js +10 -0
- package/dist/TenantGuard.d.ts +26 -0
- package/dist/TenantGuard.d.ts.map +1 -0
- package/dist/TenantGuard.js +28 -0
- package/dist/TenantProvider.d.ts +23 -0
- package/dist/TenantProvider.d.ts.map +1 -0
- package/dist/TenantProvider.js +83 -0
- package/dist/TenantScopedQuery.d.ts +24 -0
- package/dist/TenantScopedQuery.d.ts.map +1 -0
- package/dist/TenantScopedQuery.js +27 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/resolver.d.ts +26 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +76 -0
- package/dist/useTenant.d.ts +16 -0
- package/dist/useTenant.d.ts.map +1 -0
- package/dist/useTenant.js +18 -0
- package/dist/useTenantBranding.d.ts +14 -0
- package/dist/useTenantBranding.d.ts.map +1 -0
- package/dist/useTenantBranding.js +21 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ObjectQL
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import type { TenantContext } from '@object-ui/types';
|
|
9
|
+
export interface TenantContextValue extends TenantContext {
|
|
10
|
+
/** Switch to a different tenant */
|
|
11
|
+
switchTenant: (tenantId: string) => Promise<void>;
|
|
12
|
+
/** Whether tenant is loading */
|
|
13
|
+
isLoading: boolean;
|
|
14
|
+
/** Error if tenant resolution failed */
|
|
15
|
+
error: Error | null;
|
|
16
|
+
}
|
|
17
|
+
export declare const TenantCtx: import("react").Context<TenantContextValue | null>;
|
|
18
|
+
//# sourceMappingURL=TenantContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TenantContext.d.ts","sourceRoot":"","sources":["../src/TenantContext.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,mCAAmC;IACnC,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,gCAAgC;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,eAAO,MAAM,SAAS,oDAAiD,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { createContext } from 'react';
|
|
9
|
+
export const TenantCtx = createContext(null);
|
|
10
|
+
TenantCtx.displayName = 'TenantContext';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
export interface TenantGuardProps {
|
|
10
|
+
/** Required feature flag */
|
|
11
|
+
feature?: string;
|
|
12
|
+
/** Required tenant plan(s) */
|
|
13
|
+
plan?: string | string[];
|
|
14
|
+
/** Required tenant status */
|
|
15
|
+
status?: string;
|
|
16
|
+
/** Fallback content when guard fails */
|
|
17
|
+
fallback?: React.ReactNode;
|
|
18
|
+
/** Children to render when guard passes */
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Guard component that conditionally renders children based on tenant configuration.
|
|
23
|
+
* Useful for feature gating and plan-based access control.
|
|
24
|
+
*/
|
|
25
|
+
export declare function TenantGuard({ feature, plan, status, fallback, children, }: TenantGuardProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
//# sourceMappingURL=TenantGuard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TenantGuard.d.ts","sourceRoot":"","sources":["../src/TenantGuard.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,gBAAgB;IAC/B,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,2CAA2C;IAC3C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,IAAI,EACJ,MAAM,EACN,QAAe,EACf,QAAQ,GACT,EAAE,gBAAgB,2CA0BlB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useTenant } from './useTenant';
|
|
3
|
+
/**
|
|
4
|
+
* Guard component that conditionally renders children based on tenant configuration.
|
|
5
|
+
* Useful for feature gating and plan-based access control.
|
|
6
|
+
*/
|
|
7
|
+
export function TenantGuard({ feature, plan, status, fallback = null, children, }) {
|
|
8
|
+
const tenantCtx = useTenant();
|
|
9
|
+
if (!tenantCtx) {
|
|
10
|
+
return _jsx(_Fragment, { children: fallback });
|
|
11
|
+
}
|
|
12
|
+
// Check feature flag
|
|
13
|
+
if (feature && !tenantCtx.features[feature]) {
|
|
14
|
+
return _jsx(_Fragment, { children: fallback });
|
|
15
|
+
}
|
|
16
|
+
// Check plan
|
|
17
|
+
if (plan) {
|
|
18
|
+
const allowedPlans = Array.isArray(plan) ? plan : [plan];
|
|
19
|
+
if (tenantCtx.tenant.plan && !allowedPlans.includes(tenantCtx.tenant.plan)) {
|
|
20
|
+
return _jsx(_Fragment, { children: fallback });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// Check status
|
|
24
|
+
if (status && tenantCtx.tenant.status !== status) {
|
|
25
|
+
return _jsx(_Fragment, { children: fallback });
|
|
26
|
+
}
|
|
27
|
+
return _jsx(_Fragment, { children: children });
|
|
28
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import type { TenantConfig, TenantBranding, TenantProviderConfig } from '@object-ui/types';
|
|
10
|
+
export interface TenantProviderProps {
|
|
11
|
+
/** Tenant configuration or function to resolve tenant */
|
|
12
|
+
tenant?: TenantConfig;
|
|
13
|
+
/** Tenant provider configuration */
|
|
14
|
+
config?: TenantProviderConfig;
|
|
15
|
+
/** Callback to fetch tenant by ID */
|
|
16
|
+
fetchTenant?: (tenantId: string) => Promise<TenantConfig>;
|
|
17
|
+
/** Default branding to merge with tenant branding */
|
|
18
|
+
defaultBranding?: Partial<TenantBranding>;
|
|
19
|
+
/** Children */
|
|
20
|
+
children: React.ReactNode;
|
|
21
|
+
}
|
|
22
|
+
export declare function TenantProvider({ tenant: initialTenant, config, fetchTenant, defaultBranding, children, }: TenantProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
//# sourceMappingURL=TenantProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TenantProvider.d.ts","sourceRoot":"","sources":["../src/TenantProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAoD,MAAM,OAAO,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG3F,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,oCAAoC;IACpC,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,qCAAqC;IACrC,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,qDAAqD;IACrD,eAAe,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAC1C,eAAe;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAQD,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EAAE,aAAa,EACrB,MAAM,EACN,WAAW,EACX,eAAe,EACf,QAAQ,GACT,EAAE,mBAAmB,2CA4ErB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ObjectUI
|
|
4
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
10
|
+
import { TenantCtx } from './TenantContext';
|
|
11
|
+
const DEFAULT_BRANDING = {
|
|
12
|
+
companyName: 'ObjectUI',
|
|
13
|
+
primaryColor: '#3b82f6',
|
|
14
|
+
secondaryColor: '#64748b',
|
|
15
|
+
};
|
|
16
|
+
export function TenantProvider({ tenant: initialTenant, config, fetchTenant, defaultBranding, children, }) {
|
|
17
|
+
const [tenant, setTenant] = useState(initialTenant ?? null);
|
|
18
|
+
const [isLoading, setIsLoading] = useState(!initialTenant);
|
|
19
|
+
const [error, setError] = useState(null);
|
|
20
|
+
// Resolve tenant on mount
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (initialTenant) {
|
|
23
|
+
setTenant(initialTenant);
|
|
24
|
+
setIsLoading(false);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (config?.defaultTenantId && fetchTenant) {
|
|
28
|
+
setIsLoading(true);
|
|
29
|
+
fetchTenant(config.defaultTenantId)
|
|
30
|
+
.then((resolved) => {
|
|
31
|
+
setTenant(resolved);
|
|
32
|
+
setError(null);
|
|
33
|
+
})
|
|
34
|
+
.catch((err) => {
|
|
35
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
36
|
+
})
|
|
37
|
+
.finally(() => {
|
|
38
|
+
setIsLoading(false);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
setIsLoading(false);
|
|
43
|
+
}
|
|
44
|
+
}, [initialTenant, config?.defaultTenantId, fetchTenant]);
|
|
45
|
+
const switchTenant = useCallback(async (tenantId) => {
|
|
46
|
+
if (!fetchTenant) {
|
|
47
|
+
throw new Error('fetchTenant is required to switch tenants');
|
|
48
|
+
}
|
|
49
|
+
setIsLoading(true);
|
|
50
|
+
try {
|
|
51
|
+
const resolved = await fetchTenant(tenantId);
|
|
52
|
+
setTenant(resolved);
|
|
53
|
+
setError(null);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
setIsLoading(false);
|
|
61
|
+
}
|
|
62
|
+
}, [fetchTenant]);
|
|
63
|
+
const mergedBranding = useMemo(() => ({
|
|
64
|
+
...DEFAULT_BRANDING,
|
|
65
|
+
...defaultBranding,
|
|
66
|
+
...tenant?.branding,
|
|
67
|
+
}), [defaultBranding, tenant?.branding]);
|
|
68
|
+
const value = useMemo(() => {
|
|
69
|
+
if (!tenant)
|
|
70
|
+
return null;
|
|
71
|
+
return {
|
|
72
|
+
tenant,
|
|
73
|
+
isTenantAdmin: false,
|
|
74
|
+
branding: mergedBranding,
|
|
75
|
+
features: tenant.features ?? {},
|
|
76
|
+
switchTenant,
|
|
77
|
+
isLoading,
|
|
78
|
+
error,
|
|
79
|
+
};
|
|
80
|
+
}, [tenant, mergedBranding, switchTenant, isLoading, error]);
|
|
81
|
+
// While loading or if no tenant, still render children (they can handle null context)
|
|
82
|
+
return _jsx(TenantCtx.Provider, { value: value, children: children });
|
|
83
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import type { TenantScopedQueryConfig } from '@object-ui/types';
|
|
9
|
+
export interface TenantScopedQueryProps {
|
|
10
|
+
/** Base query/filter */
|
|
11
|
+
filter?: Record<string, unknown>[];
|
|
12
|
+
/** Tenant ID to scope to */
|
|
13
|
+
tenantId: string;
|
|
14
|
+
/** Scoped query configuration */
|
|
15
|
+
config: TenantScopedQueryConfig;
|
|
16
|
+
/** Object name for exclusion check */
|
|
17
|
+
objectName?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Applies tenant scoping to a query filter.
|
|
21
|
+
* Returns the original filter with tenant isolation applied.
|
|
22
|
+
*/
|
|
23
|
+
export declare function TenantScopedQuery({ filter, tenantId, config, objectName, }: TenantScopedQueryProps): Record<string, unknown>[];
|
|
24
|
+
//# sourceMappingURL=TenantScopedQuery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TenantScopedQuery.d.ts","sourceRoot":"","sources":["../src/TenantScopedQuery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAEhE,MAAM,WAAW,sBAAsB;IACrC,wBAAwB;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,MAAM,EAAE,uBAAuB,CAAC;IAChC,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAW,EACX,QAAQ,EACR,MAAM,EACN,UAAU,GACX,EAAE,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAkBpD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Applies tenant scoping to a query filter.
|
|
10
|
+
* Returns the original filter with tenant isolation applied.
|
|
11
|
+
*/
|
|
12
|
+
export function TenantScopedQuery({ filter = [], tenantId, config, objectName, }) {
|
|
13
|
+
// Skip scoping for excluded objects
|
|
14
|
+
if (objectName && config.excludedObjects?.includes(objectName)) {
|
|
15
|
+
return filter;
|
|
16
|
+
}
|
|
17
|
+
if (!config.autoFilter) {
|
|
18
|
+
return filter;
|
|
19
|
+
}
|
|
20
|
+
// Add tenant filter condition
|
|
21
|
+
const tenantFilter = {
|
|
22
|
+
field: config.tenantField,
|
|
23
|
+
operator: '=',
|
|
24
|
+
value: tenantId,
|
|
25
|
+
};
|
|
26
|
+
return [...filter, tenantFilter];
|
|
27
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @object-ui/tenant
|
|
10
|
+
*
|
|
11
|
+
* Multi-tenancy support for Object UI providing:
|
|
12
|
+
* - TenantProvider context for React apps
|
|
13
|
+
* - useTenant hook for accessing tenant context
|
|
14
|
+
* - Tenant isolation and scoped queries
|
|
15
|
+
* - Custom branding per tenant
|
|
16
|
+
*
|
|
17
|
+
* @packageDocumentation
|
|
18
|
+
*/
|
|
19
|
+
export { TenantProvider, type TenantProviderProps } from './TenantProvider';
|
|
20
|
+
export { useTenant } from './useTenant';
|
|
21
|
+
export { useTenantBranding } from './useTenantBranding';
|
|
22
|
+
export { TenantGuard, type TenantGuardProps } from './TenantGuard';
|
|
23
|
+
export { createTenantResolver, type TenantResolver } from './resolver';
|
|
24
|
+
export { TenantScopedQuery, type TenantScopedQueryProps } from './TenantScopedQuery';
|
|
25
|
+
export type { TenantConfig, TenantBranding, TenantLimits, TenantContext, TenantProviderConfig, TenantScopedQueryConfig, TenantIsolationStrategy, TenantStatus, TenantPlan, TenantResolutionStrategy, } from '@object-ui/types';
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAGrF,YAAY,EACV,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,wBAAwB,GACzB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @object-ui/tenant
|
|
10
|
+
*
|
|
11
|
+
* Multi-tenancy support for Object UI providing:
|
|
12
|
+
* - TenantProvider context for React apps
|
|
13
|
+
* - useTenant hook for accessing tenant context
|
|
14
|
+
* - Tenant isolation and scoped queries
|
|
15
|
+
* - Custom branding per tenant
|
|
16
|
+
*
|
|
17
|
+
* @packageDocumentation
|
|
18
|
+
*/
|
|
19
|
+
export { TenantProvider } from './TenantProvider';
|
|
20
|
+
export { useTenant } from './useTenant';
|
|
21
|
+
export { useTenantBranding } from './useTenantBranding';
|
|
22
|
+
export { TenantGuard } from './TenantGuard';
|
|
23
|
+
export { createTenantResolver } from './resolver';
|
|
24
|
+
export { TenantScopedQuery } from './TenantScopedQuery';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import type { TenantResolutionStrategy } from '@object-ui/types';
|
|
9
|
+
export interface TenantResolver {
|
|
10
|
+
/** Resolve tenant ID from the current context */
|
|
11
|
+
resolve: () => string | null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a tenant resolver based on the specified strategy.
|
|
15
|
+
*
|
|
16
|
+
* @param strategy - How to extract the tenant identifier
|
|
17
|
+
* @param options - Strategy-specific options
|
|
18
|
+
* @returns A TenantResolver instance
|
|
19
|
+
*/
|
|
20
|
+
export declare function createTenantResolver(strategy: TenantResolutionStrategy, options?: {
|
|
21
|
+
headerName?: string;
|
|
22
|
+
queryParam?: string;
|
|
23
|
+
cookieName?: string;
|
|
24
|
+
customResolver?: () => string | null;
|
|
25
|
+
}): TenantResolver;
|
|
26
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,OAAO,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,wBAAwB,EAClC,OAAO,CAAC,EAAE;IACR,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACtC,GACA,cAAc,CA8DhB"}
|
package/dist/resolver.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Creates a tenant resolver based on the specified strategy.
|
|
10
|
+
*
|
|
11
|
+
* @param strategy - How to extract the tenant identifier
|
|
12
|
+
* @param options - Strategy-specific options
|
|
13
|
+
* @returns A TenantResolver instance
|
|
14
|
+
*/
|
|
15
|
+
export function createTenantResolver(strategy, options) {
|
|
16
|
+
switch (strategy) {
|
|
17
|
+
case 'subdomain':
|
|
18
|
+
return {
|
|
19
|
+
resolve: () => {
|
|
20
|
+
if (typeof window === 'undefined')
|
|
21
|
+
return null;
|
|
22
|
+
const parts = window.location.hostname.split('.');
|
|
23
|
+
return parts.length > 2 ? parts[0] : null;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
case 'path':
|
|
27
|
+
return {
|
|
28
|
+
resolve: () => {
|
|
29
|
+
if (typeof window === 'undefined')
|
|
30
|
+
return null;
|
|
31
|
+
const parts = window.location.pathname.split('/').filter(Boolean);
|
|
32
|
+
return parts.length > 0 ? parts[0] : null;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
case 'header':
|
|
36
|
+
return {
|
|
37
|
+
resolve: () => null, // Headers are server-side only
|
|
38
|
+
};
|
|
39
|
+
case 'query':
|
|
40
|
+
return {
|
|
41
|
+
resolve: () => {
|
|
42
|
+
if (typeof window === 'undefined')
|
|
43
|
+
return null;
|
|
44
|
+
const params = new URLSearchParams(window.location.search);
|
|
45
|
+
return params.get(options?.queryParam ?? 'tenant');
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
case 'cookie':
|
|
49
|
+
return {
|
|
50
|
+
resolve: () => {
|
|
51
|
+
if (typeof document === 'undefined')
|
|
52
|
+
return null;
|
|
53
|
+
const name = options?.cookieName ?? 'tenant_id';
|
|
54
|
+
const cookies = document.cookie.split('; ');
|
|
55
|
+
for (const cookie of cookies) {
|
|
56
|
+
const eqIndex = cookie.indexOf('=');
|
|
57
|
+
if (eqIndex === -1)
|
|
58
|
+
continue;
|
|
59
|
+
const key = cookie.substring(0, eqIndex);
|
|
60
|
+
if (key === name) {
|
|
61
|
+
return decodeURIComponent(cookie.substring(eqIndex + 1));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
case 'custom':
|
|
68
|
+
return {
|
|
69
|
+
resolve: () => options?.customResolver?.() ?? null,
|
|
70
|
+
};
|
|
71
|
+
default:
|
|
72
|
+
return {
|
|
73
|
+
resolve: () => null,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { type TenantContextValue } from './TenantContext';
|
|
9
|
+
/**
|
|
10
|
+
* Hook to access the current tenant context.
|
|
11
|
+
* Must be used within a TenantProvider.
|
|
12
|
+
*
|
|
13
|
+
* @returns Tenant context value or null if no tenant is configured
|
|
14
|
+
*/
|
|
15
|
+
export declare function useTenant(): TenantContextValue | null;
|
|
16
|
+
//# sourceMappingURL=useTenant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTenant.d.ts","sourceRoot":"","sources":["../src/useTenant.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAa,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,SAAS,IAAI,kBAAkB,GAAG,IAAI,CAErD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { useContext } from 'react';
|
|
9
|
+
import { TenantCtx } from './TenantContext';
|
|
10
|
+
/**
|
|
11
|
+
* Hook to access the current tenant context.
|
|
12
|
+
* Must be used within a TenantProvider.
|
|
13
|
+
*
|
|
14
|
+
* @returns Tenant context value or null if no tenant is configured
|
|
15
|
+
*/
|
|
16
|
+
export function useTenant() {
|
|
17
|
+
return useContext(TenantCtx);
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import type { TenantBranding } from '@object-ui/types';
|
|
9
|
+
/**
|
|
10
|
+
* Hook to access resolved tenant branding.
|
|
11
|
+
* Returns default branding if no tenant is configured.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useTenantBranding(): TenantBranding;
|
|
14
|
+
//# sourceMappingURL=useTenantBranding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTenantBranding.d.ts","sourceRoot":"","sources":["../src/useTenantBranding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,CAYlD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { useMemo } from 'react';
|
|
9
|
+
import { useTenant } from './useTenant';
|
|
10
|
+
/**
|
|
11
|
+
* Hook to access resolved tenant branding.
|
|
12
|
+
* Returns default branding if no tenant is configured.
|
|
13
|
+
*/
|
|
14
|
+
export function useTenantBranding() {
|
|
15
|
+
const tenantCtx = useTenant();
|
|
16
|
+
return useMemo(() => tenantCtx?.branding ?? {
|
|
17
|
+
companyName: 'ObjectUI',
|
|
18
|
+
primaryColor: '#3b82f6',
|
|
19
|
+
secondaryColor: '#64748b',
|
|
20
|
+
}, [tenantCtx?.branding]);
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@object-ui/tenant",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Multi-tenancy support for Object UI with tenant isolation, scoped queries, and custom branding.",
|
|
7
|
+
"homepage": "https://www.objectui.org",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/objectstack-ai/objectui.git",
|
|
11
|
+
"directory": "packages/tenant"
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@object-ui/types": "0.5.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/react": "^19.2.13",
|
|
33
|
+
"react": "^19.1.0",
|
|
34
|
+
"typescript": "^5.9.3",
|
|
35
|
+
"vitest": "^4.0.18"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"clean": "rm -rf dist",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"type-check": "tsc --noEmit",
|
|
42
|
+
"lint": "eslint ."
|
|
43
|
+
}
|
|
44
|
+
}
|