@svadmin/core 0.34.2 → 0.36.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.2",
3
+ "version": "0.36.0",
4
4
  "author": "zuohuadong",
5
5
  "repository": {
6
6
  "type": "git",
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
+ createTenantCacheKey,
13
+ resolveTenantProviderMeta,
14
+ withProviderMeta,
15
+ } from './provider-bundle';
16
+ import { keys } from './query-keys';
17
+ import type { QueryMatcher } from './query-keys';
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,28 @@ 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[];
110
+ resolveDataProviderName(resourceName: string, overrideName?: string): string;
74
111
  getDataProviderForResource(resourceName: string, overrideName?: string): DataProvider;
112
+ queryKeys(resourceName?: string, overrideName?: string): ReturnType<typeof keys>;
113
+ queryKeyMatcher(resourceName?: string, overrideName?: string): Pick<QueryMatcher, 'provider' | 'tenant'>;
114
+ getProviderMeta(resourceName: string | undefined, meta?: Record<string, unknown>): Record<string, unknown> | undefined;
75
115
  getResource(nameOrIdentifier: string): ResourceDefinition;
76
116
  currentPath(): string;
77
117
  formatLink(path: string): string;
@@ -80,6 +120,22 @@ export interface AdminContextAccessor {
80
120
  }
81
121
 
82
122
  const [getRequiredAdminContext, setAdminContext] = createContext<AdminContextValue>();
123
+ const [getRequiredTenantContext, setScopedTenantContext] = createContext<TenantContext>();
124
+
125
+ /** Provide tenant state to a request/component subtree. No module-level fallback is created. */
126
+ export function provideTenantContext(tenant: TenantContext): TenantContext {
127
+ setScopedTenantContext(tenant);
128
+ return tenant;
129
+ }
130
+
131
+ /** Return the nearest request/tree-scoped tenant when called during initialization. */
132
+ export function getTenantContext(): TenantContext | undefined {
133
+ try {
134
+ return getRequiredTenantContext();
135
+ } catch {
136
+ return undefined;
137
+ }
138
+ }
83
139
 
84
140
  function normalizeDataProviders(provider: DataProviderInput): Record<string, DataProvider> {
85
141
  return isDataProvider(provider)
@@ -87,14 +143,49 @@ function normalizeDataProviders(provider: DataProviderInput): Record<string, Dat
87
143
  : provider;
88
144
  }
89
145
 
146
+ function resolveProviderBundle(source: AdminContextSource): ProviderBundle {
147
+ const bundled = source.providerBundle;
148
+ const dataProvider = source.dataProvider ?? bundled?.dataProvider;
149
+ if (!dataProvider) {
150
+ throw new Error('ProviderBundle requires a dataProvider.');
151
+ }
152
+
153
+ return {
154
+ dataProvider,
155
+ authProvider: source.authProvider !== undefined ? source.authProvider : bundled?.authProvider,
156
+ accessControlProvider: source.accessControlProvider !== undefined
157
+ ? source.accessControlProvider
158
+ : bundled?.accessControlProvider,
159
+ liveProvider: source.liveProvider ?? bundled?.liveProvider,
160
+ auditLogProvider: source.auditLogProvider !== undefined ? source.auditLogProvider : bundled?.auditLogProvider,
161
+ notificationProvider: source.notificationProvider !== undefined
162
+ ? source.notificationProvider
163
+ : bundled?.notificationProvider,
164
+ chatProvider: source.chatProvider !== undefined ? source.chatProvider : bundled?.chatProvider,
165
+ agentProvider: source.agentProvider !== undefined ? source.agentProvider : bundled?.agentProvider,
166
+ taskProvider: source.taskProvider ?? bundled?.taskProvider,
167
+ routerProvider: source.routerProvider ?? bundled?.routerProvider,
168
+ tenantAdapter: source.tenantAdapter ?? bundled?.tenantAdapter,
169
+ };
170
+ }
171
+
90
172
  export function createAdminContext(source: AdminContextSource): AdminContextValue {
173
+ const inheritedTenant = getTenantContext();
91
174
  return {
92
- get providers() { return normalizeDataProviders(source.dataProvider); },
93
- get authProvider() { return source.authProvider ?? null; },
175
+ get providerBundle() { return resolveProviderBundle(source); },
176
+ get providers() { return normalizeDataProviders(resolveProviderBundle(source).dataProvider); },
177
+ get authProvider() { return resolveProviderBundle(source).authProvider ?? null; },
94
178
  get resources() { return source.resources; },
95
- get routerProvider() { return source.routerProvider; },
96
- get liveProvider() { return source.liveProvider; },
97
- get taskProvider() { return source.taskProvider; },
179
+ get routerProvider() { return resolveProviderBundle(source).routerProvider; },
180
+ get liveProvider() { return resolveProviderBundle(source).liveProvider; },
181
+ get taskProvider() { return resolveProviderBundle(source).taskProvider; },
182
+ get accessControlProvider() { return resolveProviderBundle(source).accessControlProvider ?? null; },
183
+ get auditLogProvider() { return resolveProviderBundle(source).auditLogProvider ?? null; },
184
+ get notificationProvider() { return resolveProviderBundle(source).notificationProvider ?? null; },
185
+ get chatProvider() { return resolveProviderBundle(source).chatProvider ?? null; },
186
+ get agentProvider() { return resolveProviderBundle(source).agentProvider ?? null; },
187
+ get tenant() { return source.tenant ?? inheritedTenant; },
188
+ get tenantAdapter() { return resolveProviderBundle(source).tenantAdapter; },
98
189
  };
99
190
  }
