@svadmin/core 0.34.1 → 0.35.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/core",
3
- "version": "0.34.1",
3
+ "version": "0.35.0",
4
4
  "author": "zuohuadong",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,14 +9,14 @@
9
9
  },
10
10
  "main": "src/index.ts",
11
11
  "devDependencies": {
12
- "@happy-dom/global-registrator": "^20.11.1",
13
- "@sveltejs/vite-plugin-svelte": "^7.2.0",
12
+ "@happy-dom/global-registrator": "^20.11.2",
13
+ "@sveltejs/vite-plugin-svelte": "^7.3.0",
14
14
  "@testing-library/dom": "^10.4.1",
15
15
  "@testing-library/svelte": "^5.4.2",
16
- "@testing-library/user-event": "^14.6.1",
16
+ "@testing-library/user-event": "^14.6.3",
17
17
  "@types/bun": "^1.3.14",
18
- "@types/jsdom": "^28.0.3",
19
- "happy-dom": "^20.11.1",
18
+ "@types/jsdom": "^30.0.0",
19
+ "happy-dom": "^20.11.2",
20
20
  "jsdom": "^30.0.1",
21
21
  "vitest": "^4.1.10"
22
22
  },
package/src/audit.ts CHANGED
@@ -50,7 +50,11 @@ export function resetAuditLogProvider(): void {
50
50
  handler = (entry) => { console.info('[audit]', entry.action, entry.resource, entry.recordId); };
51
51
  }
52
52
 
