@svadmin/core 0.50.0 → 0.53.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.
Files changed (98) hide show
  1. package/package.json +21 -4
  2. package/src/access-control-contract.ts +250 -0
  3. package/src/adapters/casbin.ts +20 -14
  4. package/src/adapters/casl.ts +22 -19
  5. package/src/admin-tool.ts +7 -7
  6. package/src/audit-contract.ts +105 -0
  7. package/src/audit.ts +88 -128
  8. package/src/auth-hooks.svelte.ts +593 -261
  9. package/src/auth-mutation-contract.ts +143 -0
  10. package/src/auth-query-contract.ts +97 -0
  11. package/src/captured-mutation.svelte.ts +45 -0
  12. package/src/chatProvider.svelte.ts +1 -1
  13. package/src/command-contract.ts +195 -0
  14. package/src/context.svelte.ts +217 -205
  15. package/src/contract-context.svelte.ts +37 -0
  16. package/src/contract-form.svelte.ts +566 -0
  17. package/src/create-contract.ts +56 -0
  18. package/src/create-hooks.svelte.ts +347 -0
  19. package/src/create-many-contract.ts +90 -0
  20. package/src/create-many-hooks.svelte.ts +396 -0
  21. package/src/data-transfer.svelte.ts +1 -215
  22. package/src/defined-options.ts +50 -0
  23. package/src/delete-contract.ts +50 -0
  24. package/src/delete-hooks.svelte.ts +403 -0
  25. package/src/delete-many-contract.ts +60 -0
  26. package/src/delete-many-hooks.svelte.ts +402 -0
  27. package/src/enterprise.ts +4 -4
  28. package/src/export-format.ts +16 -14
  29. package/src/export-hooks.svelte.ts +191 -0
  30. package/src/feature-gate-contract.ts +53 -0
  31. package/src/hook-utils.svelte.ts +53 -105
  32. package/src/hooks.svelte.ts +19 -776
  33. package/src/http.ts +88 -87
  34. package/src/i18n.svelte.ts +64 -2
  35. package/src/import-contract.ts +106 -0
  36. package/src/import-hooks.svelte.ts +310 -0
  37. package/src/index.ts +77 -30
  38. package/src/inferencer-graphql.ts +7 -6
  39. package/src/inferencer.ts +9 -0
  40. package/src/infinite-query.svelte.ts +228 -0
  41. package/src/invalidation-contract.ts +26 -0
  42. package/src/live-sse.ts +91 -127
  43. package/src/live-subscription.svelte.ts +130 -0
  44. package/src/live-transport.ts +127 -0
  45. package/src/live-websocket.ts +85 -118
  46. package/src/live.svelte.ts +66 -68
  47. package/src/notification.svelte.ts +17 -15
  48. package/src/permission-catalog.ts +121 -0
  49. package/src/permission-hints-contract.ts +48 -0
  50. package/src/permissions.svelte.ts +112 -59
  51. package/src/plain-data.ts +61 -0
  52. package/src/provider-bundle.ts +85 -79
  53. package/src/provider-response.ts +35 -0
  54. package/src/query-hooks.svelte.ts +241 -288
  55. package/src/query-invalidation.ts +172 -0
  56. package/src/query-keys.ts +221 -110
  57. package/src/query-session.svelte.ts +220 -0
  58. package/src/query-snapshot.ts +144 -0
  59. package/src/reactive-projection.ts +53 -0
  60. package/src/record-decoder.ts +63 -0
  61. package/src/resource-binding.svelte.ts +19 -0
  62. package/src/resource-contract.ts +595 -0
  63. package/src/resource-mutation-types.ts +30 -0
  64. package/src/resource-schemas.ts +371 -0
  65. package/src/resource-transport-config.ts +156 -0
  66. package/src/route-parsing.ts +11 -11
  67. package/src/router-provider.ts +64 -59
  68. package/src/router.ts +8 -3
  69. package/src/routing-hooks.svelte.ts +90 -62
  70. package/src/schema-validation.ts +92 -0
  71. package/src/schema.ts +13 -0
  72. package/src/select-options.ts +95 -0
  73. package/src/select-query.svelte.ts +191 -0
  74. package/src/session-query.svelte.ts +103 -0
  75. package/src/strict-command-hooks.svelte.ts +333 -0
  76. package/src/strict-hooks.svelte.ts +198 -0
  77. package/src/table-hooks.svelte.ts +180 -174
  78. package/src/table-state.svelte.ts +117 -117
  79. package/src/task-contract.ts +87 -0
  80. package/src/task-hooks.svelte.ts +190 -180
  81. package/src/task-provider.ts +115 -0
  82. package/src/task-subscription.ts +132 -0
  83. package/src/theme.svelte.ts +155 -154
  84. package/src/toast.svelte.ts +38 -37
  85. package/src/types.ts +68 -110
  86. package/src/unsafe.ts +7 -0
  87. package/src/update-contract.ts +65 -0
  88. package/src/update-hooks.svelte.ts +347 -0
  89. package/src/update-many-contract.ts +79 -0
  90. package/src/update-many-hooks.svelte.ts +373 -0
  91. package/src/url-sync.ts +11 -10
  92. package/src/useCan.ts +96 -31
  93. package/src/useParsed.svelte.ts +18 -14
  94. package/src/useStepsForm.svelte.ts +123 -63
  95. package/src/utility-hooks.svelte.ts +101 -158
  96. package/src/form-hooks.svelte.ts +0 -889
  97. package/src/mutation-hooks.svelte.ts +0 -561
  98. package/src/schema-form.ts +0 -31
