@svadmin/core 0.49.1 → 0.52.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/README.md +32 -0
- package/package.json +21 -4
- package/src/access-control-contract.ts +250 -0
- package/src/adapters/casbin.ts +20 -14
- package/src/adapters/casl.ts +22 -19
- package/src/admin-tool.ts +7 -7
- package/src/audit-contract.ts +105 -0
- package/src/audit.ts +88 -128
- package/src/auth-hooks.svelte.ts +593 -261
- package/src/auth-mutation-contract.ts +143 -0
- package/src/auth-query-contract.ts +97 -0
- package/src/captured-mutation.svelte.ts +45 -0
- package/src/chatProvider.svelte.ts +1 -1
- package/src/command-contract.ts +195 -0
- package/src/context.svelte.ts +217 -205
- package/src/contract-context.svelte.ts +37 -0
- package/src/contract-form.svelte.ts +566 -0
- package/src/create-contract.ts +56 -0
- package/src/create-hooks.svelte.ts +347 -0
- package/src/create-many-contract.ts +90 -0
- package/src/create-many-hooks.svelte.ts +396 -0
- package/src/data-transfer.svelte.ts +1 -215
- package/src/defined-options.ts +50 -0
- package/src/delete-contract.ts +50 -0
- package/src/delete-hooks.svelte.ts +403 -0
- package/src/delete-many-contract.ts +60 -0
- package/src/delete-many-hooks.svelte.ts +402 -0
- package/src/enterprise.ts +4 -4
- package/src/export-format.ts +16 -14
- package/src/export-hooks.svelte.ts +191 -0
- package/src/feature-gate-contract.ts +53 -0
- package/src/hook-utils.svelte.ts +53 -105
- package/src/hooks.svelte.ts +19 -774
- package/src/http.ts +88 -87
- package/src/i18n.svelte.ts +56 -2
- package/src/import-contract.ts +106 -0
- package/src/import-hooks.svelte.ts +310 -0
- package/src/index.ts +77 -28
- package/src/inferencer-graphql.ts +7 -6
- package/src/infinite-query.svelte.ts +228 -0
- package/src/invalidation-contract.ts +26 -0
- package/src/live-sse.ts +91 -127
- package/src/live-subscription.svelte.ts +130 -0
- package/src/live-transport.ts +127 -0
- package/src/live-websocket.ts +85 -118
- package/src/live.svelte.ts +66 -68
- package/src/notification.svelte.ts +17 -15
- package/src/permission-catalog.ts +121 -0
- package/src/permission-hints-contract.ts +48 -0
- package/src/permissions.svelte.ts +112 -59
- package/src/plain-data.ts +61 -0
- package/src/provider-bundle.ts +85 -79
- package/src/provider-response.ts +35 -0
- package/src/query-hooks.svelte.ts +241 -286
- package/src/query-invalidation.ts +172 -0
- package/src/query-keys.ts +221 -110
- package/src/query-session.svelte.ts +216 -0
- package/src/query-snapshot.ts +144 -0
- package/src/reactive-projection.ts +53 -0
- package/src/record-decoder.ts +63 -0
- package/src/resource-binding.svelte.ts +19 -0
- package/src/resource-contract.ts +595 -0
- package/src/resource-mutation-types.ts +30 -0
- package/src/resource-schemas.ts +371 -0
- package/src/resource-transport-config.ts +156 -0
- package/src/route-parsing.ts +11 -11
- package/src/router-provider.ts +64 -59
- package/src/router.ts +8 -3
- package/src/routing-hooks.svelte.ts +90 -62
- package/src/schema-validation.ts +92 -0
- package/src/schema.ts +13 -0
- package/src/select-options.ts +95 -0
- package/src/select-query.svelte.ts +191 -0
- package/src/session-query.svelte.ts +88 -0
- package/src/strict-command-hooks.svelte.ts +333 -0
- package/src/strict-hooks.svelte.ts +198 -0
- package/src/table-hooks.svelte.ts +180 -174
- package/src/table-state.svelte.ts +117 -117
- package/src/task-contract.ts +87 -0
- package/src/task-hooks.svelte.ts +193 -149
- package/src/task-provider.ts +115 -0
- package/src/task-subscription.ts +132 -0
- package/src/theme.svelte.ts +155 -154
- package/src/toast.svelte.ts +38 -37
- package/src/types.ts +55 -103
- package/src/unsafe.ts +7 -0
- package/src/update-contract.ts +65 -0
- package/src/update-hooks.svelte.ts +347 -0
- package/src/update-many-contract.ts +79 -0
- package/src/update-many-hooks.svelte.ts +373 -0
- package/src/url-sync.ts +11 -10
- package/src/useCan.ts +96 -31
- package/src/useParsed.svelte.ts +18 -14
- package/src/useStepsForm.svelte.ts +123 -63
- package/src/utility-hooks.svelte.ts +101 -158
- package/src/form-hooks.svelte.ts +0 -889
- package/src/mutation-hooks.svelte.ts +0 -561
package/src/provider-bundle.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { definedOptions } from './defined-options';
|
|
1
2
|
import type {
|
|
2
3
|
AgentProvider,
|
|
3
4
|
ChatProvider,
|
|
@@ -40,50 +41,50 @@ import type {
|
|
|
40
41
|
UpdateResult,
|
|
41
42
|
} from './types';
|
|
42
43
|
|
|
43
|
-
export type DataProviderInput
|
|
44
|
+
export type DataProviderInput=DataProvider|Record<string,DataProvider>;
|
|
44
45
|
|
|
45
46
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
47
|
+
* First-class provider configuration object. Field names match the existing standalone provider APIs,
|
|
48
|
+
* allowing callers to migrate gradually from multiple props to one typed bundle.
|
|
48
49
|
*/
|
|
49
50
|
export interface ProviderBundle {
|
|
50
51
|
readonly dataProvider: DataProviderInput;
|
|
51
|
-
readonly authProvider?: AuthProvider
|
|
52
|
-
readonly accessControlProvider?: AccessControlProvider
|
|
52
|
+
readonly authProvider?: AuthProvider|null;
|
|
53
|
+
readonly accessControlProvider?: AccessControlProvider|null;
|
|
53
54
|
readonly liveProvider?: LiveProvider;
|
|
54
|
-
readonly auditLogProvider?: AuditLogProvider
|
|
55
|
-
readonly notificationProvider?: NotificationProvider
|
|
56
|
-
readonly chatProvider?: ChatProvider
|
|
57
|
-
readonly agentProvider?: AgentProvider
|
|
55
|
+
readonly auditLogProvider?: AuditLogProvider|null;
|
|
56
|
+
readonly notificationProvider?: NotificationProvider|null;
|
|
57
|
+
readonly chatProvider?: ChatProvider|null;
|
|
58
|
+
readonly agentProvider?: AgentProvider|null;
|
|
58
59
|
readonly taskProvider?: TaskProvider;
|
|
59
60
|
readonly routerProvider?: RouterProvider;
|
|
60
61
|
readonly tenantAdapter?: TenantAdapter;
|
|
61
|
-
readonly organizationProvider?: OrganizationProvider
|
|
62
|
-
readonly identityGovernanceProvider?: IdentityGovernanceProvider
|
|
63
|
-
readonly sessionProvider?: SessionProvider
|
|
64
|
-
readonly credentialProvider?: CredentialProvider
|
|
62
|
+
readonly organizationProvider?: OrganizationProvider|null;
|
|
63
|
+
readonly identityGovernanceProvider?: IdentityGovernanceProvider|null;
|
|
64
|
+
readonly sessionProvider?: SessionProvider|null;
|
|
65
|
+
readonly credentialProvider?: CredentialProvider|null;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
/**
|
|
68
|
+
/** Preserves the input object's exact type while applying compile-time ProviderBundle validation. */
|
|
68
69
|
export function createProviderBundle<const TBundle extends ProviderBundle>(bundle: TBundle): TBundle {
|
|
69
70
|
return bundle;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
export type TenantId
|
|
73
|
-
export type TenantCacheIdentity
|
|
73
|
+
export type TenantId=string|number;
|
|
74
|
+
export type TenantCacheIdentity=string|number;
|
|
74
75
|
|
|
75
|
-
/**
|
|
76
|
+
/** Tenant state within a request tree; never share it between SSR requests through module-level variables. */
|
|
76
77
|
export interface TenantContext {
|
|
77
78
|
readonly tenantId: TenantId;
|
|
78
|
-
readonly meta?: Readonly<Record<string,
|
|
79
|
+
readonly meta?: Readonly<Record<string,unknown>>;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
/**
|
|
82
|
-
*
|
|
83
|
-
* Core
|
|
83
|
+
* Projects the standard TenantContext into provider metadata and cache identity for a concrete backend.
|
|
84
|
+
* Core always writes the standard `tenantId`; adapters cannot override it.
|
|
84
85
|
*/
|
|
85
86
|
export interface TenantAdapter {
|
|
86
|
-
readonly getProviderMeta?: (tenant: TenantContext) => Readonly<Record<string,
|
|
87
|
+
readonly getProviderMeta?: (tenant: TenantContext) => Readonly<Record<string,unknown>>;
|
|
87
88
|
readonly getCacheIdentity?: (tenant: TenantContext) => TenantCacheIdentity;
|
|
88
89
|
}
|
|
89
90
|
|
|
@@ -91,38 +92,38 @@ export interface TenantCacheKey {
|
|
|
91
92
|
readonly __svadminTenant: TenantCacheIdentity;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
export const defaultTenantAdapter: Required<TenantAdapter
|
|
95
|
-
getProviderMeta: (tenant) => tenant.meta
|
|
95
|
+
export const defaultTenantAdapter: Required<TenantAdapter>={
|
|
96
|
+
getProviderMeta: (tenant) => tenant.meta??{},
|
|
96
97
|
getCacheIdentity: (tenant) => tenant.tenantId,
|
|
97
98
|
};
|
|
98
99
|
|
|
99
100
|
export function resolveTenantProviderMeta(
|
|
100
101
|
tenant: TenantContext,
|
|
101
|
-
adapter: TenantAdapter
|
|
102
|
-
): Record<string,
|
|
102
|
+
adapter: TenantAdapter=defaultTenantAdapter,
|
|
103
|
+
): Record<string,unknown> {
|
|
103
104
|
return {
|
|
104
|
-
...(tenant.meta
|
|
105
|
-
...(adapter.getProviderMeta?.(tenant)
|
|
105
|
+
...(tenant.meta??{}),
|
|
106
|
+
...(adapter.getProviderMeta?.(tenant)??{}),
|
|
106
107
|
tenantId: tenant.tenantId,
|
|
107
108
|
};
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
export function createTenantCacheKey(
|
|
111
112
|
tenant: TenantContext,
|
|
112
|
-
adapter: TenantAdapter
|
|
113
|
+
adapter: TenantAdapter=defaultTenantAdapter,
|
|
113
114
|
): TenantCacheKey {
|
|
114
115
|
return {
|
|
115
|
-
__svadminTenant: adapter.getCacheIdentity?.(tenant)
|
|
116
|
+
__svadminTenant: adapter.getCacheIdentity?.(tenant)??tenant.tenantId,
|
|
116
117
|
};
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
export function isTenantCacheKey(value: unknown): value is TenantCacheKey {
|
|
120
121
|
return Boolean(
|
|
121
122
|
value
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
&&typeof value==='object'
|
|
124
|
+
&&Object.keys(value).length===1
|
|
125
|
+
&&'__svadminTenant' in value
|
|
126
|
+
&&(typeof value.__svadminTenant==='string'||typeof value.__svadminTenant==='number'),
|
|
126
127
|
);
|
|
127
128
|
}
|
|
128
129
|
|
|
@@ -132,9 +133,9 @@ export function isTenantCacheKey(value: unknown): value is TenantCacheKey {
|
|
|
132
133
|
*/
|
|
133
134
|
export function appendTenantCacheKey(
|
|
134
135
|
queryKey: readonly unknown[],
|
|
135
|
-
tenantCacheKey: TenantCacheKey
|
|
136
|
+
tenantCacheKey: TenantCacheKey|undefined,
|
|
136
137
|
): readonly unknown[] {
|
|
137
|
-
return tenantCacheKey
|
|
138
|
+
return tenantCacheKey? [...queryKey,tenantCacheKey]:queryKey;
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
/**
|
|
@@ -143,64 +144,69 @@ export function appendTenantCacheKey(
|
|
|
143
144
|
*/
|
|
144
145
|
export function queryKeyMatchesTenant(
|
|
145
146
|
queryKey: readonly unknown[],
|
|
146
|
-
tenantCacheKey: TenantCacheKey
|
|
147
|
+
tenantCacheKey: TenantCacheKey|undefined,
|
|
147
148
|
): boolean {
|
|
148
|
-
const candidate
|
|
149
|
-
const keyTenant
|
|
150
|
-
if
|
|
151
|
-
return keyTenant?.__svadminTenant
|
|
149
|
+
const candidate=queryKey.at(-1);
|
|
150
|
+
const keyTenant=isTenantCacheKey(candidate)? candidate:undefined;
|
|
151
|
+
if(!tenantCacheKey) return keyTenant===undefined;
|
|
152
|
+
return keyTenant?.__svadminTenant===tenantCacheKey.__svadminTenant;
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
export interface ProviderMetaInput {
|
|
155
156
|
readonly resource?: string;
|
|
156
|
-
readonly meta?: Record<string,
|
|
157
|
+
readonly meta?: Record<string,unknown>;
|
|
157
158
|
}
|
|
158
159
|
|
|
159
|
-
export type ProviderMetaResolver
|
|
160
|
+
export type ProviderMetaResolver=(input: ProviderMetaInput) => Record<string,unknown>|undefined;
|
|
160
161
|
|
|
161
162
|
/**
|
|
162
|
-
*
|
|
163
|
+
* Adds metadata to every standard DataProvider operation while preserving undeclared extension fields from the original provider.
|
|
163
164
|
*/
|
|
164
|
-
export function withProviderMeta(provider: DataProvider,
|
|
165
|
-
|
|
165
|
+
export function withProviderMeta(provider: DataProvider,resolveMeta: ProviderMetaResolver): DataProvider {
|
|
166
|
+
function resolvedParams<P extends ProviderMetaInput>(params: P) {
|
|
167
|
+
const { meta: _meta,...rest }=params;
|
|
168
|
+
return { ...rest,...definedOptions({ meta: resolveMeta(params) }) };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const adapted: DataProvider={
|
|
166
172
|
...provider,
|
|
167
|
-
getList:
|
|
168
|
-
provider.getList
|
|
169
|
-
getOne:
|
|
170
|
-
provider.getOne
|
|
171
|
-
create:
|
|
172
|
-
provider.create
|
|
173
|
-
update:
|
|
174
|
-
provider.update
|
|
175
|
-
deleteOne:
|
|
176
|
-
provider.deleteOne
|
|
173
|
+
getList: (params: GetListParams): Promise<GetListResult<BaseRecord>> =>
|
|
174
|
+
provider.getList(resolvedParams(params)),
|
|
175
|
+
getOne: (params: GetOneParams): Promise<GetOneResult<BaseRecord>> =>
|
|
176
|
+
provider.getOne(resolvedParams(params)),
|
|
177
|
+
create: (params: CreateParams<unknown>): Promise<CreateResult<BaseRecord>> =>
|
|
178
|
+
provider.create(resolvedParams(params)),
|
|
179
|
+
update: (params: UpdateParams<unknown>): Promise<UpdateResult<BaseRecord>> =>
|
|
180
|
+
provider.update(resolvedParams(params)),
|
|
181
|
+
deleteOne: (params: DeleteParams<unknown>): Promise<DeleteResult<BaseRecord>> =>
|
|
182
|
+
provider.deleteOne(resolvedParams(params)),
|
|
177
183
|
getApiUrl: () => provider.getApiUrl(),
|
|
178
184
|
};
|
|
179
185
|
|
|
180
|
-
const getMany
|
|
181
|
-
if
|
|
182
|
-
adapted.getMany
|
|
183
|
-
getMany
|
|
186
|
+
const getMany=provider.getMany?.bind(provider);
|
|
187
|
+
if(getMany) {
|
|
188
|
+
adapted.getMany=(params: GetManyParams): Promise<GetManyResult<BaseRecord>> =>
|
|
189
|
+
getMany(resolvedParams(params));
|
|
184
190
|
}
|
|
185
|
-
const createMany
|
|
186
|
-
if
|
|
187
|
-
adapted.createMany
|
|
188
|
-
createMany
|
|
191
|
+
const createMany=provider.createMany?.bind(provider);
|
|
192
|
+
if(createMany) {
|
|
193
|
+
adapted.createMany=(params: CreateManyParams<unknown>): Promise<CreateManyResult<BaseRecord>> =>
|
|
194
|
+
createMany(resolvedParams(params));
|
|
189
195
|
}
|
|
190
|
-
const updateMany
|
|
191
|
-
if
|
|
192
|
-
adapted.updateMany
|
|
193
|
-
updateMany
|
|
196
|
+
const updateMany=provider.updateMany?.bind(provider);
|
|
197
|
+
if(updateMany) {
|
|
198
|
+
adapted.updateMany=(params: UpdateManyParams<unknown>): Promise<UpdateManyResult<BaseRecord>> =>
|
|
199
|
+
updateMany(resolvedParams(params));
|
|
194
200
|
}
|
|
195
|
-
const deleteMany
|
|
196
|
-
if
|
|
197
|
-
adapted.deleteMany
|
|
198
|
-
deleteMany
|
|
201
|
+
const deleteMany=provider.deleteMany?.bind(provider);
|
|
202
|
+
if(deleteMany) {
|
|
203
|
+
adapted.deleteMany=(params: DeleteManyParams<unknown>): Promise<DeleteManyResult<BaseRecord>> =>
|
|
204
|
+
deleteMany(resolvedParams(params));
|
|
199
205
|
}
|
|
200
|
-
const custom
|
|
201
|
-
if
|
|
202
|
-
adapted.custom
|
|
203
|
-
custom
|
|
206
|
+
const custom=provider.custom?.bind(provider);
|
|
207
|
+
if(custom) {
|
|
208
|
+
adapted.custom=(params: CustomParams<unknown>): Promise<CustomResult> =>
|
|
209
|
+
custom(resolvedParams(params));
|
|
204
210
|
}
|
|
205
211
|
|
|
206
212
|
return adapted;
|
|
@@ -209,10 +215,10 @@ export function withProviderMeta(provider: DataProvider, resolveMeta: ProviderMe
|
|
|
209
215
|
export function withTenantDataProvider(
|
|
210
216
|
provider: DataProvider,
|
|
211
217
|
tenant: TenantContext,
|
|
212
|
-
adapter: TenantAdapter
|
|
218
|
+
adapter: TenantAdapter=defaultTenantAdapter,
|
|
213
219
|
): DataProvider {
|
|
214
|
-
return withProviderMeta(provider,
|
|
215
|
-
...(meta
|
|
216
|
-
...resolveTenantProviderMeta(tenant,
|
|
220
|
+
return withProviderMeta(provider,({ meta }) => ({
|
|
221
|
+
...(meta??{}),
|
|
222
|
+
...resolveTenantProviderMeta(tenant,adapter),
|
|
217
223
|
}));
|
|
218
224
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { DataProvider } from './types';
|
|
2
|
+
import { decodeBaseRecord, decodeOneResult, decodeManyResult, decodeListResult, decodeCustomResult } from './record-decoder';
|
|
3
|
+
|
|
4
|
+
type UntrustedResponse<F> = F extends (...params: infer P) => Promise<unknown>
|
|
5
|
+
? (...params: P) => Promise<unknown> : F;
|
|
6
|
+
|
|
7
|
+
/** Transport implementations cannot claim a response shape before validation. */
|
|
8
|
+
export type DataTransport = { [K in keyof DataProvider]: UntrustedResponse<DataProvider[K]> };
|
|
9
|
+
|
|
10
|
+
export function withValidatedResponses(transport: DataTransport): DataProvider {
|
|
11
|
+
const {
|
|
12
|
+
getMany: rawGetMany, createMany: rawCreateMany, updateMany: rawUpdateMany,
|
|
13
|
+
deleteMany: rawDeleteMany, custom: rawCustom, ...required
|
|
14
|
+
} = transport;
|
|
15
|
+
const provider: DataProvider = {
|
|
16
|
+
...required,
|
|
17
|
+
getApiUrl: () => transport.getApiUrl(),
|
|
18
|
+
getList: async params => decodeListResult(await transport.getList(params), decodeBaseRecord),
|
|
19
|
+
getOne: async params => decodeOneResult(await transport.getOne(params), decodeBaseRecord),
|
|
20
|
+
create: async params => decodeOneResult(await transport.create(params), decodeBaseRecord, true),
|
|
21
|
+
update: async params => decodeOneResult(await transport.update(params), decodeBaseRecord, true),
|
|
22
|
+
deleteOne: async params => decodeOneResult(await transport.deleteOne(params), decodeBaseRecord, true),
|
|
23
|
+
};
|
|
24
|
+
const getMany = rawGetMany?.bind(transport);
|
|
25
|
+
if (getMany) provider.getMany = async params => decodeManyResult(await getMany(params), decodeBaseRecord);
|
|
26
|
+
const createMany = rawCreateMany?.bind(transport);
|
|
27
|
+
if (createMany) provider.createMany = async params => decodeManyResult(await createMany(params), decodeBaseRecord, true);
|
|
28
|
+
const updateMany = rawUpdateMany?.bind(transport);
|
|
29
|
+
if (updateMany) provider.updateMany = async params => decodeManyResult(await updateMany(params), decodeBaseRecord, true);
|
|
30
|
+
const deleteMany = rawDeleteMany?.bind(transport);
|
|
31
|
+
if (deleteMany) provider.deleteMany = async params => decodeManyResult(await deleteMany(params), decodeBaseRecord, true);
|
|
32
|
+
const custom = rawCustom?.bind(transport);
|
|
33
|
+
if (custom) provider.custom = async params => decodeCustomResult(await custom(params), params.method !== 'get');
|
|
34
|
+
return provider;
|
|
35
|
+
}
|