53
- export function audit(entry: Omit<AuditEntry, 'timestamp'>): void {
53
+ /** Record an entry through an explicitly scoped provider and the compatibility handler. */
54
+ export function auditWithProvider(
55
+ entry: Omit<AuditEntry, 'timestamp'>,
56
+ provider: AuditLogProvider | null | undefined,
57
+ ): void {
54
58
  const fullEntry: AuditEntry = { ...entry, timestamp: new Date().toISOString() };
55
59
  try {
56
60
  const result = handler(fullEntry);
@@ -60,8 +64,8 @@ export function audit(entry: Omit<AuditEntry, 'timestamp'>): void {
60
64
  } catch (e) {
61
65
  console.error('[audit] handler error:', e);
62
66
  }
63
- if (auditLogProvider) {
64
- auditLogProvider.create({
67
+ if (provider) {
68
+ provider.create({
65
69
  resource: entry.resource ?? '',
66
70
  action: entry.action,
67
71
  data: entry.data ?? entry.details,
@@ -70,3 +74,7 @@ export function audit(entry: Omit<AuditEntry, 'timestamp'>): void {
70
74
  }).catch(e => console.error('[audit] provider create error:', e));
71
75
  }
72
76
  }
77
+
78
+ export function audit(entry: Omit<AuditEntry, 'timestamp'>): void {
79
+ auditWithProvider(entry, auditLogProvider);
80
+ }
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { captureAdminContext } from './context.svelte';
6
6
  import type { AdminContextAccessor } from './context.svelte';
7
- import { notify } from './notification.svelte';
7
+ import { notifyWithProvider } from './notification.svelte';
8
8
  import { t, useTranslation } from './i18n.svelte';
9
9
  import type { AuthActionResult, CheckResult, Identity, AuthProvider } from './types';
10
10
  import { useQueryClient } from '@tanstack/svelte-query';
@@ -26,33 +26,72 @@ function createAuthMutation(options: CreateAuthMutationOptions) {
26
26
  const adminContext = captureAdminContext();
27
27
  const i18n = useTranslation();
28
28
  let isLoading = $state(false);
29
+ let mutationEpoch = 0;
30
+
31
+ const activeTenantIdentity = () => adminContext.tenantCacheKey?.__svadminTenant;
32
+ let observedProvider = adminContext.authProvider;
33
+ let observedTenantIdentity = activeTenantIdentity();
34
+
35
+ function isActiveMutation(
36
+ epoch: number,
37
+ provider: AuthProvider,
38
+ tenantIdentity: string | number | undefined,
39
+ ): boolean {
40
+ return epoch === mutationEpoch
41
+ && provider === adminContext.authProvider
42
+ && tenantIdentity === activeTenantIdentity();
43
+ }
29
44
 
30
45
  async function mutate(params?: Record<string, unknown>): Promise<AuthActionResult> {
31
46
  const provider = adminContext.authProvider;
32
47
  if (!provider) throw new Error('AuthProvider not configured');
33
48
  const fn = provider[options.method] as ((params?: Record<string, unknown>) => Promise<AuthActionResult>) | undefined;
34
49
  if (!fn) throw new Error(`AuthProvider.${options.method} not implemented`);
35
-
50
+
51
+ const epoch = ++mutationEpoch;
52
+ const tenantIdentity = activeTenantIdentity();
53
+ const notificationProvider = adminContext.notificationProvider;
54
+ const sendNotification = (notification: Parameters<typeof notifyWithProvider>[0]) =>
55
+ notifyWithProvider(notification, notificationProvider);
36
56
  isLoading = true;
37
57
  try {
38
58
  const result = await fn.call(provider, params);
59
+ if (!isActiveMutation(epoch, provider, tenantIdentity)) return result;
39
60
  if (result.success) {
40
- if (options.successMessage) notify({ type: 'success', message: options.successMessage });
61
+ if (options.successMessage) sendNotification({ type: 'success', message: options.successMessage });
41
62
  if (options.onSuccess) await options.onSuccess(result, adminContext);
42
63
  } else {
43
64
  const msg = result.error?.message ?? (typeof options.errorMessage === 'string' ? options.errorMessage : i18n.t('common.operationFailed'));
44
- if (options.errorMessage !== false) notify({ type: 'error', message: msg });
65
+ if (options.errorMessage !== false) sendNotification({ type: 'error', message: msg });
45
66
  }
46
67
  return result;
47
68
  } catch (err) {
48
69
  const msg = err instanceof Error ? err.message : (typeof options.errorMessage === 'string' ? options.errorMessage : i18n.t('common.operationFailed'));
49
- if (options.errorMessage !== false) notify({ type: 'error', message: msg });
70
+ if (isActiveMutation(epoch, provider, tenantIdentity) && options.errorMessage !== false) {
71
+ sendNotification({ type: 'error', message: msg });
72
+ }
50
73
  return { success: false, error: { message: msg } };
51
74
  } finally {
52
- isLoading = false;
75
+ if (isActiveMutation(epoch, provider, tenantIdentity)) isLoading = false;
53
76
  }
54
77
  }
55
78
 
79
+ if (typeof window !== 'undefined') {
80
+ $effect(() => {
81
+ const provider = adminContext.authProvider;
82
+ const tenantIdentity = activeTenantIdentity();
83
+ if (provider !== observedProvider || tenantIdentity !== observedTenantIdentity) {
84
+ observedProvider = provider;
85
+ observedTenantIdentity = tenantIdentity;
86
+ mutationEpoch++;
87
+ isLoading = false;
88
+ }
89
+ return () => {
90
+ mutationEpoch++;
91
+ };
92
+ });
93
+ }
94
+
56
95
  return {
57
96
  mutate,
58
97
  get isLoading() { return isLoading; },
@@ -152,26 +191,57 @@ export function useGetIdentity() {
152
191
  let data = $state<Identity | null>(null);
153
192
  let isLoading = $state(true);
154
193
  let error = $state<Error | null>(null);
194
+ let requestEpoch = 0;
195
+ let observedLogoutVersion = _logoutVersion;
155
196
 
156
- function fetch() {
157
- const provider = adminContext.authProvider;
158
- if (!provider) { isLoading = false; return; }
197
+ function requestIdentity(provider: AuthProvider | null): void {
198
+ const epoch = ++requestEpoch;
199
+ error = null;
200
+ if (!provider) {
201
+ data = null;
202
+ isLoading = false;
203
+ return;
204
+ }
159
205
  isLoading = true;
160
206
  provider.getIdentity().then(identity => {
207
+ if (epoch !== requestEpoch) return;
161
208
  data = identity;
162
209
  isLoading = false;
163
210
  }).catch(err => {
211
+ if (epoch !== requestEpoch) return;
164
212
  error = err instanceof Error ? err : new Error(String(err));
165
213
  isLoading = false;
166
214
  console.warn('[svadmin] useGetIdentity failed:', err);
167
215
  });
168
216
  }
169
217
 
218
+ function refetchIdentity(): void {
219
+ requestIdentity(adminContext.authProvider);
220
+ }
221
+
222
+ function loadScopedIdentity(provider: AuthProvider | null): void {
223
+ data = null;
224
+ requestIdentity(provider);
225
+ }
226
+
170
227
  if (typeof window !== 'undefined') {
171
- fetch();
172
228
  $effect(() => {
173
- void _logoutVersion;
174
- data = null;
229
+ const provider = adminContext.authProvider;
230
+ void adminContext.tenantCacheKey?.__svadminTenant;
231
+ const logoutVersion = _logoutVersion;
232
+ if (logoutVersion !== observedLogoutVersion) {
233
+ observedLogoutVersion = logoutVersion;
234
+ requestEpoch++;
235
+ data = null;
236
+ error = null;
237
+ isLoading = false;
238
+ return;
239
+ }
240
+
241
+ loadScopedIdentity(provider);
242
+ return () => {
243
+ requestEpoch++;
244
+ };
175
245
  });
176
246
  }
177
247
 
@@ -179,7 +249,7 @@ export function useGetIdentity() {
179
249
  get data() { return data; },
180
250
  get isLoading() { return isLoading; },
181
251
  get error() { return error; },
182
- refetch: fetch,
252
+ refetch: refetchIdentity,
183
253
  };
184
254
  }
185
255
 
@@ -5,12 +5,33 @@ import { createContext } from 'svelte';
5
5
  import type { DataProvider, AuthProvider, ResourceDefinition, TaskProvider } from './types';
6
6
  import type { RouterProvider } from './router-provider';
7
7
  import type { LiveProvider } from './live.svelte';
8
- import { resetNotificationProvider } from './notification.svelte';
9
- import { resetAuditLogProvider } from './audit';
10
- import { resetAccessControlProvider } from './permissions.svelte';
8
+ import type { AccessControlProvider } from './permissions.svelte';
9
+ import type { AuditLogProvider } from './audit';
10
+ import type { AgentProvider, ChatProvider } from './chatProvider.svelte';
11
+ import {
12
+ appendTenantCacheKey,
13
+ createTenantCacheKey,
14
+ queryKeyMatchesTenant,
15
+ resolveTenantProviderMeta,
16
+ withProviderMeta,
17
+ } from './provider-bundle';
18
+ import type {
19
+ DataProviderInput,
20
+ ProviderBundle,
21
+ TenantAdapter,
22
+ TenantCacheKey,
23
+ TenantContext,
24
+ } from './provider-bundle';
25
+ import { getNotificationProvider as getLegacyNotificationProvider, resetNotificationProvider } from './notification.svelte';
26
+ import { getAuditLogProvider as getLegacyAuditLogProvider, resetAuditLogProvider } from './audit';
27
+ import { getAccessControlProvider as getLegacyAccessControlProvider, resetAccessControlProvider } from './permissions.svelte';
11
28
  import { resetAdminOptions } from './options.svelte';
12
29
  import { resetI18n } from './i18n.svelte';
13
- import { resetChatProvider } from './chatProvider.svelte';
30
+ import {
31
+ getAgentProvider as getLegacyAgentProvider,
32
+ getChatProvider as getLegacyChatProvider,
33
+ resetChatProvider,
34
+ } from './chatProvider.svelte';
14
35
  import { resetToast } from './toast.svelte';
15
36
  import { resetTheme } from './theme.svelte';
16
37
  import {
@@ -29,29 +50,35 @@ import { resetSidebarCollapsed } from './hooks.svelte';
29
50
 
30
51
  // ─── DataProvider (supports single or multiple) ─────────────────
31
52
 
32
- /**
33
- * Accepts either:
34
- * - A single DataProvider
35
- * - A record of named providers: { default: rest, cms: graphql }
36
- */
37
- export type DataProviderInput = DataProvider | Record<string, DataProvider>;
53
+ export type { DataProviderInput } from './provider-bundle';
38
54
 
39
- export interface AdminContextSource {
40
- readonly dataProvider: DataProviderInput;
41
- readonly authProvider?: AuthProvider | null;
55
+ interface AdminContextSourceBase extends Omit<Partial<ProviderBundle>, 'dataProvider'> {
42
56
  readonly resources: ResourceDefinition[];
43
- readonly routerProvider?: RouterProvider;
44
- readonly liveProvider?: LiveProvider;
45
- readonly taskProvider?: TaskProvider;
57
+ /** 请求树内租户;未传入时继承最近的 provideTenantContext。 */
58
+ readonly tenant?: TenantContext;
46
59
  }
47
60
 
61
+ /** 必须提供旧式 dataProvider 或一等 ProviderBundle;同名顶层字段优先。 */
62
+ export type AdminContextSource = AdminContextSourceBase & (
63
+ | { readonly dataProvider: DataProviderInput; readonly providerBundle?: ProviderBundle }
64
+ | { readonly dataProvider?: DataProviderInput; readonly providerBundle: ProviderBundle }
65
+ );
66
+
48
67
  export interface AdminContextValue {
68
+ readonly providerBundle: ProviderBundle;
49
69
  readonly providers: Record<string, DataProvider>;
50
70
  readonly authProvider: AuthProvider | null;
51
71
  readonly resources: ResourceDefinition[];
52
72
  readonly routerProvider: RouterProvider | undefined;
53
73
  readonly liveProvider: LiveProvider | undefined;
54
74
  readonly taskProvider: TaskProvider | undefined;
75
+ readonly accessControlProvider: AccessControlProvider | null;
76
+ readonly auditLogProvider: AuditLogProvider | null;
77
+ readonly notificationProvider: ProviderBundle['notificationProvider'];
78
+ readonly chatProvider: ChatProvider | null;
79
+ readonly agentProvider: AgentProvider | null;
80
+ readonly tenant: TenantContext | undefined;
81
+ readonly tenantAdapter: TenantAdapter | undefined;
55
82
  }
56
83
 
57
84
  /**
@@ -63,15 +90,27 @@ export interface AdminContextValue {
63
90
  * read the module-level compatibility setters dynamically.
64
91
  */
65
92
  export interface AdminContextAccessor {
93
+ readonly providerBundle: ProviderBundle;
66
94
  readonly providers: Record<string, DataProvider> | null;
67
95
  readonly authProvider: AuthProvider | null;
68
96
  readonly resources: ResourceDefinition[];
69
97
  readonly routerProvider: RouterProvider | undefined;
70
98
  readonly liveProvider: LiveProvider | undefined;
71
99
  readonly taskProvider: TaskProvider | undefined;
100
+ readonly accessControlProvider: AccessControlProvider | null;
101
+ readonly auditLogProvider: AuditLogProvider | null;
102
+ readonly notificationProvider: ProviderBundle['notificationProvider'];
103
+ readonly chatProvider: ChatProvider | null;
104
+ readonly agentProvider: AgentProvider | null;
105
+ readonly tenant: TenantContext | undefined;
106
+ readonly tenantAdapter: TenantAdapter | undefined;
107
+ readonly tenantCacheKey: TenantCacheKey | undefined;
72
108
  getDataProvider(name?: string): DataProvider;
73
109
  getDataProviderNames(): string[];
74
110
  getDataProviderForResource(resourceName: string, overrideName?: string): DataProvider;
111
+ getProviderMeta(resourceName: string | undefined, meta?: Record<string, unknown>): Record<string, unknown> | undefined;
112
+ withTenantCacheKey(queryKey: readonly unknown[]): readonly unknown[];
113
+ matchesTenantQuery(queryKey: readonly unknown[]): boolean;
75
114
  getResource(nameOrIdentifier: string): ResourceDefinition;
76
115
  currentPath(): string;
77
116
  formatLink(path: string): string;
@@ -80,6 +119,22 @@ export interface AdminContextAccessor {
80
119
  }
81
120
 
82
121
  const [getRequiredAdminContext, setAdminContext] = createContext<AdminContextValue>();
122
+ const [getRequiredTenantContext, setScopedTenantContext] = createContext<TenantContext>();
123
+
124
+ /** Provide tenant state to a request/component subtree. No module-level fallback is created. */
125
+ export function provideTenantContext(tenant: TenantContext): TenantContext {
126
+ setScopedTenantContext(tenant);
127
+ return tenant;
128
+ }
129
+
130
+ /** Return the nearest request/tree-scoped tenant when called during initialization. */
131
+ export function getTenantContext(): TenantContext | undefined {
132
+ try {
133
+ return getRequiredTenantContext();
134
+ } catch {
135
+ return undefined;
136
+ }
137
+ }
83
138
 
84
139
  function normalizeDataProviders(provider: DataProviderInput): Record<string, DataProvider> {
85
140
  return isDataProvider(provider)
@@ -87,14 +142,49 @@ function normalizeDataProviders(provider: DataProviderInput): Record<string, Dat
87
142
  : provider;
88
143
  }
89
144
 
145
+ function resolveProviderBundle(source: AdminContextSource): ProviderBundle {
146
+ const bundled = source.providerBundle;
147
+ const dataProvider = source.dataProvider ?? bundled?.dataProvider;
148
+ if (!dataProvider) {
149
+ throw new Error('ProviderBundle requires a dataProvider.');
150
+ }
151
+
152
+ return {
153
+ dataProvider,
154
+ authProvider: source.authProvider !== undefined ? source.authProvider : bundled?.authProvider,
155
+ accessControlProvider: source.accessControlProvider !== undefined
156
+ ? source.accessControlProvider
157
+ : bundled?.accessControlProvider,
158
+ liveProvider: source.liveProvider ?? bundled?.liveProvider,
159
+ auditLogProvider: source.auditLogProvider !== undefined ? source.auditLogProvider : bundled?.auditLogProvider,
160
+ notificationProvider: source.notificationProvider !== undefined
161
+ ? source.notificationProvider
162
+ : bundled?.notificationProvider,
163
+ chatProvider: source.chatProvider !== undefined ? source.chatProvider : bundled?.chatProvider,
164
+ agentProvider: source.agentProvider !== undefined ? source.agentProvider : bundled?.agentProvider,
165
+ taskProvider: source.taskProvider ?? bundled?.taskProvider,
166
+ routerProvider: source.routerProvider ?? bundled?.routerProvider,
167
+ tenantAdapter: source.tenantAdapter ?? bundled?.tenantAdapter,
168
+ };
169
+ }
170
+
90
171
  export function createAdminContext(source: AdminContextSource): AdminContextValue {
172
+ const inheritedTenant = getTenantContext();
91
173
  return {
92
- get providers() { return normalizeDataProviders(source.dataProvider); },
93
- get authProvider() { return source.authProvider ?? null; },
174
+ get providerBundle() { return resolveProviderBundle(source); },
175
+ get providers() { return normalizeDataProviders(resolveProviderBundle(source).dataProvider); },
176
+ get authProvider() { return resolveProviderBundle(source).authProvider ?? null; },
94
177
  get resources() { return source.resources; },
95
- get routerProvider() { return source.routerProvider; },
96
- get liveProvider() { return source.liveProvider; },
97
- get taskProvider() { return source.taskProvider; },
178
+ get routerProvider() { return resolveProviderBundle(source).routerProvider; },
179
+ get liveProvider() { return resolveProviderBundle(source).liveProvider; },
180
+ get taskProvider() { return resolveProviderBundle(source).taskProvider; },
181
+ get accessControlProvider() { return resolveProviderBundle(source).accessControlProvider ?? null; },
182
+ get auditLogProvider() { return resolveProviderBundle(source).auditLogProvider ?? null; },
183
+ get notificationProvider() { return resolveProviderBundle(source).notificationProvider ?? null; },
184
+ get chatProvider() { return resolveProviderBundle(source).chatProvider ?? null; },
185
+ get agentProvider() { return resolveProviderBundle(source).agentProvider ?? null; },
186
+ get tenant() { return source.tenant ?? inheritedTenant; },
187
+ get tenantAdapter() { return resolveProviderBundle(source).tenantAdapter; },
98
188
  };
99
189
  }
100
190
 
@@ -119,14 +209,55 @@ let providers = $state<Record<string, DataProvider> | null>(null);
119
209
  /** Capture the current tree once so delayed callbacks never re-enter getContext. */
120
210
  export function captureAdminContext(): AdminContextAccessor {
121
211
  const scopedContext = getAdminContext();
212
+ const adaptedProviders = new Map<DataProvider, DataProvider>();
213
+
214
+ function adaptProvider(provider: DataProvider): DataProvider {
215
+ const hasResourceMetadata = accessor.resources.some((resource) => resource.provider !== undefined);
216
+ if (!accessor.tenant && !hasResourceMetadata) return provider;
217
+ const cached = adaptedProviders.get(provider);
218
+ if (cached) return cached;
219
+ const adapted = withProviderMeta(provider, ({ resource, meta }) => accessor.getProviderMeta(resource, meta));
220
+ adaptedProviders.set(provider, adapted);
221
+ return adapted;
222
+ }
122
223
 
123
224
  const accessor: AdminContextAccessor = {
225
+ get providerBundle() {
226
+ if (scopedContext) return scopedContext.providerBundle;
227
+ if (!providers) throw new Error('DataProvider not found. Did you call setDataProvider in App.svelte?');
228
+ return {
229
+ dataProvider: providers,
230
+ authProvider,
231
+ accessControlProvider: getLegacyAccessControlProvider(),
232
+ liveProvider: liveProviderState,
233
+ auditLogProvider: getLegacyAuditLogProvider(),
234
+ notificationProvider: getLegacyNotificationProvider(),
235
+ chatProvider: getLegacyChatProvider(),
236
+ agentProvider: getLegacyAgentProvider(),
237
+ taskProvider: taskProviderState,
238
+ routerProvider,
239
+ };
240
+ },
124
241
  get providers() { return scopedContext ? scopedContext.providers : providers; },
125
242
  get authProvider() { return scopedContext ? scopedContext.authProvider : authProvider; },
126
243
  get resources() { return scopedContext ? scopedContext.resources : resources ?? []; },
127
244
  get routerProvider() { return scopedContext ? scopedContext.routerProvider : routerProvider; },
128
245
  get liveProvider() { return scopedContext ? scopedContext.liveProvider : liveProviderState; },
129
246
  get taskProvider() { return scopedContext ? scopedContext.taskProvider : taskProviderState; },
247
+ get accessControlProvider() {
248
+ return scopedContext ? scopedContext.accessControlProvider : getLegacyAccessControlProvider();
249
+ },
250
+ get auditLogProvider() { return scopedContext ? scopedContext.auditLogProvider : getLegacyAuditLogProvider(); },
251
+ get notificationProvider() {
252
+ return scopedContext ? scopedContext.notificationProvider : getLegacyNotificationProvider();
253
+ },
254
+ get chatProvider() { return scopedContext ? scopedContext.chatProvider : getLegacyChatProvider(); },
255
+ get agentProvider() { return scopedContext ? scopedContext.agentProvider : getLegacyAgentProvider(); },
256
+ get tenant() { return scopedContext?.tenant; },
257
+ get tenantAdapter() { return scopedContext?.tenantAdapter; },
258
+ get tenantCacheKey() {
259
+ return accessor.tenant ? createTenantCacheKey(accessor.tenant, accessor.tenantAdapter) : undefined;
260
+ },
130
261
  getDataProvider(name) {
131
262
  const activeProviders = accessor.providers;
132
263
  if (!activeProviders) throw new Error('DataProvider not found. Did you call setDataProvider in App.svelte?');
@@ -135,7 +266,7 @@ export function captureAdminContext(): AdminContextAccessor {
135
266
  if (!provider) {
136
267
  throw new Error(`DataProvider "${key}" not found. Available: ${Object.keys(activeProviders).join(', ')}`);
137
268
  }
138
- return provider;
269
+ return adaptProvider(provider);
139
270
  },
140
271
  getDataProviderNames() {
141
272
  return accessor.providers ? Object.keys(accessor.providers) : [];
@@ -150,13 +281,35 @@ export function captureAdminContext(): AdminContextAccessor {
150
281
  }
151
282
  try {
152
283
  const resource = accessor.getResource(resourceName);
153
- const dataProviderName = resource.meta?.dataProviderName as string | undefined;
284
+ const dataProviderName = resource.provider?.dataProviderName ?? resource.meta?.dataProviderName;
154
285
  if (dataProviderName) return accessor.getDataProvider(dataProviderName);
155
286
  } catch {
156
287
  // Preserve the historical fallback when a resource is not registered.
157
288
  }
158
289
  return accessor.getDataProvider();
159
290
  },
291
+ getProviderMeta(resourceName, meta) {
292
+ const resource = resourceName
293
+ ? accessor.resources.find((candidate) => candidate.identifier === resourceName || candidate.name === resourceName)
294
+ : undefined;
295
+ const providerConfig = resource?.provider;
296
+ const tenant = accessor.tenant;
297
+ if (!providerConfig && !tenant) return meta;
298
+
299
+ return {
300
+ ...(providerConfig?.meta ?? {}),
301
+ ...(meta ?? {}),
302
+ ...(providerConfig?.transport === undefined ? {} : { transport: providerConfig.transport }),
303
+ ...(providerConfig?.adapter === undefined ? {} : { adapter: providerConfig.adapter }),
304
+ ...(tenant ? resolveTenantProviderMeta(tenant, accessor.tenantAdapter) : {}),
305
+ };
306
+ },
307
+ withTenantCacheKey(queryKey) {
308
+ return appendTenantCacheKey(queryKey, accessor.tenantCacheKey);
309
+ },
310
+ matchesTenantQuery(queryKey) {
311
+ return queryKeyMatchesTenant(queryKey, accessor.tenantCacheKey);
312
+ },
160
313
  getResource(nameOrIdentifier) {
161
314
  const resource = accessor.resources.find(
162
315
  (candidate) => candidate.identifier === nameOrIdentifier || candidate.name === nameOrIdentifier,
@@ -208,6 +361,10 @@ export function getDataProviderForResource(resourceName: string, overrideName?:
208
361
  return captureAdminContext().getDataProviderForResource(resourceName, overrideName);
209
362
  }
210
363
 
364
+ export function getProviderBundle(): ProviderBundle {
365
+ return captureAdminContext().providerBundle;
366
+ }
367
+
211
368
  function isDataProvider(value: unknown): value is DataProvider {
212
369
  return (
213
370
  value !== null &&