@svgrid/enterprise 2.0.3 → 2.2.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 +18 -6
- package/dist/cdn/svgrid-enterprise.svelte-external.js +14025 -6836
- package/dist/node/studio.js +7889 -2460
- package/package.json +9 -4
- package/src/SvGridMasterDetail.svelte +24 -3
- package/src/SvGridScheduler.svelte +4410 -0
- package/src/SvPivotDesigner.svelte +1990 -1045
- package/src/SvSchemaChart.svelte +10 -9
- package/src/ai.test.ts +522 -522
- package/src/ai.ts +202 -2
- package/src/index.ts +409 -384
- package/src/install.ts +10 -0
- package/src/pivot-chart.test.ts +86 -0
- package/src/pivot-chart.ts +112 -0
- package/src/scheduler.ts +37 -0
- package/src/scheduling.test.ts +194 -0
- package/src/scheduling.ts +293 -0
- package/src/sources/filters.ts +6 -0
- package/src/studio/HANDLERS-DESIGN.md +142 -0
- package/src/studio/cli.ts +7 -2
- package/src/studio/emit-project.test.ts +1447 -13
- package/src/studio/emit-project.ts +3995 -1273
- package/src/studio/emit-schema.ts +146 -29
- package/src/studio/index.ts +320 -195
- package/src/studio/project.test.ts +370 -0
- package/src/studio/project.ts +1146 -26
- package/src/studio/sample-data.ts +4 -1
- package/src/studio/samples/ats.ts +2 -2
- package/src/studio/samples/clinic.ts +4 -2
- package/src/studio/samples/crm.ts +16 -8
- package/src/studio/samples/events.ts +4 -2
- package/src/studio/samples/fleet.ts +4 -2
- package/src/studio/samples/gym.ts +4 -2
- package/src/studio/samples/hr.ts +3 -1
- package/src/studio/samples/live-data.ts +308 -308
- package/src/studio/samples/projects.ts +2 -2
- package/src/studio/samples/restaurant.ts +4 -2
- package/src/studio/samples/samples.test.ts +13 -5
- package/src/studio/samples/shared.ts +346 -305
- package/src/studio/samples/support.ts +3 -1
- package/src/studio/scaffold.test.ts +15 -1
- package/src/studio/scaffold.ts +16 -0
- package/src/studio/themes.ts +7 -0
- package/src/studio/ui-components.ts +472 -0
- package/src/sveltekit/transport.test.ts +26 -0
- package/src/sveltekit/transport.ts +50 -5
- package/dist/designer/assets/index-Dp44bTid.js +0 -939
- package/dist/designer/assets/index-RJp6x8tw.css +0 -1
- package/dist/designer/assets/jszip.min-CjMo-QGg.js +0 -2
- package/dist/designer/index.html +0 -13
|
@@ -139,6 +139,28 @@ export type KitHandlerOptions<TData extends RowData> = {
|
|
|
139
139
|
* logged, so an audit-sink hiccup never fails the underlying write.
|
|
140
140
|
*/
|
|
141
141
|
audit?: (entry: KitAuditEntry<TData>) => void | Promise<void>
|
|
142
|
+
/**
|
|
143
|
+
* Business-logic lifecycle hooks, run server-side around each mutation (the
|
|
144
|
+
* Studio generator compiles a screen's visual "triggers" into these). A
|
|
145
|
+
* `before*` hook may transform the payload (return a new object) or throw to
|
|
146
|
+
* reject the write (-> `422 { error }`), so a rule enforced here holds even if
|
|
147
|
+
* the client skips it. `after*` hooks run once the write succeeds (cascades,
|
|
148
|
+
* notifications) - a throw there fails the request (`500`).
|
|
149
|
+
*/
|
|
150
|
+
hooks?: KitHooks<TData>
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** The SvelteKit event slice handed to every hook (read `locals` for the actor). */
|
|
154
|
+
export type KitEvent = { request: Request; locals?: Record<string, unknown> }
|
|
155
|
+
|
|
156
|
+
/** Server-side mutation lifecycle hooks (see `KitHandlerOptions.hooks`). */
|
|
157
|
+
export type KitHooks<TData extends RowData> = {
|
|
158
|
+
beforeCreate?: (ctx: { values: Partial<TData>; event: KitEvent }) => Partial<TData> | void | Promise<Partial<TData> | void>
|
|
159
|
+
afterCreate?: (ctx: { row: TData; event: KitEvent }) => void | Promise<void>
|
|
160
|
+
beforeUpdate?: (ctx: { id: string; patch: Partial<TData>; event: KitEvent }) => Partial<TData> | void | Promise<Partial<TData> | void>
|
|
161
|
+
afterUpdate?: (ctx: { id: string; row: TData; event: KitEvent }) => void | Promise<void>
|
|
162
|
+
beforeDelete?: (ctx: { id: string; event: KitEvent }) => void | Promise<void>
|
|
163
|
+
afterDelete?: (ctx: { id: string; event: KitEvent }) => void | Promise<void>
|
|
142
164
|
}
|
|
143
165
|
|
|
144
166
|
/** The change details handed to the `audit` hook after a successful mutation. */
|
|
@@ -166,7 +188,7 @@ const actionOf = <TData extends RowData>(msg: KitMessage<TData>): KitAction =>
|
|
|
166
188
|
export function createKitHandlers<TData extends RowData>(
|
|
167
189
|
options: KitHandlerOptions<TData>,
|
|
168
190
|
): KitHandlers {
|
|
169
|
-
const { source, authorize, validate, audit } = options
|
|
191
|
+
const { source, authorize, validate, audit, hooks } = options
|
|
170
192
|
const idField = options.schema.idField ?? options.schema.fields.find((f) => f.primaryKey)?.field ?? 'id'
|
|
171
193
|
const rowId = (row: unknown): string | null => {
|
|
172
194
|
const v = (row as Record<string, unknown> | null)?.[idField]
|
|
@@ -222,21 +244,44 @@ export function createKitHandlers<TData extends RowData>(
|
|
|
222
244
|
}
|
|
223
245
|
if (msg.kind === 'mutate') {
|
|
224
246
|
const evt = { request, locals: event?.locals }
|
|
247
|
+
// A `before*` hook throwing is a business-rule rejection -> 422 (not a 500).
|
|
248
|
+
const runBefore = async <R>(fn: () => Promise<R>): Promise<R | Response> => {
|
|
249
|
+
try { return await fn() } catch (err) { return jsonResponse({ error: err instanceof Error ? err.message : 'rejected' }, 422) }
|
|
250
|
+
}
|
|
225
251
|
if (msg.op === 'create') {
|
|
226
252
|
if (!source.createRow) return jsonResponse({ error: 'create not supported' }, 405)
|
|
227
|
-
|
|
228
|
-
|
|
253
|
+
let input = msg.input
|
|
254
|
+
if (hooks?.beforeCreate) {
|
|
255
|
+
const r = await runBefore(() => Promise.resolve(hooks.beforeCreate!({ values: input, event: evt })))
|
|
256
|
+
if (r instanceof Response) return r
|
|
257
|
+
if (r) input = r as Partial<TData>
|
|
258
|
+
}
|
|
259
|
+
const row = await source.createRow(input)
|
|
260
|
+
if (hooks?.afterCreate) await hooks.afterCreate({ row, event: evt })
|
|
261
|
+
await fireAudit({ action: 'create', id: rowId(row), values: input, result: row, event: evt })
|
|
229
262
|
return jsonResponse(row)
|
|
230
263
|
}
|
|
231
264
|
if (msg.op === 'update') {
|
|
232
265
|
if (!source.updateRow) return jsonResponse({ error: 'update not supported' }, 405)
|
|
233
|
-
|
|
234
|
-
|
|
266
|
+
let patch = msg.patch
|
|
267
|
+
if (hooks?.beforeUpdate) {
|
|
268
|
+
const r = await runBefore(() => Promise.resolve(hooks.beforeUpdate!({ id: msg.id, patch, event: evt })))
|
|
269
|
+
if (r instanceof Response) return r
|
|
270
|
+
if (r) patch = r as Partial<TData>
|
|
271
|
+
}
|
|
272
|
+
const row = await source.updateRow(msg.id, patch)
|
|
273
|
+
if (hooks?.afterUpdate) await hooks.afterUpdate({ id: msg.id, row, event: evt })
|
|
274
|
+
await fireAudit({ action: 'update', id: msg.id, values: patch, result: row, event: evt })
|
|
235
275
|
return jsonResponse(row)
|
|
236
276
|
}
|
|
237
277
|
if (msg.op === 'delete') {
|
|
238
278
|
if (!source.deleteRow) return jsonResponse({ error: 'delete not supported' }, 405)
|
|
279
|
+
if (hooks?.beforeDelete) {
|
|
280
|
+
const r = await runBefore(() => Promise.resolve(hooks.beforeDelete!({ id: msg.id, event: evt })))
|
|
281
|
+
if (r instanceof Response) return r
|
|
282
|
+
}
|
|
239
283
|
await source.deleteRow(msg.id)
|
|
284
|
+
if (hooks?.afterDelete) await hooks.afterDelete({ id: msg.id, event: evt })
|
|
240
285
|
await fireAudit({ action: 'delete', id: msg.id, event: evt })
|
|
241
286
|
return jsonResponse({ ok: true })
|
|
242
287
|
}
|