@brandboostinggmbh/observable-workflows 0.7.2 → 0.7.3

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/index.d.ts CHANGED
@@ -8,6 +8,15 @@ declare function createStepContext(context: StepContextOptions): Promise<{
8
8
  }>;
9
9
 
10
10
  //#endregion
11
+ //#region src/observableWorkflows/migrations.d.ts
12
+
13
+ /**
14
+ * Main migration function that ensures all tables exist and are up-to-date.
15
+ * This function is idempotent and can be safely run multiple times.
16
+ *
17
+ * @param db - D1Database instance
18
+ */
19
+ declare function ensureTables(db: D1Database): Promise<void>; //#endregion
11
20
  //#region src/observableWorkflows/helperFunctions.d.ts
12
21
  declare function finalizeWorkflowRecord(options: InternalWorkflowContextOptions, {
13
22
  workflowStatus,
@@ -113,11 +122,6 @@ declare function deserializeWithExternalStorage(data: string | null, externalRef
113
122
  * - timestamp: number (timestamp)
114
123
  * - type: string (info, error, warning)
115
124
  */
116
- /**
117
- * Ensure that the required tables exist in D1 (SQLite) and migrate schema if needed.
118
- * This function is idempotent and ensures the schema matches the current requirements.
119
- */
120
- declare function ensureTables(db: D1Database): Promise<void>;
121
125
  declare function workflowTableRowToWorkflowRun(row: {
122
126
  instanceId: string;
123
127
  workflowType: string;
package/dist/index.js CHANGED
@@ -1,3 +1,196 @@
1
+ //#region src/observableWorkflows/migrations.ts
2
+ /**
3
+ * Detect the current schema version for each table
4
+ */
5
+ async function detectSchemaVersion(db) {
6
+ const workflowTableInfo = await db.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='WorkflowTable'`).first();
7
+ let workflowTable = "missing";
8
+ if (workflowTableInfo) {
9
+ const hasInputRef = workflowTableInfo.sql.includes("inputRef");
10
+ const inputHasNotNull = workflowTableInfo.sql.includes("input TEXT NOT NULL");
11
+ if (hasInputRef && !inputHasNotNull) workflowTable = "v2+";
12
+ else if (!hasInputRef && inputHasNotNull) workflowTable = "v1";
13
+ else workflowTable = "v1";
14
+ }
15
+ const stepTableInfo = await db.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='StepTable'`).first();
16
+ let stepTable = "missing";
17
+ if (stepTableInfo) {
18
+ const hasResultRef = stepTableInfo.sql.includes("resultRef");
19
+ const hasErrorRef = stepTableInfo.sql.includes("errorRef");
20
+ if (hasResultRef && hasErrorRef) stepTable = "v2+";
21
+ else stepTable = "v1";
22
+ }
23
+ const logTableInfo = await db.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='LogTable'`).first();
24
+ let logTable = "missing";
25
+ if (logTableInfo) logTable = "v1";
26
+ const workflowPropertiesInfo = await db.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='WorkflowProperties'`).first();
27
+ let workflowProperties = "missing";
28
+ if (workflowPropertiesInfo) workflowProperties = "v1";
29
+ return {
30
+ workflowTable,
31
+ stepTable,
32
+ logTable,
33
+ workflowProperties
34
+ };
35
+ }
36
+ /**
37
+ * Create or migrate WorkflowTable to the latest schema
38
+ */
39
+ async function migrateWorkflowTable(db, currentVersion) {
40
+ if (currentVersion === "missing") {
41
+ await db.prepare(
42
+ /* sql */
43
+ `CREATE TABLE WorkflowTable (
44
+ instanceId TEXT NOT NULL,
45
+ workflowType TEXT NOT NULL,
46
+ workflowName TEXT NOT NULL,
47
+ workflowMetadata TEXT NOT NULL,
48
+ input TEXT,
49
+ inputRef TEXT,
50
+ tenantId TEXT NOT NULL,
51
+ workflowStatus TEXT NOT NULL,
52
+ startTime INTEGER NOT NULL,
53
+ endTime INTEGER,
54
+ parentInstanceId TEXT,
55
+ PRIMARY KEY (instanceId)
56
+ )`
57
+ ).run();
58
+ return;
59
+ }
60
+ if (currentVersion === "v1") {
61
+ const workflowTableInfo = await db.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='WorkflowTable'`).first();
62
+ const hasInputRef = workflowTableInfo.sql.includes("inputRef");
63
+ if (!hasInputRef) await db.prepare(`ALTER TABLE WorkflowTable ADD COLUMN inputRef TEXT`).run();
64
+ const inputHasNotNull = workflowTableInfo.sql.includes("input TEXT NOT NULL");
65
+ if (inputHasNotNull) await db.batch([
66
+ db.prepare(
67
+ /* sql */
68
+ `CREATE TABLE WorkflowTable_new (
69
+ instanceId TEXT NOT NULL,
70
+ workflowType TEXT NOT NULL,
71
+ workflowName TEXT NOT NULL,
72
+ workflowMetadata TEXT NOT NULL,
73
+ input TEXT,
74
+ inputRef TEXT,
75
+ tenantId TEXT NOT NULL,
76
+ workflowStatus TEXT NOT NULL,
77
+ startTime INTEGER NOT NULL,
78
+ endTime INTEGER,
79
+ parentInstanceId TEXT,
80
+ PRIMARY KEY (instanceId)
81
+ )`
82
+ ),
83
+ db.prepare(
84
+ /* sql */
85
+ `INSERT INTO WorkflowTable_new
86
+ SELECT instanceId, workflowType, workflowName, workflowMetadata,
87
+ CASE WHEN input = '' THEN NULL ELSE input END as input,
88
+ inputRef, tenantId, workflowStatus, startTime, endTime, parentInstanceId
89
+ FROM WorkflowTable`
90
+ ),
91
+ db.prepare(`DROP TABLE WorkflowTable`),
92
+ db.prepare(`ALTER TABLE WorkflowTable_new RENAME TO WorkflowTable`)
93
+ ]);
94
+ }
95
+ }
96
+ /**
97
+ * Create or migrate StepTable to the latest schema
98
+ */
99
+ async function migrateStepTable(db, currentVersion) {
100
+ if (currentVersion === "missing") {
101
+ await db.prepare(
102
+ /* sql */
103
+ `CREATE TABLE StepTable (
104
+ instanceId TEXT NOT NULL,
105
+ stepName TEXT NOT NULL,
106
+ stepStatus TEXT NOT NULL,
107
+ stepMetadata TEXT NOT NULL,
108
+ startTime INTEGER NOT NULL,
109
+ endTime INTEGER,
110
+ result TEXT,
111
+ error TEXT,
112
+ resultRef TEXT,
113
+ errorRef TEXT,
114
+ tenantId TEXT NOT NULL,
115
+ PRIMARY KEY (instanceId, stepName)
116
+ )`
117
+ ).run();
118
+ return;
119
+ }
120
+ if (currentVersion === "v1") {
121
+ const stepTableInfo = await db.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='StepTable'`).first();
122
+ const hasResultRef = stepTableInfo.sql.includes("resultRef");
123
+ const hasErrorRef = stepTableInfo.sql.includes("errorRef");
124
+ if (!hasResultRef) await db.prepare(`ALTER TABLE StepTable ADD COLUMN resultRef TEXT`).run();
125
+ if (!hasErrorRef) await db.prepare(`ALTER TABLE StepTable ADD COLUMN errorRef TEXT`).run();
126
+ }
127
+ }
128
+ /**
129
+ * Create or migrate LogTable to the latest schema
130
+ */
131
+ async function migrateLogTable(db, currentVersion) {
132
+ if (currentVersion === "missing") {
133
+ await db.prepare(
134
+ /* sql */
135
+ `CREATE TABLE LogTable (
136
+ instanceId TEXT NOT NULL,
137
+ stepName TEXT,
138
+ log TEXT NOT NULL,
139
+ timestamp INTEGER NOT NULL,
140
+ type TEXT NOT NULL,
141
+ logOrder INTEGER NOT NULL,
142
+ tenantId TEXT NOT NULL,
143
+ FOREIGN KEY (instanceId, stepName) REFERENCES StepTable(instanceId, stepName)
144
+ DEFERRABLE INITIALLY DEFERRED
145
+ )`
146
+ ).run();
147
+ return;
148
+ }
149
+ }
150
+ /**
151
+ * Create or migrate WorkflowProperties table to the latest schema
152
+ */
153
+ async function migrateWorkflowPropertiesTable(db, currentVersion) {
154
+ if (currentVersion === "missing") {
155
+ await db.prepare(
156
+ /* sql */
157
+ `CREATE TABLE WorkflowProperties (
158
+ instanceId TEXT NOT NULL,
159
+ key TEXT NOT NULL,
160
+ value TEXT NOT NULL,
161
+ valueType TEXT NOT NULL,
162
+ tenantId TEXT NOT NULL,
163
+ PRIMARY KEY (instanceId, key),
164
+ FOREIGN KEY (instanceId) REFERENCES WorkflowTable(instanceId)
165
+ ON DELETE CASCADE
166
+ )`
167
+ ).run();
168
+ return;
169
+ }
170
+ }
171
+ /**
172
+ * Create necessary indexes
173
+ */
174
+ async function createIndexes(db) {
175
+ await db.prepare(`CREATE INDEX IF NOT EXISTS idx_workflows_parent_instance_id ON WorkflowTable (parentInstanceId)`).run();
176
+ }
177
+ /**
178
+ * Main migration function that ensures all tables exist and are up-to-date.
179
+ * This function is idempotent and can be safely run multiple times.
180
+ *
181
+ * @param db - D1Database instance
182
+ */
183
+ async function ensureTables(db) {
184
+ await db.prepare(`PRAGMA foreign_keys = ON`).run();
185
+ const schemaVersion = await detectSchemaVersion(db);
186
+ await migrateWorkflowTable(db, schemaVersion.workflowTable);
187
+ await migrateStepTable(db, schemaVersion.stepTable);
188
+ await migrateLogTable(db, schemaVersion.logTable);
189
+ await migrateWorkflowPropertiesTable(db, schemaVersion.workflowProperties);
190
+ await createIndexes(db);
191
+ }
192
+
193
+ //#endregion
1
194
  //#region src/observableWorkflows/helperFunctions.ts
2
195
  function finalizeWorkflowRecord(options, { workflowStatus, endTime, instanceId }) {
3
196
  return options.D1.prepare(
@@ -145,117 +338,6 @@ function safeConsoleArgsToString(message, ...optionalParams) {
145
338
  * - timestamp: number (timestamp)
146
339
  * - type: string (info, error, warning)
147
340
  */
148
- /**
149
- * Ensure that the required tables exist in D1 (SQLite) and migrate schema if needed.
150
- * This function is idempotent and ensures the schema matches the current requirements.
151
- */
152
- async function ensureTables(db) {
153
- await db.prepare(`PRAGMA foreign_keys = ON`).run();
154
- const workflowTableInfo = await db.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='WorkflowTable'`).first();
155
- if (!workflowTableInfo) await db.prepare(
156
- /* sql */
157
- `CREATE TABLE WorkflowTable (
158
- instanceId TEXT NOT NULL,
159
- workflowType TEXT NOT NULL,
160
- workflowName TEXT NOT NULL,
161
- workflowMetadata TEXT NOT NULL,
162
- input TEXT,
163
- inputRef TEXT,
164
- tenantId TEXT NOT NULL,
165
- workflowStatus TEXT NOT NULL,
166
- startTime INTEGER NOT NULL,
167
- endTime INTEGER,
168
- parentInstanceId TEXT,
169
- PRIMARY KEY (instanceId)
170
- )`
171
- ).run();
172
- else {
173
- const hasInputRef = workflowTableInfo.sql.includes("inputRef");
174
- const inputHasNotNull = workflowTableInfo.sql.includes("input TEXT NOT NULL");
175
- if (!hasInputRef) await db.prepare(`ALTER TABLE WorkflowTable ADD COLUMN inputRef TEXT`).run();
176
- if (inputHasNotNull) await db.batch([
177
- db.prepare(
178
- /* sql */
179
- `CREATE TABLE WorkflowTable_new (
180
- instanceId TEXT NOT NULL,
181
- workflowType TEXT NOT NULL,
182
- workflowName TEXT NOT NULL,
183
- workflowMetadata TEXT NOT NULL,
184
- input TEXT,
185
- inputRef TEXT,
186
- tenantId TEXT NOT NULL,
187
- workflowStatus TEXT NOT NULL,
188
- startTime INTEGER NOT NULL,
189
- endTime INTEGER,
190
- parentInstanceId TEXT,
191
- PRIMARY KEY (instanceId)
192
- )`
193
- ),
194
- db.prepare(
195
- /* sql */
196
- `INSERT INTO WorkflowTable_new
197
- SELECT instanceId, workflowType, workflowName, workflowMetadata,
198
- CASE WHEN input = '' THEN NULL ELSE input END as input,
199
- inputRef, tenantId, workflowStatus, startTime, endTime, parentInstanceId
200
- FROM WorkflowTable`
201
- ),
202
- db.prepare(`DROP TABLE WorkflowTable`),
203
- db.prepare(`ALTER TABLE WorkflowTable_new RENAME TO WorkflowTable`)
204
- ]);
205
- }
206
- const stepTableInfo = await db.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='StepTable'`).first();
207
- if (!stepTableInfo) await db.prepare(
208
- /* sql */
209
- `CREATE TABLE StepTable (
210
- instanceId TEXT NOT NULL,
211
- stepName TEXT NOT NULL,
212
- stepStatus TEXT NOT NULL,
213
- stepMetadata TEXT NOT NULL,
214
- startTime INTEGER NOT NULL,
215
- endTime INTEGER,
216
- result TEXT,
217
- error TEXT,
218
- resultRef TEXT,
219
- errorRef TEXT,
220
- tenantId TEXT NOT NULL,
221
- PRIMARY KEY (instanceId, stepName)
222
- )`
223
- ).run();
224
- else {
225
- const hasResultRef = stepTableInfo.sql.includes("resultRef");
226
- const hasErrorRef = stepTableInfo.sql.includes("errorRef");
227
- if (!hasResultRef) await db.prepare(`ALTER TABLE StepTable ADD COLUMN resultRef TEXT`).run();
228
- if (!hasErrorRef) await db.prepare(`ALTER TABLE StepTable ADD COLUMN errorRef TEXT`).run();
229
- }
230
- await db.prepare(
231
- /* sql */
232
- `CREATE TABLE IF NOT EXISTS LogTable (
233
- instanceId TEXT NOT NULL,
234
- stepName TEXT,
235
- log TEXT NOT NULL,
236
- timestamp INTEGER NOT NULL,
237
- type TEXT NOT NULL,
238
- logOrder INTEGER NOT NULL,
239
- tenantId TEXT NOT NULL,
240
- FOREIGN KEY (instanceId, stepName) REFERENCES StepTable(instanceId, stepName)
241
- DEFERRABLE INITIALLY DEFERRED
242
- )`
243
- ).run();
244
- await db.prepare(
245
- /* sql */
246
- `CREATE TABLE IF NOT EXISTS WorkflowProperties (
247
- instanceId TEXT NOT NULL,
248
- key TEXT NOT NULL,
249
- value TEXT NOT NULL,
250
- valueType TEXT NOT NULL,
251
- tenantId TEXT NOT NULL,
252
- PRIMARY KEY (instanceId, key),
253
- FOREIGN KEY (instanceId) REFERENCES WorkflowTable(instanceId)
254
- ON DELETE CASCADE
255
- )`
256
- ).run();
257
- await db.prepare(`CREATE INDEX IF NOT EXISTS idx_workflows_parent_instance_id ON WorkflowTable (parentInstanceId)`).run();
258
- }
259
341
  async function workflowTableRowToWorkflowRun(row, serializer, externalBlobStorage) {
260
342
  const input = await deserializeWithExternalStorage(row.input, row.inputRef, serializer, externalBlobStorage);
261
343
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brandboostinggmbh/observable-workflows",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "My awesome typescript library",
5
5
  "type": "module",
6
6
  "license": "MIT",