@svadmin/core 0.34.2 → 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.
@@ -5,7 +5,9 @@ import { useQueryClient } from '@tanstack/svelte-query';
5
5
  import type { LiveProvider, LiveEvent, LiveMode } from './live.svelte';
6
6
  import { captureAdminContext } from './context.svelte';
7
7
  import type { AdminContextAccessor } from './context.svelte';
8
- import { notify } from './notification.svelte';
8
+ import { queryKeyMatchesTenant } from './provider-bundle';
9
+ import { notifyWithProvider } from './notification.svelte';
10
+ import type { NotificationProvider } from './types';
9
11
 
10
12
  // ─── Auth Error Delegate ────────────────────────────────────────
11
13
  // Delegate auth errors to authProvider.onError() — refine pattern
@@ -88,6 +90,7 @@ export interface LiveSubscriptionParams {
88
90
 
89
91
  export function createLiveSubscription(paramsFn: () => LiveSubscriptionParams): void {
90
92
  const queryClient = useQueryClient();
93
+ const adminContext = captureAdminContext();
91
94
 
92
95
  $effect(() => {
93
96
  const params = paramsFn();
@@ -106,7 +109,8 @@ export function createLiveSubscription(paramsFn: () => LiveSubscriptionParams):
106
109
  params.onLiveEvent?.(event);
107
110
  if (liveMode === 'auto') {
108
111
  const dpN = params.dataProviderName;
109
- const dpMatch = (q: { queryKey: readonly unknown[] }) => q.queryKey[0] === dpN;
112
+ const dpMatch = (q: { queryKey: readonly unknown[] }) =>
113
+ queryKeyMatchesTenant(q.queryKey, adminContext.tenantCacheKey) && q.queryKey[0] === dpN;
110
114
  queryClient.invalidateQueries({ predicate: (q) => dpMatch(q) && q.queryKey[1] === params.resource });
111
115
  }
112
116
  },
@@ -127,36 +131,44 @@ export type NotificationConfig =
127
131
  | ((data?: unknown, values?: unknown, resource?: string) => { message: string; description?: string; type?: 'success' | 'error' })
128
132
  | undefined;
129
133
 
130
- export function fireSuccessNotification(
131
- config: NotificationConfig,
132
- defaultMessage: string,
133
- data?: unknown,
134
- values?: unknown,
135
- resource?: string,
136
- ): void {
134
+ export interface SuccessNotificationRequest {
135
+ config: NotificationConfig;
136
+ defaultMessage: string;
137
+ data?: unknown;
138
+ values?: unknown;
139
+ resource?: string;
140
+ provider?: NotificationProvider | null;
141
+ }
142
+
143
+ export interface ErrorNotificationRequest {
144
+ config: NotificationConfig;
145
+ defaultMessage: string;
146
+ error?: unknown;
147
+ resource?: string;
148
+ provider?: NotificationProvider | null;
149
+ }
150
+
151
+ export function fireSuccessNotification(request: SuccessNotificationRequest): void {
152
+ const { config, defaultMessage, data, values, resource, provider } = request;
137
153
  if (config === false) return;
138
154
  if (!config && !defaultMessage) return;
139
155
  if (typeof config === 'function') {
140
156
  const result = config(data, values, resource);
141
- notify({ type: 'success', message: result.message, description: result.description });
157
+ notifyWithProvider({ type: 'success', message: result.message, description: result.description }, provider);
142
158
  return;
143
159
  }
144
- notify({ type: 'success', message: config || defaultMessage });
160
+ notifyWithProvider({ type: 'success', message: config || defaultMessage }, provider);
145
161
  }
146
162
 
147
- export function fireErrorNotification(
148
- config: NotificationConfig,
149
- defaultMessage: string,
150
- error?: unknown,
151
- resource?: string,
152
- ): void {
163
+ export function fireErrorNotification(request: ErrorNotificationRequest): void {
164
+ const { config, defaultMessage, error, resource, provider } = request;
153
165
  if (config === false) return;
154
166
  if (!config && !defaultMessage) return;
155
167
  if (typeof config === 'function') {
156
168
  const result = config(error, undefined, resource);
157
- notify({ type: 'error', message: result.message, description: result.description });
169
+ notifyWithProvider({ type: 'error', message: result.message, description: result.description }, provider);
158
170
  return;
159
171
  }
160
172
  const errMsg = error instanceof Error ? error.message : String(error ?? '');
161
- notify({ type: 'error', message: config || `${defaultMessage}${errMsg ? ': ' + errMsg : ''}` });
173
+ notifyWithProvider({ type: 'error', message: config || `${defaultMessage}${errMsg ? ': ' + errMsg : ''}` }, provider);
162
174
  }
@@ -24,13 +24,14 @@ export type { OvertimeResult, OvertimeOptions, NotificationConfig } from './hook
24
24
  import { createQuery, createInfiniteQuery, createMutation, useQueryClient } from '@tanstack/svelte-query';
25
25
  import { getAdminOptions } from './options.svelte';
26
26
  import { captureAdminContext } from './context.svelte';
27
+ import { appendTenantCacheKey, queryKeyMatchesTenant } from './provider-bundle';
27
28
  import { useParsed } from './useParsed.svelte';
28
29
  import { createOvertimeTracker, createLiveSubscription, fireSuccessNotification, fireErrorNotification, checkError } from './hook-utils.svelte';
29
30
  import { invalidateByScopes, publishLiveEvent } from './mutation-hooks.svelte';
30
31
  import type { NotificationConfig, OvertimeOptions, LiveSubscriptionParams } from './hook-utils.svelte';
31
32
  import type { BaseRecord, HttpError, Pagination, Sort, Filter, DataProvider, KnownResources } from './types';
32
33
  import type { LiveMode, LiveEvent } from './live.svelte';
33
- import { audit } from './audit';
34
+ import { auditWithProvider } from './audit';
34
35
 
35
36
  // ─── useInfiniteList ────────────────────────────────────────────────
36
37
 
@@ -58,7 +59,15 @@ export function useInfiniteList<TData extends BaseRecord = BaseRecord, TError =
58
59
  const resource = options.resource ?? parsed.resource ?? '';
59
60
  const provider = adminContext.getDataProviderForResource(resource, options.dataProviderName);
60
61
  return {
61
- queryKey: [options.dataProviderName, resource, 'infiniteList', options.pagination?.pageSize, options.sorters, options.filters, options.meta],
62
+ queryKey: appendTenantCacheKey([
63
+ options.dataProviderName,
64
+ resource,
65
+ 'infiniteList',
66
+ options.pagination?.pageSize,
67
+ options.sorters,
68
+ options.filters,
69
+ options.meta,
70
+ ], adminContext.tenantCacheKey),
62
71
  queryFn: async ({ pageParam = 1 }) => {
63
72
  const result = await provider.getList<TData>({
64
73
  resource,
@@ -99,11 +108,23 @@ export function useInfiniteList<TData extends BaseRecord = BaseRecord, TError =
99
108
  $effect(() => {
100
109
  if (query.isSuccess && query.dataUpdatedAt > lastSuccessAt && options.successNotification) {
101
110
  lastSuccessAt = query.dataUpdatedAt;
102
- fireSuccessNotification(options.successNotification, '', query.data, undefined, options.resource ?? parsed.resource ?? '');
111
+ fireSuccessNotification({
112
+ config: options.successNotification,
113
+ defaultMessage: '',
114
+ data: query.data,
115
+ resource: options.resource ?? parsed.resource ?? '',
116
+ provider: adminContext.notificationProvider,
117
+ });
103
118
  } else if (query.isError && query.errorUpdatedAt > lastErrorAt) {
104
119
  lastErrorAt = query.errorUpdatedAt;
105
120
  checkError(query.error, adminContext);
106
- fireErrorNotification(options.errorNotification, 'Fetch failed', query.error, options.resource ?? parsed.resource ?? '');
121
+ fireErrorNotification({
122
+ config: options.errorNotification,
123
+ defaultMessage: 'Fetch failed',
124
+ error: query.error,
125
+ resource: options.resource ?? parsed.resource ?? '',
126
+ provider: adminContext.notificationProvider,
127
+ });
107
128
  }
108
129
  });
109
130
 
@@ -152,7 +173,9 @@ export function useSelect<TData extends BaseRecord = BaseRecord, TOption = { lab
152
173
  const query = createQuery<{ data: TData[]; total: number }>(() => {
153
174
  const provider = adminContext.getDataProviderForResource(resource, dataProviderName);
154
175
  return {
155
- queryKey: [dataProviderName, resource, 'select', allFilters, sorters, pagination, meta],
176
+ queryKey: appendTenantCacheKey([
177
+ dataProviderName, resource, 'select', allFilters, sorters, pagination, meta,
178
+ ], adminContext.tenantCacheKey),
156
179
  queryFn: () => provider.getList<TData>({ resource, sorters, filters: allFilters, pagination: { current: 1, pageSize: effectivePageSize }, meta }),
157
180
  enabled: options.queryOptions?.enabled ?? true,
158
181
  staleTime: options.queryOptions?.staleTime ?? adminOptions.reactQuery?.staleTime,
@@ -165,7 +188,9 @@ export function useSelect<TData extends BaseRecord = BaseRecord, TOption = { lab
165
188
  ? createQuery<{ data: TData[] }>(() => {
166
189
  const provider = adminContext.getDataProviderForResource(resource, dataProviderName);
167
190
  return {
168
- queryKey: [dataProviderName, resource, 'select-defaults', defaultValueIds],
191
+ queryKey: appendTenantCacheKey([
192
+ dataProviderName, resource, 'select-defaults', defaultValueIds,
193
+ ], adminContext.tenantCacheKey),
169
194
  queryFn: async () => {
170
195
  if (provider.getMany) return provider.getMany<TData>({ resource, ids: defaultValueIds, meta });
171
196
  const results = await Promise.all(defaultValueIds.map(id => provider.getOne<TData>({ resource, id, meta })));
@@ -203,11 +228,23 @@ export function useSelect<TData extends BaseRecord = BaseRecord, TOption = { lab
203
228
  $effect(() => {
204
229
  if (query.isSuccess && query.dataUpdatedAt > lastSuccessAt && options.successNotification) {
205
230
  lastSuccessAt = query.dataUpdatedAt;
206
- fireSuccessNotification(options.successNotification, '', query.data, undefined, resource);
231
+ fireSuccessNotification({
232
+ config: options.successNotification,
233
+ defaultMessage: '',
234
+ data: query.data,
235
+ resource,
236
+ provider: adminContext.notificationProvider,
237
+ });
207
238
  } else if (query.isError && query.errorUpdatedAt > lastErrorAt) {
208
239
  lastErrorAt = query.errorUpdatedAt;
209
240
  checkError(query.error, adminContext);
210
- fireErrorNotification(options.errorNotification, 'Fetch failed', query.error, resource);
241
+ fireErrorNotification({
242
+ config: options.errorNotification,
243
+ defaultMessage: 'Fetch failed',
244
+ error: query.error,
245
+ resource,
246
+ provider: adminContext.notificationProvider,
247
+ });
211
248
  }
212
249
  });
213
250
 
@@ -250,7 +287,9 @@ export function useCustom<TData = unknown, TError = HttpError>(options: UseCusto
250
287
  const query = createQuery<{ data: TData }, TError>(() => {
251
288
  const provider = adminContext.getDataProvider(options.dataProviderName);
252
289
  return {
253
- queryKey: [options.dataProviderName, 'custom', options.url, options.method, options.config, options.meta],
290
+ queryKey: appendTenantCacheKey([
291
+ options.dataProviderName, 'custom', options.url, options.method, options.config, options.meta,
292
+ ], adminContext.tenantCacheKey),
254
293
  queryFn: async () => {
255
294
  if (!provider.custom) throw new Error('DataProvider does not support custom method');
256
295
  return provider.custom<TData>({
@@ -276,11 +315,22 @@ export function useCustom<TData = unknown, TError = HttpError>(options: UseCusto
276
315
  $effect(() => {
277
316
  if (query.isSuccess && query.dataUpdatedAt > lastSuccessAt && options.successNotification) {
278
317
  lastSuccessAt = query.dataUpdatedAt;
279
- fireSuccessNotification(options.successNotification, '', query.data);
318
+ fireSuccessNotification({
319
+ config: options.successNotification,
320
+ defaultMessage: '',
321
+ data: query.data,
322
+ provider: adminContext.notificationProvider,
323
+ });
280
324
  } else if (query.isError && query.errorUpdatedAt > lastErrorAt) {
281
325
  lastErrorAt = query.errorUpdatedAt;
282
326
  checkError(query.error, adminContext);
283
- fireErrorNotification(options.errorNotification, 'Custom request failed', query.error, '');
327
+ fireErrorNotification({
328
+ config: options.errorNotification,
329
+ defaultMessage: 'Custom request failed',
330
+ error: query.error,
331
+ resource: '',
332
+ provider: adminContext.notificationProvider,
333
+ });
284
334
  }
285
335
  });
286
336
 
@@ -301,7 +351,15 @@ export function useCustomMutation<TData = unknown, TError = HttpError, TVariable
301
351
  },
302
352
  onSuccess: (data, params) => {
303
353
  if (params.resource && params.invalidates !== false) {
304
- invalidateByScopes(queryClient, params.resource, params.invalidates, ['list', 'many'], undefined, dataProviderName);
354
+ invalidateByScopes(
355
+ queryClient,
356
+ params.resource,
357
+ params.invalidates,
358
+ ['list', 'many'],
359
+ undefined,
360
+ dataProviderName,
361
+ (queryKey) => queryKeyMatchesTenant(queryKey, adminContext.tenantCacheKey),
362
+ );
305
363
  }
306
364
  },
307
365
  onError: (error) => {
@@ -331,17 +389,41 @@ export function useCreateMany<TData extends BaseRecord = BaseRecord, TError = Ht
331
389
  },
332
390
  onSuccess: (data, params) => {
333
391
  const resName = params.resource ?? resource;
334
- fireSuccessNotification(undefined, 'Created successfully', data.data, params.variables, resName);
335
- audit({ action: 'create', resource: resName });
392
+ fireSuccessNotification({
393
+ config: undefined,
394
+ defaultMessage: 'Created successfully',
395
+ data: data.data,
396
+ values: params.variables,
397
+ resource: resName,
398
+ provider: adminContext.notificationProvider,
399
+ });
400
+ auditWithProvider(
401
+ { action: 'create', resource: resName, meta: adminContext.getProviderMeta(resName) },
402
+ adminContext.auditLogProvider,
403
+ );
336
404
  publishLiveEvent(resName, 'created', undefined, adminContext);
337
405
  },
338
406
  onError: (error, params) => {
339
407
  checkError(error, adminContext);
340
- fireErrorNotification(undefined, 'Create many failed', error, params.resource ?? resource);
408
+ fireErrorNotification({
409
+ config: undefined,
410
+ defaultMessage: 'Create many failed',
411
+ error,
412
+ resource: params.resource ?? resource,
413
+ provider: adminContext.notificationProvider,
414
+ });
341
415
  },
342
416
  onSettled: (_d, _e, params) => {
343
417
  const resName = params.resource ?? resource;
344
- invalidateByScopes(queryClient, resName, undefined, ['list', 'many'], undefined, params.dataProviderName);
418
+ invalidateByScopes(
419
+ queryClient,
420
+ resName,
421
+ undefined,
422
+ ['list', 'many'],
423
+ undefined,
424
+ params.dataProviderName,
425
+ (queryKey) => queryKeyMatchesTenant(queryKey, adminContext.tenantCacheKey),
426
+ );
345
427
  },
346
428
  }));
347
429
 
@@ -365,19 +447,49 @@ export function useUpdateMany<TData extends BaseRecord = BaseRecord, TError = Ht
365
447
  },
366
448
  onSuccess: (data, params) => {
367
449
  const resName = params.resource ?? resource;
368
- fireSuccessNotification(undefined, 'Updated successfully', data.data, params.variables, resName);
369
- audit({ action: 'update', resource: resName });
450
+ fireSuccessNotification({
451
+ config: undefined,
452
+ defaultMessage: 'Updated successfully',
453
+ data: data.data,
454
+ values: params.variables,
455
+ resource: resName,
456
+ provider: adminContext.notificationProvider,
457
+ });
458
+ auditWithProvider(
459
+ { action: 'update', resource: resName, meta: adminContext.getProviderMeta(resName) },
460
+ adminContext.auditLogProvider,
461
+ );
370
462
  publishLiveEvent(resName, 'updated', params.ids, adminContext);
371
463
  },
372
464
  onError: (error, params) => {
373
465
  checkError(error, adminContext);
374
- fireErrorNotification(undefined, 'Update many failed', error, params.resource ?? resource);
466
+ fireErrorNotification({
467
+ config: undefined,
468
+ defaultMessage: 'Update many failed',
469
+ error,
470
+ resource: params.resource ?? resource,
471
+ provider: adminContext.notificationProvider,
472
+ });
375
473
  },
376
474
  onSettled: (_d, _e, params) => {
377
475
  const resName = params.resource ?? resource;
378
- invalidateByScopes(queryClient, resName, undefined, ['list', 'many', 'detail'], undefined, params.dataProviderName);
476
+ invalidateByScopes(
477
+ queryClient,
478
+ resName,
479
+ undefined,
480
+ ['list', 'many', 'detail'],
481
+ undefined,
482
+ params.dataProviderName,
483
+ (queryKey) => queryKeyMatchesTenant(queryKey, adminContext.tenantCacheKey),
484
+ );
379
485
  for (const id of params.ids) {
380
- queryClient.invalidateQueries({ predicate: (q) => q.queryKey[0] === params.dataProviderName && q.queryKey[1] === resName && q.queryKey[2] === 'one' && q.queryKey[3] === id });
486
+ queryClient.invalidateQueries({
487
+ predicate: (q) => queryKeyMatchesTenant(q.queryKey, adminContext.tenantCacheKey)
488
+ && q.queryKey[0] === params.dataProviderName
489
+ && q.queryKey[1] === resName
490
+ && q.queryKey[2] === 'one'
491
+ && q.queryKey[3] === id,
492
+ });
381
493
  }
382
494
  },
383
495
  }));
@@ -402,19 +514,48 @@ export function useDeleteMany<TData extends BaseRecord = BaseRecord, TError = Ht
402
514
  },
403
515
  onSuccess: (data, params) => {
404
516
  const resName = params.resource ?? resource;
405
- fireSuccessNotification(undefined, 'Deleted successfully', data.data, undefined, resName);
406
- audit({ action: 'delete', resource: resName });
517
+ fireSuccessNotification({
518
+ config: undefined,
519
+ defaultMessage: 'Deleted successfully',
520
+ data: data.data,
521
+ resource: resName,
522
+ provider: adminContext.notificationProvider,
523
+ });
524
+ auditWithProvider(
525
+ { action: 'delete', resource: resName, meta: adminContext.getProviderMeta(resName) },
526
+ adminContext.auditLogProvider,
527
+ );
407
528
  publishLiveEvent(resName, 'deleted', params.ids, adminContext);
408
529
  },
409
530
  onError: (error, params) => {
410
531
  checkError(error, adminContext);
411
- fireErrorNotification(undefined, 'Delete many failed', error, params.resource ?? resource);
532
+ fireErrorNotification({
533
+ config: undefined,
534
+ defaultMessage: 'Delete many failed',
535
+ error,
536
+ resource: params.resource ?? resource,
537
+ provider: adminContext.notificationProvider,
538
+ });
412
539
  },
413
540
  onSettled: (_d, _e, params) => {
414
541
  const resName = params.resource ?? resource;
415
- invalidateByScopes(queryClient, resName, undefined, ['list', 'many'], undefined, params.dataProviderName);
542
+ invalidateByScopes(
543
+ queryClient,
544
+ resName,
545
+ undefined,
546
+ ['list', 'many'],
547
+ undefined,
548
+ params.dataProviderName,
549
+ (queryKey) => queryKeyMatchesTenant(queryKey, adminContext.tenantCacheKey),
550
+ );
416
551
  for (const id of params.ids) {
417
- queryClient.removeQueries({ predicate: (q) => q.queryKey[0] === params.dataProviderName && q.queryKey[1] === resName && q.queryKey[2] === 'one' && q.queryKey[3] === id });
552
+ queryClient.removeQueries({
553
+ predicate: (q) => queryKeyMatchesTenant(q.queryKey, adminContext.tenantCacheKey)
554
+ && q.queryKey[0] === params.dataProviderName
555
+ && q.queryKey[1] === resName
556
+ && q.queryKey[2] === 'one'
557
+ && q.queryKey[3] === id,
558
+ });
418
559
  }
419
560
  },
420
561
  }));
@@ -426,15 +567,21 @@ export function useDeleteMany<TData extends BaseRecord = BaseRecord, TError = Ht
426
567
 
427
568
  export function useInvalidate() {
428
569
  const queryClient = useQueryClient();
570
+ const adminContext = captureAdminContext();
429
571
  return (params: { resource?: string; invalidates?: string[] | 'all' | false; id?: string | number; dataProviderName?: string }) => {
430
572
  if (params.invalidates === false) return;
431
573
  if (params.invalidates === 'all' || !params.resource) {
432
- queryClient.invalidateQueries({ predicate: (q) => q.queryKey[0] === params.dataProviderName });
574
+ queryClient.invalidateQueries({
575
+ predicate: (q) => queryKeyMatchesTenant(q.queryKey, adminContext.tenantCacheKey)
576
+ && q.queryKey[0] === params.dataProviderName,
577
+ });
433
578
  return;
434
579
  }
435
580
  const scopes = params.invalidates || ['resourceAll'];
436
581
  const res = params.resource;
437
- const dpMatch = (q: { queryKey: readonly unknown[] }) => q.queryKey[0] === params.dataProviderName;
582
+ const dpMatch = (q: { queryKey: readonly unknown[] }) =>
583
+ queryKeyMatchesTenant(q.queryKey, adminContext.tenantCacheKey)
584
+ && q.queryKey[0] === params.dataProviderName;
438
585
  for (const scope of scopes) {
439
586
  if (scope === 'resourceAll') queryClient.invalidateQueries({ predicate: (q) => dpMatch(q) && q.queryKey[1] === res });
440
587
  else if ((scope === 'detail' || scope === 'one') && params.id) queryClient.invalidateQueries({ predicate: (q) => dpMatch(q) && q.queryKey[1] === res && q.queryKey[2] === 'one' && q.queryKey[3] === params.id });
package/src/index.ts CHANGED
@@ -8,9 +8,31 @@ export {
8
8
  setLiveProvider, getLiveProvider,
9
9
  setTaskProvider, getTaskProvider,
10
10
  createAdminContext, provideAdminContext, getAdminContext, captureAdminContext,
11
+ provideTenantContext, getTenantContext, getProviderBundle,
11
12
  resetContext,
12
13
  } from './context.svelte';
13
14
  export type { DataProviderInput, AdminContextSource, AdminContextValue, AdminContextAccessor } from './context.svelte';
15
+ export {
16
+ appendTenantCacheKey,
17
+ createProviderBundle,
18
+ createTenantCacheKey,
19
+ defaultTenantAdapter,
20
+ isTenantCacheKey,
21
+ queryKeyMatchesTenant,
22
+ resolveTenantProviderMeta,
23
+ withProviderMeta,
24
+ withTenantDataProvider,
25
+ } from './provider-bundle';
26
+ export type {
27
+ ProviderBundle,
28
+ ProviderMetaInput,
29
+ ProviderMetaResolver,
30
+ TenantAdapter,
31
+ TenantCacheIdentity,
32
+ TenantCacheKey,
33
+ TenantContext,
34
+ TenantId,
35
+ } from './provider-bundle';
14
36
  export {
15
37
  useList, useInfiniteList,
16
38
  useOne, useShow,
@@ -36,10 +58,17 @@ export { readURLState, writeURLState } from './url-sync';
36
58
  export { setAccessControlProvider, getAccessControlProvider, getAccessControlOptions, canAccessAsync, createFeatureGate } from './permissions.svelte';
37
59
  export { useLive, useSubscription, usePublish } from './live.svelte';
38
60
  export { toast, getToastQueue, consumeToastQueue, getPromiseQueue, consumePromiseQueue, resetToast } from './toast.svelte';
39
- export { notify, closeNotification, setNotificationProvider, getNotificationProvider } from './notification.svelte';
61
+ export {
62
+ notify,
63
+ notifyWithProvider,
64
+ closeNotification,
65
+ setNotificationProvider,
66
+ getNotificationProvider,
67
+ } from './notification.svelte';
68
+ export type { NotificationParams } from './notification.svelte';
40
69
  export { t, setLocale, getLocale, getAvailableLocales, addTranslations, useTranslation, setI18nProvider, getI18nProvider, createI18nScope, provideI18nScope, getI18nScope, resetI18n } from './i18n.svelte';
41
70
  export type { I18nProvider, I18nScope, I18nScopeOptions } from './i18n.svelte';
42
- export { audit, setAuditHandler, setAuditLogProvider, getAuditLogProvider } from './audit';
71
+ export { audit, auditWithProvider, setAuditHandler, setAuditLogProvider, getAuditLogProvider } from './audit';
43
72
  export type { AuditLogProvider } from './audit';
44
73
  export { setChatProvider, getChatProvider, setChatContext, getChatContext, setAgentProvider, getAgentProvider, registerApproval, resolveApproval, hasPendingApprovals, resetChatProvider } from './chatProvider.svelte';
45
74
  export type { ChatProvider, ChatMessage, ChatContext, ChatAction, AgentProvider, AgentEvent, AgentOptions, AdminTool, AdminToolParameter, ToolResult } from './chatProvider.svelte';
@@ -67,7 +96,8 @@ export type {
67
96
  DeleteParams, DeleteResult,
68
97
  DeleteManyParams, DeleteManyResult,
69
98
  Pagination, Sort, Filter, Identity,
70
- ResourceDefinition, FieldDefinition, MenuItem,
99
+ ResourceDefinition, ResourceProviderConfig, ResourceTransportConfig, ResourceAdapterConfig,
100
+ FieldDefinition, MenuItem,
71
101
  AuthActionResult, CheckResult,
72
102
  ResourceTypeMap, KnownResources, InferData,
73
103
  BaseRecord, Role, AuditLog
@@ -1,6 +1,8 @@
1
1
  // LiveProvider — Real-time subscription interface + hooks
2
2
 
3
3
  import { useQueryClient } from '@tanstack/svelte-query';
4
+ import { captureAdminContext } from './context.svelte';
5
+ import { queryKeyMatchesTenant } from './provider-bundle';
4
6
 
5
7
  // ─── Types ──────────────────────────────────────────────────────
6
8
 
@@ -34,6 +36,7 @@ export function useLive(
34
36
  options?: { liveMode?: LiveMode | (() => LiveMode); onLiveEvent?: (event: LiveEvent) => void; liveParams?: Record<string, unknown> | (() => Record<string, unknown>); dataProviderName?: string }
35
37
  ): void {
36
38
  const queryClient = useQueryClient();
39
+ const adminContext = captureAdminContext();
37
40
 
38
41
  $effect(() => {
39
42
  const lp = typeof liveProvider === 'function' ? liveProvider() : liveProvider;
@@ -51,7 +54,8 @@ export function useLive(
51
54
  options?.onLiveEvent?.(event);
52
55
  if (liveMode === 'auto') {
53
56
  const dpName = options?.dataProviderName;
54
- const dpMatch = (q: { queryKey: readonly unknown[] }) => q.queryKey[0] === dpName;
57
+ const dpMatch = (q: { queryKey: readonly unknown[] }) =>
58
+ queryKeyMatchesTenant(q.queryKey, adminContext.tenantCacheKey) && q.queryKey[0] === dpName;
55
59
  queryClient.invalidateQueries({ predicate: (q) => dpMatch(q) && q.queryKey[1] === res });
56
60
  }
57
61
  },