@ganju/utils 0.0.21 → 0.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.
Files changed (51) hide show
  1. package/dist/data.d.ts +4 -4
  2. package/dist/data.d.ts.map +1 -1
  3. package/dist/data.js +16 -2
  4. package/dist/dataApi.d.ts +32 -2
  5. package/dist/dataApi.d.ts.map +1 -1
  6. package/dist/dataApi.js +11 -1
  7. package/dist/dataCompiler.d.ts +1 -0
  8. package/dist/dataCompiler.d.ts.map +1 -1
  9. package/dist/dataCompiler.js +3 -2
  10. package/dist/dataEngine.d.ts +17 -1
  11. package/dist/dataEngine.d.ts.map +1 -1
  12. package/dist/dataEngine.js +368 -10
  13. package/dist/dataImport.d.ts +3 -0
  14. package/dist/dataImport.d.ts.map +1 -1
  15. package/dist/dataImport.js +12 -2
  16. package/dist/dataRpc.d.ts +9 -1
  17. package/dist/dataRpc.d.ts.map +1 -1
  18. package/dist/dataSheet.d.ts +34 -0
  19. package/dist/dataSheet.d.ts.map +1 -0
  20. package/dist/dataSheet.js +100 -0
  21. package/dist/dataTriggers.d.ts +43 -4
  22. package/dist/dataTriggers.d.ts.map +1 -1
  23. package/dist/dataTriggers.js +401 -19
  24. package/dist/dataTypes.d.ts +23 -0
  25. package/dist/dataTypes.d.ts.map +1 -1
  26. package/dist/dataTypes.js +15 -1
  27. package/dist/index.d.ts +35 -4
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -0
  30. package/dist/localizeDataError.d.ts.map +1 -1
  31. package/dist/localizeDataError.js +45 -1
  32. package/dist/schema.d.ts +33 -4
  33. package/dist/schema.d.ts.map +1 -1
  34. package/dist/schema.js +26 -3
  35. package/dist/untrustedContent.d.ts +7 -0
  36. package/dist/untrustedContent.d.ts.map +1 -1
  37. package/dist/untrustedContent.js +13 -1
  38. package/package.json +1 -1
  39. package/src/data.ts +19 -3
  40. package/src/dataApi.ts +14 -2
  41. package/src/dataCompiler.ts +4 -1
  42. package/src/dataEngine.ts +485 -10
  43. package/src/dataImport.ts +16 -1
  44. package/src/dataRpc.ts +10 -1
  45. package/src/dataSheet.ts +147 -0
  46. package/src/dataTriggers.ts +528 -19
  47. package/src/dataTypes.ts +46 -0
  48. package/src/index.ts +4 -0
  49. package/src/localizeDataError.ts +82 -1
  50. package/src/schema.ts +27 -3
  51. package/src/untrustedContent.ts +15 -0
@@ -1,9 +1,35 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.renameColumnInTriggers = exports.triggersUsingColumn = exports.clearOutbox = exports.outboxSize = exports.deleteOutbox = exports.readOutbox = exports.collectTriggerEvents = exports.readEventRow = exports.ensureChangeCapture = exports.changeCaptureTriggers = exports.replaceManagedTriggers = exports.setTriggerEnabled = exports.dropTrigger = exports.saveTrigger = exports.getTrigger = exports.listTriggers = exports.normaliseTrigger = exports.normaliseCustomerAction = exports.renderCustomerMessage = exports.renderTriggerText = exports.conditionMatches = exports.utf8Bytes = exports.DATA_TRIGGER_MAX_MESSAGE_BYTES = exports.DATA_TRIGGER_MAX_ROW_BYTES = exports.DATA_OUTBOX_BATCH = exports.DATA_TRIGGER_MAX_EMAILS = exports.DATA_TRIGGER_MAX_TEXT = exports.DATA_TRIGGER_MAX_RATE = exports.DATA_TRIGGER_DEFAULT_RATE = exports.DATA_TRIGGER_MAX_DEPTH = exports.DATA_MAX_TRIGGERS = exports.DATA_TRIGGER_ACTIONS = exports.DATA_TRIGGER_EVENTS = void 0;
3
+ exports.scanTimeTriggers = exports.hasTimeTriggers = exports.syncTimeIndexes = exports.timeTriggersCountingFrom = exports.renameColumnInTriggers = exports.triggersUsingColumn = exports.clearOutbox = exports.outboxSize = exports.deleteOutbox = exports.readOutbox = exports.collectTriggerEvents = exports.readEventRow = exports.ensureChangeCapture = exports.changeCaptureTriggers = exports.replaceManagedTriggers = exports.setTriggerEnabled = exports.dropTrigger = exports.saveTrigger = exports.getTrigger = exports.listTriggers = exports.normaliseTrigger = exports.normaliseCustomerAction = exports.renderCustomerMessage = exports.renderTriggerText = exports.conditionMatches = exports.utf8Bytes = exports.DATA_TRIGGER_MAX_MESSAGE_BYTES = exports.DATA_TRIGGER_MAX_ROW_BYTES = exports.DATA_OUTBOX_BATCH = exports.DATA_TRIGGER_MAX_EMAILS = exports.DATA_TRIGGER_MAX_TEXT = exports.DATA_TRIGGER_MAX_RATE = exports.DATA_TRIGGER_DEFAULT_RATE = exports.DATA_TRIGGER_MAX_DEPTH = exports.DATA_MAX_TRIGGERS = exports.DATA_TRIGGER_ACTIONS = exports.DATA_TIME_EVENTS_PER_DAY = exports.DATA_TIME_ROWS_PER_SCAN = exports.DATA_TIME_EVENTS_PER_SCAN = exports.DATA_MAX_TIME_TRIGGERS = exports.DATA_TIME_MAX_MINUTES = exports.DATA_TIME_MIN_MINUTES = exports.DATA_TIME_SCAN_MS = exports.DATA_CHANGE_EVENTS = exports.DATA_TRIGGER_EVENTS = void 0;
4
4
  const dataCompiler_1 = require("./dataCompiler");
