@brandboostinggmbh/observable-workflows 0.8.5 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -1
- package/dist/index.js +111 -64
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -406,11 +406,12 @@ declare const createLogAccessor: (context: {
|
|
|
406
406
|
limit?: number;
|
|
407
407
|
offset?: number;
|
|
408
408
|
instanceId?: string;
|
|
409
|
+
populateData?: boolean;
|
|
409
410
|
}): Promise<Step[]>;
|
|
410
411
|
};
|
|
411
412
|
getStep: (instanceId: string, stepName: string) => Promise<Step | null>;
|
|
412
413
|
listWorkflows: (limit: number, offset: number, filter?: WorkflowFilter) => Promise<WorkflowRun[]>;
|
|
413
|
-
getWorkflow: (instanceId: string) => Promise<WorkflowRun | null>;
|
|
414
|
+
getWorkflow: (instanceId: string, populateData?: boolean) => Promise<WorkflowRun | null>;
|
|
414
415
|
getWorkflowTypesByTenantId: (tenantId: string) => Promise<string[]>;
|
|
415
416
|
getPropertiesKeys: (instanceId?: string) => Promise<WorkflowPropertyDefinition[]>;
|
|
416
417
|
};
|
package/dist/index.js
CHANGED
|
@@ -207,7 +207,7 @@ async function insertWorkflowRecord(options, { instanceId, workflowType, workflo
|
|
|
207
207
|
`INSERT INTO WorkflowTable
|
|
208
208
|
(instanceId, workflowType, workflowName, workflowMetadata, input, inputRef, tenantId, workflowStatus, startTime, endTime, parentInstanceId)
|
|
209
209
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
210
|
-
).bind(instanceId, workflowType, workflowName, options.serializer.serialize(workflowMetadata), inputData
|
|
210
|
+
).bind(instanceId, workflowType, workflowName, options.serializer.serialize(workflowMetadata), inputData, inputRef, tenantId, workflowStatus, startTime, endTime ?? null, parentInstanceId ?? null).run();
|
|
211
211
|
}
|
|
212
212
|
function insertStepRecordFull(context, { instanceId, name, status, metadata, startTime, endTime, result, error, resultRef, errorRef }) {
|
|
213
213
|
return context.D1.prepare(
|
|
@@ -247,6 +247,49 @@ function pushLogToDB(options, { instanceId, stepName, message, timestamp, type,
|
|
|
247
247
|
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
248
248
|
).bind(instanceId, stepName, message, timestamp, type, logOrder, tenantId).run();
|
|
249
249
|
}
|
|
250
|
+
var LogBatcher = class {
|
|
251
|
+
batch = [];
|
|
252
|
+
flushTimer = null;
|
|
253
|
+
isDestroyed = false;
|
|
254
|
+
constructor(options, batchSize = 100, flushInterval = 2e3) {
|
|
255
|
+
this.options = options;
|
|
256
|
+
this.batchSize = batchSize;
|
|
257
|
+
this.flushInterval = flushInterval;
|
|
258
|
+
}
|
|
259
|
+
addLog(entry) {
|
|
260
|
+
if (this.isDestroyed) return pushLogToDB(this.options, entry);
|
|
261
|
+
this.batch.push(entry);
|
|
262
|
+
if (!this.flushTimer) this.flushTimer = setTimeout(() => {
|
|
263
|
+
this.flush();
|
|
264
|
+
}, this.flushInterval);
|
|
265
|
+
if (this.batch.length >= this.batchSize) return this.flush();
|
|
266
|
+
return Promise.resolve();
|
|
267
|
+
}
|
|
268
|
+
async flush() {
|
|
269
|
+
if (this.flushTimer) {
|
|
270
|
+
clearTimeout(this.flushTimer);
|
|
271
|
+
this.flushTimer = null;
|
|
272
|
+
}
|
|
273
|
+
if (this.batch.length === 0) return;
|
|
274
|
+
const logsToFlush = this.batch.splice(0);
|
|
275
|
+
try {
|
|
276
|
+
const statements = logsToFlush.map((entry) => this.options.D1.prepare(
|
|
277
|
+
/* sql */
|
|
278
|
+
`INSERT INTO LogTable
|
|
279
|
+
(instanceId, stepName, log, timestamp, type, logOrder, tenantId)
|
|
280
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
281
|
+
).bind(entry.instanceId, entry.stepName, entry.message, entry.timestamp, entry.type, entry.logOrder, entry.tenantId));
|
|
282
|
+
await this.options.D1.batch(statements);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error("Batch log insert failed, falling back to individual inserts:", error);
|
|
285
|
+
await Promise.all(logsToFlush.map((entry) => pushLogToDB(this.options, entry)));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async destroy() {
|
|
289
|
+
this.isDestroyed = true;
|
|
290
|
+
await this.flush();
|
|
291
|
+
}
|
|
292
|
+
};
|
|
250
293
|
function trySerializeObj(obj, serializer) {
|
|
251
294
|
try {
|
|
252
295
|
return serializer.serialize(obj);
|
|
@@ -401,46 +444,44 @@ const createLogAccessor = (context) => {
|
|
|
401
444
|
const selectClause = "SELECT DISTINCT w.*";
|
|
402
445
|
const fromClause = "FROM WorkflowTable w";
|
|
403
446
|
const propertyWhereConditions = [];
|
|
404
|
-
if (filter) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
447
|
+
if (filter && filter.properties && Object.keys(filter.properties).length > 0) {
|
|
448
|
+
let propIndex = 0;
|
|
449
|
+
for (const [key, propertyFilter] of Object.entries(filter.properties)) {
|
|
450
|
+
const propAlias = `p${propIndex}`;
|
|
451
|
+
joinClause += ` LEFT JOIN WorkflowProperties ${propAlias} ON w.instanceId = ${propAlias}.instanceId AND ${propAlias}.key = ? AND ${propAlias}.tenantId = ?`;
|
|
452
|
+
bindings.push(key, context.tenantId);
|
|
453
|
+
if (propertyFilter.equals !== void 0) propertyWhereConditions.push({
|
|
454
|
+
condition: `${propAlias}.value = ?`,
|
|
455
|
+
binding: String(propertyFilter.equals)
|
|
456
|
+
});
|
|
457
|
+
if (propertyFilter.contains !== void 0) propertyWhereConditions.push({
|
|
458
|
+
condition: `${propAlias}.value LIKE ?`,
|
|
459
|
+
binding: `%${propertyFilter.contains}%`
|
|
460
|
+
});
|
|
461
|
+
if (propertyFilter.gt !== void 0) propertyWhereConditions.push({
|
|
462
|
+
condition: `CAST(${propAlias}.value AS REAL) > ?`,
|
|
463
|
+
binding: propertyFilter.gt
|
|
464
|
+
});
|
|
465
|
+
if (propertyFilter.gte !== void 0) propertyWhereConditions.push({
|
|
466
|
+
condition: `CAST(${propAlias}.value AS REAL) >= ?`,
|
|
467
|
+
binding: propertyFilter.gte
|
|
468
|
+
});
|
|
469
|
+
if (propertyFilter.lt !== void 0) propertyWhereConditions.push({
|
|
470
|
+
condition: `CAST(${propAlias}.value AS REAL) < ?`,
|
|
471
|
+
binding: propertyFilter.lt
|
|
472
|
+
});
|
|
473
|
+
if (propertyFilter.lte !== void 0) propertyWhereConditions.push({
|
|
474
|
+
condition: `CAST(${propAlias}.value AS REAL) <= ?`,
|
|
475
|
+
binding: propertyFilter.lte
|
|
476
|
+
});
|
|
477
|
+
if (propertyFilter.in !== void 0 && Array.isArray(propertyFilter.in)) {
|
|
478
|
+
const placeholders = propertyFilter.in.map(() => "?").join(", ");
|
|
479
|
+
propertyWhereConditions.push({
|
|
480
|
+
condition: `${propAlias}.value IN (${placeholders})`,
|
|
481
|
+
bindings: propertyFilter.in.map(String)
|
|
418
482
|
});
|
|
419
|
-
if (propertyFilter.gt !== void 0) propertyWhereConditions.push({
|
|
420
|
-
condition: `CAST(${propAlias}.value AS REAL) > ?`,
|
|
421
|
-
binding: propertyFilter.gt
|
|
422
|
-
});
|
|
423
|
-
if (propertyFilter.gte !== void 0) propertyWhereConditions.push({
|
|
424
|
-
condition: `CAST(${propAlias}.value AS REAL) >= ?`,
|
|
425
|
-
binding: propertyFilter.gte
|
|
426
|
-
});
|
|
427
|
-
if (propertyFilter.lt !== void 0) propertyWhereConditions.push({
|
|
428
|
-
condition: `CAST(${propAlias}.value AS REAL) < ?`,
|
|
429
|
-
binding: propertyFilter.lt
|
|
430
|
-
});
|
|
431
|
-
if (propertyFilter.lte !== void 0) propertyWhereConditions.push({
|
|
432
|
-
condition: `CAST(${propAlias}.value AS REAL) <= ?`,
|
|
433
|
-
binding: propertyFilter.lte
|
|
434
|
-
});
|
|
435
|
-
if (propertyFilter.in !== void 0 && Array.isArray(propertyFilter.in)) {
|
|
436
|
-
const placeholders = propertyFilter.in.map(() => "?").join(", ");
|
|
437
|
-
propertyWhereConditions.push({
|
|
438
|
-
condition: `${propAlias}.value IN (${placeholders})`,
|
|
439
|
-
bindings: propertyFilter.in.map(String)
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
propIndex++;
|
|
443
483
|
}
|
|
484
|
+
propIndex++;
|
|
444
485
|
}
|
|
445
486
|
}
|
|
446
487
|
propertyWhereConditions.forEach((item) => {
|
|
@@ -533,6 +574,7 @@ const createLogAccessor = (context) => {
|
|
|
533
574
|
let limit;
|
|
534
575
|
let actualOffset;
|
|
535
576
|
let actualInstanceId;
|
|
577
|
+
let populateData = void 0;
|
|
536
578
|
if (typeof limitOrOptions === "number") {
|
|
537
579
|
limit = limitOrOptions;
|
|
538
580
|
actualOffset = offset;
|
|
@@ -542,7 +584,9 @@ const createLogAccessor = (context) => {
|
|
|
542
584
|
limit = options.limit;
|
|
543
585
|
actualOffset = options.offset;
|
|
544
586
|
actualInstanceId = options.instanceId;
|
|
587
|
+
populateData = options.populateData;
|
|
545
588
|
}
|
|
589
|
+
if (populateData === void 0) populateData = false;
|
|
546
590
|
let result;
|
|
547
591
|
if (limit !== void 0 && actualOffset !== void 0) result = await context.D1.prepare(
|
|
548
592
|
/* sql */
|
|
@@ -558,8 +602,12 @@ const createLogAccessor = (context) => {
|
|
|
558
602
|
).bind(context.tenantId, actualInstanceId ?? null, actualInstanceId ?? null).all();
|
|
559
603
|
if (result.results) {
|
|
560
604
|
const steps = await Promise.all(result.results.map(async (row) => {
|
|
561
|
-
|
|
562
|
-
|
|
605
|
+
let deserializedResult = null;
|
|
606
|
+
let deserializedError = null;
|
|
607
|
+
if (populateData) {
|
|
608
|
+
deserializedResult = await deserializeWithExternalStorage(row.result, row.resultRef, internalSerializer, context.externalBlobStorage);
|
|
609
|
+
deserializedError = await deserializeWithExternalStorage(row.error, row.errorRef, internalSerializer, context.externalBlobStorage);
|
|
610
|
+
}
|
|
563
611
|
return {
|
|
564
612
|
instanceId: row.instanceId,
|
|
565
613
|
name: row.stepName,
|
|
@@ -567,8 +615,8 @@ const createLogAccessor = (context) => {
|
|
|
567
615
|
status: row.stepStatus,
|
|
568
616
|
startTime: row.startTime,
|
|
569
617
|
endTime: row.endTime,
|
|
570
|
-
result:
|
|
571
|
-
error
|
|
618
|
+
result: deserializedResult,
|
|
619
|
+
error: deserializedError
|
|
572
620
|
};
|
|
573
621
|
}));
|
|
574
622
|
return steps;
|
|
@@ -625,10 +673,13 @@ const createLogAccessor = (context) => {
|
|
|
625
673
|
const workflow = await workflowTableRowToWorkflowRun(result, internalSerializer, context.externalBlobStorage);
|
|
626
674
|
return workflow;
|
|
627
675
|
};
|
|
628
|
-
const getWorkflow = async (instanceId) => {
|
|
676
|
+
const getWorkflow = async (instanceId, populateData = false) => {
|
|
629
677
|
const workflow = await getWorkflowShallow(instanceId);
|
|
630
678
|
if (!workflow) return null;
|
|
631
|
-
const steps = await listSteps({
|
|
679
|
+
const steps = await listSteps({
|
|
680
|
+
instanceId,
|
|
681
|
+
populateData
|
|
682
|
+
});
|
|
632
683
|
const retryWorkflows = await getWorkflowByParentId(instanceId);
|
|
633
684
|
const parentWorkflow = workflow.parentInstanceId ? await getWorkflowShallow(workflow.parentInstanceId) : null;
|
|
634
685
|
workflow.retries = retryWorkflows;
|
|
@@ -768,13 +819,10 @@ async function createStepContext(context) {
|
|
|
768
819
|
const stepNameParam = typeof step$1 === "string" ? step$1 : step$1.name;
|
|
769
820
|
const stepMetadataParam = typeof step$1 === "string" ? void 0 : step$1.metadata;
|
|
770
821
|
if (context.parentInstanceId && reuseSuccessfulSteps) {
|
|
771
|
-
console.warn("temp: try to reuse successful steps");
|
|
772
822
|
const existingStep = await getStepRecord(context, stepNameParam, context.parentInstanceId);
|
|
773
823
|
if (existingStep) {
|
|
774
|
-
console.warn("temp: found existing step", existingStep);
|
|
775
824
|
const row = existingStep;
|
|
776
825
|
if (row.status === "completed") {
|
|
777
|
-
console.warn("temp: found existing step with completed status", row);
|
|
778
826
|
const { data: resultData, externalRef: resultRef } = await serializeWithExternalStorage(row.result, context.serializer, context.externalBlobStorage);
|
|
779
827
|
const { data: errorData, externalRef: errorRef } = await serializeWithExternalStorage(row.error, context.serializer, context.externalBlobStorage);
|
|
780
828
|
await insertStepRecordFull(context, {
|
|
@@ -790,10 +838,10 @@ async function createStepContext(context) {
|
|
|
790
838
|
errorRef
|
|
791
839
|
});
|
|
792
840
|
return row.result;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
const
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
const logBatcher = new LogBatcher(context);
|
|
797
845
|
const startTime = Date.now();
|
|
798
846
|
const stepStatus = "pending";
|
|
799
847
|
const stepMetadata = context.serializer.serialize(stepMetadataParam || {});
|
|
@@ -814,7 +862,7 @@ async function createStepContext(context) {
|
|
|
814
862
|
const log = (message, type = "info") => {
|
|
815
863
|
logOrder++;
|
|
816
864
|
const timestamp = Date.now();
|
|
817
|
-
|
|
865
|
+
logBatcher.addLog({
|
|
818
866
|
stepName: stepNameParam,
|
|
819
867
|
instanceId,
|
|
820
868
|
message,
|
|
@@ -823,7 +871,6 @@ async function createStepContext(context) {
|
|
|
823
871
|
logOrder,
|
|
824
872
|
tenantId: context.tenantId
|
|
825
873
|
});
|
|
826
|
-
waitFor.push(logPromise);
|
|
827
874
|
};
|
|
828
875
|
const ctx = { console: {
|
|
829
876
|
log: (message, ...optionalParams) => {
|
|
@@ -851,7 +898,7 @@ async function createStepContext(context) {
|
|
|
851
898
|
SET stepStatus = ?, endTime = ?, result = ?, error = ?, resultRef = ?, errorRef = ?
|
|
852
899
|
WHERE instanceId = ? AND stepName = ?`
|
|
853
900
|
).bind(stepStatus$1, endTime, resultData, stepError$1, resultRef, null, instanceId, stepName).run();
|
|
854
|
-
await
|
|
901
|
+
await logBatcher.destroy();
|
|
855
902
|
return result;
|
|
856
903
|
} catch (error) {
|
|
857
904
|
const endTime = Date.now();
|
|
@@ -864,7 +911,7 @@ async function createStepContext(context) {
|
|
|
864
911
|
SET stepStatus = ?, endTime = ?, result = ?, error = ?, resultRef = ?, errorRef = ?
|
|
865
912
|
WHERE instanceId = ? AND stepName = ?`
|
|
866
913
|
).bind(stepStatus$1, endTime, stepResult$1, errorData, null, errorRef, instanceId, stepName).run();
|
|
867
|
-
await
|
|
914
|
+
await logBatcher.destroy();
|
|
868
915
|
throw error;
|
|
869
916
|
}
|
|
870
917
|
};
|
|
@@ -899,12 +946,12 @@ function createWorkflowContext(options) {
|
|
|
899
946
|
parentInstanceId,
|
|
900
947
|
tenantId
|
|
901
948
|
});
|
|
902
|
-
const
|
|
949
|
+
const logBatcher = new LogBatcher(internalContext);
|
|
903
950
|
let logOrder = 0;
|
|
904
951
|
const log = (message, type = "info") => {
|
|
905
952
|
logOrder++;
|
|
906
953
|
const timestamp = Date.now();
|
|
907
|
-
|
|
954
|
+
logBatcher.addLog({
|
|
908
955
|
instanceId,
|
|
909
956
|
stepName: null,
|
|
910
957
|
message,
|
|
@@ -913,7 +960,6 @@ function createWorkflowContext(options) {
|
|
|
913
960
|
logOrder,
|
|
914
961
|
tenantId
|
|
915
962
|
});
|
|
916
|
-
waitFor.push(logPromise);
|
|
917
963
|
};
|
|
918
964
|
const stepContext = await createStepContext({
|
|
919
965
|
D1: options.D1,
|
|
@@ -963,7 +1009,7 @@ function createWorkflowContext(options) {
|
|
|
963
1009
|
endTime,
|
|
964
1010
|
instanceId
|
|
965
1011
|
});
|
|
966
|
-
await
|
|
1012
|
+
await logBatcher.destroy();
|
|
967
1013
|
} catch (error) {
|
|
968
1014
|
const endTime = Date.now();
|
|
969
1015
|
const workflowStatus = "failed";
|
|
@@ -972,7 +1018,7 @@ function createWorkflowContext(options) {
|
|
|
972
1018
|
endTime,
|
|
973
1019
|
instanceId
|
|
974
1020
|
});
|
|
975
|
-
await
|
|
1021
|
+
await logBatcher.destroy();
|
|
976
1022
|
throw error;
|
|
977
1023
|
}
|
|
978
1024
|
};
|
|
@@ -983,14 +1029,15 @@ function createWorkflowContext(options) {
|
|
|
983
1029
|
}
|
|
984
1030
|
const oldRun = await options.D1.prepare(
|
|
985
1031
|
/* sql */
|
|
986
|
-
`SELECT input, workflowName, tenantId FROM WorkflowTable WHERE instanceId = ? `
|
|
1032
|
+
`SELECT input, workflowName, tenantId, inputRef FROM WorkflowTable WHERE instanceId = ? `
|
|
987
1033
|
).bind(retryInstanceId).first();
|
|
988
1034
|
const oldWorkflowName = oldRun?.workflowName;
|
|
989
1035
|
const tenantId = oldRun?.tenantId;
|
|
990
1036
|
if (!tenantId) throw new Error(`No tenantId found for instanceId ${retryInstanceId}`);
|
|
1037
|
+
const inputRef = oldRun?.inputRef;
|
|
991
1038
|
const encodedInput = oldRun?.input;
|
|
992
|
-
if (
|
|
993
|
-
const input = internalContext.serializer.
|
|
1039
|
+
if (inputRef === void 0 || encodedInput === void 0) throw new Error(`No input found for instanceId ${retryInstanceId}`);
|
|
1040
|
+
const input = await deserializeWithExternalStorage(encodedInput, inputRef, internalContext.serializer, options.externalBlobStorage);
|
|
994
1041
|
await call({
|
|
995
1042
|
workflow,
|
|
996
1043
|
input,
|