100
191
 
@@ -119,14 +210,55 @@ let providers = $state<Record<string, DataProvider> | null>(null);
119
210
  /** Capture the current tree once so delayed callbacks never re-enter getContext. */
120
211
  export function captureAdminContext(): AdminContextAccessor {
121
212
  const scopedContext = getAdminContext();
213
+ const adaptedProviders = new Map<DataProvider, DataProvider>();
214
+
215
+ function adaptProvider(provider: DataProvider): DataProvider {
216
+ const hasResourceMetadata = accessor.resources.some((resource) => resource.provider !== undefined);
217
+ if (!accessor.tenant && !hasResourceMetadata) return provider;
218
+ const cached = adaptedProviders.get(provider);
219
+ if (cached) return cached;
220
+ const adapted = withProviderMeta(provider, ({ resource, meta }) => accessor.getProviderMeta(resource, meta));
221
+ adaptedProviders.set(provider, adapted);
222
+ return adapted;
223
+ }
122
224
 
123
225
  const accessor: AdminContextAccessor = {
226
+ get providerBundle() {
227
+ if (scopedContext) return scopedContext.providerBundle;
228
+ if (!providers) throw new Error('DataProvider not found. Did you call setDataProvider in App.svelte?');
229
+ return {
230
+ dataProvider: providers,
231
+ authProvider,
232
+ accessControlProvider: getLegacyAccessControlProvider(),
233
+ liveProvider: liveProviderState,
234
+ auditLogProvider: getLegacyAuditLogProvider(),
235
+ notificationProvider: getLegacyNotificationProvider(),
236
+ chatProvider: getLegacyChatProvider(),
237
+ agentProvider: getLegacyAgentProvider(),
238
+ taskProvider: taskProviderState,
239
+ routerProvider,
240
+ };
241
+ },
124
242
  get providers() { return scopedContext ? scopedContext.providers : providers; },
125
243
  get authProvider() { return scopedContext ? scopedContext.authProvider : authProvider; },
126
244
  get resources() { return scopedContext ? scopedContext.resources : resources ?? []; },
127
245
  get routerProvider() { return scopedContext ? scopedContext.routerProvider : routerProvider; },
128
246
  get liveProvider() { return scopedContext ? scopedContext.liveProvider : liveProviderState; },
129
247
  get taskProvider() { return scopedContext ? scopedContext.taskProvider : taskProviderState; },
248
+ get accessControlProvider() {
249
+ return scopedContext ? scopedContext.accessControlProvider : getLegacyAccessControlProvider();
250
+ },
251
+ get auditLogProvider() { return scopedContext ? scopedContext.auditLogProvider : getLegacyAuditLogProvider(); },
252
+ get notificationProvider() {
253
+ return scopedContext ? scopedContext.notificationProvider : getLegacyNotificationProvider();
254
+ },
255
+ get chatProvider() { return scopedContext ? scopedContext.chatProvider : getLegacyChatProvider(); },
256
+ get agentProvider() { return scopedContext ? scopedContext.agentProvider : getLegacyAgentProvider(); },
257
+ get tenant() { return scopedContext?.tenant; },
258
+ get tenantAdapter() { return scopedContext?.tenantAdapter; },
259
+ get tenantCacheKey() {
260
+ return accessor.tenant ? createTenantCacheKey(accessor.tenant, accessor.tenantAdapter) : undefined;
261
+ },
130
262
  getDataProvider(name) {
131
263
  const activeProviders = accessor.providers;
132
264
  if (!activeProviders) throw new Error('DataProvider not found. Did you call setDataProvider in App.svelte?');
@@ -135,27 +267,64 @@ export function captureAdminContext(): AdminContextAccessor {
135
267
  if (!provider) {
136
268
  throw new Error(`DataProvider "${key}" not found. Available: ${Object.keys(activeProviders).join(', ')}`);
137
269
  }
138
- return provider;
270
+ return adaptProvider(provider);
139
271
  },
140
272
  getDataProviderNames() {
141
273
  return accessor.providers ? Object.keys(accessor.providers) : [];
142
274
  },
143
- getDataProviderForResource(resourceName, overrideName) {
275
+ resolveDataProviderName(resourceName, overrideName) {
144
276
  if (overrideName) {
145
277
  try {
146
- return accessor.getDataProvider(overrideName);
278
+ accessor.getDataProvider(overrideName);
279
+ return overrideName;
147
280
  } catch {
148
- // Preserve the historical fallback to the resource/default provider.
281
+ // Fallback to resource/default provider as before.
149
282
  }
150
283
  }
284
+
151
285
  try {
152
286
  const resource = accessor.getResource(resourceName);
153
- const dataProviderName = resource.meta?.dataProviderName as string | undefined;
154
- if (dataProviderName) return accessor.getDataProvider(dataProviderName);
287
+ const providerName = resource.provider?.dataProviderName ?? resource.meta?.dataProviderName;
288
+ if (providerName) {
289
+ accessor.getDataProvider(providerName);
290
+ return providerName;
291
+ }
155
292
  } catch {
156
- // Preserve the historical fallback when a resource is not registered.
293
+ // Preserve fallback when a resource is not found or malformed.
157
294
  }
158
- return accessor.getDataProvider();
295
+
296
+ return 'default';
297
+ },
298
+ getDataProviderForResource(resourceName, overrideName) {
299
+ return accessor.getDataProvider(accessor.resolveDataProviderName(resourceName, overrideName));
300
+ },
301
+ queryKeys(resourceName, overrideName) {
302
+ return keys({
303
+ provider: accessor.resolveDataProviderName(resourceName ?? '', overrideName),
304
+ tenant: accessor.tenantCacheKey?.__svadminTenant,
305
+ });
306
+ },
307
+ queryKeyMatcher(resourceName, overrideName) {
308
+ return {
309
+ provider: accessor.resolveDataProviderName(resourceName ?? '', overrideName),
310
+ tenant: accessor.tenantCacheKey?.__svadminTenant,
311
+ };
312
+ },
313
+ getProviderMeta(resourceName, meta) {
314
+ const resource = resourceName
315
+ ? accessor.resources.find((candidate) => candidate.identifier === resourceName || candidate.name === resourceName)
316
+ : undefined;
317
+ const providerConfig = resource?.provider;
318
+ const tenant = accessor.tenant;
319
+ if (!providerConfig && !tenant) return meta;
320
+
321
+ return {
322
+ ...(providerConfig?.meta ?? {}),
323
+ ...(meta ?? {}),
324
+ ...(providerConfig?.transport === undefined ? {} : { transport: providerConfig.transport }),
325
+ ...(providerConfig?.adapter === undefined ? {} : { adapter: providerConfig.adapter }),
326
+ ...(tenant ? resolveTenantProviderMeta(tenant, accessor.tenantAdapter) : {}),
327
+ };
159
328
  },
160
329
  getResource(nameOrIdentifier) {
161
330
  const resource = accessor.resources.find(
@@ -208,6 +377,10 @@ export function getDataProviderForResource(resourceName: string, overrideName?:
208
377
  return captureAdminContext().getDataProviderForResource(resourceName, overrideName);
209
378
  }
210
379
 
380
+ export function getProviderBundle(): ProviderBundle {
381
+ return captureAdminContext().providerBundle;
382
+ }
383
+
211
384
  function isDataProvider(value: unknown): value is DataProvider {
212
385
  return (
213
386
  value !== null &&