5
5
  const dataTypes_1 = require("./dataTypes");
6
- exports.DATA_TRIGGER_EVENTS = ['insert', 'update', 'delete'];
6
+ exports.DATA_TRIGGER_EVENTS = [
7
+ 'insert',
8
+ 'update',
9
+ 'delete',
10
+ 'time'
11
+ ];
12
+ // The events a write fires; "time" fires from the alarm's scan instead.
13
+ exports.DATA_CHANGE_EVENTS = ['insert', 'update', 'delete'];
14
+ // Time triggers: the alarm scans this often, a trigger waits at least this
15
+ // long (before or after its column's time) and at most 30 days, and a
16
+ // database holds this many of them.
17
+ exports.DATA_TIME_SCAN_MS = 300_000;
18
+ exports.DATA_TIME_MIN_MINUTES = 5;
19
+ exports.DATA_TIME_MAX_MINUTES = 43_200;
20
+ exports.DATA_MAX_TIME_TRIGGERS = 10;
21
+ // Events one time trigger fires per scan (more wait for the next), rows one
22
+ // scan looks at per trigger, and events a database's time triggers fire a
23
+ // day (UTC) before the one that goes past it is turned off.
24
+ exports.DATA_TIME_EVENTS_PER_SCAN = 100;
25
+ exports.DATA_TIME_ROWS_PER_SCAN = 1_000;
26
+ exports.DATA_TIME_EVENTS_PER_DAY = 2_000;
27
+ // A scan looks back at most this far: a due time missed for longer (the
28
+ // object down for a day) is skipped rather than sent late.
29
+ const TIME_LOOKBACK_MS = 86_400_000;
30
+ // Fired marks are kept this long past their due time; a scan never looks
31
+ // that far back, so older ones can't stop anything.
32
+ const FIRED_KEEP_MS = 2 * TIME_LOOKBACK_MS;
7
33
  exports.DATA_TRIGGER_ACTIONS = [
8
34
  'function',
9
35
  'webhook',
@@ -50,7 +76,9 @@ const isScalar = (value) => value === null ||
50
76
  (typeof value === 'number' && Number.isFinite(value));
51
77
  // Conditions
52
78
  const TEST_KEYS = ['changedTo', 'changedFrom', 'changed', 'equals'];
53
- const normaliseCondition = (table, raw, depth = 0, count = { tests: 0 }) => {
79
+ // A time trigger reads the row as it is, with nothing changing, so only
80
+ // `equals` means anything there.
81
+ const normaliseCondition = (table, raw, depth = 0, count = { tests: 0 }, equalsOnly = false) => {
54
82
  if (!isPlainObject(raw) || Object.keys(raw).length === 0) {
55
83
  throw new dataTypes_1.DataError('"when" is an object such as { "status": { "changedTo": "READY" } }.');
56
84
  }
@@ -64,7 +92,7 @@ const normaliseCondition = (table, raw, depth = 0, count = { tests: 0 }) => {
64
92
  raw[key].length === 0) {
65
93
  throw new dataTypes_1.DataError(`"${key}" is a non-empty list of conditions, alone in its object.`);
66
94
  }
67
- const list = raw[key].map(item => normaliseCondition(table, item, depth + 1, count));
95
+ const list = raw[key].map(item => normaliseCondition(table, item, depth + 1, count, equalsOnly));
68
96
  return key === 'all' ? { all: list } : { any: list };
69
97
  }
70
98
  const out = {};
@@ -81,6 +109,9 @@ const normaliseCondition = (table, raw, depth = 0, count = { tests: 0 }) => {
81
109
  if (!TEST_KEYS.includes(key)) {
82
110
  throw new dataTypes_1.DataError(`"when".${column}.${key}: a test is changedTo, changedFrom, changed or equals.`);
83
111
  }
112
+ if (equalsOnly && key !== 'equals') {
113
+ throw new dataTypes_1.DataError(`"when".${column}.${key}: a time trigger reads the row as it is when it comes due, so its tests are "equals" only.`);
114
+ }
84
115
  if (key === 'changed') {
85
116
  if (value !== true)
86
117
  throw new dataTypes_1.DataError(`"when".${column}.changed is true.`);
@@ -284,6 +315,39 @@ const normaliseCustomerAction = (schema, tableName, value) => {
284
315
  return normaliseAction(table, { customer: value });
285
316
  };
286
317
  exports.normaliseCustomerAction = normaliseCustomerAction;
318
+ // The columns a time trigger can count from, and whether `minutes` may be
319
+ // negative: "before" a row was created or changed has already passed.
320
+ const scheduleColumn = (table, name) => {
321
+ if (name === 'created_at' || name === 'updated_at')
322
+ return { kind: 'datetime', system: true };
323
+ const column = table.columns.find(c => c.name === name);
324
+ if (column?.type === 'datetime')
325
+ return { kind: 'datetime', system: false };
326
+ if (column?.type === 'date')
327
+ return { kind: 'date', system: false };
328
+ return null;
329
+ };
330
+ const normaliseSchedule = (table, raw) => {
331
+ if (!isPlainObject(raw)) {
332
+ throw new dataTypes_1.DataError('A time trigger needs "after": { "column", "minutes" }, such as { "column": "created_at", "minutes": 30 }.');
333
+ }
334
+ const name = raw.column;
335
+ const column = typeof name === 'string' ? scheduleColumn(table, name) : null;
336
+ if (typeof name !== 'string' || !column) {
337
+ throw new dataTypes_1.DataError(`"after.column" is created_at, updated_at or a date or datetime column of ${table.name}.`);
338
+ }
339
+ const minutes = raw.minutes;
340
+ if (typeof minutes !== 'number' ||
341
+ !Number.isInteger(minutes) ||
342
+ Math.abs(minutes) < exports.DATA_TIME_MIN_MINUTES ||
343
+ Math.abs(minutes) > exports.DATA_TIME_MAX_MINUTES) {
344
+ throw new dataTypes_1.DataError(`"after.minutes" is a whole number of minutes from ${exports.DATA_TIME_MIN_MINUTES} to ${exports.DATA_TIME_MAX_MINUTES} (30 days), negative for before the column's time.`);
345
+ }
346
+ if (minutes < 0 && column.system) {
347
+ throw new dataTypes_1.DataError(`"after.minutes" can't be negative on ${name}: that time has already passed when the row is written. Use a date or datetime column for a reminder before.`);
348
+ }
349
+ return { column: name, minutes };
350
+ };
287
351
  const defaultName = (table, on, action) => {
288
352
  const target = action.type === 'function'
289
353
  ? action.function.toLowerCase().replace(/[^a-z0-9_-]/g, '-')
@@ -301,7 +365,12 @@ const normaliseTrigger = (schema, input) => {
301
365
  }
302
366
  const on = input.on;
303
367
  if (!exports.DATA_TRIGGER_EVENTS.includes(on)) {
304
- throw new dataTypes_1.DataError('"on" is insert, update or delete.');
368
+ throw new dataTypes_1.DataError('"on" is insert, update, delete or time.');
369
+ }
370
+ const timed = on === 'time';
371
+ const hasAfter = input.after !== undefined && input.after !== null;
372
+ if (!timed && hasAfter) {
373
+ throw new dataTypes_1.DataError('"after" is for "on": "time" only.');
305
374
  }
306
375
  const action = normaliseAction(table, input);
307
376
  const unnamed = input.name === undefined || input.name === null;
@@ -332,9 +401,10 @@ const normaliseTrigger = (schema, input) => {
332
401
  : null,
333
402
  table,
334
403
  on: on,
404
+ after: timed ? normaliseSchedule(table, input.after) : null,
335
405
  when: input.when === undefined || input.when === null
336
406
  ? null
337
- : normaliseCondition(table, input.when),
407
+ : normaliseCondition(table, input.when, 0, { tests: 0 }, timed),
338
408
  action,
339
409
  enabled: typeof input.enabled === 'boolean' ? input.enabled : null,
340
410
  replace: input.replace !== false,
@@ -371,6 +441,7 @@ const toTrigger = (schema, row) => ({
371
441
  description: row.description ?? null,
372
442
  table: schema.tables.find(t => t.id === Number(row.table_id))?.name ?? '',
373
443
  on: row.event,
444
+ after: parse(row.schedule),
374
445
  when: parse(row.condition),
375
446
  action: parse(row.action),
376
447
  enabled: row.enabled === 1,
@@ -422,6 +493,13 @@ const upsert = (runner, schema, caller, trigger, managed) => {
422
493
  throw new dataTypes_1.DataError(`A database holds up to ${exports.DATA_MAX_TRIGGERS} triggers.`);
423
494
  }
424
495
  }
496
+ const wasTimed = existing?.event === 'time';
497
+ if (trigger.on === 'time' && !wasTimed) {
498
+ const timed = Number(runner.all("SELECT count(*) AS n FROM _ganju_triggers WHERE event = 'time'")[0]?.n ?? 0);
499
+ if (timed >= exports.DATA_MAX_TIME_TRIGGERS) {
500
+ throw new dataTypes_1.DataError(`A database holds up to ${exports.DATA_MAX_TIME_TRIGGERS} time triggers.`);
501
+ }
502
+ }
425
503
  // A webhook keeps its secret while it stays a webhook.
426
504
  const keep = existing?.secret && trigger.action.type === 'webhook'
427
505
  ? String(existing.secret)
@@ -436,10 +514,23 @@ const upsert = (runner, schema, caller, trigger, managed) => {
436
514
  const reason = enabled
437
515
  ? null
438
516
  : (existing?.disabled_reason ?? null);
439
- runner.all(`INSERT INTO _ganju_triggers (id, name, description, table_id, event, condition, action, enabled, disabled_reason, managed, rate_limit, secret, updated_at, updated_by)
440
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), ?)
517
+ // A time trigger counts due times from now: when it is new, when it
518
+ // counts from another column or delay, and when it is turned back on.
519
+ const schedule = trigger.after ? JSON.stringify(trigger.after) : null;
520
+ const restart = trigger.on === 'time' &&
521
+ (!wasTimed ||
522
+ existing?.schedule !== schedule ||
523
+ (enabled && existing?.enabled !== 1));
524
+ const scanState = restart
525
+ ? freshScan()
526
+ : trigger.on === 'time'
527
+ ? (existing?.scan_state ?? freshScan())
528
+ : null;
529
+ runner.all(`INSERT INTO _ganju_triggers (id, name, description, table_id, event, schedule, scan_state, condition, action, enabled, disabled_reason, managed, rate_limit, secret, updated_at, updated_by)
530
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), ?)
441
531
  ON CONFLICT(id) DO UPDATE SET name = excluded.name, description = excluded.description,
442
- table_id = excluded.table_id, event = excluded.event, condition = excluded.condition,
532
+ table_id = excluded.table_id, event = excluded.event, schedule = excluded.schedule,
533
+ scan_state = excluded.scan_state, condition = excluded.condition,
443
534
  action = excluded.action, enabled = excluded.enabled, disabled_reason = excluded.disabled_reason,
444
535
  managed = excluded.managed, rate_limit = excluded.rate_limit, secret = excluded.secret,
445
536
  updated_at = excluded.updated_at, updated_by = excluded.updated_by`, [
@@ -448,6 +539,8 @@ const upsert = (runner, schema, caller, trigger, managed) => {
448
539
  trigger.description,
449
540
  trigger.table.id,
450
541
  trigger.on,
542
+ schedule,
543
+ scanState,
451
544
  trigger.when ? JSON.stringify(trigger.when) : null,
452
545
  JSON.stringify(trigger.action),
453
546
  enabled ? 1 : 0,
@@ -457,7 +550,10 @@ const upsert = (runner, schema, caller, trigger, managed) => {
457
550
  secret,
458
551
  caller.actor
459
552
  ]);
460
- (0, exports.ensureChangeCapture)(runner, trigger.table);
553
+ if (trigger.on === 'time' || wasTimed)
554
+ (0, exports.syncTimeIndexes)(runner);
555
+ else
556
+ (0, exports.ensureChangeCapture)(runner, trigger.table);
461
557
  const saved = toTrigger(schema, findRow(runner, id));
462
558
  return secret && secret !== keep
463
559
  ? { trigger: saved, secret }
@@ -473,6 +569,12 @@ exports.saveTrigger = saveTrigger;
473
569
  const dropTrigger = (runner, nameOrId) => {
474
570
  const row = findRow(runner, nameOrId);
475
571
  runner.all('DELETE FROM _ganju_triggers WHERE id = ?', [row.id]);
572
+ if (row.event === 'time') {
573
+ runner.all('DELETE FROM _ganju_trigger_fired WHERE trigger_id = ?', [
574
+ row.id
575
+ ]);
576
+ (0, exports.syncTimeIndexes)(runner);
577
+ }
476
578
  return { dropped: String(row.name) };
477
579
  };
478
580
  exports.dropTrigger = dropTrigger;
@@ -480,7 +582,15 @@ exports.dropTrigger = dropTrigger;
480
582
  const setTriggerEnabled = (runner, schema, caller, nameOrId, enabled, reason = null) => {
481
583
  const row = findRow(runner, nameOrId);
482
584
  runner.all(`UPDATE _ganju_triggers SET enabled = ?, disabled_reason = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), updated_by = ? WHERE id = ?`, [enabled ? 1 : 0, enabled ? null : reason, caller.actor, row.id]);
483
- if (enabled) {
585
+ // Back on, a time trigger counts from now: what came due while it was off
586
+ // doesn't all go out at once.
587
+ if (enabled && row.event === 'time' && row.enabled !== 1) {
588
+ runner.all('UPDATE _ganju_triggers SET scan_state = ? WHERE id = ?', [
589
+ freshScan(),
590
+ row.id
591
+ ]);
592
+ }
593
+ if (enabled && row.event !== 'time') {
484
594
  const table = schema.tables.find(t => t.id === Number(row.table_id));
485
595
  if (table)
486
596
  (0, exports.ensureChangeCapture)(runner, table);
@@ -521,6 +631,9 @@ const replaceManagedTriggers = (runner, schema, caller, inputs) => {
521
631
  if (names.has(String(row.name)))
522
632
  continue;
523
633
  runner.all('DELETE FROM _ganju_triggers WHERE id = ?', [row.id]);
634
+ runner.all('DELETE FROM _ganju_trigger_fired WHERE trigger_id = ?', [
635
+ row.id
636
+ ]);
524
637
  removed.push(String(row.name));
525
638
  }
526
639
  const secrets = {};
@@ -530,6 +643,7 @@ const replaceManagedTriggers = (runner, schema, caller, inputs) => {
530
643
  secrets[saved.trigger.name] = saved.secret;
531
644
  return saved.trigger;
532
645
  });
646
+ (0, exports.syncTimeIndexes)(runner);
533
647
  return { triggers, removed, secrets };
534
648
  });
535
649
  };
@@ -701,13 +815,17 @@ const outboxSize = (runner) => Number(runner.all('SELECT count(*) AS n FROM _gan
701
815
  exports.outboxSize = outboxSize;
702
816
  // After a restore: events pending at the restored moment were sent then.
703
817
  // Those stored before `before` came back with the restore; later ones are
704
- // writes made since, and stay.
818
+ // writes made since, and stay. Time triggers count from now again, since
819
+ // what came due after the restored moment was sent then too.
705
820
  const clearOutbox = (runner, before) => {
706
821
  const n = Number(runner.all('SELECT count(*) AS n FROM _ganju_outbox WHERE created_at < ?', [
707
822
  before
708
823
  ])[0]?.n ?? 0);
709
824
  runner.all('DELETE FROM _ganju_outbox WHERE created_at < ?', [before]);
710
825
  runner.all('DELETE FROM _ganju_changes');
826
+ runner.all("UPDATE _ganju_triggers SET scan_state = ? WHERE event = 'time'", [
827
+ freshScan()
828
+ ]);
711
829
  return n;
712
830
  };
713
831
  exports.clearOutbox = clearOutbox;
@@ -733,13 +851,14 @@ const actionTexts = (action) => {
733
851
  }
734
852
  };
735
853
  /**
736
- * The triggers whose condition or {{field}} texts name a column. Dropping it
737
- * is refused while any does, since they are only checked when saved and
738
- * would quietly stop matching.
854
+ * The triggers whose condition, {{field}} texts or time column name a
855
+ * column. Dropping it is refused while any does, since they are only checked
856
+ * when saved and would quietly stop matching.
739
857
  */
740
858
  const triggersUsingColumn = (runner, tableId, column) => runner
741
- .all('SELECT name, condition, action FROM _ganju_triggers WHERE table_id = ? ORDER BY name', [tableId])
742
- .filter(row => conditionColumns(parse(row.condition)).includes(column) ||
859
+ .all('SELECT name, schedule, condition, action FROM _ganju_triggers WHERE table_id = ? ORDER BY name', [tableId])
860
+ .filter(row => parse(row.schedule)?.column === column ||
861
+ conditionColumns(parse(row.condition)).includes(column) ||
743
862
  actionTexts(parse(row.action)).some(text => templateFields(text).includes(column)))
744
863
  .map(row => String(row.name));
745
864
  exports.triggersUsingColumn = triggersUsingColumn;
@@ -788,10 +907,14 @@ const renameInAction = (action, from, to) => {
788
907
  const renameColumnInTriggers = (runner, tableId, from, to) => {
789
908
  const names = (0, exports.triggersUsingColumn)(runner, tableId, from);
790
909
  for (const name of names) {
791
- const row = runner.all('SELECT id, condition, action FROM _ganju_triggers WHERE name = ?', [name])[0];
910
+ const row = runner.all('SELECT id, schedule, condition, action FROM _ganju_triggers WHERE name = ?', [name])[0];
911
+ const schedule = parse(row.schedule);
792
912
  const condition = parse(row.condition);
793
913
  const action = parse(row.action);
794
- runner.all(`UPDATE _ganju_triggers SET condition = ?, action = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = ?`, [
914
+ runner.all(`UPDATE _ganju_triggers SET schedule = ?, condition = ?, action = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = ?`, [
915
+ schedule
916
+ ? JSON.stringify(schedule.column === from ? { ...schedule, column: to } : schedule)
917
+ : null,
795
918
  condition === null
796
919
  ? null
797
920
  : JSON.stringify(renameInCondition(condition, from, to)),
@@ -802,3 +925,262 @@ const renameColumnInTriggers = (runner, tableId, from, to) => {
802
925
  return names;
803
926
  };
804
927
  exports.renameColumnInTriggers = renameColumnInTriggers;
928
+ // Time triggers
929
+ // The time triggers counting from a column: it must stay a date or datetime.
930
+ const timeTriggersCountingFrom = (runner, tableId, column) => runner
931
+ .all("SELECT name, schedule FROM _ganju_triggers WHERE table_id = ? AND event = 'time' ORDER BY name", [tableId])
932
+ .filter(row => parse(row.schedule)?.column === column)
933
+ .map(row => String(row.name));
934
+ exports.timeTriggersCountingFrom = timeTriggersCountingFrom;
935
+ const freshScan = () => JSON.stringify({ through: (0, dataCompiler_1.isoSeconds)(new Date()) });
936
+ const shiftIso = (iso, minutes) => (0, dataCompiler_1.isoSeconds)(new Date(Date.parse(iso) + minutes * 60_000));
937
+ /**
938
+ * The dates of a date column whose due time falls in (from, to]: midnight
939
+ * in the database's zone plus `minutes`. Each date maps to its due time.
940
+ */
941
+ const dueDates = (from, to, minutes, timeZone) => {
942
+ const out = new Map();
943
+ const day = 86_400_000;
944
+ // A day either side covers any zone's offset.
945
+ const start = Math.floor((Date.parse(from) - minutes * 60_000) / day) * day - day;
946
+ const end = Date.parse(to) - minutes * 60_000 + day;
947
+ for (let at = start; at <= end; at += day) {
948
+ const date = new Date(at).toISOString().slice(0, 10);
949
+ const midnight = (0, dataCompiler_1.localDatetime)(`${date}T00:00:00`, timeZone);
950
+ if (!midnight || Number.isNaN(midnight.getTime()))
951
+ continue;
952
+ const due = (0, dataCompiler_1.isoSeconds)(new Date(midnight.getTime() + minutes * 60_000));
953
+ if (due > from && due <= to)
954
+ out.set(date, due);
955
+ }
956
+ return out;
957
+ };
958
+ // A time trigger's own index on its column, named apart from the table's
959
+ // (ix_ / ux_), which the table's own changes remake from its columns.
960
+ const timeIndexName = (tableId, column) => `it_${tableId}_${column}`;
961
+ /**
962
+ * Indexes the columns time triggers count from, and drops the ones no time
963
+ * trigger uses any more. A column the table already indexes (filterable or
964
+ * unique) needs nothing more. Cheap, so every scan runs it too: a table
965
+ * rebuilt for a column change loses the index and gets it back.
966
+ */
967
+ const syncTimeIndexes = (runner) => {
968
+ const wanted = new Map();
969
+ for (const row of runner.all("SELECT tr.schedule, t.id AS table_id, t.name AS table_name FROM _ganju_triggers tr JOIN _ganju_tables t ON t.id = tr.table_id WHERE tr.event = 'time'")) {
970
+ const after = parse(row.schedule);
971
+ if (!after)
972
+ continue;
973
+ const tableId = Number(row.table_id);
974
+ let name;
975
+ if (after.column === 'created_at' || after.column === 'updated_at') {
976
+ name = timeIndexName(tableId, after.column);
977
+ }
978
+ else {
979
+ const column = runner.all('SELECT id, filterable, is_unique FROM _ganju_columns WHERE table_id = ? AND name = ?', [tableId, after.column])[0];
980
+ if (!column || column.filterable === 1 || column.is_unique === 1)
981
+ continue;
982
+ name = timeIndexName(tableId, Number(column.id));
983
+ }
984
+ wanted.set(name, `CREATE INDEX IF NOT EXISTS "${name}" ON "${String(row.table_name)}"("${after.column}")`);
985
+ }
986
+ const existing = runner
987
+ .all("SELECT name FROM sqlite_master WHERE type = 'index' AND name LIKE 'it\\_%' ESCAPE '\\'")
988
+ .map(row => String(row.name));
989
+ for (const name of existing) {
990
+ if (!wanted.has(name))
991
+ runner.all(`DROP INDEX IF EXISTS "${name}"`);
992
+ }
993
+ for (const [name, sql] of wanted) {
994
+ if (!existing.includes(name))
995
+ runner.all(sql);
996
+ }
997
+ };
998
+ exports.syncTimeIndexes = syncTimeIndexes;
999
+ const hasTimeTriggers = (runner) => runner.all("SELECT 1 AS found FROM _ganju_triggers WHERE event = 'time' AND enabled = 1 LIMIT 1").length > 0;
1000
+ exports.hasTimeTriggers = hasTimeTriggers;
1001
+ const DAY_COUNT_KEY = 'time_events_day';
1002
+ /**
1003
+ * One scan: for each enabled time trigger, the rows that came due since its
1004
+ * last scan, matching its `when`, not fired for that due time before, become
1005
+ * events in the outbox. One transaction, so a crash fires nothing twice.
1006
+ */
1007
+ const scanTimeTriggers = (runner, schema, now = new Date()) => runner.transaction(() => {
1008
+ const result = { events: 0, disabled: [] };
1009
+ const rows = runner.all("SELECT * FROM _ganju_triggers WHERE event = 'time' AND enabled = 1 ORDER BY created_at, id");
1010
+ if (rows.length === 0)
1011
+ return result;
1012
+ (0, exports.syncTimeIndexes)(runner);
1013
+ const to = (0, dataCompiler_1.isoSeconds)(now);
1014
+ const floor = (0, dataCompiler_1.isoSeconds)(new Date(now.getTime() - TIME_LOOKBACK_MS));
1015
+ runner.all('DELETE FROM _ganju_trigger_fired WHERE due_at < ?', [
1016
+ (0, dataCompiler_1.isoSeconds)(new Date(now.getTime() - FIRED_KEEP_MS))
1017
+ ]);
1018
+ const today = to.slice(0, 10);
1019
+ const counted = parse(runner.all('SELECT value FROM _ganju_meta WHERE key = ?', [
1020
+ DAY_COUNT_KEY
1021
+ ])[0]?.value);
1022
+ let firedToday = counted?.day === today ? counted.count : 0;
1023
+ const artifactId = runner.all("SELECT value FROM _ganju_meta WHERE key = 'artifact_id'")[0]
1024
+ ?.value ?? '';
1025
+ const timeZone = schema.timeZone ?? undefined;
1026
+ const turnOff = (row, reason) => {
1027
+ runner.all(`UPDATE _ganju_triggers SET enabled = 0, disabled_reason = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), updated_by = 'system:time' WHERE id = ?`, [reason, row.id]);
1028
+ result.disabled.push({ name: String(row.name), reason });
1029
+ };
1030
+ for (const row of rows) {
1031
+ const trigger = toTrigger(schema, row);
1032
+ const table = schema.tables.find(t => t.id === Number(row.table_id));
1033
+ const after = trigger.after;
1034
+ if (!table || !after)
1035
+ continue;
1036
+ const kind = scheduleColumn(table, after.column)?.kind;
1037
+ if (!kind) {
1038
+ turnOff(row, `${table.name}.${after.column} is no longer a date or datetime column.`);
1039
+ continue;
1040
+ }
1041
+ const state = parse(row.scan_state) ?? {
1042
+ through: (0, dataCompiler_1.isoSeconds)(new Date(trigger.createdAt))
1043
+ };
1044
+ const from = state.through > floor ? state.through : floor;
1045
+ if (from >= to)
1046
+ continue;
1047
+ const column = `t."${after.column}"`;
1048
+ const params = [];
1049
+ let range;
1050
+ let due;
1051
+ if (kind === 'date') {
1052
+ const dates = dueDates(from, to, after.minutes, timeZone);
1053
+ if (dates.size === 0) {
1054
+ runner.all('UPDATE _ganju_triggers SET scan_state = ? WHERE id = ?', [
1055
+ JSON.stringify({ through: to }),
1056
+ row.id
1057
+ ]);
1058
+ continue;
1059
+ }
1060
+ due = `CASE ${column} ${[...dates].map(() => 'WHEN ? THEN ?').join(' ')} END`;
1061
+ for (const [date, at] of dates)
1062
+ params.push(date, at);
1063
+ range = `${column} IN (${[...dates.keys()].map(() => '?').join(', ')})`;
1064
+ params.push(...dates.keys());
1065
+ }
1066
+ else {
1067
+ const sign = after.minutes >= 0 ? '+' : '';
1068
+ due = `strftime('%Y-%m-%dT%H:%M:%SZ', ${column}, '${sign}${after.minutes} minutes')`;
1069
+ range = `${column} > ? AND ${column} <= ?`;
1070
+ params.push(shiftIso(from, -after.minutes), shiftIso(to, -after.minutes));
1071
+ }
1072
+ if (state.resume) {
1073
+ range += ` AND (${column} > ? OR (${column} = ? AND t.id > ?))`;
1074
+ params.push(state.resume.value, state.resume.value, state.resume.id);
1075
+ }
1076
+ params.push(trigger.id);
1077
+ // Read first, written after: nothing else runs while the read is open.
1078
+ let looked = 0;
1079
+ let more = false;
1080
+ let stoppedAt = null;
1081
+ // The last row this scan is done with: fired, or not matching.
1082
+ let done = null;
1083
+ let capped = false;
1084
+ const firing = [];
1085
+ for (const stored of runner.iterate(`SELECT x.* FROM (SELECT t.*, ${due} AS _ganju_due FROM "${table.name}" t WHERE ${range}) x
1086
+ WHERE NOT EXISTS (SELECT 1 FROM _ganju_trigger_fired f WHERE f.trigger_id = ? AND f.row_id = x.id AND f.due_at = x._ganju_due)
1087
+ ORDER BY x."${after.column}", x.id`, params)) {
1088
+ if (looked >= exports.DATA_TIME_ROWS_PER_SCAN ||
1089
+ firing.length >= exports.DATA_TIME_EVENTS_PER_SCAN) {
1090
+ more = true;
1091
+ break;
1092
+ }
1093
+ looked++;
1094
+ const at = {
1095
+ value: String(stored[after.column]),
1096
+ id: Number(stored.id)
1097
+ };
1098
+ const current = shape(table, JSON.stringify(stored));
1099
+ stoppedAt = at;
1100
+ if (!(0, exports.conditionMatches)(trigger.when, current, null)) {
1101
+ done = at;
1102
+ continue;
1103
+ }
1104
+ if (firedToday + firing.length >= exports.DATA_TIME_EVENTS_PER_DAY) {
1105
+ capped = true;
1106
+ break;
1107
+ }
1108
+ done = at;
1109
+ const customer = current?.participant_id;
1110
+ const message = {
1111
+ eventId: `evt_${crypto.randomUUID().replace(/-/g, '')}`,
1112
+ artifactId,
1113
+ triggerId: trigger.id,
1114
+ triggerName: trigger.name,
1115
+ table: table.name,
1116
+ event: 'time',
1117
+ rowId: at.id,
1118
+ row: current,
1119
+ previous: null,
1120
+ actor: null,
1121
+ depth: 1,
1122
+ at: now.toISOString(),
1123
+ dueAt: String(stored._ganju_due),
1124
+ ...(typeof customer === 'string' && customer
1125
+ ? { participantId: customer }
1126
+ : {})
1127
+ };
1128
+ if ((0, exports.utf8Bytes)(current) > exports.DATA_TRIGGER_MAX_ROW_BYTES) {
1129
+ message.row = null;
1130
+ message.truncated = true;
1131
+ }
1132
+ firing.push(message);
1133
+ }
1134
+ for (const message of firing) {
1135
+ runner.all('INSERT INTO _ganju_outbox (id, trigger_id, message) VALUES (?, ?, ?)', [message.eventId, trigger.id, JSON.stringify(message)]);
1136
+ runner.all('INSERT OR IGNORE INTO _ganju_trigger_fired (trigger_id, row_id, due_at) VALUES (?, ?, ?)', [trigger.id, message.rowId, message.dueAt ?? '']);
1137
+ }
1138
+ firedToday += firing.length;
1139
+ result.events += firing.length;
1140
+ // Events stored in this scan still run: the queue skips those of a
1141
+ // trigger that is off. So a trigger that fired some before the cap stays
1142
+ // on, carries on after them, and is turned off by the next scan.
1143
+ if (capped && firing.length > 0) {
1144
+ capped = false;
1145
+ more = true;
1146
+ stoppedAt = done;
1147
+ }
1148
+ if (capped) {
1149
+ const reason = `This database's time triggers fired ${exports.DATA_TIME_EVENTS_PER_DAY.toLocaleString('en-US')} events today (UTC), the most a day, so each one with more due today is turned off. Turn it back on to continue; due times from while it was off are skipped.`;
1150
+ turnOff(row, reason);
1151
+ // The queue records it on the runs list and tells the admins.
1152
+ const notice = {
1153
+ eventId: `evt_${crypto.randomUUID().replace(/-/g, '')}`,
1154
+ artifactId,
1155
+ triggerId: trigger.id,
1156
+ triggerName: trigger.name,
1157
+ table: table.name,
1158
+ event: 'time',
1159
+ rowId: stoppedAt?.id ?? 0,
1160
+ row: null,
1161
+ previous: null,
1162
+ actor: null,
1163
+ depth: 1,
1164
+ at: now.toISOString(),
1165
+ error: `Not run: ${reason}`,
1166
+ turnedOff: true
1167
+ };
1168
+ runner.all('INSERT INTO _ganju_outbox (id, trigger_id, message) VALUES (?, ?, ?)', [notice.eventId, trigger.id, JSON.stringify(notice)]);
1169
+ continue;
1170
+ }
1171
+ // Stopped early: the next scan carries on after the last row looked at,
1172
+ // within the same window. Otherwise the window is done.
1173
+ const next = more && stoppedAt
1174
+ ? { through: from, resume: stoppedAt }
1175
+ : { through: to };
1176
+ runner.all('UPDATE _ganju_triggers SET scan_state = ? WHERE id = ?', [
1177
+ JSON.stringify(next),
1178
+ row.id
1179
+ ]);
1180
+ }
1181
+ if (result.events > 0) {
1182
+ runner.all('INSERT INTO _ganju_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value', [DAY_COUNT_KEY, JSON.stringify({ day: today, count: firedToday })]);
1183
+ }
1184
+ return result;
1185
+ });
1186
+ exports.scanTimeTriggers = scanTimeTriggers;
@@ -30,8 +30,29 @@ export interface DataTableInput {
30
30
  access?: DataAccess;
31
31
  customerScoped?: boolean;
32
32
  history?: boolean;
33
+ source?: DataTableSource | null;
33
34
  columns: DataColumnInput[];
34
35
  }
36
+ export interface DataTableSource {
37
+ uri: string;
38
+ sheet?: string | null;
39
+ key: string;
40
+ title?: string | null;
41
+ }
42
+ export interface DataTableSync {
43
+ at: string;
44
+ status: 'ok' | 'failed';
45
+ inserted: number;
46
+ updated: number;
47
+ deleted: number;
48
+ unchanged: number;
49
+ error: string | null;
50
+ version?: string | null;
51
+ stale?: boolean;
52
+ }
53
+ export declare const DATA_SHEET_SYNC_ACTOR = "system:sheet-sync";
54
+ export declare const DATA_SHEET_MAX_BYTES: number;
55
+ export declare const DATA_SHEET_MAX_ROWS = 20000;
35
56
  export interface DataColumn {
36
57
  id: number;
37
58
  name: string;
@@ -61,6 +82,8 @@ export interface DataTable {
61
82
  history: boolean;
62
83
  position: number;
63
84
  columns: DataColumn[];
85
+ source?: DataTableSource | null;
86
+ sync?: DataTableSync | null;
64
87
  }
65
88
  export interface DataSchema {
66
89
  tables: DataTable[];
@@ -1 +1 @@
1
- {"version":3,"file":"dataTypes.d.ts","sourceRoot":"","sources":["../src/dataTypes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dataTypes.d.ts","sourceRoot":"","sources":["../src/dataTypes.ts"],"names":[],"mappings":"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"}