@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.
- package/dist/data.d.ts +4 -4
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +16 -2
- package/dist/dataApi.d.ts +32 -2
- package/dist/dataApi.d.ts.map +1 -1
- package/dist/dataApi.js +11 -1
- package/dist/dataCompiler.d.ts +1 -0
- package/dist/dataCompiler.d.ts.map +1 -1
- package/dist/dataCompiler.js +3 -2
- package/dist/dataEngine.d.ts +17 -1
- package/dist/dataEngine.d.ts.map +1 -1
- package/dist/dataEngine.js +368 -10
- package/dist/dataImport.d.ts +3 -0
- package/dist/dataImport.d.ts.map +1 -1
- package/dist/dataImport.js +12 -2
- package/dist/dataRpc.d.ts +9 -1
- package/dist/dataRpc.d.ts.map +1 -1
- package/dist/dataSheet.d.ts +34 -0
- package/dist/dataSheet.d.ts.map +1 -0
- package/dist/dataSheet.js +100 -0
- package/dist/dataTriggers.d.ts +43 -4
- package/dist/dataTriggers.d.ts.map +1 -1
- package/dist/dataTriggers.js +401 -19
- package/dist/dataTypes.d.ts +23 -0
- package/dist/dataTypes.d.ts.map +1 -1
- package/dist/dataTypes.js +15 -1
- package/dist/index.d.ts +35 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/localizeDataError.d.ts.map +1 -1
- package/dist/localizeDataError.js +45 -1
- package/dist/schema.d.ts +33 -4
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +26 -3
- package/dist/untrustedContent.d.ts +7 -0
- package/dist/untrustedContent.d.ts.map +1 -1
- package/dist/untrustedContent.js +13 -1
- package/package.json +1 -1
- package/src/data.ts +19 -3
- package/src/dataApi.ts +14 -2
- package/src/dataCompiler.ts +4 -1
- package/src/dataEngine.ts +485 -10
- package/src/dataImport.ts +16 -1
- package/src/dataRpc.ts +10 -1
- package/src/dataSheet.ts +147 -0
- package/src/dataTriggers.ts +528 -19
- package/src/dataTypes.ts +46 -0
- package/src/index.ts +4 -0
- package/src/localizeDataError.ts +82 -1
- package/src/schema.ts +27 -3
- package/src/untrustedContent.ts +15 -0
package/dist/dataTriggers.js
CHANGED
|
@@ -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 = [
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
440
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
737
|
-
* is refused while any does, since they are only checked
|
|
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 =>
|
|
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;
|
package/dist/dataTypes.d.ts
CHANGED
|
@@ -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[];
|
package/dist/dataTypes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataTypes.d.ts","sourceRoot":"","sources":["../src/dataTypes.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,kJAepB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAIhE,eAAO,MAAM,kBAAkB,0CAA2C,CAAC;AAC3E,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7D,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAKvD,CAAC;AAEF,eAAO,MAAM,cAAc,8CAA+C,CAAC;AAC3E,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAIhE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,YAAY,CAAC;IAExB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;
|
|
1
|
+
{"version":3,"file":"dataTypes.d.ts","sourceRoot":"","sources":["../src/dataTypes.ts"],"names":[],"mappings":"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"}
|