@@ -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 = DataProvider | Record<string, DataProvider>;
44
+ export type DataProviderInput=DataProvider|Record<string,DataProvider>;
44
45
 
45
46
  /**
46
- * 一等 Provider 配置对象。字段名与现有独立 provider API 保持一致,
47
- * 因此调用方可以渐进地从多个 props 迁移为一个 typed bundle。
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 | null;
52
- readonly accessControlProvider?: AccessControlProvider | null;
52
+ readonly authProvider?: AuthProvider|null;
53
+ readonly accessControlProvider?: AccessControlProvider|null;
53
54
  readonly liveProvider?: LiveProvider;
54
- readonly auditLogProvider?: AuditLogProvider | null;
55
- readonly notificationProvider?: NotificationProvider | null;
56
- readonly chatProvider?: ChatProvider | null;
57
- readonly agentProvider?: AgentProvider | null;
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 | null;
62
- readonly identityGovernanceProvider?: IdentityGovernanceProvider | null;
63
- readonly sessionProvider?: SessionProvider | null;
64
- readonly credentialProvider?: CredentialProvider | null;
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
- /** 保留传入对象的精确类型,同时执行 ProviderBundle 的编译期校验。 */
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 = string | number;
73
- export type TenantCacheIdentity = string | number;
73
+ export type TenantId=string|number;
74
+ export type TenantCacheIdentity=string|number;
74
75
 
75
- /** 请求树内的租户状态;不得通过模块级变量在 SSR 请求之间共享。 */
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, unknown>>;
79
+ readonly meta?: Readonly<Record<string,unknown>>;
79
80
  }
80
81
 
