@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.
- package/package.json +1 -1
- package/src/audit.ts +11 -3
- package/src/auth-hooks.svelte.ts +83 -13
- package/src/context.svelte.ts +180 -23
- package/src/form-hooks.svelte.ts +63 -15
- package/src/hook-utils.svelte.ts +31 -19
- package/src/hooks.svelte.ts +175 -28
- package/src/index.ts +33 -3
- package/src/live.svelte.ts +5 -1
- package/src/mutation-hooks.svelte.ts +116 -19
- package/src/notification.svelte.ts +14 -4
- package/src/provider-bundle.ts +200 -0
- package/src/query-hooks.svelte.ts +54 -9
- package/src/task-hooks.svelte.ts +66 -19
- package/src/types.ts +25 -0
- package/src/useCan.ts +18 -4
- package/src/utility-hooks.svelte.ts +31 -2
package/src/hook-utils.svelte.ts
CHANGED
|
@@ -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 {
|
|
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[] }) =>
|
|
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
|
|
131
|
-
config: NotificationConfig
|
|
132
|
-
defaultMessage: string
|
|
133
|
-
data?: unknown
|
|
134
|
-
values?: unknown
|
|
135
|
-
resource?: string
|
|
136
|
-
|
|
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
|
-
|
|
157
|
+
notifyWithProvider({ type: 'success', message: result.message, description: result.description }, provider);
|
|
142
158
|
return;
|
|
143
159
|
}
|
|
144
|
-
|
|
160
|
+
notifyWithProvider({ type: 'success', message: config || defaultMessage }, provider);
|
|
145
161
|
}
|
|
146
162
|
|
|
147
|
-
export function fireErrorNotification(
|
|
148
|
-
config
|
|
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
|
-
|
|
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
|
-
|
|
173
|
+
notifyWithProvider({ type: 'error', message: config || `${defaultMessage}${errMsg ? ': ' + errMsg : ''}` }, provider);
|
|
162
174
|
}
|
package/src/hooks.svelte.ts
CHANGED
|
@@ -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 {
|
|
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: [
|
|
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(
|
|
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(
|
|
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: [
|
|
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: [
|
|
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(
|
|
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(
|
|
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: [
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
335
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
369
|
-
|
|
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(
|
|
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(
|
|
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({
|
|
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(
|
|
406
|
-
|
|
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(
|
|
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(
|
|
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({
|
|
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({
|
|
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[] }) =>
|
|
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 {
|
|
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,
|
|
99
|
+
ResourceDefinition, ResourceProviderConfig, ResourceTransportConfig, ResourceAdapterConfig,
|
|
100
|
+
FieldDefinition, MenuItem,
|
|
71
101
|
AuthActionResult, CheckResult,
|
|
72
102
|
ResourceTypeMap, KnownResources, InferData,
|
|
73
103
|
BaseRecord, Role, AuditLog
|
package/src/live.svelte.ts
CHANGED
|
@@ -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[] }) =>
|
|
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
|
},
|