@persei/perseed-api 5.0.21 → 5.0.22
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/dist/client.cjs +678 -37
- package/dist/client.cjs.map +1 -1
- package/dist/client.mjs +678 -37
- package/dist/client.mjs.map +1 -1
- package/dist/{lib-CDM5aEJG.cjs → dates.helper-CDM5aEJG.cjs} +1 -1
- package/dist/dates.helper-CDM5aEJG.cjs.map +1 -0
- package/dist/{lib-XRCJ_UAQ.js → dates.helper-XRCJ_UAQ.js} +1 -1
- package/dist/dates.helper-XRCJ_UAQ.js.map +1 -0
- package/dist/ecosystem-action-system.cjs +42 -32
- package/dist/ecosystem-action-system.cjs.map +1 -1
- package/dist/ecosystem-action-system.mjs +42 -32
- package/dist/ecosystem-action-system.mjs.map +1 -1
- package/dist/ecosystem-container-widget.cjs +6 -6
- package/dist/ecosystem-container-widget.cjs.map +1 -1
- package/dist/ecosystem-container-widget.mjs +1 -1
- package/dist/global-dates.cjs +9 -3
- package/dist/global-dates.cjs.map +1 -1
- package/dist/global-dates.mjs +7 -1
- package/dist/global-dates.mjs.map +1 -1
- package/dist/lib/src/generated/client.d.ts +5338 -879
- package/dist/lib/src/generated/client.d.ts.map +1 -1
- package/dist/src/Services/TriggerService.d.ts.map +1 -1
- package/dist/src/config/apiPublicKey.d.ts +19 -4
- package/dist/src/config/apiPublicKey.d.ts.map +1 -1
- package/dist/src/config/apiPublicKey.test.d.ts +2 -0
- package/dist/src/config/apiPublicKey.test.d.ts.map +1 -0
- package/dist/src/constants/SeedPrivilege.d.ts +1 -0
- package/dist/src/constants/SeedPrivilege.d.ts.map +1 -1
- package/dist/src/controllers/IntrospectionController.d.ts.map +1 -1
- package/dist/src/controllers/UserController.d.ts.map +1 -1
- package/dist/src/schemas/container_widget/create_widget_payload.schema.json +1 -1
- package/dist/src/schemas/container_widget/update_widget_payload.schema.json +1 -1
- package/dist/src/schemas/container_widget/widgetTypes/PatientListProps.d.ts +2 -1
- package/dist/src/schemas/container_widget/widgetTypes/PatientListProps.d.ts.map +1 -1
- package/dist/src/test/e2e-permissions.d.ts +32 -3
- package/dist/src/test/e2e-permissions.d.ts.map +1 -1
- package/dist/src/test/test-helpers.d.ts +12 -0
- package/dist/src/test/test-helpers.d.ts.map +1 -1
- package/dist/src/v2/modules/ecosystem/action-system/action-system.helper/action-system.helper.d.ts +1 -1
- package/dist/src/v2/modules/ecosystem/action-system/action-system.helper/action-system.helper.d.ts.map +1 -1
- package/dist/src/v2/modules/ecosystem/action-system/action-system.helper/manual-trigger.helper.d.ts.map +1 -1
- package/dist/src/v2/modules/ecosystem/action-system/action-system.model.d.ts +5 -2
- package/dist/src/v2/modules/ecosystem/action-system/action-system.model.d.ts.map +1 -1
- package/dist/src/v2/modules/ecosystem/action-system/action-system.model.test.d.ts +2 -0
- package/dist/src/v2/modules/ecosystem/action-system/action-system.model.test.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/action-system/action-system.schema.d.ts +69 -58
- package/dist/src/v2/modules/ecosystem/action-system/action-system.schema.d.ts.map +1 -1
- package/dist/src/v2/modules/ecosystem/action-system/action-system.service/automatic-trigger.service.d.ts +14 -6
- package/dist/src/v2/modules/ecosystem/action-system/action-system.service/automatic-trigger.service.d.ts.map +1 -1
- package/dist/src/v2/modules/ecosystem/action-system/action-system.service/manual-trigger.service.d.ts +18 -10
- package/dist/src/v2/modules/ecosystem/action-system/action-system.service/manual-trigger.service.d.ts.map +1 -1
- package/dist/src/v2/modules/ecosystem/action-system/action-system.service/trigger-engine.service.d.ts +1 -0
- package/dist/src/v2/modules/ecosystem/action-system/action-system.service/trigger-engine.service.d.ts.map +1 -1
- package/dist/src/v2/modules/ecosystem/alert/alert.controller.d.ts +12 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.controller.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.controller.e2e.d.ts +2 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.controller.e2e.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.controller.test.d.ts +2 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.controller.test.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.helper.d.ts +55 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.helper.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.helper.test.d.ts +2 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.helper.test.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.model.d.ts +61 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.model.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.model.test.d.ts +2 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.model.test.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.schema.d.ts +134 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.schema.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.service.d.ts +73 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.service.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.service.test.d.ts +2 -0
- package/dist/src/v2/modules/ecosystem/alert/alert.service.test.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert/index.d.ts +7 -0
- package/dist/src/v2/modules/ecosystem/alert/index.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.controller.d.ts +6 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.controller.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.controller.e2e.d.ts +2 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.controller.e2e.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.controller.test.d.ts +2 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.controller.test.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.helper.d.ts +14 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.helper.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.helper.test.d.ts +2 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.helper.test.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.model.d.ts +18 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.model.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.schema.d.ts +29 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.schema.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.service.d.ts +38 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.service.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.service.test.d.ts +2 -0
- package/dist/src/v2/modules/ecosystem/alert-type/alert-type.service.test.d.ts.map +1 -0
- package/dist/src/v2/modules/ecosystem/alert-type/index.d.ts +6 -0
- package/dist/src/v2/modules/ecosystem/alert-type/index.d.ts.map +1 -0
- package/dist/src/v2/modules/global/auth-token/auth-token.controller.d.ts.map +1 -1
- package/dist/src/v2/modules/global/dates/dates.helper.d.ts.map +1 -1
- package/dist/src/v2/modules/global/dates/dates.schema.d.ts +39 -0
- package/dist/src/v2/modules/global/dates/dates.schema.d.ts.map +1 -1
- package/dist/src/v2/modules/global/permissions/index.d.ts +2 -0
- package/dist/src/v2/modules/global/permissions/index.d.ts.map +1 -0
- package/dist/src/v2/modules/global/permissions/permissions.helper.d.ts +32 -0
- package/dist/src/v2/modules/global/permissions/permissions.helper.d.ts.map +1 -0
- package/dist/src/v2/modules/global/permissions/permissions.helper.test.d.ts +2 -0
- package/dist/src/v2/modules/global/permissions/permissions.helper.test.d.ts.map +1 -0
- package/dist/src/v2/routes/v2/ecosystem/_ecosystemName/alert/index.d.ts +3 -0
- package/dist/src/v2/routes/v2/ecosystem/_ecosystemName/alert/index.d.ts.map +1 -0
- package/dist/src/v2/routes/v2/ecosystem/_ecosystemName/alert-type/index.d.ts +3 -0
- package/dist/src/v2/routes/v2/ecosystem/_ecosystemName/alert-type/index.d.ts.map +1 -0
- package/dist/src/v2/routes/v2/ecosystem/_ecosystemName/project/_projectName/alert/index.d.ts +3 -0
- package/dist/src/v2/routes/v2/ecosystem/_ecosystemName/project/_projectName/alert/index.d.ts.map +1 -0
- package/dist/v1/perseed-api.cjs +2 -0
- package/dist/v1/perseed-api.cjs.map +1 -1
- package/dist/v1/perseed-api.mjs +2 -0
- package/dist/v1/perseed-api.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/lib-CDM5aEJG.cjs.map +0 -1
- package/dist/lib-XRCJ_UAQ.js.map +0 -1
- package/dist/src/test/global-setup.d.ts +0 -2
- package/dist/src/test/global-setup.d.ts.map +0 -1
|
@@ -24,7 +24,6 @@ const conditionsFormatSchema = Type.Enum(Object.values(ConditionsFormat), {
|
|
|
24
24
|
});
|
|
25
25
|
const TriggerSource = {
|
|
26
26
|
MODEL: "model",
|
|
27
|
-
DESIGNER: "designer",
|
|
28
27
|
DISPLAYER: "displayer"
|
|
29
28
|
};
|
|
30
29
|
const triggerSourceSchema = Type.Enum(Object.values(TriggerSource), {
|
|
@@ -148,12 +147,19 @@ const actionSchema = Type.Object(
|
|
|
148
147
|
description: "Project this action belongs to. Null when it is ecosystem-wide"
|
|
149
148
|
})
|
|
150
149
|
),
|
|
151
|
-
|
|
150
|
+
// Optional for the same reason as the trigger's: it labels the action where it is
|
|
151
|
+
// administered, and nothing renders it to whoever fires the trigger, so requiring it only
|
|
152
|
+
// forced a placeholder. Null when it has none.
|
|
153
|
+
name: Type.Optional(
|
|
154
|
+
Type.Union([Type.String({ maxLength: 255 }), Type.Null()], {
|
|
155
|
+
description: "Human-readable label of the action. Null when it has none"
|
|
156
|
+
})
|
|
157
|
+
),
|
|
152
158
|
type: actionTypeSchema,
|
|
153
159
|
config: actionConfigSchema,
|
|
154
160
|
retention_days: Type.Optional(
|
|
155
161
|
Type.Union([Type.Number(), Type.Null()], {
|
|
156
|
-
description: "Retention in days for the process_log entries of this action
|
|
162
|
+
description: "Retention in days for the process_log entries of this action. Null = inherit (use the firing trigger retention; never if the trigger is null/0); 0 = disabled (never expires, regardless of the trigger); N (>0) = enabled (expires N days after write)."
|
|
157
163
|
})
|
|
158
164
|
),
|
|
159
165
|
// Mirrors `dashboard_widget.library`. False by default, so an action created
|
|
@@ -201,6 +207,16 @@ const triggerActionInputSchema = Type.Union(
|
|
|
201
207
|
const triggerActionsInputSchema = Type.Array(triggerActionInputSchema, {
|
|
202
208
|
description: "Full ordered set of actions the trigger runs; the array position is the execution order. An empty array leaves the trigger with none"
|
|
203
209
|
});
|
|
210
|
+
const triggerNameSchema = Type.Optional(
|
|
211
|
+
Type.Union([Type.String({ maxLength: 255 }), Type.Null()], {
|
|
212
|
+
description: "Human-readable label of the trigger. Null when it has none"
|
|
213
|
+
})
|
|
214
|
+
);
|
|
215
|
+
const triggerDescriptionSchema = Type.Optional(
|
|
216
|
+
Type.Union([Type.String(), Type.Null()], {
|
|
217
|
+
description: "What the trigger is for, for whoever administers it. Null when it has none"
|
|
218
|
+
})
|
|
219
|
+
);
|
|
204
220
|
const automaticTriggerSchema = Type.Object(
|
|
205
221
|
{
|
|
206
222
|
id: Type.Number({ description: "Unique identifier of the automatic trigger" }),
|
|
@@ -209,6 +225,8 @@ const automaticTriggerSchema = Type.Object(
|
|
|
209
225
|
description: "Project this trigger belongs to. Null when it is ecosystem-wide"
|
|
210
226
|
})
|
|
211
227
|
),
|
|
228
|
+
name: triggerNameSchema,
|
|
229
|
+
description: triggerDescriptionSchema,
|
|
212
230
|
source: triggerSourceSchema,
|
|
213
231
|
target: Type.Optional(
|
|
214
232
|
Type.Union([Type.String({ maxLength: 255 }), Type.Null()], {
|
|
@@ -233,7 +251,7 @@ const automaticTriggerSchema = Type.Object(
|
|
|
233
251
|
enabled: Type.Optional(Type.Boolean({ description: "Whether the trigger is active" })),
|
|
234
252
|
retention_days: Type.Optional(
|
|
235
253
|
Type.Union([Type.Number(), Type.Null()], {
|
|
236
|
-
description: "Retention in days for the process_log entries of this trigger
|
|
254
|
+
description: "Retention in days for the process_log entries of this trigger. Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write)."
|
|
237
255
|
})
|
|
238
256
|
)
|
|
239
257
|
},
|
|
@@ -252,6 +270,8 @@ const includeActionsQuerySchema = Type.Object(
|
|
|
252
270
|
);
|
|
253
271
|
const createAutomaticTriggerSchema = Type.Object(
|
|
254
272
|
{
|
|
273
|
+
name: triggerNameSchema,
|
|
274
|
+
description: triggerDescriptionSchema,
|
|
255
275
|
source: triggerSourceSchema,
|
|
256
276
|
target: Type.Optional(
|
|
257
277
|
Type.Union([Type.String({ maxLength: 255 }), Type.Null()], {
|
|
@@ -276,7 +296,7 @@ const createAutomaticTriggerSchema = Type.Object(
|
|
|
276
296
|
enabled: Type.Optional(Type.Boolean({ description: "Whether the trigger is active" })),
|
|
277
297
|
retention_days: Type.Optional(
|
|
278
298
|
Type.Union([Type.Number(), Type.Null()], {
|
|
279
|
-
description: "Retention in days for the process_log entries of this trigger
|
|
299
|
+
description: "Retention in days for the process_log entries of this trigger. Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write)."
|
|
280
300
|
})
|
|
281
301
|
),
|
|
282
302
|
actions: Type.Optional(triggerActionsInputSchema)
|
|
@@ -311,6 +331,11 @@ const iconPositionSchema = Type.Enum(Object.values(IconPosition), {
|
|
|
311
331
|
const manualTriggerButtonStyleSchema = Type.Object(
|
|
312
332
|
{
|
|
313
333
|
text: Type.Optional(Type.String({ description: "Handlebars template rendered as the button label" })),
|
|
334
|
+
tooltip: Type.Optional(
|
|
335
|
+
Type.String({
|
|
336
|
+
description: "Handlebars template rendered as the button tooltip. Written for whoever uses the listing, unlike `name`, which labels the trigger where it is administered"
|
|
337
|
+
})
|
|
338
|
+
),
|
|
314
339
|
textColor: Type.Optional(Type.String({ description: "Label colour" })),
|
|
315
340
|
bgColor: Type.Optional(Type.String({ description: "Background colour" })),
|
|
316
341
|
icon: Type.Optional(Type.String({ description: "Icon name, resolved by the consumer" })),
|
|
@@ -350,7 +375,8 @@ const manualTriggerSchema = Type.Object(
|
|
|
350
375
|
description: "Project this trigger belongs to. Null when it is ecosystem-wide"
|
|
351
376
|
})
|
|
352
377
|
),
|
|
353
|
-
name:
|
|
378
|
+
name: triggerNameSchema,
|
|
379
|
+
description: triggerDescriptionSchema,
|
|
354
380
|
source: triggerSourceSchema,
|
|
355
381
|
target: Type.String({
|
|
356
382
|
maxLength: 255,
|
|
@@ -369,7 +395,7 @@ const manualTriggerSchema = Type.Object(
|
|
|
369
395
|
enabled: Type.Optional(Type.Boolean({ description: "Whether the trigger is active" })),
|
|
370
396
|
retention_days: Type.Optional(
|
|
371
397
|
Type.Union([Type.Number(), Type.Null()], {
|
|
372
|
-
description: "Retention in days for the process_log entries of this trigger
|
|
398
|
+
description: "Retention in days for the process_log entries of this trigger. Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write)."
|
|
373
399
|
})
|
|
374
400
|
),
|
|
375
401
|
config: Type.Optional(
|
|
@@ -382,7 +408,8 @@ const manualTriggerSchema = Type.Object(
|
|
|
382
408
|
);
|
|
383
409
|
const createManualTriggerSchema = Type.Object(
|
|
384
410
|
{
|
|
385
|
-
name:
|
|
411
|
+
name: triggerNameSchema,
|
|
412
|
+
description: triggerDescriptionSchema,
|
|
386
413
|
source: triggerSourceSchema,
|
|
387
414
|
target: Type.String({
|
|
388
415
|
maxLength: 255,
|
|
@@ -401,7 +428,7 @@ const createManualTriggerSchema = Type.Object(
|
|
|
401
428
|
enabled: Type.Optional(Type.Boolean({ description: "Whether the trigger is active" })),
|
|
402
429
|
retention_days: Type.Optional(
|
|
403
430
|
Type.Union([Type.Number(), Type.Null()], {
|
|
404
|
-
description: "Retention in days for the process_log entries of this trigger
|
|
431
|
+
description: "Retention in days for the process_log entries of this trigger. Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write)."
|
|
405
432
|
})
|
|
406
433
|
),
|
|
407
434
|
config: Type.Optional(
|
|
@@ -425,7 +452,7 @@ const updateManualTriggerSchema = Type.Partial(createManualTriggerSchema, {
|
|
|
425
452
|
const manualTriggerAvailabilitySchema = Type.Object(
|
|
426
453
|
{
|
|
427
454
|
id: Type.Number({ description: "Identifier of the manual trigger" }),
|
|
428
|
-
name:
|
|
455
|
+
name: triggerNameSchema,
|
|
429
456
|
matched: Type.Boolean({
|
|
430
457
|
description: "Whether the trigger conditions hold for this entity right now. A trigger with no conditions always matches. False means the button is shown disabled: triggers configured to hide when unmatched are not listed"
|
|
431
458
|
}),
|
|
@@ -476,14 +503,6 @@ const automaticTriggerActionSchema = Type.Object(
|
|
|
476
503
|
},
|
|
477
504
|
{ title: "AutomaticTriggerAction", description: "Ordered link between an automatic trigger and an action" }
|
|
478
505
|
);
|
|
479
|
-
const createAutomaticTriggerActionSchema = Type.Omit(automaticTriggerActionSchema, ["id"], {
|
|
480
|
-
title: "CreateAutomaticTriggerAction",
|
|
481
|
-
description: "Row shape of an automatic trigger-action link"
|
|
482
|
-
});
|
|
483
|
-
const updateAutomaticTriggerActionSchema = Type.Partial(createAutomaticTriggerActionSchema, {
|
|
484
|
-
title: "UpdateAutomaticTriggerAction",
|
|
485
|
-
description: "Partial row shape of an automatic trigger-action link"
|
|
486
|
-
});
|
|
487
506
|
const manualTriggerActionSchema = Type.Object(
|
|
488
507
|
{
|
|
489
508
|
id: Type.Number({ description: "Unique identifier of the trigger-action link" }),
|
|
@@ -495,14 +514,6 @@ const manualTriggerActionSchema = Type.Object(
|
|
|
495
514
|
},
|
|
496
515
|
{ title: "ManualTriggerAction", description: "Ordered link between a manual trigger and an action" }
|
|
497
516
|
);
|
|
498
|
-
const createManualTriggerActionSchema = Type.Omit(manualTriggerActionSchema, ["id"], {
|
|
499
|
-
title: "CreateManualTriggerAction",
|
|
500
|
-
description: "Row shape of a manual trigger-action link"
|
|
501
|
-
});
|
|
502
|
-
const updateManualTriggerActionSchema = Type.Partial(createManualTriggerActionSchema, {
|
|
503
|
-
title: "UpdateManualTriggerAction",
|
|
504
|
-
description: "Partial row shape of a manual trigger-action link"
|
|
505
|
-
});
|
|
506
517
|
const withResolvedActions = Type.Object({
|
|
507
518
|
actions: Type.Optional(Type.Array(actionSchema, { description: "Actions the trigger runs, in execution order" }))
|
|
508
519
|
});
|
|
@@ -518,8 +529,11 @@ const triggerExecutionResultSchema = Type.Object(
|
|
|
518
529
|
{
|
|
519
530
|
triggerId: Type.Number({ description: "Identifier of the evaluated trigger" }),
|
|
520
531
|
matched: Type.Boolean({ description: "Whether the conditions held, so the actions were run" }),
|
|
521
|
-
actionsRun: Type.Number({ description: "Number of actions that were executed" }),
|
|
522
|
-
actionsAborted: Type.Number({ description: "Number of actions aborted by context validation" })
|
|
532
|
+
actionsRun: Type.Number({ description: "Number of actions that were executed, whether they succeeded or not" }),
|
|
533
|
+
actionsAborted: Type.Number({ description: "Number of actions aborted by context validation" }),
|
|
534
|
+
actionsFailed: Type.Number({
|
|
535
|
+
description: "Number of executed actions that ended in error. A subset of actionsRun"
|
|
536
|
+
})
|
|
523
537
|
},
|
|
524
538
|
{ title: "TriggerExecutionResult", description: "Outcome of evaluating and running one trigger" }
|
|
525
539
|
);
|
|
@@ -552,9 +566,7 @@ export {
|
|
|
552
566
|
automaticTriggerWithActionsSchema,
|
|
553
567
|
conditionsFormatSchema,
|
|
554
568
|
createActionSchema,
|
|
555
|
-
createAutomaticTriggerActionSchema,
|
|
556
569
|
createAutomaticTriggerSchema,
|
|
557
|
-
createManualTriggerActionSchema,
|
|
558
570
|
createManualTriggerSchema,
|
|
559
571
|
iconPositionSchema,
|
|
560
572
|
includeActionsQuerySchema,
|
|
@@ -577,9 +589,7 @@ export {
|
|
|
577
589
|
triggerSourceSchema,
|
|
578
590
|
unmatchedBehaviorSchema,
|
|
579
591
|
updateActionSchema,
|
|
580
|
-
updateAutomaticTriggerActionSchema,
|
|
581
592
|
updateAutomaticTriggerSchema,
|
|
582
|
-
updateManualTriggerActionSchema,
|
|
583
593
|
updateManualTriggerSchema,
|
|
584
594
|
withIdParamsSchema
|
|
585
595
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ecosystem-action-system.mjs","sources":["../../src/v2/routes/v2/ecosystem/_ecosystemName/param.schema.ts","../../src/v2/routes/v2/ecosystem/_ecosystemName/project/_projectName/params.schema.ts","../../src/v2/modules/ecosystem/action-system/action-system.schema.ts"],"sourcesContent":["import { Type } from 'typebox'\n\nexport const ecosystemParamsSchema = Type.Object(\n {\n ecosystemName: Type.String({ maxLength: 255, description: 'Ecosystem name code' }),\n },\n { title: 'EcosystemParams', description: 'Params with ecosystem name' },\n)\n","import { Type } from 'typebox'\nimport { ecosystemParamsSchema } from '@/routes/v2/ecosystem/_ecosystemName/param.schema'\n\nexport const ecosystemProjectParamsSchema = Type.Intersect(\n [\n ecosystemParamsSchema,\n Type.Object({\n projectName: Type.String({ maxLength: 255, description: 'Project name code' }),\n }),\n ],\n { title: 'EcosystemProjectParams', description: 'Params with ecosystem name and project name' },\n)\n","import { Type, Static, type TSchema } from 'typebox'\nimport { ecosystemProjectParamsSchema } from '@/routes/v2/ecosystem/_ecosystemName/project/_projectName/params.schema'\n\n// ── Enums / literal unions ────────────────────────────────────────────\n// Each enum is a const object (source of truth, ergonomic dot access like\n// `TriggerSource.MODEL`) from which the runtime schema is derived with\n// `Type.Enum(Object.values(...))` and the type with `Static`.\n\nexport const ConditionsFormat = {\n JSON_LOGIC: 'jsonLogic',\n CUSTOM: 'custom',\n} as const\nexport const conditionsFormatSchema = Type.Enum(Object.values(ConditionsFormat), {\n title: 'ConditionsFormat',\n description: 'Format of the conditions evaluated by the conditions matcher',\n})\nexport type ConditionsFormat = Static<typeof conditionsFormatSchema>\n\nexport const TriggerSource = {\n MODEL: 'model',\n DESIGNER: 'designer',\n DISPLAYER: 'displayer',\n} as const\nexport const triggerSourceSchema = Type.Enum(Object.values(TriggerSource), {\n title: 'TriggerSource',\n description: 'Source of the event that fires a trigger',\n})\nexport type TriggerSource = Static<typeof triggerSourceSchema>\n\nexport const TriggerOperation = {\n CREATE: 'create',\n UPDATE: 'update',\n DELETE: 'delete',\n} as const\nexport const triggerOperationSchema = Type.Enum(Object.values(TriggerOperation), {\n title: 'TriggerOperation',\n description: 'Operation on the target that fires the trigger',\n})\nexport type TriggerOperation = Static<typeof triggerOperationSchema>\n\n// Executable mechanism of an action. `ApiAction` now; extensible to\n// `ExternalApiCall`, `Webhook`, … in the future (each adds a member here plus a\n// config schema below).\nexport const ActionType = {\n API_ACTION: 'ApiAction',\n} as const\nexport const actionTypeSchema = Type.Enum(Object.values(ActionType), {\n title: 'ActionType',\n description: 'Executable mechanism of an action',\n})\nexport type ActionType = Static<typeof actionTypeSchema>\n\n// ── Action config (discriminated union by `actions.type`) ─────────────\n// Forward-compatible contract shared with front/app via @persei/perseed-api.\n// Principles that the structure keeps prepared (not implemented here):\n// - `id` + fixed per-type fields: every type extends `ActionConfigBase`, so its\n// structure is known from `actions.type`.\n// - Objects always under a key (`payload`, …), never loose object fields, so new\n// fields can be added later without breaking compatibility.\n// - default + override: a type may carry a default config plus a `payload` that\n// overrides parts of it, both with the same typing (effective config = merge).\n//\n// `overridable`/`default`/`location` are generic base support for future\n// override-types (e.g. `ExternalApiCall`, #175): an override-type MUST declare\n// `overridable` (the allow-list of keys its payload may override on top of\n// `default`); `location` is a generic string here, refined per type when a\n// concrete type needs it. `ApiAction` is NOT an override-type — its `payload`\n// is the Handlebars data mapping, not an override of a default config — so it\n// gains these fields optionally but never sets `overridable`/`default`.\n\nexport const actionConfigBaseSchema = Type.Object(\n {\n id: Type.String({\n description:\n 'Identifier of the concrete action within its type (e.g. the ApiAction value for type \"ApiAction\"). Any identifier for future types.',\n }),\n location: Type.Optional(\n Type.String({\n description:\n 'Where the effective config is resolved from. Generic string in the base; refined per type in the future (e.g. \"inline\" | \"file\").',\n }),\n ),\n overridable: Type.Optional(\n Type.Array(Type.String(), {\n description:\n 'Allow-list of config keys a payload may override on top of `default`. Mandatory for override-types; absent for non-override types such as ApiAction.',\n }),\n ),\n default: Type.Optional(\n Type.Record(Type.String(), Type.Unknown(), {\n description: 'Base config an override-type merges its `payload` over (only overridable keys are applied).',\n }),\n ),\n },\n { title: 'ActionConfigBase', description: 'Base fields shared by every action config' },\n)\nexport type ActionConfigBaseData = Static<typeof actionConfigBaseSchema>\n\n// type = 'ApiAction' (current). `payload` is the data mapping needed to run the\n// ApiAction; it is always an object under a key for forward compatibility.\nexport const apiActionConfigSchema = Type.Intersect(\n [\n actionConfigBaseSchema,\n Type.Object({\n payload: Type.Record(Type.String(), Type.Unknown(), {\n description: 'Data mapping needed to run the ApiAction',\n }),\n }),\n ],\n { title: 'ApiActionConfig', description: 'Config for actions of type \"ApiAction\"' },\n)\nexport type ApiActionConfig = Static<typeof apiActionConfigSchema>\n\n// Discriminated (by `actions.type`) union of every action config. Extensible:\n// new types add a member that inherits from `ActionConfigBase`.\nexport const actionConfigSchema = Type.Union([apiActionConfigSchema], {\n title: 'ActionConfig',\n description: 'Union of action configs, selected by actions.type',\n})\nexport type ActionConfig = Static<typeof actionConfigSchema>\n\n// Registry mapping each action type to its config schema. Single seam to extend\n// when a new type is added, and the source used to validate a config by type.\nexport const ACTION_CONFIG_SCHEMAS: Record<ActionType, TSchema> = {\n [ActionType.API_ACTION]: apiActionConfigSchema,\n}\n\n// ── Action context (assembled data each action type evaluates against) ─\n// Mirrors the config-schema pattern above but for the *runtime* context\n// (jsonLogic conditions + handlebars payload refs are resolved against it),\n// not the static config. `additionalProperties: true` because the context is\n// enriched dynamically by the engine/ApiActionController (e.g. `ctx`).\n\nexport const actionContextBaseSchema = Type.Object(\n {\n current: Type.Record(Type.String(), Type.Unknown(), {\n description: 'Current state of the entity that fired the event',\n }),\n old: Type.Optional(\n Type.Union([Type.Record(Type.String(), Type.Unknown()), Type.Null()], {\n description: 'Previous state of the entity, when applicable (e.g. update/delete). Null otherwise',\n }),\n ),\n requestContextData: Type.Optional(\n Type.Unknown({ description: 'Snapshot of the request context (user, ecosystem, project…) when available' }),\n ),\n // Declared even though `additionalProperties: true` would already let it through: what a\n // condition may be written against is part of the contract, and a field only the engine knows\n // about is one no admin can discover.\n requester: Type.Optional(\n Type.Unknown({\n description:\n 'Who is making the request — role, administrator flags, privileges and clinics of their profiling in the project. Absent when the request carries no user (an internal call, a job)',\n }),\n ),\n projectId: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description: 'Project the event belongs to. Null for ecosystem-wide events',\n }),\n ),\n },\n {\n title: 'ActionContextBase',\n description: 'Base fields of the context assembled for every action execution',\n additionalProperties: true,\n },\n)\nexport type ActionContextBaseData = Static<typeof actionContextBaseSchema>\n\n// type = 'ApiAction' (current). No extra required keys beyond the base context\n// yet; kept as a distinct schema so future types can refine it independently.\nexport const apiActionContextSchema = actionContextBaseSchema\n\n// Registry mapping each action type to its context schema. Mirrors\n// `ACTION_CONFIG_SCHEMAS` — single seam to extend when a new type is added.\nexport const ACTION_CONTEXT_SCHEMAS: Record<ActionType, TSchema> = {\n [ActionType.API_ACTION]: apiActionContextSchema,\n}\n\n// Related models to load into the context before a trigger is evaluated — the\n// \"Load relations\" selector of the admin UI. Declared explicitly instead of being\n// derived from the paths a trigger uses: an action templating the whole context\n// (`{{json current}}`) references `current`, never `current.patient`, so deriving\n// would tie the loaded data to how a template is written rather than to what the\n// action needs. An object (not a bare list) because assembling the context has\n// historically taken more than one knob, leaving room to grow.\n//\n// Closed to unknown keys on purpose. The legacy trigger system had a second knob,\n// `load_patient` (`{where, join}`), with semantics this engine does not implement:\n// accepting it would persist a configuration that is then silently ignored, so a\n// trigger would look configured while its conditions never see the related data.\n// Rejecting it at the edge surfaces the mismatch as a 400 instead. Adding a knob\n// here is a deliberate act: declare the field AND read it in `loadRelations`.\nexport const triggerRelationsSchema = Type.Object(\n {\n join: Type.Optional(\n Type.Array(\n // A relation name, not free text: it is interpolated into the eager-loading graph\n // expression. Objection resolves it against the relations declared on the model (never\n // SQL), so an unknown name throws rather than leaking data — but validating the shape\n // here turns that runtime failure into a 400 at the edge, which is cheaper to diagnose.\n Type.String({ pattern: '^[A-Za-z_][A-Za-z0-9_]*$' }),\n { description: 'Relation names to eager-load onto `current` (e.g. [\"patient\"])' },\n ),\n ),\n },\n { title: 'TriggerRelations', additionalProperties: false },\n)\nexport type TriggerRelations = Static<typeof triggerRelationsSchema>\n\n// ── actions (independent reusable catalog) ────────────────────────────\n// Declared before the triggers because a trigger payload can carry actions\n// inline (see `triggerActionsInputSchema` below).\n\nexport const actionSchema = Type.Object(\n {\n id: Type.Number({ description: 'Unique identifier of the action' }),\n project_id: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description: 'Project this action belongs to. Null when it is ecosystem-wide',\n }),\n ),\n name: Type.String({ maxLength: 255, description: 'Human-readable identifier of the action' }),\n type: actionTypeSchema,\n config: actionConfigSchema,\n retention_days: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description:\n 'Retention in days for the process_log entries of this action (issue #175). Null = inherit (use the firing trigger retention; never if the trigger is null/0); 0 = disabled (never expires, regardless of the trigger); N (>0) = enabled (expires N days after write).',\n }),\n ),\n // Mirrors `dashboard_widget.library`. False by default, so an action created\n // inline from a trigger screen is single-use unless it is said otherwise.\n library: Type.Optional(\n Type.Boolean({\n description:\n 'Whether the action belongs to the reusable catalog. False (the default) marks it single-use: it is deleted once no trigger links it any more, including before it is ever linked — a standalone action created through POST /action and left unlinked is swept as soon as any trigger of the project writes its links. Set it to true to create one that survives on its own',\n }),\n ),\n },\n { title: 'Action', description: 'Reusable executable action (ecosystem level)' },\n)\nexport type ActionData = Static<typeof actionSchema>\n\n// `Type.Omit` keeps this a plain object — unlike `Type.Intersect` — so closing it here works: an\n// undeclared key is rejected with its name instead of being dropped on the way in. `id` and\n// `project_id` are omitted because the server sets them.\nexport const createActionSchema = Type.Omit(actionSchema, ['id', 'project_id'], {\n title: 'CreateAction',\n description: 'Payload used to create an action',\n additionalProperties: false,\n})\nexport type CreateActionData = Static<typeof createActionSchema>\n\n// Restated: `Type.Partial` rebuilds the object and does not carry over the options it derives from.\nexport const updateActionSchema = Type.Partial(createActionSchema, {\n title: 'UpdateAction',\n description: 'Payload used to update an action',\n additionalProperties: false,\n})\nexport type UpdateActionData = Static<typeof updateActionSchema>\n\n// ── Actions carried by a trigger payload ──────────────────────────────\n// One array, not two fields (`action_ids` + `new_actions`): the position IS the\n// `execution_order`, so splitting the list would make an order that interleaves\n// new and existing actions inexpressible. Each item takes one of three forms,\n// which `number` vs object and object with/without `id` disambiguate on their\n// own — no marker field needed:\n//\n// number link an existing catalog action, untouched\n// object without `id` create the action and link it\n// object with `id` update that catalog action and link it\n//\n// An object carrying only `id` is equivalent to passing the bare number.\n//\n// A malformed id is rejected rather than reinterpreted: `{ id: \"55\", name, type, config }` matches\n// no branch — the update one needs a numeric `id`, and the create one is closed against the key —\n// so it answers 400 instead of quietly landing on \"create a new action\".\nexport const triggerActionInputSchema = Type.Union(\n [\n Type.Number({ description: 'Identifier of an existing action to link, left untouched' }),\n Type.Object(\n { ...createActionSchema.properties },\n {\n title: 'TriggerActionCreate',\n description: 'Action created and linked in the same request',\n additionalProperties: false,\n },\n ),\n Type.Object(\n { id: Type.Number(), ...updateActionSchema.properties },\n {\n title: 'TriggerActionUpdate',\n description: 'Existing action updated and linked in the same request',\n additionalProperties: false,\n },\n ),\n ],\n { title: 'TriggerActionInput', description: 'An action a trigger runs: linked, created, or updated in place' },\n)\nexport type TriggerActionInput = Static<typeof triggerActionInputSchema>\n\nexport const triggerActionsInputSchema = Type.Array(triggerActionInputSchema, {\n description:\n 'Full ordered set of actions the trigger runs; the array position is the execution order. An empty array leaves the trigger with none',\n})\n\n// ── automatic_trigger ─────────────────────────────────────────────────\n\nexport const automaticTriggerSchema = Type.Object(\n {\n id: Type.Number({ description: 'Unique identifier of the automatic trigger' }),\n project_id: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description: 'Project this trigger belongs to. Null when it is ecosystem-wide',\n }),\n ),\n source: triggerSourceSchema,\n target: Type.Optional(\n Type.Union([Type.String({ maxLength: 255 }), Type.Null()], {\n description: 'Concrete model/designer/field the event refers to. Null for sourceless events',\n }),\n ),\n operation: Type.Optional(\n Type.Union([triggerOperationSchema, Type.Null()], {\n description: 'Operation on the target that fires the trigger. Null when not applicable',\n }),\n ),\n conditions: Type.Optional(\n Type.Union([Type.Object({}, { additionalProperties: true }), Type.Null()], {\n description: 'jsonLogic conditions (root object) evaluated to decide whether the trigger runs',\n }),\n ),\n relations: Type.Optional(\n Type.Union([triggerRelationsSchema, Type.Null()], {\n description:\n 'Related models loaded into the context before evaluating the conditions and running the actions. Null means none.',\n }),\n ),\n enabled: Type.Optional(Type.Boolean({ description: 'Whether the trigger is active' })),\n retention_days: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description:\n 'Retention in days for the process_log entries of this trigger (issue #175). Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write).',\n }),\n ),\n },\n { title: 'AutomaticTrigger', description: 'Automatic trigger definition (ecosystem level)' },\n)\nexport type AutomaticTriggerData = Static<typeof automaticTriggerSchema>\n\n// `includeActions` arrives from the query string, where everything is a string: Fastify coerces\n// `?includeActions=true` to a boolean against this schema, so the handler reads a real boolean.\n//\n// Opt-in rather than always-on: the join costs a query per trigger and most reads do not need it.\n// The read reuses the `*WithActions` schema of each kind (declared with the write responses below):\n// `actions` is optional there, so with the flag off the key is simply absent — the same way a\n// write that did not touch the field says nothing about it instead of reporting an empty set.\n//\n// Shared by both trigger kinds: the flag means the same thing on each, and the query schema carries\n// no kind-specific field.\nexport const includeActionsQuerySchema = Type.Object(\n {\n includeActions: Type.Optional(\n Type.Boolean({\n default: false,\n description: 'Return each trigger with its ordered `actions` resolved, in a single request',\n }),\n ),\n },\n { title: 'IncludeActionsQuery', description: 'Opt-in flag to embed the actions of a trigger' },\n)\n\n// Written out rather than derived from `automaticTriggerSchema`: a request schema and an entity\n// schema answer different questions — what a client may send vs what a row holds — and only the\n// first can close itself. `additionalProperties: false` does not survive `Type.Intersect`, so a\n// derived payload had to be cleaned before validating, which dropped unknown keys silently. Spelled\n// out, an undeclared key is a 400 that names it, and `id`/`project_id` are absent because the server\n// owns them, not because an `Omit` removed them.\nexport const createAutomaticTriggerSchema = Type.Object(\n {\n source: triggerSourceSchema,\n target: Type.Optional(\n Type.Union([Type.String({ maxLength: 255 }), Type.Null()], {\n description: 'Concrete model/designer/field the event refers to. Null for sourceless events',\n }),\n ),\n operation: Type.Optional(\n Type.Union([triggerOperationSchema, Type.Null()], {\n description: 'Operation on the target that fires the trigger. Null when not applicable',\n }),\n ),\n conditions: Type.Optional(\n Type.Union([Type.Object({}, { additionalProperties: true }), Type.Null()], {\n description: 'jsonLogic conditions (root object) evaluated to decide whether the trigger runs',\n }),\n ),\n relations: Type.Optional(\n Type.Union([triggerRelationsSchema, Type.Null()], {\n description:\n 'Related models loaded into the context before evaluating the conditions and running the actions. Null means none.',\n }),\n ),\n enabled: Type.Optional(Type.Boolean({ description: 'Whether the trigger is active' })),\n retention_days: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description:\n 'Retention in days for the process_log entries of this trigger (issue #175). Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write).',\n }),\n ),\n actions: Type.Optional(triggerActionsInputSchema),\n },\n {\n title: 'CreateAutomaticTrigger',\n description: 'Payload used to create an automatic trigger, optionally with the actions it runs',\n additionalProperties: false,\n },\n)\nexport type CreateAutomaticTriggerData = Static<typeof createAutomaticTriggerSchema>\n\n// `actions` is inherited from the create schema and stays optional here, which is\n// what the update contract wants: absent leaves the links untouched, present\n// replaces the whole ordered set (never merges), `[]` leaves the trigger with none.\n// `additionalProperties: false` is restated: `Type.Partial` rebuilds the object and drops the\n// options of the schema it derives from, so inheriting it would leave this payload open.\nexport const updateAutomaticTriggerSchema = Type.Partial(createAutomaticTriggerSchema, {\n title: 'UpdateAutomaticTrigger',\n description: 'Payload used to update an automatic trigger, optionally replacing the actions it runs',\n additionalProperties: false,\n})\nexport type UpdateAutomaticTriggerData = Static<typeof updateAutomaticTriggerSchema>\n\n// ── manual_trigger ────────────────────────────────────────────────────\n\n// What the consumer does with the button when the trigger conditions do not hold. It lives at the\n// root of the config, outside `styles`, so no conditional style can reach it: whether the button is\n// active is decided by the trigger conditions alone, and the style system only dresses it.\nexport const UnmatchedBehavior = {\n HIDE: 'hide',\n DISABLE: 'disable',\n} as const\nexport const unmatchedBehaviorSchema = Type.Enum(Object.values(UnmatchedBehavior), {\n title: 'UnmatchedBehavior',\n description: 'What to do with the button when the trigger does not match: hide it or disable it',\n})\nexport type UnmatchedBehavior = Static<typeof unmatchedBehaviorSchema>\n\nexport const IconPosition = {\n LEFT: 'left',\n RIGHT: 'right',\n} as const\nexport const iconPositionSchema = Type.Enum(Object.values(IconPosition), {\n title: 'IconPosition',\n description: 'Side of the label the icon sits on',\n})\nexport type IconPosition = Static<typeof iconPositionSchema>\n\nexport const manualTriggerButtonStyleSchema = Type.Object(\n {\n text: Type.Optional(Type.String({ description: 'Handlebars template rendered as the button label' })),\n textColor: Type.Optional(Type.String({ description: 'Label colour' })),\n bgColor: Type.Optional(Type.String({ description: 'Background colour' })),\n icon: Type.Optional(Type.String({ description: 'Icon name, resolved by the consumer' })),\n iconPosition: Type.Optional(iconPositionSchema),\n },\n { title: 'ManualTriggerButtonStyle', description: 'Presentation of the button a manual trigger is offered on' },\n)\nexport type ManualTriggerButtonStyleData = Static<typeof manualTriggerButtonStyleSchema>\n\n// Same shape as the base style plus the conditions that select it. Every style field is optional:\n// an entry only overrides what it declares.\nexport const manualTriggerConditionalStyleSchema = Type.Intersect(\n [\n manualTriggerButtonStyleSchema,\n Type.Object({\n conditions: Type.Optional(\n Type.Union([Type.Object({}, { additionalProperties: true }), Type.Null()], {\n description: 'jsonLogic conditions selecting this style. Absent or null always applies',\n }),\n ),\n continueEvaluate: Type.Optional(\n Type.Boolean({ description: 'Keep evaluating later entries after this one matches' }),\n ),\n }),\n ],\n { title: 'ManualTriggerConditionalStyle' },\n)\nexport type ManualTriggerConditionalStyleData = Static<typeof manualTriggerConditionalStyleSchema>\n\n/**\n * Envelope for everything a manual trigger configures beyond its firing rules. Today it carries\n * whether an unmatched trigger is hidden or shown disabled, plus the button presentation;\n * `additionalProperties` stays open so a future concern can be added without another column.\n *\n * The two are deliberately separate concerns: `unmatchedBehavior` answers whether the button is\n * offered at all, which only the trigger `conditions` decide, while `styles`/`conditionalStyles`\n * only dress a button that is already going to be shown.\n */\nexport const manualTriggerConfigSchema = Type.Object(\n {\n unmatchedBehavior: Type.Optional(unmatchedBehaviorSchema),\n styles: Type.Optional(manualTriggerButtonStyleSchema),\n conditionalStyles: Type.Optional(Type.Array(manualTriggerConditionalStyleSchema)),\n },\n { title: 'ManualTriggerConfig', additionalProperties: true },\n)\nexport type ManualTriggerConfigData = Static<typeof manualTriggerConfigSchema>\n\nexport const manualTriggerSchema = Type.Object(\n {\n id: Type.Number({ description: 'Unique identifier of the manual trigger' }),\n project_id: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description: 'Project this trigger belongs to. Null when it is ecosystem-wide',\n }),\n ),\n name: Type.String({ maxLength: 255, description: 'Human-readable label of the trigger where it is offered' }),\n source: triggerSourceSchema,\n target: Type.String({\n maxLength: 255,\n description: 'Concrete model/designer/field the trigger is bound to. Always set: there is no wildcard',\n }),\n conditions: Type.Optional(\n Type.Union([Type.Object({}, { additionalProperties: true }), Type.Null()], {\n description:\n 'jsonLogic conditions (root object) evaluated to decide whether the trigger runs. Evaluated against the selected entity and the request context; `old` is always null, because the trigger is invoked explicitly and there is no previous state to compare against.',\n }),\n ),\n relations: Type.Optional(\n Type.Union([triggerRelationsSchema, Type.Null()], {\n description:\n 'Related models loaded into the context before evaluating the conditions and running the actions. Null means none.',\n }),\n ),\n enabled: Type.Optional(Type.Boolean({ description: 'Whether the trigger is active' })),\n retention_days: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description:\n 'Retention in days for the process_log entries of this trigger (issue #175). Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write).',\n }),\n ),\n config: Type.Optional(\n Type.Union([manualTriggerConfigSchema, Type.Null()], {\n description: 'Configuration of the trigger beyond its firing rules. Null means none',\n }),\n ),\n },\n { title: 'ManualTrigger', description: 'Manual trigger definition (ecosystem level)' },\n)\nexport type ManualTriggerData = Static<typeof manualTriggerSchema>\n\n// Written out and closed, for the same reasons as `createAutomaticTriggerSchema` above.\nexport const createManualTriggerSchema = Type.Object(\n {\n name: Type.String({ maxLength: 255, description: 'Human-readable label of the trigger where it is offered' }),\n source: triggerSourceSchema,\n target: Type.String({\n maxLength: 255,\n description: 'Concrete model/designer/field the trigger is bound to. Always set: there is no wildcard',\n }),\n conditions: Type.Optional(\n Type.Union([Type.Object({}, { additionalProperties: true }), Type.Null()], {\n description:\n 'jsonLogic conditions (root object) evaluated to decide whether the trigger runs. Evaluated against the selected entity and the request context; `old` is always null, because the trigger is invoked explicitly and there is no previous state to compare against.',\n }),\n ),\n relations: Type.Optional(\n Type.Union([triggerRelationsSchema, Type.Null()], {\n description:\n 'Related models loaded into the context before evaluating the conditions and running the actions. Null means none.',\n }),\n ),\n enabled: Type.Optional(Type.Boolean({ description: 'Whether the trigger is active' })),\n retention_days: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description:\n 'Retention in days for the process_log entries of this trigger (issue #175). Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write).',\n }),\n ),\n config: Type.Optional(\n Type.Union([manualTriggerConfigSchema, Type.Null()], {\n description: 'Configuration of the trigger beyond its firing rules. Null means none',\n }),\n ),\n actions: Type.Optional(triggerActionsInputSchema),\n },\n {\n title: 'CreateManualTrigger',\n description: 'Payload used to create a manual trigger, optionally with the actions it runs',\n additionalProperties: false,\n },\n)\nexport type CreateManualTriggerData = Static<typeof createManualTriggerSchema>\n\n// Same inheritance and semantics as `updateAutomaticTriggerSchema`.\nexport const updateManualTriggerSchema = Type.Partial(createManualTriggerSchema, {\n title: 'UpdateManualTrigger',\n description: 'Payload used to update a manual trigger, optionally replacing the actions it runs',\n additionalProperties: false,\n})\nexport type UpdateManualTriggerData = Static<typeof updateManualTriggerSchema>\n\n// What the listing endpoint returns per applicable manual trigger: enough to\n// render it and nothing more. Only triggers that are going to be shown are\n// listed, so `matched: false` means a button rendered disabled, never a hidden\n// one. `matched` is never an authorisation: running re-evaluates server-side.\nexport const manualTriggerAvailabilitySchema = Type.Object(\n {\n id: Type.Number({ description: 'Identifier of the manual trigger' }),\n name: Type.String({ description: 'Human-readable label of the trigger' }),\n matched: Type.Boolean({\n description:\n 'Whether the trigger conditions hold for this entity right now. A trigger with no conditions always matches. False means the button is shown disabled: triggers configured to hide when unmatched are not listed',\n }),\n actionsCount: Type.Number({ description: 'Number of actions the trigger would run' }),\n relationsSkipped: Type.Optional(\n Type.Union([Type.String(), Type.Null()], {\n description:\n 'Reason the declared relations could not be loaded before evaluating, when that happened. Null otherwise',\n }),\n ),\n /**\n * Resolved server-side: conditional styles already evaluated and label already compiled. Absent\n * when the trigger has no config — the consumer then paints its own default button.\n */\n style: Type.Optional(manualTriggerButtonStyleSchema),\n },\n {\n title: 'ManualTriggerAvailability',\n description: 'A manual trigger applicable to an entity, with its conditions already evaluated',\n },\n)\nexport type ManualTriggerAvailability = Static<typeof manualTriggerAvailabilitySchema>\n\nexport const manualTriggerAvailabilityListSchema = Type.Object(\n { results: Type.Array(manualTriggerAvailabilitySchema, { description: 'Applicable manual triggers' }) },\n {\n title: 'ManualTriggerAvailabilityList',\n description: 'Manual triggers applicable to an entity, with their conditions evaluated',\n },\n)\n\n// Query the listing endpoint is called with: the entity the triggers are offered\n// on. `source`/`target` are the coordinates a manual trigger is bound to, and\n// `entityId` the concrete row.\nexport const manualTriggerAvailabilityQuerySchema = Type.Object(\n {\n source: triggerSourceSchema,\n target: Type.String({ maxLength: 255, description: 'Concrete model/designer/field the triggers are bound to' }),\n entityId: Type.Number({ description: 'Identifier of the entity the triggers would run against' }),\n },\n { title: 'ManualTriggerAvailabilityQuery', description: 'Entity the applicable manual triggers are listed for' },\n)\nexport type ManualTriggerAvailabilityQuery = Static<typeof manualTriggerAvailabilityQuerySchema>\n\n// Body of the run endpoint. It carries only the entity: `source`/`target` are\n// read from the trigger row, never from the client, so a trigger cannot be\n// re-pointed at an entity it was not bound to.\nexport const runManualTriggerSchema = Type.Object(\n { entityId: Type.Number({ description: 'Identifier of the entity to run the trigger against' }) },\n { title: 'RunManualTrigger', description: 'Payload used to run a manual trigger against an entity' },\n)\nexport type RunManualTriggerData = Static<typeof runManualTriggerSchema>\n\n// ── automatic_trigger_action (intermediate N:M automatic_trigger↔actions,\n// ordered) ──────────────────────────────────────────────────────────────\n// Dedicated table per trigger kind (no ORM polymorphism): `automatic_trigger_id`\n// is a real FK. `manual_trigger_action` is a future table for manual triggers.\n\nexport const automaticTriggerActionSchema = Type.Object(\n {\n id: Type.Number({ description: 'Unique identifier of the trigger-action link' }),\n automatic_trigger_id: Type.Number({\n description: 'Automatic trigger executing the action (FK to automatic_trigger)',\n }),\n action_id: Type.Number({ description: 'Action executed by the trigger (FK to actions)' }),\n execution_order: Type.Number({ description: 'Execution order of the action within the trigger' }),\n },\n { title: 'AutomaticTriggerAction', description: 'Ordered link between an automatic trigger and an action' },\n)\nexport type AutomaticTriggerActionData = Static<typeof automaticTriggerActionSchema>\n\n// The `create*`/`update*` pair below is not an endpoint contract: the links are\n// written through the `actions` field of the trigger create/update payloads.\n// They remain because `action-system.model.ts` builds the Objection\n// `jsonSchema` of the link models out of their union.\nexport const createAutomaticTriggerActionSchema = Type.Omit(automaticTriggerActionSchema, ['id'], {\n title: 'CreateAutomaticTriggerAction',\n description: 'Row shape of an automatic trigger-action link',\n})\nexport type CreateAutomaticTriggerActionData = Static<typeof createAutomaticTriggerActionSchema>\n\nexport const updateAutomaticTriggerActionSchema = Type.Partial(createAutomaticTriggerActionSchema, {\n title: 'UpdateAutomaticTriggerAction',\n description: 'Partial row shape of an automatic trigger-action link',\n})\nexport type UpdateAutomaticTriggerActionData = Static<typeof updateAutomaticTriggerActionSchema>\n\n// ── manual_trigger_action (intermediate N:M manual_trigger↔actions,\n// ordered) ──────────────────────────────────────────────────────────────\n// Same dedicated-table-per-trigger-kind rule as `automatic_trigger_action`:\n// `manual_trigger_id` is a real FK, and the `actions` catalog is shared\n// between kinds — only the link table differs.\n\nexport const manualTriggerActionSchema = Type.Object(\n {\n id: Type.Number({ description: 'Unique identifier of the trigger-action link' }),\n manual_trigger_id: Type.Number({\n description: 'Manual trigger executing the action (FK to manual_trigger)',\n }),\n action_id: Type.Number({ description: 'Action executed by the trigger (FK to actions)' }),\n execution_order: Type.Number({ description: 'Execution order of the action within the trigger' }),\n },\n { title: 'ManualTriggerAction', description: 'Ordered link between a manual trigger and an action' },\n)\nexport type ManualTriggerActionData = Static<typeof manualTriggerActionSchema>\n\n// Same as the automatic pair above: kept for the model `jsonSchema`, not as an\n// endpoint contract.\nexport const createManualTriggerActionSchema = Type.Omit(manualTriggerActionSchema, ['id'], {\n title: 'CreateManualTriggerAction',\n description: 'Row shape of a manual trigger-action link',\n})\nexport type CreateManualTriggerActionData = Static<typeof createManualTriggerActionSchema>\n\nexport const updateManualTriggerActionSchema = Type.Partial(createManualTriggerActionSchema, {\n title: 'UpdateManualTriggerAction',\n description: 'Partial row shape of a manual trigger-action link',\n})\nexport type UpdateManualTriggerActionData = Static<typeof updateManualTriggerActionSchema>\n\n// ── Declarative trigger↔actions contract ──────────────────────────────\n// The link tables are written as a whole through the `actions` field of the trigger payload,\n// never row by row. The array position IS the `execution_order`, so ordering can\n// never disagree with the list the client sent — which per-row endpoints allowed\n// (duplicate or gapped orders after a reorder). Shared by both trigger kinds:\n// only the parent FK differs, and that comes from the URL.\n//\n// The field is `actions`, not `action_ids`: items are no longer only ids (see\n// `triggerActionInputSchema`). Same body as the `actions` field of the create and\n// update trigger payloads, so the three ways of writing the set agree.\n\n// What create/update answer with. `actions` carries the resolved catalog rows —\n// not the link rows — so a client that sent actions inline learns the ids they\n// were given without chaining a second request. It is present only when the\n// request carried the field: a create/update that did not mention actions says\n// nothing about them, rather than reporting an empty set it never touched.\nconst withResolvedActions = Type.Object({\n actions: Type.Optional(Type.Array(actionSchema, { description: 'Actions the trigger runs, in execution order' })),\n})\n\nexport const automaticTriggerWithActionsSchema = Type.Intersect([automaticTriggerSchema, withResolvedActions], {\n title: 'AutomaticTriggerWithActions',\n description:\n 'An automatic trigger and, when the request wrote them or `includeActions` asked for them, the actions it runs',\n})\nexport type AutomaticTriggerWithActionsData = Static<typeof automaticTriggerWithActionsSchema>\n\nexport const manualTriggerWithActionsSchema = Type.Intersect([manualTriggerSchema, withResolvedActions], {\n title: 'ManualTriggerWithActions',\n description:\n 'A manual trigger and, when the request wrote them or `includeActions` asked for them, the actions it runs',\n})\nexport type ManualTriggerWithActionsData = Static<typeof manualTriggerWithActionsSchema>\n\n// ── Execution engine (pure, model-independent contract types) ──────────\n// Exported types the engine (service/helper) works with. Kept here (rather\n// than in service/helper) so they can be imported without pulling in\n// service/helper code. None of them references a model: the engine types\n// itself structurally (see the `*Like` shapes below).\n\nexport interface ActionEvent {\n source: TriggerSource\n target?: string | null\n operation?: TriggerOperation | null\n current: Record<string, unknown>\n old?: Record<string, unknown> | null\n}\n\n// Outcome of evaluating (and, when matched, running) a single trigger. Declared\n// as a TypeBox schema — not just an interface — because the manual-trigger run\n// endpoint returns it verbatim; `TriggerExecutionResult` is its static type, so\n// engine and endpoint cannot drift apart.\nexport const triggerExecutionResultSchema = Type.Object(\n {\n triggerId: Type.Number({ description: 'Identifier of the evaluated trigger' }),\n matched: Type.Boolean({ description: 'Whether the conditions held, so the actions were run' }),\n actionsRun: Type.Number({ description: 'Number of actions that were executed' }),\n actionsAborted: Type.Number({ description: 'Number of actions aborted by context validation' }),\n },\n { title: 'TriggerExecutionResult', description: 'Outcome of evaluating and running one trigger' },\n)\nexport type TriggerExecutionResult = Static<typeof triggerExecutionResultSchema>\n\n// ── Trigger-kind-agnostic engine contract ─────────────────────────────\n// The execution engine is shared by every kind of trigger: it consumes the\n// minimal structural shapes below rather than a concrete model, so a new kind\n// only has to resolve its own candidates and hand them over. The `*Like` types\n// are deliberately structural (not the Objection classes) so the engine never\n// imports a model to type itself — `AutomaticTrigger` / `AutomaticTriggerAction`\n// / `Action` satisfy them as they are, without a cast.\n\n/**\n * Structural shape the engine needs from an action it is about to run: the\n * identity it logs, the `type` that selects the validation/dispatch branch, the\n * config it executes with, and the retention it resolves `expires_at` from.\n * Deliberately loose (`type: string`, not `ActionType`) so an unknown type still\n * reaches the always-blocking context validation instead of failing to typecheck.\n */\nexport interface ActionLike {\n id: number\n type: string\n config: ActionConfig\n retention_days?: number | null\n}\n\n/** Ordered trigger↔action link in the minimal shape the engine consumes. */\nexport interface TriggerActionLike {\n action_id: number\n execution_order: number\n action?: ActionLike\n}\n\n/** Minimal shape of a trigger for the engine, whatever its kind. */\nexport interface EvaluableTrigger {\n id: number\n conditions?: object | null\n relations?: TriggerRelations | null\n retention_days?: number | null\n trigger_actions?: TriggerActionLike[]\n}\n\n/**\n * A batch of already-resolved candidates a trigger kind hands to the engine,\n * together with the event data they were resolved for. `relatedModel` is what\n * the evaluation `process_log` row is related to (the trigger table of that\n * kind), which is how trigger evaluations stay independently queryable per kind.\n */\nexport interface TriggerBatch<T extends EvaluableTrigger = EvaluableTrigger> {\n triggers: T[]\n relatedModel: string\n source: TriggerSource\n target?: string | null\n operation?: TriggerOperation | null\n current: Record<string, unknown>\n old?: Record<string, unknown> | null\n}\n\n// ── Context validation (pure data types) ────────────────────────────────\n\nexport interface ContextValidationResult {\n ok: boolean\n type: string\n baseErrors: { path: string; message: string }[]\n missingRefs: string[]\n referencedRefs: string[]\n overridableErrors: string[]\n}\n\n// Structural shape `buildEffectiveConfig`/`validateActionContext` need from an\n// action config — deliberately loose (not `ActionConfig`) so it works for any\n// current or future type without a cast at every call site.\nexport interface ActionConfigLike {\n payload?: Record<string, unknown>\n overridable?: string[]\n default?: Record<string, unknown>\n}\n\nexport interface EffectiveConfigResult {\n effective: Record<string, unknown>\n overridableErrors: string[]\n}\n\n// ── Route-level schemas ───────────────────────────────────────────────\n// Shared by every router of the module.\n\nexport const successResponseSchema = Type.Object(\n { success: Type.Boolean({ description: 'If the request success or not' }) },\n { title: 'ActionSystemSuccessResponse', description: 'Response when the request is successful' },\n)\n\nexport const withIdParamsSchema = Type.Intersect(\n [ecosystemProjectParamsSchema, Type.Object({ id: Type.Number({ description: 'Entity ID' }) })],\n { title: 'EcosystemProjectWithActionSystemIdParams', description: 'Params with ecosystem, project and entity ID' },\n)\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ecosystem-action-system.mjs","sources":["../../src/v2/routes/v2/ecosystem/_ecosystemName/param.schema.ts","../../src/v2/routes/v2/ecosystem/_ecosystemName/project/_projectName/params.schema.ts","../../src/v2/modules/ecosystem/action-system/action-system.schema.ts"],"sourcesContent":["import { Type } from 'typebox'\n\nexport const ecosystemParamsSchema = Type.Object(\n {\n ecosystemName: Type.String({ maxLength: 255, description: 'Ecosystem name code' }),\n },\n { title: 'EcosystemParams', description: 'Params with ecosystem name' },\n)\n","import { Type } from 'typebox'\nimport { ecosystemParamsSchema } from '@/routes/v2/ecosystem/_ecosystemName/param.schema'\n\nexport const ecosystemProjectParamsSchema = Type.Intersect(\n [\n ecosystemParamsSchema,\n Type.Object({\n projectName: Type.String({ maxLength: 255, description: 'Project name code' }),\n }),\n ],\n { title: 'EcosystemProjectParams', description: 'Params with ecosystem name and project name' },\n)\n","import { Type, Static, type TSchema } from 'typebox'\nimport { ecosystemProjectParamsSchema } from '@/routes/v2/ecosystem/_ecosystemName/project/_projectName/params.schema'\n\n// ── Enums / literal unions ────────────────────────────────────────────\n// Each enum is a const object (source of truth, ergonomic dot access like\n// `TriggerSource.MODEL`) from which the runtime schema is derived with\n// `Type.Enum(Object.values(...))` and the type with `Static`.\n\nexport const ConditionsFormat = {\n JSON_LOGIC: 'jsonLogic',\n CUSTOM: 'custom',\n} as const\nexport const conditionsFormatSchema = Type.Enum(Object.values(ConditionsFormat), {\n title: 'ConditionsFormat',\n description: 'Format of the conditions evaluated by the conditions matcher',\n})\nexport type ConditionsFormat = Static<typeof conditionsFormatSchema>\n\n/**\n * What a trigger binds to: a row of a model, or the data of a form.\n *\n * These are the two the platform emits — `emitModelAfterSave` for one, the form save for the\n * other — and the two `resolveTargetQuery` can load an entity from. A third, `designer` (the form\n * definition), was declared and never emitted: an automatic trigger bound to it could not fire,\n * and a manual one answered 404 both when listing its availability and when running it, because\n * the name of a form does not resolve through the relation-loadable models it fell back to.\n */\nexport const TriggerSource = {\n MODEL: 'model',\n DISPLAYER: 'displayer',\n} as const\nexport const triggerSourceSchema = Type.Enum(Object.values(TriggerSource), {\n title: 'TriggerSource',\n description: 'Source of the event that fires a trigger',\n})\nexport type TriggerSource = Static<typeof triggerSourceSchema>\n\nexport const TriggerOperation = {\n CREATE: 'create',\n UPDATE: 'update',\n DELETE: 'delete',\n} as const\nexport const triggerOperationSchema = Type.Enum(Object.values(TriggerOperation), {\n title: 'TriggerOperation',\n description: 'Operation on the target that fires the trigger',\n})\nexport type TriggerOperation = Static<typeof triggerOperationSchema>\n\n// Executable mechanism of an action. `ApiAction` now; extensible to\n// `ExternalApiCall`, `Webhook`, … in the future (each adds a member here plus a\n// config schema below).\nexport const ActionType = {\n API_ACTION: 'ApiAction',\n} as const\nexport const actionTypeSchema = Type.Enum(Object.values(ActionType), {\n title: 'ActionType',\n description: 'Executable mechanism of an action',\n})\nexport type ActionType = Static<typeof actionTypeSchema>\n\n// ── Action config (discriminated union by `actions.type`) ─────────────\n// Forward-compatible contract shared with front/app via @persei/perseed-api.\n// Principles that the structure keeps prepared (not implemented here):\n// - `id` + fixed per-type fields: every type extends `ActionConfigBase`, so its\n// structure is known from `actions.type`.\n// - Objects always under a key (`payload`, …), never loose object fields, so new\n// fields can be added later without breaking compatibility.\n// - default + override: a type may carry a default config plus a `payload` that\n// overrides parts of it, both with the same typing (effective config = merge).\n//\n// `overridable`/`default`/`location` are generic base support for future\n// override-types (e.g. `ExternalApiCall`): an override-type MUST declare\n// `overridable` (the allow-list of keys its payload may override on top of\n// `default`); `location` is a generic string here, refined per type when a\n// concrete type needs it. `ApiAction` is NOT an override-type — its `payload`\n// is the Handlebars data mapping, not an override of a default config — so it\n// gains these fields optionally but never sets `overridable`/`default`.\n\nexport const actionConfigBaseSchema = Type.Object(\n {\n id: Type.String({\n description:\n 'Identifier of the concrete action within its type (e.g. the ApiAction value for type \"ApiAction\"). Any identifier for future types.',\n }),\n location: Type.Optional(\n Type.String({\n description:\n 'Where the effective config is resolved from. Generic string in the base; refined per type in the future (e.g. \"inline\" | \"file\").',\n }),\n ),\n overridable: Type.Optional(\n Type.Array(Type.String(), {\n description:\n 'Allow-list of config keys a payload may override on top of `default`. Mandatory for override-types; absent for non-override types such as ApiAction.',\n }),\n ),\n default: Type.Optional(\n Type.Record(Type.String(), Type.Unknown(), {\n description: 'Base config an override-type merges its `payload` over (only overridable keys are applied).',\n }),\n ),\n },\n { title: 'ActionConfigBase', description: 'Base fields shared by every action config' },\n)\nexport type ActionConfigBaseData = Static<typeof actionConfigBaseSchema>\n\n// type = 'ApiAction' (current). `payload` is the data mapping needed to run the\n// ApiAction; it is always an object under a key for forward compatibility.\nexport const apiActionConfigSchema = Type.Intersect(\n [\n actionConfigBaseSchema,\n Type.Object({\n payload: Type.Record(Type.String(), Type.Unknown(), {\n description: 'Data mapping needed to run the ApiAction',\n }),\n }),\n ],\n { title: 'ApiActionConfig', description: 'Config for actions of type \"ApiAction\"' },\n)\nexport type ApiActionConfig = Static<typeof apiActionConfigSchema>\n\n// Discriminated (by `actions.type`) union of every action config. Extensible:\n// new types add a member that inherits from `ActionConfigBase`.\nexport const actionConfigSchema = Type.Union([apiActionConfigSchema], {\n title: 'ActionConfig',\n description: 'Union of action configs, selected by actions.type',\n})\nexport type ActionConfig = Static<typeof actionConfigSchema>\n\n// Registry mapping each action type to its config schema. Single seam to extend\n// when a new type is added, and the source used to validate a config by type.\nexport const ACTION_CONFIG_SCHEMAS: Record<ActionType, TSchema> = {\n [ActionType.API_ACTION]: apiActionConfigSchema,\n}\n\n// ── Action context (assembled data each action type evaluates against) ─\n// Mirrors the config-schema pattern above but for the *runtime* context\n// (jsonLogic conditions + handlebars payload refs are resolved against it),\n// not the static config. `additionalProperties: true` because the context is\n// enriched dynamically by the engine/ApiActionController (e.g. `ctx`).\n\nexport const actionContextBaseSchema = Type.Object(\n {\n current: Type.Record(Type.String(), Type.Unknown(), {\n description: 'Current state of the entity that fired the event',\n }),\n old: Type.Optional(\n Type.Union([Type.Record(Type.String(), Type.Unknown()), Type.Null()], {\n description: 'Previous state of the entity, when applicable (e.g. update/delete). Null otherwise',\n }),\n ),\n requestContextData: Type.Optional(\n Type.Unknown({ description: 'Snapshot of the request context (user, ecosystem, project…) when available' }),\n ),\n // Declared even though `additionalProperties: true` would already let it through: what a\n // condition may be written against is part of the contract, and a field only the engine knows\n // about is one no admin can discover.\n requester: Type.Optional(\n Type.Unknown({\n description:\n 'Who is making the request — role, administrator flags, privileges and clinics of their profiling in the project. Absent when the request carries no user (an internal call, a job)',\n }),\n ),\n projectId: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description: 'Project the event belongs to. Null for ecosystem-wide events',\n }),\n ),\n },\n {\n title: 'ActionContextBase',\n description: 'Base fields of the context assembled for every action execution',\n additionalProperties: true,\n },\n)\nexport type ActionContextBaseData = Static<typeof actionContextBaseSchema>\n\n// type = 'ApiAction' (current). No extra required keys beyond the base context\n// yet; kept as a distinct schema so future types can refine it independently.\nexport const apiActionContextSchema = actionContextBaseSchema\n\n// Registry mapping each action type to its context schema. Mirrors\n// `ACTION_CONFIG_SCHEMAS` — single seam to extend when a new type is added.\nexport const ACTION_CONTEXT_SCHEMAS: Record<ActionType, TSchema> = {\n [ActionType.API_ACTION]: apiActionContextSchema,\n}\n\n// Related models to load into the context before a trigger is evaluated — the\n// \"Load relations\" selector of the admin UI. Declared explicitly instead of being\n// derived from the paths a trigger uses: an action templating the whole context\n// (`{{json current}}`) references `current`, never `current.patient`, so deriving\n// would tie the loaded data to how a template is written rather than to what the\n// action needs. An object (not a bare list) because assembling the context has\n// historically taken more than one knob, leaving room to grow.\n//\n// Closed to unknown keys on purpose. The legacy trigger system had a second knob,\n// `load_patient` (`{where, join}`), with semantics this engine does not implement:\n// accepting it would persist a configuration that is then silently ignored, so a\n// trigger would look configured while its conditions never see the related data.\n// Rejecting it at the edge surfaces the mismatch as a 400 instead. Adding a knob\n// here is a deliberate act: declare the field AND read it in `loadRelations`.\nexport const triggerRelationsSchema = Type.Object(\n {\n join: Type.Optional(\n Type.Array(\n // A relation name, not free text: it is interpolated into the eager-loading graph\n // expression. Objection resolves it against the relations declared on the model (never\n // SQL), so an unknown name throws rather than leaking data — but validating the shape\n // here turns that runtime failure into a 400 at the edge, which is cheaper to diagnose.\n Type.String({ pattern: '^[A-Za-z_][A-Za-z0-9_]*$' }),\n { description: 'Relation names to eager-load onto `current` (e.g. [\"patient\"])' },\n ),\n ),\n },\n { title: 'TriggerRelations', additionalProperties: false },\n)\nexport type TriggerRelations = Static<typeof triggerRelationsSchema>\n\n// ── actions (independent reusable catalog) ────────────────────────────\n// Declared before the triggers because a trigger payload can carry actions\n// inline (see `triggerActionsInputSchema` below).\n\nexport const actionSchema = Type.Object(\n {\n id: Type.Number({ description: 'Unique identifier of the action' }),\n project_id: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description: 'Project this action belongs to. Null when it is ecosystem-wide',\n }),\n ),\n // Optional for the same reason as the trigger's: it labels the action where it is\n // administered, and nothing renders it to whoever fires the trigger, so requiring it only\n // forced a placeholder. Null when it has none.\n name: Type.Optional(\n Type.Union([Type.String({ maxLength: 255 }), Type.Null()], {\n description: 'Human-readable label of the action. Null when it has none',\n }),\n ),\n type: actionTypeSchema,\n config: actionConfigSchema,\n retention_days: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description:\n 'Retention in days for the process_log entries of this action. Null = inherit (use the firing trigger retention; never if the trigger is null/0); 0 = disabled (never expires, regardless of the trigger); N (>0) = enabled (expires N days after write).',\n }),\n ),\n // Mirrors `dashboard_widget.library`. False by default, so an action created\n // inline from a trigger screen is single-use unless it is said otherwise.\n library: Type.Optional(\n Type.Boolean({\n description:\n 'Whether the action belongs to the reusable catalog. False (the default) marks it single-use: it is deleted once no trigger links it any more, including before it is ever linked — a standalone action created through POST /action and left unlinked is swept as soon as any trigger of the project writes its links. Set it to true to create one that survives on its own',\n }),\n ),\n },\n { title: 'Action', description: 'Reusable executable action (ecosystem level)' },\n)\nexport type ActionData = Static<typeof actionSchema>\n\n// `Type.Omit` keeps this a plain object — unlike `Type.Intersect` — so closing it here works: an\n// undeclared key is rejected with its name instead of being dropped on the way in. `id` and\n// `project_id` are omitted because the server sets them.\nexport const createActionSchema = Type.Omit(actionSchema, ['id', 'project_id'], {\n title: 'CreateAction',\n description: 'Payload used to create an action',\n additionalProperties: false,\n})\nexport type CreateActionData = Static<typeof createActionSchema>\n\n// Restated: `Type.Partial` rebuilds the object and does not carry over the options it derives from.\nexport const updateActionSchema = Type.Partial(createActionSchema, {\n title: 'UpdateAction',\n description: 'Payload used to update an action',\n additionalProperties: false,\n})\nexport type UpdateActionData = Static<typeof updateActionSchema>\n\n// ── Actions carried by a trigger payload ──────────────────────────────\n// One array, not two fields (`action_ids` + `new_actions`): the position IS the\n// `execution_order`, so splitting the list would make an order that interleaves\n// new and existing actions inexpressible. Each item takes one of three forms,\n// which `number` vs object and object with/without `id` disambiguate on their\n// own — no marker field needed:\n//\n// number link an existing catalog action, untouched\n// object without `id` create the action and link it\n// object with `id` update that catalog action and link it\n//\n// An object carrying only `id` is equivalent to passing the bare number.\n//\n// A malformed id is rejected rather than reinterpreted: `{ id: \"55\", name, type, config }` matches\n// no branch — the update one needs a numeric `id`, and the create one is closed against the key —\n// so it answers 400 instead of quietly landing on \"create a new action\".\nexport const triggerActionInputSchema = Type.Union(\n [\n Type.Number({ description: 'Identifier of an existing action to link, left untouched' }),\n Type.Object(\n { ...createActionSchema.properties },\n {\n title: 'TriggerActionCreate',\n description: 'Action created and linked in the same request',\n additionalProperties: false,\n },\n ),\n Type.Object(\n { id: Type.Number(), ...updateActionSchema.properties },\n {\n title: 'TriggerActionUpdate',\n description: 'Existing action updated and linked in the same request',\n additionalProperties: false,\n },\n ),\n ],\n { title: 'TriggerActionInput', description: 'An action a trigger runs: linked, created, or updated in place' },\n)\nexport type TriggerActionInput = Static<typeof triggerActionInputSchema>\n\nexport const triggerActionsInputSchema = Type.Array(triggerActionInputSchema, {\n description:\n 'Full ordered set of actions the trigger runs; the array position is the execution order. An empty array leaves the trigger with none',\n})\n\n// ── automatic_trigger ─────────────────────────────────────────────────\n\n/** Optional label a trigger is known by wherever it is administered. Not shown to end users. */\nconst triggerNameSchema = Type.Optional(\n Type.Union([Type.String({ maxLength: 255 }), Type.Null()], {\n description: 'Human-readable label of the trigger. Null when it has none',\n }),\n)\n\n/** Free text for whoever maintains the trigger; never rendered where the trigger is offered. */\nconst triggerDescriptionSchema = Type.Optional(\n Type.Union([Type.String(), Type.Null()], {\n description: 'What the trigger is for, for whoever administers it. Null when it has none',\n }),\n)\n\nexport const automaticTriggerSchema = Type.Object(\n {\n id: Type.Number({ description: 'Unique identifier of the automatic trigger' }),\n project_id: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description: 'Project this trigger belongs to. Null when it is ecosystem-wide',\n }),\n ),\n name: triggerNameSchema,\n description: triggerDescriptionSchema,\n source: triggerSourceSchema,\n target: Type.Optional(\n Type.Union([Type.String({ maxLength: 255 }), Type.Null()], {\n description: 'Concrete model/designer/field the event refers to. Null for sourceless events',\n }),\n ),\n operation: Type.Optional(\n Type.Union([triggerOperationSchema, Type.Null()], {\n description: 'Operation on the target that fires the trigger. Null when not applicable',\n }),\n ),\n conditions: Type.Optional(\n Type.Union([Type.Object({}, { additionalProperties: true }), Type.Null()], {\n description: 'jsonLogic conditions (root object) evaluated to decide whether the trigger runs',\n }),\n ),\n relations: Type.Optional(\n Type.Union([triggerRelationsSchema, Type.Null()], {\n description:\n 'Related models loaded into the context before evaluating the conditions and running the actions. Null means none.',\n }),\n ),\n enabled: Type.Optional(Type.Boolean({ description: 'Whether the trigger is active' })),\n retention_days: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description:\n 'Retention in days for the process_log entries of this trigger. Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write).',\n }),\n ),\n },\n { title: 'AutomaticTrigger', description: 'Automatic trigger definition (ecosystem level)' },\n)\nexport type AutomaticTriggerData = Static<typeof automaticTriggerSchema>\n\n// `includeActions` arrives from the query string, where everything is a string: Fastify coerces\n// `?includeActions=true` to a boolean against this schema, so the handler reads a real boolean.\n//\n// Opt-in rather than always-on: the join costs a query per trigger and most reads do not need it.\n// The read reuses the `*WithActions` schema of each kind (declared with the write responses below):\n// `actions` is optional there, so with the flag off the key is simply absent — the same way a\n// write that did not touch the field says nothing about it instead of reporting an empty set.\n//\n// Shared by both trigger kinds: the flag means the same thing on each, and the query schema carries\n// no kind-specific field.\nexport const includeActionsQuerySchema = Type.Object(\n {\n includeActions: Type.Optional(\n Type.Boolean({\n default: false,\n description: 'Return each trigger with its ordered `actions` resolved, in a single request',\n }),\n ),\n },\n { title: 'IncludeActionsQuery', description: 'Opt-in flag to embed the actions of a trigger' },\n)\n\n// Written out rather than derived from `automaticTriggerSchema`: a request schema and an entity\n// schema answer different questions — what a client may send vs what a row holds — and only the\n// first can close itself. `additionalProperties: false` does not survive `Type.Intersect`, so a\n// derived payload had to be cleaned before validating, which dropped unknown keys silently. Spelled\n// out, an undeclared key is a 400 that names it, and `id`/`project_id` are absent because the server\n// owns them, not because an `Omit` removed them.\nexport const createAutomaticTriggerSchema = Type.Object(\n {\n name: triggerNameSchema,\n description: triggerDescriptionSchema,\n source: triggerSourceSchema,\n target: Type.Optional(\n Type.Union([Type.String({ maxLength: 255 }), Type.Null()], {\n description: 'Concrete model/designer/field the event refers to. Null for sourceless events',\n }),\n ),\n operation: Type.Optional(\n Type.Union([triggerOperationSchema, Type.Null()], {\n description: 'Operation on the target that fires the trigger. Null when not applicable',\n }),\n ),\n conditions: Type.Optional(\n Type.Union([Type.Object({}, { additionalProperties: true }), Type.Null()], {\n description: 'jsonLogic conditions (root object) evaluated to decide whether the trigger runs',\n }),\n ),\n relations: Type.Optional(\n Type.Union([triggerRelationsSchema, Type.Null()], {\n description:\n 'Related models loaded into the context before evaluating the conditions and running the actions. Null means none.',\n }),\n ),\n enabled: Type.Optional(Type.Boolean({ description: 'Whether the trigger is active' })),\n retention_days: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description:\n 'Retention in days for the process_log entries of this trigger. Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write).',\n }),\n ),\n actions: Type.Optional(triggerActionsInputSchema),\n },\n {\n title: 'CreateAutomaticTrigger',\n description: 'Payload used to create an automatic trigger, optionally with the actions it runs',\n additionalProperties: false,\n },\n)\nexport type CreateAutomaticTriggerData = Static<typeof createAutomaticTriggerSchema>\n\n// `actions` is inherited from the create schema and stays optional here, which is\n// what the update contract wants: absent leaves the links untouched, present\n// replaces the whole ordered set (never merges), `[]` leaves the trigger with none.\n// `additionalProperties: false` is restated: `Type.Partial` rebuilds the object and drops the\n// options of the schema it derives from, so inheriting it would leave this payload open.\nexport const updateAutomaticTriggerSchema = Type.Partial(createAutomaticTriggerSchema, {\n title: 'UpdateAutomaticTrigger',\n description: 'Payload used to update an automatic trigger, optionally replacing the actions it runs',\n additionalProperties: false,\n})\nexport type UpdateAutomaticTriggerData = Static<typeof updateAutomaticTriggerSchema>\n\n// ── manual_trigger ────────────────────────────────────────────────────\n\n// What the consumer does with the button when the trigger conditions do not hold. It lives at the\n// root of the config, outside `styles`, so no conditional style can reach it: whether the button is\n// active is decided by the trigger conditions alone, and the style system only dresses it.\nexport const UnmatchedBehavior = {\n HIDE: 'hide',\n DISABLE: 'disable',\n} as const\nexport const unmatchedBehaviorSchema = Type.Enum(Object.values(UnmatchedBehavior), {\n title: 'UnmatchedBehavior',\n description: 'What to do with the button when the trigger does not match: hide it or disable it',\n})\nexport type UnmatchedBehavior = Static<typeof unmatchedBehaviorSchema>\n\nexport const IconPosition = {\n LEFT: 'left',\n RIGHT: 'right',\n} as const\nexport const iconPositionSchema = Type.Enum(Object.values(IconPosition), {\n title: 'IconPosition',\n description: 'Side of the label the icon sits on',\n})\nexport type IconPosition = Static<typeof iconPositionSchema>\n\nexport const manualTriggerButtonStyleSchema = Type.Object(\n {\n text: Type.Optional(Type.String({ description: 'Handlebars template rendered as the button label' })),\n tooltip: Type.Optional(\n Type.String({\n description:\n 'Handlebars template rendered as the button tooltip. Written for whoever uses the listing, unlike `name`, which labels the trigger where it is administered',\n }),\n ),\n textColor: Type.Optional(Type.String({ description: 'Label colour' })),\n bgColor: Type.Optional(Type.String({ description: 'Background colour' })),\n icon: Type.Optional(Type.String({ description: 'Icon name, resolved by the consumer' })),\n iconPosition: Type.Optional(iconPositionSchema),\n },\n { title: 'ManualTriggerButtonStyle', description: 'Presentation of the button a manual trigger is offered on' },\n)\nexport type ManualTriggerButtonStyleData = Static<typeof manualTriggerButtonStyleSchema>\n\n// Same shape as the base style plus the conditions that select it. Every style field is optional:\n// an entry only overrides what it declares.\nexport const manualTriggerConditionalStyleSchema = Type.Intersect(\n [\n manualTriggerButtonStyleSchema,\n Type.Object({\n conditions: Type.Optional(\n Type.Union([Type.Object({}, { additionalProperties: true }), Type.Null()], {\n description: 'jsonLogic conditions selecting this style. Absent or null always applies',\n }),\n ),\n continueEvaluate: Type.Optional(\n Type.Boolean({ description: 'Keep evaluating later entries after this one matches' }),\n ),\n }),\n ],\n { title: 'ManualTriggerConditionalStyle' },\n)\nexport type ManualTriggerConditionalStyleData = Static<typeof manualTriggerConditionalStyleSchema>\n\n/**\n * Envelope for everything a manual trigger configures beyond its firing rules. Today it carries\n * whether an unmatched trigger is hidden or shown disabled, plus the button presentation;\n * `additionalProperties` stays open so a future concern can be added without another column.\n *\n * The two are deliberately separate concerns: `unmatchedBehavior` answers whether the button is\n * offered at all, which only the trigger `conditions` decide, while `styles`/`conditionalStyles`\n * only dress a button that is already going to be shown.\n */\nexport const manualTriggerConfigSchema = Type.Object(\n {\n unmatchedBehavior: Type.Optional(unmatchedBehaviorSchema),\n styles: Type.Optional(manualTriggerButtonStyleSchema),\n conditionalStyles: Type.Optional(Type.Array(manualTriggerConditionalStyleSchema)),\n },\n { title: 'ManualTriggerConfig', additionalProperties: true },\n)\nexport type ManualTriggerConfigData = Static<typeof manualTriggerConfigSchema>\n\nexport const manualTriggerSchema = Type.Object(\n {\n id: Type.Number({ description: 'Unique identifier of the manual trigger' }),\n project_id: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description: 'Project this trigger belongs to. Null when it is ecosystem-wide',\n }),\n ),\n name: triggerNameSchema,\n description: triggerDescriptionSchema,\n source: triggerSourceSchema,\n target: Type.String({\n maxLength: 255,\n description: 'Concrete model/designer/field the trigger is bound to. Always set: there is no wildcard',\n }),\n conditions: Type.Optional(\n Type.Union([Type.Object({}, { additionalProperties: true }), Type.Null()], {\n description:\n 'jsonLogic conditions (root object) evaluated to decide whether the trigger runs. Evaluated against the selected entity and the request context; `old` is always null, because the trigger is invoked explicitly and there is no previous state to compare against.',\n }),\n ),\n relations: Type.Optional(\n Type.Union([triggerRelationsSchema, Type.Null()], {\n description:\n 'Related models loaded into the context before evaluating the conditions and running the actions. Null means none.',\n }),\n ),\n enabled: Type.Optional(Type.Boolean({ description: 'Whether the trigger is active' })),\n retention_days: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description:\n 'Retention in days for the process_log entries of this trigger. Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write).',\n }),\n ),\n config: Type.Optional(\n Type.Union([manualTriggerConfigSchema, Type.Null()], {\n description: 'Configuration of the trigger beyond its firing rules. Null means none',\n }),\n ),\n },\n { title: 'ManualTrigger', description: 'Manual trigger definition (ecosystem level)' },\n)\nexport type ManualTriggerData = Static<typeof manualTriggerSchema>\n\n// Written out and closed, for the same reasons as `createAutomaticTriggerSchema` above.\nexport const createManualTriggerSchema = Type.Object(\n {\n name: triggerNameSchema,\n description: triggerDescriptionSchema,\n source: triggerSourceSchema,\n target: Type.String({\n maxLength: 255,\n description: 'Concrete model/designer/field the trigger is bound to. Always set: there is no wildcard',\n }),\n conditions: Type.Optional(\n Type.Union([Type.Object({}, { additionalProperties: true }), Type.Null()], {\n description:\n 'jsonLogic conditions (root object) evaluated to decide whether the trigger runs. Evaluated against the selected entity and the request context; `old` is always null, because the trigger is invoked explicitly and there is no previous state to compare against.',\n }),\n ),\n relations: Type.Optional(\n Type.Union([triggerRelationsSchema, Type.Null()], {\n description:\n 'Related models loaded into the context before evaluating the conditions and running the actions. Null means none.',\n }),\n ),\n enabled: Type.Optional(Type.Boolean({ description: 'Whether the trigger is active' })),\n retention_days: Type.Optional(\n Type.Union([Type.Number(), Type.Null()], {\n description:\n 'Retention in days for the process_log entries of this trigger. Origin — no inheritance: null or 0 = disabled (never expires); N (>0) = enabled (expires N days after write).',\n }),\n ),\n config: Type.Optional(\n Type.Union([manualTriggerConfigSchema, Type.Null()], {\n description: 'Configuration of the trigger beyond its firing rules. Null means none',\n }),\n ),\n actions: Type.Optional(triggerActionsInputSchema),\n },\n {\n title: 'CreateManualTrigger',\n description: 'Payload used to create a manual trigger, optionally with the actions it runs',\n additionalProperties: false,\n },\n)\nexport type CreateManualTriggerData = Static<typeof createManualTriggerSchema>\n\n// Same inheritance and semantics as `updateAutomaticTriggerSchema`.\nexport const updateManualTriggerSchema = Type.Partial(createManualTriggerSchema, {\n title: 'UpdateManualTrigger',\n description: 'Payload used to update a manual trigger, optionally replacing the actions it runs',\n additionalProperties: false,\n})\nexport type UpdateManualTriggerData = Static<typeof updateManualTriggerSchema>\n\n// What the listing endpoint returns per applicable manual trigger: enough to\n// render it and nothing more. Only triggers that are going to be shown are\n// listed, so `matched: false` means a button rendered disabled, never a hidden\n// one. `matched` is never an authorisation: running re-evaluates server-side.\nexport const manualTriggerAvailabilitySchema = Type.Object(\n {\n id: Type.Number({ description: 'Identifier of the manual trigger' }),\n name: triggerNameSchema,\n matched: Type.Boolean({\n description:\n 'Whether the trigger conditions hold for this entity right now. A trigger with no conditions always matches. False means the button is shown disabled: triggers configured to hide when unmatched are not listed',\n }),\n actionsCount: Type.Number({ description: 'Number of actions the trigger would run' }),\n relationsSkipped: Type.Optional(\n Type.Union([Type.String(), Type.Null()], {\n description:\n 'Reason the declared relations could not be loaded before evaluating, when that happened. Null otherwise',\n }),\n ),\n /**\n * Resolved server-side: conditional styles already evaluated and label already compiled. Absent\n * when the trigger has no config — the consumer then paints its own default button.\n */\n style: Type.Optional(manualTriggerButtonStyleSchema),\n },\n {\n title: 'ManualTriggerAvailability',\n description: 'A manual trigger applicable to an entity, with its conditions already evaluated',\n },\n)\nexport type ManualTriggerAvailability = Static<typeof manualTriggerAvailabilitySchema>\n\nexport const manualTriggerAvailabilityListSchema = Type.Object(\n { results: Type.Array(manualTriggerAvailabilitySchema, { description: 'Applicable manual triggers' }) },\n {\n title: 'ManualTriggerAvailabilityList',\n description: 'Manual triggers applicable to an entity, with their conditions evaluated',\n },\n)\n\n// Query the listing endpoint is called with: the entity the triggers are offered\n// on. `source`/`target` are the coordinates a manual trigger is bound to, and\n// `entityId` the concrete row.\nexport const manualTriggerAvailabilityQuerySchema = Type.Object(\n {\n source: triggerSourceSchema,\n target: Type.String({ maxLength: 255, description: 'Concrete model/designer/field the triggers are bound to' }),\n entityId: Type.Number({ description: 'Identifier of the entity the triggers would run against' }),\n },\n { title: 'ManualTriggerAvailabilityQuery', description: 'Entity the applicable manual triggers are listed for' },\n)\nexport type ManualTriggerAvailabilityQuery = Static<typeof manualTriggerAvailabilityQuerySchema>\n\n// Body of the run endpoint. It carries only the entity: `source`/`target` are\n// read from the trigger row, never from the client, so a trigger cannot be\n// re-pointed at an entity it was not bound to.\nexport const runManualTriggerSchema = Type.Object(\n { entityId: Type.Number({ description: 'Identifier of the entity to run the trigger against' }) },\n { title: 'RunManualTrigger', description: 'Payload used to run a manual trigger against an entity' },\n)\nexport type RunManualTriggerData = Static<typeof runManualTriggerSchema>\n\n// ── automatic_trigger_action (intermediate N:M automatic_trigger↔actions,\n// ordered) ──────────────────────────────────────────────────────────────\n// Dedicated table per trigger kind (no ORM polymorphism): `automatic_trigger_id`\n// is a real FK. `manual_trigger_action` is a future table for manual triggers.\n\nexport const automaticTriggerActionSchema = Type.Object(\n {\n id: Type.Number({ description: 'Unique identifier of the trigger-action link' }),\n automatic_trigger_id: Type.Number({\n description: 'Automatic trigger executing the action (FK to automatic_trigger)',\n }),\n action_id: Type.Number({ description: 'Action executed by the trigger (FK to actions)' }),\n execution_order: Type.Number({ description: 'Execution order of the action within the trigger' }),\n },\n { title: 'AutomaticTriggerAction', description: 'Ordered link between an automatic trigger and an action' },\n)\nexport type AutomaticTriggerActionData = Static<typeof automaticTriggerActionSchema>\n\n// ── manual_trigger_action (intermediate N:M manual_trigger↔actions,\n// ordered) ──────────────────────────────────────────────────────────────\n// Same dedicated-table-per-trigger-kind rule as `automatic_trigger_action`:\n// `manual_trigger_id` is a real FK, and the `actions` catalog is shared\n// between kinds — only the link table differs.\n\nexport const manualTriggerActionSchema = Type.Object(\n {\n id: Type.Number({ description: 'Unique identifier of the trigger-action link' }),\n manual_trigger_id: Type.Number({\n description: 'Manual trigger executing the action (FK to manual_trigger)',\n }),\n action_id: Type.Number({ description: 'Action executed by the trigger (FK to actions)' }),\n execution_order: Type.Number({ description: 'Execution order of the action within the trigger' }),\n },\n { title: 'ManualTriggerAction', description: 'Ordered link between a manual trigger and an action' },\n)\nexport type ManualTriggerActionData = Static<typeof manualTriggerActionSchema>\n\n// ── Declarative trigger↔actions contract ──────────────────────────────\n// The link tables are written as a whole through the `actions` field of the trigger payload,\n// never row by row. The array position IS the `execution_order`, so ordering can\n// never disagree with the list the client sent — which per-row endpoints allowed\n// (duplicate or gapped orders after a reorder). Shared by both trigger kinds:\n// only the parent FK differs, and that comes from the URL.\n//\n// The field is `actions`, not `action_ids`: items are no longer only ids (see\n// `triggerActionInputSchema`). Same body as the `actions` field of the create and\n// update trigger payloads, so the three ways of writing the set agree.\n\n// What create/update answer with. `actions` carries the resolved catalog rows —\n// not the link rows — so a client that sent actions inline learns the ids they\n// were given without chaining a second request. It is present only when the\n// request carried the field: a create/update that did not mention actions says\n// nothing about them, rather than reporting an empty set it never touched.\nconst withResolvedActions = Type.Object({\n actions: Type.Optional(Type.Array(actionSchema, { description: 'Actions the trigger runs, in execution order' })),\n})\n\nexport const automaticTriggerWithActionsSchema = Type.Intersect([automaticTriggerSchema, withResolvedActions], {\n title: 'AutomaticTriggerWithActions',\n description:\n 'An automatic trigger and, when the request wrote them or `includeActions` asked for them, the actions it runs',\n})\nexport type AutomaticTriggerWithActionsData = Static<typeof automaticTriggerWithActionsSchema>\n\nexport const manualTriggerWithActionsSchema = Type.Intersect([manualTriggerSchema, withResolvedActions], {\n title: 'ManualTriggerWithActions',\n description:\n 'A manual trigger and, when the request wrote them or `includeActions` asked for them, the actions it runs',\n})\nexport type ManualTriggerWithActionsData = Static<typeof manualTriggerWithActionsSchema>\n\n// ── Execution engine (pure, model-independent contract types) ──────────\n// Exported types the engine (service/helper) works with. Kept here (rather\n// than in service/helper) so they can be imported without pulling in\n// service/helper code. None of them references a model: the engine types\n// itself structurally (see the `*Like` shapes below).\n\nexport interface ActionEvent {\n source: TriggerSource\n target?: string | null\n operation?: TriggerOperation | null\n current: Record<string, unknown>\n old?: Record<string, unknown> | null\n}\n\n// Outcome of evaluating (and, when matched, running) a single trigger. Declared\n// as a TypeBox schema — not just an interface — because the manual-trigger run\n// endpoint returns it verbatim; `TriggerExecutionResult` is its static type, so\n// engine and endpoint cannot drift apart.\nexport const triggerExecutionResultSchema = Type.Object(\n {\n triggerId: Type.Number({ description: 'Identifier of the evaluated trigger' }),\n matched: Type.Boolean({ description: 'Whether the conditions held, so the actions were run' }),\n actionsRun: Type.Number({ description: 'Number of actions that were executed, whether they succeeded or not' }),\n actionsAborted: Type.Number({ description: 'Number of actions aborted by context validation' }),\n actionsFailed: Type.Number({\n description: 'Number of executed actions that ended in error. A subset of actionsRun',\n }),\n },\n { title: 'TriggerExecutionResult', description: 'Outcome of evaluating and running one trigger' },\n)\nexport type TriggerExecutionResult = Static<typeof triggerExecutionResultSchema>\n\n// ── Trigger-kind-agnostic engine contract ─────────────────────────────\n// The execution engine is shared by every kind of trigger: it consumes the\n// minimal structural shapes below rather than a concrete model, so a new kind\n// only has to resolve its own candidates and hand them over. The `*Like` types\n// are deliberately structural (not the Objection classes) so the engine never\n// imports a model to type itself — `AutomaticTrigger` / `AutomaticTriggerAction`\n// / `Action` satisfy them as they are, without a cast.\n\n/**\n * Structural shape the engine needs from an action it is about to run: the\n * identity it logs, the `type` that selects the validation/dispatch branch, the\n * config it executes with, and the retention it resolves `expires_at` from.\n * Deliberately loose (`type: string`, not `ActionType`) so an unknown type still\n * reaches the always-blocking context validation instead of failing to typecheck.\n */\nexport interface ActionLike {\n id: number\n type: string\n config: ActionConfig\n retention_days?: number | null\n}\n\n/** Ordered trigger↔action link in the minimal shape the engine consumes. */\nexport interface TriggerActionLike {\n action_id: number\n execution_order: number\n action?: ActionLike\n}\n\n/** Minimal shape of a trigger for the engine, whatever its kind. */\nexport interface EvaluableTrigger {\n id: number\n conditions?: object | null\n relations?: TriggerRelations | null\n retention_days?: number | null\n trigger_actions?: TriggerActionLike[]\n}\n\n/**\n * A batch of already-resolved candidates a trigger kind hands to the engine,\n * together with the event data they were resolved for. `relatedModel` is what\n * the evaluation `process_log` row is related to (the trigger table of that\n * kind), which is how trigger evaluations stay independently queryable per kind.\n */\nexport interface TriggerBatch<T extends EvaluableTrigger = EvaluableTrigger> {\n triggers: T[]\n relatedModel: string\n source: TriggerSource\n target?: string | null\n operation?: TriggerOperation | null\n current: Record<string, unknown>\n old?: Record<string, unknown> | null\n}\n\n// ── Context validation (pure data types) ────────────────────────────────\n\nexport interface ContextValidationResult {\n ok: boolean\n type: string\n baseErrors: { path: string; message: string }[]\n missingRefs: string[]\n referencedRefs: string[]\n overridableErrors: string[]\n}\n\n// Structural shape `buildEffectiveConfig`/`validateActionContext` need from an\n// action config — deliberately loose (not `ActionConfig`) so it works for any\n// current or future type without a cast at every call site.\nexport interface ActionConfigLike {\n payload?: Record<string, unknown>\n overridable?: string[]\n default?: Record<string, unknown>\n}\n\nexport interface EffectiveConfigResult {\n effective: Record<string, unknown>\n overridableErrors: string[]\n}\n\n// ── Route-level schemas ───────────────────────────────────────────────\n// Shared by every router of the module.\n\nexport const successResponseSchema = Type.Object(\n { success: Type.Boolean({ description: 'If the request success or not' }) },\n { title: 'ActionSystemSuccessResponse', description: 'Response when the request is successful' },\n)\n\nexport const withIdParamsSchema = Type.Intersect(\n [ecosystemProjectParamsSchema, Type.Object({ id: Type.Number({ description: 'Entity ID' }) })],\n { title: 'EcosystemProjectWithActionSystemIdParams', description: 'Params with ecosystem, project and entity ID' },\n)\n"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const
|
|
3
|
+
const dates_helper = require("./dates.helper-CDM5aEJG.cjs");
|
|
4
4
|
const globalLocale = require("./global-locale.cjs");
|
|
5
5
|
function resolveTrendWindow(nowMs, config, weekStart = globalLocale.getWeekStart(), utc = false) {
|
|
6
6
|
const { fromValue, unit } = config;
|
|
7
7
|
const toValue = config.toValue ?? 0;
|
|
8
8
|
return {
|
|
9
|
-
start:
|
|
10
|
-
end:
|
|
9
|
+
start: dates_helper.getPeriodStart(dates_helper.subtractPeriods(nowMs, fromValue, unit, utc), unit, weekStart, utc),
|
|
10
|
+
end: dates_helper.getPeriodStart(dates_helper.subtractPeriods(nowMs, toValue - 1, unit, utc), unit, weekStart, utc)
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
function resolveRelativePeriod(nowMs, { value, unit, excludeCurrentPeriod = false }, weekStart = globalLocale.getWeekStart(), utc = false) {
|
|
@@ -21,11 +21,11 @@ function resolveRelativePeriod(nowMs, { value, unit, excludeCurrentPeriod = fals
|
|
|
21
21
|
function resolveRelativeDateRange(nowMs, config, format, weekStart = globalLocale.getWeekStart(), utc = false) {
|
|
22
22
|
const { start, end } = resolveRelativePeriod(nowMs, config, weekStart, utc);
|
|
23
23
|
const cappedEnd = Math.min(end, nowMs);
|
|
24
|
-
const startDate =
|
|
24
|
+
const startDate = dates_helper.toCalendar(start, utc);
|
|
25
25
|
if (format === "discrete") {
|
|
26
|
-
return { start_date: startDate.toISOString(), end_date:
|
|
26
|
+
return { start_date: startDate.toISOString(), end_date: dates_helper.toCalendar(cappedEnd, utc).toISOString() };
|
|
27
27
|
}
|
|
28
|
-
const endDate = config.excludeCurrentPeriod ?
|
|
28
|
+
const endDate = config.excludeCurrentPeriod ? dates_helper.toCalendar(end, utc).subtract(1, "day") : dates_helper.toCalendar(cappedEnd, utc);
|
|
29
29
|
return { start_date: startDate.format("YYYY-MM-DD"), end_date: endDate.format("YYYY-MM-DD") };
|
|
30
30
|
}
|
|
31
31
|
exports.resolveRelativeDateRange = resolveRelativeDateRange;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ecosystem-container-widget.cjs","sources":["../../src/v2/modules/ecosystem/container-widget/container-widget.helper.ts"],"sourcesContent":["import { getPeriodStart, subtractPeriods, toCalendar } from '../../global/dates/dates.helper'\nimport { getWeekStart } from '../../global/locale/locale.helper'\nimport { type RelativePeriodConfig, type TrendWindow, type TrendWindowConfig } from './container-widget.schema'\n\n/**\n * Trend-widget time windows: the `value`/`from`/`to` → concrete-window resolution shared by the trend\n * widget (front, viewer-local) and the health-data time-range filter (backend, UTC). Built on the\n * generic calendar primitives of `global/dates` (which take the week start as a plain number) and the\n * locale week-start resolver of `global/locale`, so the trend-specific rules live here in one place.\n *\n * The `utc` flag is forwarded to the primitives: `false` (default) uses the ambient local calendar\n * (the front, viewer's timezone); `true` uses UTC (the backend). Both callers produce the exact same\n * window for the same inputs and timezone.\n */\n\n/**\n * Resolves a `from`/`to` range into a concrete, calendar-aligned window. `fromValue` and `toValue`\n * are inclusive bucket offsets (\"N units ago\"), so the window covers the **complete** buckets from\n * `toValue`-ago up to `fromValue`-ago (both ends inclusive) and spans `fromValue − toValue + 1`\n * buckets:\n *\n * - `start` (inclusive) = `getPeriodStart(now − fromValue·unit)` — start of the oldest bucket.\n * - `end` (exclusive ms bound) = `getPeriodStart(now − (toValue − 1)·unit)` — the start of the\n * bucket just after the newest included one, i.e. the end of the `toValue` bucket. When\n * `toValue = 0` this is the start of the next bucket, so the current (in-progress) bucket is\n * included in full — the window has no `now` cap, both extremes are complete buckets.\n *\n * Example (unit = week, weekStart = 1 Monday, now = Tue 2026-07-07):\n * from 2, to 0 → [Mon 22 Jun, Mon 13 Jul) (3 weeks, the current one included in full)\n * from 3, to 1 → [Mon 15 Jun, Mon 06 Jul) (3 complete weeks, the current one excluded)\n */\nexport function resolveTrendWindow(\n nowMs: number,\n config: TrendWindowConfig,\n weekStart: number = getWeekStart(),\n utc = false,\n): TrendWindow {\n const { fromValue, unit } = config\n const toValue = config.toValue ?? 0\n return {\n start: getPeriodStart(subtractPeriods(nowMs, fromValue, unit, utc), unit, weekStart, utc),\n end: getPeriodStart(subtractPeriods(nowMs, toValue - 1, unit, utc), unit, weekStart, utc),\n }\n}\n\n/**\n * Resolves a count-based relative period into a concrete window — the single source of truth for the\n * `value` → from/to mapping shared by every consumer (backend health-data filter, front trend widget),\n * so the \"current period counts as one of the N\" rule is defined exactly once:\n * - including it → `from = value - 1`, `to = 0` (buckets `value-1`…0 ago, ending now);\n * - excluding it → `from = value`, `to = 1` (`value` complete buckets before the current one).\n */\nexport function resolveRelativePeriod(\n nowMs: number,\n { value, unit, excludeCurrentPeriod = false }: RelativePeriodConfig,\n weekStart: number = getWeekStart(),\n utc = false,\n): TrendWindow {\n return resolveTrendWindow(\n nowMs,\n { fromValue: excludeCurrentPeriod ? value : value - 1, toValue: excludeCurrentPeriod ? 1 : 0, unit },\n weekStart,\n utc,\n )\n}\n\n/**\n * Resolves a count-based relative period into a formatted date range for the backend health-data\n * query, keeping the whole calculation (from/to mapping + the day-level rule + formatting) in one\n * shared place:\n * - `daily`: date-only `YYYY-MM-DD`; when excluding the current period the inclusive end becomes the\n * last completed calendar day (the day before the current period's start).\n * - `discrete`: ISO-8601.\n *\n * Unlike `resolveTrendWindow` (which now covers the current bucket in full for the front display), the\n * query end is **capped at `now`**: a data query must never reach past the present into the rest of an\n * in-progress bucket, so `to = 0` yields \"up to now\", not \"up to the end of the current bucket\".\n */\nexport function resolveRelativeDateRange(\n nowMs: number,\n config: RelativePeriodConfig,\n format: 'daily' | 'discrete',\n weekStart: number = getWeekStart(),\n utc = false,\n): { start_date: string; end_date: string } {\n const { start, end } = resolveRelativePeriod(nowMs, config, weekStart, utc)\n const cappedEnd = Math.min(end, nowMs)\n const startDate = toCalendar(start, utc)\n if (format === 'discrete') {\n return { start_date: startDate.toISOString(), end_date: toCalendar(cappedEnd, utc).toISOString() }\n }\n const endDate = config.excludeCurrentPeriod ? toCalendar(end, utc).subtract(1, 'day') : toCalendar(cappedEnd, utc)\n return { start_date: startDate.format('YYYY-MM-DD'), end_date: endDate.format('YYYY-MM-DD') }\n}\n"],"names":["getWeekStart","getPeriodStart","subtractPeriods","toCalendar"],"mappings":";;;;AA+BO,SAAS,mBACd,OACA,QACA,YAAoBA,aAAAA,aAAA,GACpB,MAAM,OACO;AACb,QAAM,EAAE,WAAW,KAAA,IAAS;AAC5B,QAAM,UAAU,OAAO,WAAW;AAClC,SAAO;AAAA,IACL,OAAOC,
|
|
1
|
+
{"version":3,"file":"ecosystem-container-widget.cjs","sources":["../../src/v2/modules/ecosystem/container-widget/container-widget.helper.ts"],"sourcesContent":["import { getPeriodStart, subtractPeriods, toCalendar } from '../../global/dates/dates.helper'\nimport { getWeekStart } from '../../global/locale/locale.helper'\nimport { type RelativePeriodConfig, type TrendWindow, type TrendWindowConfig } from './container-widget.schema'\n\n/**\n * Trend-widget time windows: the `value`/`from`/`to` → concrete-window resolution shared by the trend\n * widget (front, viewer-local) and the health-data time-range filter (backend, UTC). Built on the\n * generic calendar primitives of `global/dates` (which take the week start as a plain number) and the\n * locale week-start resolver of `global/locale`, so the trend-specific rules live here in one place.\n *\n * The `utc` flag is forwarded to the primitives: `false` (default) uses the ambient local calendar\n * (the front, viewer's timezone); `true` uses UTC (the backend). Both callers produce the exact same\n * window for the same inputs and timezone.\n */\n\n/**\n * Resolves a `from`/`to` range into a concrete, calendar-aligned window. `fromValue` and `toValue`\n * are inclusive bucket offsets (\"N units ago\"), so the window covers the **complete** buckets from\n * `toValue`-ago up to `fromValue`-ago (both ends inclusive) and spans `fromValue − toValue + 1`\n * buckets:\n *\n * - `start` (inclusive) = `getPeriodStart(now − fromValue·unit)` — start of the oldest bucket.\n * - `end` (exclusive ms bound) = `getPeriodStart(now − (toValue − 1)·unit)` — the start of the\n * bucket just after the newest included one, i.e. the end of the `toValue` bucket. When\n * `toValue = 0` this is the start of the next bucket, so the current (in-progress) bucket is\n * included in full — the window has no `now` cap, both extremes are complete buckets.\n *\n * Example (unit = week, weekStart = 1 Monday, now = Tue 2026-07-07):\n * from 2, to 0 → [Mon 22 Jun, Mon 13 Jul) (3 weeks, the current one included in full)\n * from 3, to 1 → [Mon 15 Jun, Mon 06 Jul) (3 complete weeks, the current one excluded)\n */\nexport function resolveTrendWindow(\n nowMs: number,\n config: TrendWindowConfig,\n weekStart: number = getWeekStart(),\n utc = false,\n): TrendWindow {\n const { fromValue, unit } = config\n const toValue = config.toValue ?? 0\n return {\n start: getPeriodStart(subtractPeriods(nowMs, fromValue, unit, utc), unit, weekStart, utc),\n end: getPeriodStart(subtractPeriods(nowMs, toValue - 1, unit, utc), unit, weekStart, utc),\n }\n}\n\n/**\n * Resolves a count-based relative period into a concrete window — the single source of truth for the\n * `value` → from/to mapping shared by every consumer (backend health-data filter, front trend widget),\n * so the \"current period counts as one of the N\" rule is defined exactly once:\n * - including it → `from = value - 1`, `to = 0` (buckets `value-1`…0 ago, ending now);\n * - excluding it → `from = value`, `to = 1` (`value` complete buckets before the current one).\n */\nexport function resolveRelativePeriod(\n nowMs: number,\n { value, unit, excludeCurrentPeriod = false }: RelativePeriodConfig,\n weekStart: number = getWeekStart(),\n utc = false,\n): TrendWindow {\n return resolveTrendWindow(\n nowMs,\n { fromValue: excludeCurrentPeriod ? value : value - 1, toValue: excludeCurrentPeriod ? 1 : 0, unit },\n weekStart,\n utc,\n )\n}\n\n/**\n * Resolves a count-based relative period into a formatted date range for the backend health-data\n * query, keeping the whole calculation (from/to mapping + the day-level rule + formatting) in one\n * shared place:\n * - `daily`: date-only `YYYY-MM-DD`; when excluding the current period the inclusive end becomes the\n * last completed calendar day (the day before the current period's start).\n * - `discrete`: ISO-8601.\n *\n * Unlike `resolveTrendWindow` (which now covers the current bucket in full for the front display), the\n * query end is **capped at `now`**: a data query must never reach past the present into the rest of an\n * in-progress bucket, so `to = 0` yields \"up to now\", not \"up to the end of the current bucket\".\n */\nexport function resolveRelativeDateRange(\n nowMs: number,\n config: RelativePeriodConfig,\n format: 'daily' | 'discrete',\n weekStart: number = getWeekStart(),\n utc = false,\n): { start_date: string; end_date: string } {\n const { start, end } = resolveRelativePeriod(nowMs, config, weekStart, utc)\n const cappedEnd = Math.min(end, nowMs)\n const startDate = toCalendar(start, utc)\n if (format === 'discrete') {\n return { start_date: startDate.toISOString(), end_date: toCalendar(cappedEnd, utc).toISOString() }\n }\n const endDate = config.excludeCurrentPeriod ? toCalendar(end, utc).subtract(1, 'day') : toCalendar(cappedEnd, utc)\n return { start_date: startDate.format('YYYY-MM-DD'), end_date: endDate.format('YYYY-MM-DD') }\n}\n"],"names":["getWeekStart","getPeriodStart","subtractPeriods","toCalendar"],"mappings":";;;;AA+BO,SAAS,mBACd,OACA,QACA,YAAoBA,aAAAA,aAAA,GACpB,MAAM,OACO;AACb,QAAM,EAAE,WAAW,KAAA,IAAS;AAC5B,QAAM,UAAU,OAAO,WAAW;AAClC,SAAO;AAAA,IACL,OAAOC,aAAAA,eAAeC,6BAAgB,OAAO,WAAW,MAAM,GAAG,GAAG,MAAM,WAAW,GAAG;AAAA,IACxF,KAAKD,aAAAA,eAAeC,6BAAgB,OAAO,UAAU,GAAG,MAAM,GAAG,GAAG,MAAM,WAAW,GAAG;AAAA,EAAA;AAE5F;AASO,SAAS,sBACd,OACA,EAAE,OAAO,MAAM,uBAAuB,MAAA,GACtC,YAAoBF,aAAAA,gBACpB,MAAM,OACO;AACb,SAAO;AAAA,IACL;AAAA,IACA,EAAE,WAAW,uBAAuB,QAAQ,QAAQ,GAAG,SAAS,uBAAuB,IAAI,GAAG,KAAA;AAAA,IAC9F;AAAA,IACA;AAAA,EAAA;AAEJ;AAcO,SAAS,yBACd,OACA,QACA,QACA,YAAoBA,0BAAA,GACpB,MAAM,OACoC;AAC1C,QAAM,EAAE,OAAO,QAAQ,sBAAsB,OAAO,QAAQ,WAAW,GAAG;AAC1E,QAAM,YAAY,KAAK,IAAI,KAAK,KAAK;AACrC,QAAM,YAAYG,aAAAA,WAAW,OAAO,GAAG;AACvC,MAAI,WAAW,YAAY;AACzB,WAAO,EAAE,YAAY,UAAU,eAAe,UAAUA,aAAAA,WAAW,WAAW,GAAG,EAAE,cAAY;AAAA,EACjG;AACA,QAAM,UAAU,OAAO,uBAAuBA,aAAAA,WAAW,KAAK,GAAG,EAAE,SAAS,GAAG,KAAK,IAAIA,aAAAA,WAAW,WAAW,GAAG;AACjH,SAAO,EAAE,YAAY,UAAU,OAAO,YAAY,GAAG,UAAU,QAAQ,OAAO,YAAY,EAAA;AAC5F;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as toCalendar, g as getPeriodStart, s as subtractPeriods } from "./
|
|
1
|
+
import { t as toCalendar, g as getPeriodStart, s as subtractPeriods } from "./dates.helper-XRCJ_UAQ.js";
|
|
2
2
|
import { getWeekStart } from "./global-locale.mjs";
|
|
3
3
|
function resolveTrendWindow(nowMs, config, weekStart = getWeekStart(), utc = false) {
|
|
4
4
|
const { fromValue, unit } = config;
|
package/dist/global-dates.cjs
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const dates_helper = require("./dates.helper-CDM5aEJG.cjs");
|
|
4
|
+
const API_DATETIME_ACCEPTED_NOTE = "Only this spelling is accepted: `T` as the separator (a space is a 400) and an offset with minutes, either `Z` or `±HH:MM` (`+01` and `+0100` are a 400). A lowercase `t`/`z` and a fractional second are fine";
|
|
5
|
+
const API_DATETIME_EXAMPLE = "2026-01-15T10:30:00Z";
|
|
6
|
+
const EMITTED_DATETIME_NOTE = "Emitted as ISO-8601 in UTC";
|
|
7
|
+
exports.getPeriodStart = dates_helper.getPeriodStart;
|
|
8
|
+
exports.subtractPeriods = dates_helper.subtractPeriods;
|
|
9
|
+
exports.API_DATETIME_ACCEPTED_NOTE = API_DATETIME_ACCEPTED_NOTE;
|
|
10
|
+
exports.API_DATETIME_EXAMPLE = API_DATETIME_EXAMPLE;
|
|
11
|
+
exports.EMITTED_DATETIME_NOTE = EMITTED_DATETIME_NOTE;
|
|
6
12
|
//# sourceMappingURL=global-dates.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-dates.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"global-dates.cjs","sources":["../../src/v2/modules/global/dates/dates.schema.ts"],"sourcesContent":["/**\n * Generic calendar unit shared by the date primitives. Pure type — the calendar arithmetic that\n * consumes it lives in `dates.helper.ts`. Domain modules that need a narrower alias (e.g. the trend\n * widget's `TrendWindowUnit`) build it on top of this one, so the set of units is defined once here.\n */\nexport type PeriodUnit = 'hour' | 'day' | 'week' | 'month'\n\n/**\n * Datetimes a request carries are declared with `format: 'date-time'` (RFC 3339): a date and a time\n * separated by `T`, an optional fraction, and a mandatory offset (`Z` or `±HH:MM`). The offset names\n * the instant; the service converts it to UTC for the column.\n *\n * The format is TypeBox's, not `ajv-formats`': routes compile through `TypeBoxValidatorCompiler`\n * (`app.ts`), and `ajv-formats` only validates the v1/Objection layer. Which one runs matters,\n * because TypeBox's is the stricter of the two and the difference is visible from outside:\n *\n * - It splits the value on `/T/i` alone, so `T`/`t` is the only separator — a space is a 400.\n * - Its offset is `(?:Z|([+-])(\\d\\d):(\\d\\d))?`, so the minutes are not optional: `+01` is a 400,\n * and so is `+0100` without the colon. That is what keeps `dayjs.utc()` from ever seeing an\n * hour-only offset, which it cannot read — it would fall back to parsing the value as a bare wall\n * clock and drop the offset, storing an instant an hour off with no error.\n * - It bounds the offset to `±23:59` (`tzH > 23 || tzM > 59`), so `+99:99` is a 400, and it checks\n * the calendar (`IsDate`, leap years included), so `2026-02-31T00:00:00Z` is a 400 before any\n * handler runs. The bound is RFC 3339's and not the tz database's, so it also takes offsets no\n * zone uses (`-14:00`, `+23:59`) — each still names one unambiguous instant, so what gets stored\n * is right.\n *\n * A `pattern` alongside the format would be dead weight: everything one could reasonably spell in it\n * is already a subset of what the format checks, and a stricter one drifts silently the day either\n * side changes. This was tried and removed (persei/tasks#294).\n *\n * This is that contract in one sentence, for the description of a request field: what the format\n * takes and, just as important, the two near-miss spellings a client is likely to try.\n */\nexport const API_DATETIME_ACCEPTED_NOTE =\n 'Only this spelling is accepted: `T` as the separator (a space is a 400) and an offset with minutes, either `Z` or `±HH:MM` (`+01` and `+0100` are a 400). A lowercase `t`/`z` and a fractional second are fine'\n\n/** An RFC 3339 datetime, for schema examples and API documentation. */\nexport const API_DATETIME_EXAMPLE = '2026-01-15T10:30:00Z'\n\n/**\n * What a datetime *coming out* of the API looks like, for the description of a response field.\n *\n * The outbound shape is narrower than the inbound one and no pattern states it: datetimes leave the\n * column as `Date` and are serialised by `fast-json-stringify` with `.toISOString()`, so the schema\n * never validates what is emitted. Saying it in the description is what puts it in the published\n * spec, which is where a client reads the contract from.\n */\nexport const EMITTED_DATETIME_NOTE = 'Emitted as ISO-8601 in UTC'\n"],"names":[],"mappings":";;;AAkCO,MAAM,6BACX;AAGK,MAAM,uBAAuB;AAU7B,MAAM,wBAAwB;;;;;;"}
|
package/dist/global-dates.mjs
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { g, s } from "./
|
|
1
|
+
import { g, s } from "./dates.helper-XRCJ_UAQ.js";
|
|
2
|
+
const API_DATETIME_ACCEPTED_NOTE = "Only this spelling is accepted: `T` as the separator (a space is a 400) and an offset with minutes, either `Z` or `±HH:MM` (`+01` and `+0100` are a 400). A lowercase `t`/`z` and a fractional second are fine";
|
|
3
|
+
const API_DATETIME_EXAMPLE = "2026-01-15T10:30:00Z";
|
|
4
|
+
const EMITTED_DATETIME_NOTE = "Emitted as ISO-8601 in UTC";
|
|
2
5
|
export {
|
|
6
|
+
API_DATETIME_ACCEPTED_NOTE,
|
|
7
|
+
API_DATETIME_EXAMPLE,
|
|
8
|
+
EMITTED_DATETIME_NOTE,
|
|
3
9
|
g as getPeriodStart,
|
|
4
10
|
s as subtractPeriods
|
|
5
11
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-dates.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"global-dates.mjs","sources":["../../src/v2/modules/global/dates/dates.schema.ts"],"sourcesContent":["/**\n * Generic calendar unit shared by the date primitives. Pure type — the calendar arithmetic that\n * consumes it lives in `dates.helper.ts`. Domain modules that need a narrower alias (e.g. the trend\n * widget's `TrendWindowUnit`) build it on top of this one, so the set of units is defined once here.\n */\nexport type PeriodUnit = 'hour' | 'day' | 'week' | 'month'\n\n/**\n * Datetimes a request carries are declared with `format: 'date-time'` (RFC 3339): a date and a time\n * separated by `T`, an optional fraction, and a mandatory offset (`Z` or `±HH:MM`). The offset names\n * the instant; the service converts it to UTC for the column.\n *\n * The format is TypeBox's, not `ajv-formats`': routes compile through `TypeBoxValidatorCompiler`\n * (`app.ts`), and `ajv-formats` only validates the v1/Objection layer. Which one runs matters,\n * because TypeBox's is the stricter of the two and the difference is visible from outside:\n *\n * - It splits the value on `/T/i` alone, so `T`/`t` is the only separator — a space is a 400.\n * - Its offset is `(?:Z|([+-])(\\d\\d):(\\d\\d))?`, so the minutes are not optional: `+01` is a 400,\n * and so is `+0100` without the colon. That is what keeps `dayjs.utc()` from ever seeing an\n * hour-only offset, which it cannot read — it would fall back to parsing the value as a bare wall\n * clock and drop the offset, storing an instant an hour off with no error.\n * - It bounds the offset to `±23:59` (`tzH > 23 || tzM > 59`), so `+99:99` is a 400, and it checks\n * the calendar (`IsDate`, leap years included), so `2026-02-31T00:00:00Z` is a 400 before any\n * handler runs. The bound is RFC 3339's and not the tz database's, so it also takes offsets no\n * zone uses (`-14:00`, `+23:59`) — each still names one unambiguous instant, so what gets stored\n * is right.\n *\n * A `pattern` alongside the format would be dead weight: everything one could reasonably spell in it\n * is already a subset of what the format checks, and a stricter one drifts silently the day either\n * side changes. This was tried and removed (persei/tasks#294).\n *\n * This is that contract in one sentence, for the description of a request field: what the format\n * takes and, just as important, the two near-miss spellings a client is likely to try.\n */\nexport const API_DATETIME_ACCEPTED_NOTE =\n 'Only this spelling is accepted: `T` as the separator (a space is a 400) and an offset with minutes, either `Z` or `±HH:MM` (`+01` and `+0100` are a 400). A lowercase `t`/`z` and a fractional second are fine'\n\n/** An RFC 3339 datetime, for schema examples and API documentation. */\nexport const API_DATETIME_EXAMPLE = '2026-01-15T10:30:00Z'\n\n/**\n * What a datetime *coming out* of the API looks like, for the description of a response field.\n *\n * The outbound shape is narrower than the inbound one and no pattern states it: datetimes leave the\n * column as `Date` and are serialised by `fast-json-stringify` with `.toISOString()`, so the schema\n * never validates what is emitted. Saying it in the description is what puts it in the published\n * spec, which is where a client reads the contract from.\n */\nexport const EMITTED_DATETIME_NOTE = 'Emitted as ISO-8601 in UTC'\n"],"names":[],"mappings":";AAkCO,MAAM,6BACX;AAGK,MAAM,uBAAuB;AAU7B,MAAM,wBAAwB;"}
|