81
82
  /**
82
- * 将标准 TenantContext 投影到具体后端所需的 provider meta 与 cache identity。
83
- * Core 始终额外写入标准 `tenantId`,adapter 不能覆盖该字段。
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, unknown>>;
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 = defaultTenantAdapter,
102
- ): Record<string, unknown> {
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 = defaultTenantAdapter,
113
+ adapter: TenantAdapter=defaultTenantAdapter,
113
114
  ): TenantCacheKey {
114
115
  return {
115
- __svadminTenant: adapter.getCacheIdentity?.(tenant) ?? tenant.tenantId,
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
- && typeof value === 'object'
123
- && Object.keys(value).length === 1
124
- && '__svadminTenant' in value
125
- && (typeof value.__svadminTenant === 'string' || typeof value.__svadminTenant === 'number'),
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 | undefined,
136
+ tenantCacheKey: TenantCacheKey|undefined,
136
137
  ): readonly unknown[] {
137
- return tenantCacheKey ? [...queryKey, tenantCacheKey] : queryKey;
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 | undefined,
147
+ tenantCacheKey: TenantCacheKey|undefined,
147
148
  ): boolean {
148
- const candidate = queryKey.at(-1);
149
- const keyTenant = isTenantCacheKey(candidate) ? candidate : undefined;
150
- if (!tenantCacheKey) return keyTenant === undefined;
151
- return keyTenant?.__svadminTenant === tenantCacheKey.__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, unknown>;
157
+ readonly meta?: Record<string,unknown>;
157
158
  }
158
159
 
159
- export type ProviderMetaResolver = (input: ProviderMetaInput) => Record<string, unknown> | undefined;
160
+ export type ProviderMetaResolver=(input: ProviderMetaInput) => Record<string,unknown>|undefined;
160
161
 
161
162
  /**
162
- * 为 DataProvider 的所有标准操作增量注入 meta;未声明的扩展字段仍由原 provider 保留。
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, resolveMeta: ProviderMetaResolver): DataProvider {
165
- const adapted: DataProvider = {
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: <TData extends BaseRecord = BaseRecord>(params: GetListParams): Promise<GetListResult<TData>> =>
168
- provider.getList<TData>({ ...params, meta: resolveMeta(params) }),
169
- getOne: <TData extends BaseRecord = BaseRecord>(params: GetOneParams): Promise<GetOneResult<TData>> =>
170
- provider.getOne<TData>({ ...params, meta: resolveMeta(params) }),
171
- create: <TData extends BaseRecord = BaseRecord, TVariables = unknown>(params: CreateParams<TVariables>): Promise<CreateResult<TData>> =>
172
- provider.create<TData, TVariables>({ ...params, meta: resolveMeta(params) }),
173
- update: <TData extends BaseRecord = BaseRecord, TVariables = unknown>(params: UpdateParams<TVariables>): Promise<UpdateResult<TData>> =>
174
- provider.update<TData, TVariables>({ ...params, meta: resolveMeta(params) }),
175
- deleteOne: <TData extends BaseRecord = BaseRecord, TVariables = unknown>(params: DeleteParams<TVariables>): Promise<DeleteResult<TData>> =>
176
- provider.deleteOne<TData, TVariables>({ ...params, meta: resolveMeta(params) }),
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 = provider.getMany?.bind(provider);
181
- if (getMany) {
182
- adapted.getMany = <TData extends BaseRecord = BaseRecord>(params: GetManyParams): Promise<GetManyResult<TData>> =>
183
- getMany<TData>({ ...params, meta: resolveMeta(params) });
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 = provider.createMany?.bind(provider);
186
- if (createMany) {
187
- adapted.createMany = <TData extends BaseRecord = BaseRecord, TVariables = unknown>(params: CreateManyParams<TVariables>): Promise<CreateManyResult<TData>> =>
188
- createMany<TData, TVariables>({ ...params, meta: resolveMeta(params) });
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 = provider.updateMany?.bind(provider);
191
- if (updateMany) {
192
- adapted.updateMany = <TData extends BaseRecord = BaseRecord, TVariables = unknown>(params: UpdateManyParams<TVariables>): Promise<UpdateManyResult<TData>> =>
193
- updateMany<TData, TVariables>({ ...params, meta: resolveMeta(params) });
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 = provider.deleteMany?.bind(provider);
196
- if (deleteMany) {
197
- adapted.deleteMany = <TData extends BaseRecord = BaseRecord, TVariables = unknown>(params: DeleteManyParams<TVariables>): Promise<DeleteManyResult<TData>> =>
198
- deleteMany<TData, TVariables>({ ...params, meta: resolveMeta(params) });
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 = provider.custom?.bind(provider);
201
- if (custom) {
202
- adapted.custom = <TData = unknown, TVariables = unknown>(params: CustomParams<TVariables>): Promise<CustomResult<TData>> =>
203
- custom<TData, TVariables>({ ...params, meta: resolveMeta(params) });
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 = defaultTenantAdapter,
218
+ adapter: TenantAdapter=defaultTenantAdapter,
213
219
  ): DataProvider {
214
- return withProviderMeta(provider, ({ meta }) => ({
215
- ...(meta ?? {}),
216
- ...resolveTenantProviderMeta(tenant, adapter),
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
+ }