@dbos-inc/postgres-datasource 3.0.8-preview → 3.0.8-preview.g493d2d1c2b
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 +5 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -38
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/index.ts +48 -48
- package/package.json +5 -6
- package/tests/datasource.test.ts +19 -57
package/index.ts
CHANGED
|
@@ -21,50 +21,40 @@ import { SuperJSON } from 'superjson';
|
|
|
21
21
|
export { IsolationLevel, PostgresTransactionOptions };
|
|
22
22
|
|
|
23
23
|
interface PostgresDataSourceContext {
|
|
24
|
-
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
25
|
+
client: postgres.TransactionSql<{}>;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
28
|
-
type Options = postgres.Options<{}>;
|
|
29
|
-
|
|
30
28
|
const asyncLocalCtx = new AsyncLocalStorage<PostgresDataSourceContext>();
|
|
31
29
|
|
|
32
30
|
class PostgresTransactionHandler implements DataSourceTransactionHandler {
|
|
33
31
|
readonly dsType = 'PostgresDataSource';
|
|
34
|
-
#
|
|
32
|
+
readonly #db: Sql;
|
|
35
33
|
|
|
36
34
|
constructor(
|
|
37
35
|
readonly name: string,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const db = this.#dbField;
|
|
43
|
-
this.#dbField = postgres(this.options);
|
|
44
|
-
await db?.end();
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
37
|
+
options: postgres.Options<{}> = {},
|
|
38
|
+
) {
|
|
39
|
+
this.#db = postgres(options);
|
|
45
40
|
}
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.#dbField = undefined;
|
|
50
|
-
await db?.end();
|
|
42
|
+
initialize(): Promise<void> {
|
|
43
|
+
return Promise.resolve();
|
|
51
44
|
}
|
|
52
45
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
throw new Error(`DataSource ${this.name} is not initialized.`);
|
|
56
|
-
}
|
|
57
|
-
return this.#dbField;
|
|
46
|
+
destroy(): Promise<void> {
|
|
47
|
+
return this.#db.end();
|
|
58
48
|
}
|
|
59
49
|
|
|
60
50
|
async #checkExecution(
|
|
61
51
|
workflowID: string,
|
|
62
|
-
|
|
52
|
+
functionNum: number,
|
|
63
53
|
): Promise<{ output: string | null } | { error: string } | undefined> {
|
|
64
54
|
type Result = { output: string | null; error: string | null };
|
|
65
55
|
const result = await this.#db<Result[]>/*sql*/ `
|
|
66
56
|
SELECT output, error FROM dbos.transaction_completion
|
|
67
|
-
WHERE workflow_id = ${workflowID} AND function_num = ${
|
|
57
|
+
WHERE workflow_id = ${workflowID} AND function_num = ${functionNum}`;
|
|
68
58
|
if (result.length === 0) {
|
|
69
59
|
return undefined;
|
|
70
60
|
}
|
|
@@ -73,15 +63,16 @@ class PostgresTransactionHandler implements DataSourceTransactionHandler {
|
|
|
73
63
|
}
|
|
74
64
|
|
|
75
65
|
static async #recordOutput(
|
|
76
|
-
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
67
|
+
client: postgres.TransactionSql<{}>,
|
|
77
68
|
workflowID: string,
|
|
78
|
-
|
|
69
|
+
functionNum: number,
|
|
79
70
|
output: string | null,
|
|
80
71
|
): Promise<void> {
|
|
81
72
|
try {
|
|
82
73
|
await client/*sql*/ `
|
|
83
74
|
INSERT INTO dbos.transaction_completion (workflow_id, function_num, output)
|
|
84
|
-
VALUES (${workflowID}, ${
|
|
75
|
+
VALUES (${workflowID}, ${functionNum}, ${output})`;
|
|
85
76
|
} catch (error) {
|
|
86
77
|
if (isPGKeyConflictError(error)) {
|
|
87
78
|
throw new DBOSWorkflowConflictError(workflowID);
|
|
@@ -91,11 +82,11 @@ class PostgresTransactionHandler implements DataSourceTransactionHandler {
|
|
|
91
82
|
}
|
|
92
83
|
}
|
|
93
84
|
|
|
94
|
-
async #recordError(workflowID: string,
|
|
85
|
+
async #recordError(workflowID: string, functionNum: number, error: string): Promise<void> {
|
|
95
86
|
try {
|
|
96
87
|
await this.#db/*sql*/ `
|
|
97
88
|
INSERT INTO dbos.transaction_completion (workflow_id, function_num, error)
|
|
98
|
-
VALUES (${workflowID}, ${
|
|
89
|
+
VALUES (${workflowID}, ${functionNum}, ${error})`;
|
|
99
90
|
} catch (error) {
|
|
100
91
|
if (isPGKeyConflictError(error)) {
|
|
101
92
|
throw new DBOSWorkflowConflictError(workflowID);
|
|
@@ -112,24 +103,26 @@ class PostgresTransactionHandler implements DataSourceTransactionHandler {
|
|
|
112
103
|
...args: Args
|
|
113
104
|
): Promise<Return> {
|
|
114
105
|
const workflowID = DBOS.workflowID;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
106
|
+
if (workflowID === undefined) {
|
|
107
|
+
throw new Error('Workflow ID is not set.');
|
|
108
|
+
}
|
|
109
|
+
const functionNum = DBOS.stepID;
|
|
110
|
+
if (functionNum === undefined) {
|
|
111
|
+
throw new Error('Function Number is not set.');
|
|
118
112
|
}
|
|
119
113
|
|
|
120
114
|
const isolationLevel = config?.isolationLevel ? `ISOLATION LEVEL ${config.isolationLevel}` : '';
|
|
121
115
|
const readOnly = config?.readOnly ?? false;
|
|
122
116
|
const accessMode = config?.readOnly === undefined ? '' : readOnly ? 'READ ONLY' : 'READ WRITE';
|
|
123
|
-
const saveResults = !readOnly && workflowID
|
|
117
|
+
const saveResults = !readOnly && workflowID;
|
|
124
118
|
|
|
125
|
-
// Retry loop if appropriate
|
|
126
119
|
let retryWaitMS = 1;
|
|
127
120
|
const backoffFactor = 1.5;
|
|
128
|
-
const maxRetryWaitMS = 2000;
|
|
121
|
+
const maxRetryWaitMS = 2000;
|
|
129
122
|
|
|
130
123
|
while (true) {
|
|
131
124
|
// Check to see if this tx has already been executed
|
|
132
|
-
const previousResult = saveResults ? await this.#checkExecution(workflowID,
|
|
125
|
+
const previousResult = saveResults ? await this.#checkExecution(workflowID, functionNum) : undefined;
|
|
133
126
|
if (previousResult) {
|
|
134
127
|
DBOS.span?.setAttribute('cached', true);
|
|
135
128
|
|
|
@@ -148,7 +141,12 @@ class PostgresTransactionHandler implements DataSourceTransactionHandler {
|
|
|
148
141
|
|
|
149
142
|
// save the output of read/write transactions
|
|
150
143
|
if (saveResults) {
|
|
151
|
-
await PostgresTransactionHandler.#recordOutput(
|
|
144
|
+
await PostgresTransactionHandler.#recordOutput(
|
|
145
|
+
client,
|
|
146
|
+
workflowID,
|
|
147
|
+
functionNum,
|
|
148
|
+
SuperJSON.stringify(result),
|
|
149
|
+
);
|
|
152
150
|
}
|
|
153
151
|
|
|
154
152
|
return result;
|
|
@@ -164,7 +162,7 @@ class PostgresTransactionHandler implements DataSourceTransactionHandler {
|
|
|
164
162
|
} else {
|
|
165
163
|
if (saveResults) {
|
|
166
164
|
const message = SuperJSON.stringify(error);
|
|
167
|
-
await this.#recordError(workflowID,
|
|
165
|
+
await this.#recordError(workflowID, functionNum, message);
|
|
168
166
|
}
|
|
169
167
|
|
|
170
168
|
throw error;
|
|
@@ -175,18 +173,20 @@ class PostgresTransactionHandler implements DataSourceTransactionHandler {
|
|
|
175
173
|
}
|
|
176
174
|
|
|
177
175
|
export class PostgresDataSource implements DBOSDataSource<PostgresTransactionOptions> {
|
|
178
|
-
|
|
176
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
177
|
+
static get client(): postgres.TransactionSql<{}> {
|
|
179
178
|
if (!DBOS.isInTransaction()) {
|
|
180
179
|
throw new Error('invalid use of PostgresDataSource.client outside of a DBOS transaction.');
|
|
181
180
|
}
|
|
182
181
|
const ctx = asyncLocalCtx.getStore();
|
|
183
182
|
if (!ctx) {
|
|
184
|
-
throw new Error('
|
|
183
|
+
throw new Error('No async local context found.');
|
|
185
184
|
}
|
|
186
185
|
return ctx.client;
|
|
187
186
|
}
|
|
188
187
|
|
|
189
|
-
|
|
188
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
189
|
+
static async initializeInternalSchema(options: postgres.Options<{}> = {}): Promise<void> {
|
|
190
190
|
const pg = postgres({ ...options, onnotice: () => {} });
|
|
191
191
|
try {
|
|
192
192
|
await pg.unsafe(createTransactionCompletionSchemaPG);
|
|
@@ -196,12 +196,12 @@ export class PostgresDataSource implements DBOSDataSource<PostgresTransactionOpt
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
readonly name: string;
|
|
199
200
|
#provider: PostgresTransactionHandler;
|
|
200
201
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
) {
|
|
202
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
203
|
+
constructor(name: string, options: postgres.Options<{}> = {}) {
|
|
204
|
+
this.name = name;
|
|
205
205
|
this.#provider = new PostgresTransactionHandler(name, options);
|
|
206
206
|
registerDataSource(this.#provider);
|
|
207
207
|
}
|
|
@@ -212,10 +212,10 @@ export class PostgresDataSource implements DBOSDataSource<PostgresTransactionOpt
|
|
|
212
212
|
|
|
213
213
|
registerTransaction<This, Args extends unknown[], Return>(
|
|
214
214
|
func: (this: This, ...args: Args) => Promise<Return>,
|
|
215
|
+
name: string,
|
|
215
216
|
config?: PostgresTransactionOptions,
|
|
216
|
-
name?: string,
|
|
217
217
|
): (this: This, ...args: Args) => Promise<Return> {
|
|
218
|
-
return registerTransaction(this.name, func, { name
|
|
218
|
+
return registerTransaction(this.name, func, { name }, config);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
transaction(config?: PostgresTransactionOptions) {
|
|
@@ -223,14 +223,14 @@ export class PostgresDataSource implements DBOSDataSource<PostgresTransactionOpt
|
|
|
223
223
|
const ds = this;
|
|
224
224
|
return function decorator<This, Args extends unknown[], Return>(
|
|
225
225
|
_target: object,
|
|
226
|
-
propertyKey:
|
|
226
|
+
propertyKey: string,
|
|
227
227
|
descriptor: TypedPropertyDescriptor<(this: This, ...args: Args) => Promise<Return>>,
|
|
228
228
|
) {
|
|
229
229
|
if (!descriptor.value) {
|
|
230
230
|
throw Error('Use of decorator when original method is undefined');
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
descriptor.value = ds.registerTransaction(descriptor.value,
|
|
233
|
+
descriptor.value = ds.registerTransaction(descriptor.value, propertyKey.toString(), config);
|
|
234
234
|
|
|
235
235
|
return descriptor;
|
|
236
236
|
};
|
package/package.json
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dbos-inc/postgres-datasource",
|
|
3
|
-
"version": "3.0.8-preview",
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.8-preview.g493d2d1c2b",
|
|
4
|
+
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"homepage": "https://docs.dbos.dev/",
|
|
9
6
|
"repository": {
|
|
10
7
|
"type": "git",
|
|
11
8
|
"url": "https://github.com/dbos-inc/dbos-transact-ts",
|
|
12
|
-
"directory": "packages/
|
|
9
|
+
"directory": "packages/knex-datasource"
|
|
13
10
|
},
|
|
11
|
+
"homepage": "https://docs.dbos.dev/",
|
|
12
|
+
"main": "index.js",
|
|
14
13
|
"scripts": {
|
|
15
14
|
"build": "tsc --project tsconfig.json",
|
|
16
15
|
"test": "jest --detectOpenHandles"
|
package/tests/datasource.test.ts
CHANGED
|
@@ -44,19 +44,13 @@ describe('PostgresDataSource', () => {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
await PostgresDataSource.initializeInternalSchema(config);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
afterAll(async () => {
|
|
50
|
-
await userDB.end();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
beforeEach(async () => {
|
|
54
47
|
DBOS.setConfig({ name: 'pg-ds-test' });
|
|
55
48
|
await DBOS.launch();
|
|
56
49
|
});
|
|
57
50
|
|
|
58
|
-
|
|
51
|
+
afterAll(async () => {
|
|
59
52
|
await DBOS.shutdown();
|
|
53
|
+
await userDB.end();
|
|
60
54
|
});
|
|
61
55
|
|
|
62
56
|
test('insert dataSource.register function', async () => {
|
|
@@ -65,7 +59,7 @@ describe('PostgresDataSource', () => {
|
|
|
65
59
|
await userDB.query('DELETE FROM greetings WHERE name = $1', [user]);
|
|
66
60
|
const workflowID = randomUUID();
|
|
67
61
|
|
|
68
|
-
await expect(DBOS.withNextWorkflowID(workflowID, () =>
|
|
62
|
+
await expect(DBOS.withNextWorkflowID(workflowID, () => regInsertWorfklowReg(user))).resolves.toMatchObject({
|
|
69
63
|
user,
|
|
70
64
|
greet_count: 1,
|
|
71
65
|
});
|
|
@@ -87,10 +81,10 @@ describe('PostgresDataSource', () => {
|
|
|
87
81
|
await userDB.query('DELETE FROM greetings WHERE name = $1', [user]);
|
|
88
82
|
const workflowID = randomUUID();
|
|
89
83
|
|
|
90
|
-
const result = await DBOS.withNextWorkflowID(workflowID, () =>
|
|
84
|
+
const result = await DBOS.withNextWorkflowID(workflowID, () => regInsertWorfklowReg(user));
|
|
91
85
|
expect(result).toMatchObject({ user, greet_count: 1 });
|
|
92
86
|
|
|
93
|
-
await expect(DBOS.withNextWorkflowID(workflowID, () =>
|
|
87
|
+
await expect(DBOS.withNextWorkflowID(workflowID, () => regInsertWorfklowReg(user))).resolves.toMatchObject(result);
|
|
94
88
|
});
|
|
95
89
|
|
|
96
90
|
test('insert dataSource.runAsTx function', async () => {
|
|
@@ -99,7 +93,7 @@ describe('PostgresDataSource', () => {
|
|
|
99
93
|
await userDB.query('DELETE FROM greetings WHERE name = $1', [user]);
|
|
100
94
|
const workflowID = randomUUID();
|
|
101
95
|
|
|
102
|
-
await expect(DBOS.withNextWorkflowID(workflowID, () =>
|
|
96
|
+
await expect(DBOS.withNextWorkflowID(workflowID, () => regInsertWorfklowRunTx(user))).resolves.toMatchObject({
|
|
103
97
|
user,
|
|
104
98
|
greet_count: 1,
|
|
105
99
|
});
|
|
@@ -121,10 +115,10 @@ describe('PostgresDataSource', () => {
|
|
|
121
115
|
await userDB.query('DELETE FROM greetings WHERE name = $1', [user]);
|
|
122
116
|
const workflowID = randomUUID();
|
|
123
117
|
|
|
124
|
-
const result = await DBOS.withNextWorkflowID(workflowID, () =>
|
|
118
|
+
const result = await DBOS.withNextWorkflowID(workflowID, () => regInsertWorfklowRunTx(user));
|
|
125
119
|
expect(result).toMatchObject({ user, greet_count: 1 });
|
|
126
120
|
|
|
127
|
-
await expect(DBOS.withNextWorkflowID(workflowID, () =>
|
|
121
|
+
await expect(DBOS.withNextWorkflowID(workflowID, () => regInsertWorfklowRunTx(user))).resolves.toMatchObject(
|
|
128
122
|
result,
|
|
129
123
|
);
|
|
130
124
|
});
|
|
@@ -287,40 +281,6 @@ describe('PostgresDataSource', () => {
|
|
|
287
281
|
{ user, greet_count: 1 },
|
|
288
282
|
]);
|
|
289
283
|
});
|
|
290
|
-
|
|
291
|
-
test('invoke-reg-tx-fun-outside-wf', async () => {
|
|
292
|
-
const user = 'outsideWfUser' + Date.now();
|
|
293
|
-
const result = await regInsertFunction(user);
|
|
294
|
-
expect(result).toMatchObject({ user, greet_count: 1 });
|
|
295
|
-
|
|
296
|
-
const txResults = await userDB.query('SELECT * FROM dbos.transaction_completion WHERE output LIKE $1', [
|
|
297
|
-
`%${user}%`,
|
|
298
|
-
]);
|
|
299
|
-
expect(txResults.rows.length).toBe(0);
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
test('invoke-reg-tx-static-method-outside-wf', async () => {
|
|
303
|
-
const user = 'outsideWfUser' + Date.now();
|
|
304
|
-
const result = await StaticClass.insertFunction(user);
|
|
305
|
-
expect(result).toMatchObject({ user, greet_count: 1 });
|
|
306
|
-
|
|
307
|
-
const txResults = await userDB.query('SELECT * FROM dbos.transaction_completion WHERE output LIKE $1', [
|
|
308
|
-
`%${user}%`,
|
|
309
|
-
]);
|
|
310
|
-
expect(txResults.rows.length).toBe(0);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
test('invoke-reg-tx-inst-method-outside-wf', async () => {
|
|
314
|
-
const user = 'outsideWfUser' + Date.now();
|
|
315
|
-
const instance = new InstanceClass();
|
|
316
|
-
const result = await instance.insertFunction(user);
|
|
317
|
-
expect(result).toMatchObject({ user, greet_count: 1 });
|
|
318
|
-
|
|
319
|
-
const txResults = await userDB.query('SELECT * FROM dbos.transaction_completion WHERE output LIKE $1', [
|
|
320
|
-
`%${user}%`,
|
|
321
|
-
]);
|
|
322
|
-
expect(txResults.rows.length).toBe(0);
|
|
323
|
-
});
|
|
324
284
|
});
|
|
325
285
|
|
|
326
286
|
export interface greetings {
|
|
@@ -340,8 +300,9 @@ async function insertFunction(user: string) {
|
|
|
340
300
|
}
|
|
341
301
|
|
|
342
302
|
async function errorFunction(user: string) {
|
|
343
|
-
const
|
|
303
|
+
const result = await insertFunction(user);
|
|
344
304
|
throw new Error(`test error ${Date.now()}`);
|
|
305
|
+
return result;
|
|
345
306
|
}
|
|
346
307
|
|
|
347
308
|
async function readFunction(user: string) {
|
|
@@ -353,9 +314,9 @@ async function readFunction(user: string) {
|
|
|
353
314
|
return { user, greet_count: row?.greet_count, now: Date.now() };
|
|
354
315
|
}
|
|
355
316
|
|
|
356
|
-
const regInsertFunction = dataSource.registerTransaction(insertFunction);
|
|
357
|
-
const regErrorFunction = dataSource.registerTransaction(errorFunction);
|
|
358
|
-
const regReadFunction = dataSource.registerTransaction(readFunction, { readOnly: true });
|
|
317
|
+
const regInsertFunction = dataSource.registerTransaction(insertFunction, 'insertFunction');
|
|
318
|
+
const regErrorFunction = dataSource.registerTransaction(errorFunction, 'errorFunction');
|
|
319
|
+
const regReadFunction = dataSource.registerTransaction(readFunction, 'readFunction', { readOnly: true });
|
|
359
320
|
|
|
360
321
|
class StaticClass {
|
|
361
322
|
static async insertFunction(user: string) {
|
|
@@ -367,8 +328,8 @@ class StaticClass {
|
|
|
367
328
|
}
|
|
368
329
|
}
|
|
369
330
|
|
|
370
|
-
StaticClass.insertFunction = dataSource.registerTransaction(StaticClass.insertFunction);
|
|
371
|
-
StaticClass.readFunction = dataSource.registerTransaction(StaticClass.readFunction,
|
|
331
|
+
StaticClass.insertFunction = dataSource.registerTransaction(StaticClass.insertFunction, 'insertFunction');
|
|
332
|
+
StaticClass.readFunction = dataSource.registerTransaction(StaticClass.readFunction, 'readFunction');
|
|
372
333
|
|
|
373
334
|
class InstanceClass {
|
|
374
335
|
async insertFunction(user: string) {
|
|
@@ -383,11 +344,12 @@ class InstanceClass {
|
|
|
383
344
|
InstanceClass.prototype.insertFunction = dataSource.registerTransaction(
|
|
384
345
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
385
346
|
InstanceClass.prototype.insertFunction,
|
|
347
|
+
'insertFunction',
|
|
386
348
|
);
|
|
387
349
|
InstanceClass.prototype.readFunction = dataSource.registerTransaction(
|
|
388
350
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
389
351
|
InstanceClass.prototype.readFunction,
|
|
390
|
-
|
|
352
|
+
'readFunction',
|
|
391
353
|
);
|
|
392
354
|
|
|
393
355
|
async function insertWorkflowReg(user: string) {
|
|
@@ -427,8 +389,8 @@ async function instanceWorkflow(user: string) {
|
|
|
427
389
|
return [result, readResult];
|
|
428
390
|
}
|
|
429
391
|
|
|
430
|
-
const
|
|
431
|
-
const
|
|
392
|
+
const regInsertWorfklowReg = DBOS.registerWorkflow(insertWorkflowReg, 'insertWorkflowReg');
|
|
393
|
+
const regInsertWorfklowRunTx = DBOS.registerWorkflow(insertWorkflowRunTx, 'insertWorkflowRunTx');
|
|
432
394
|
const regErrorWorkflowReg = DBOS.registerWorkflow(errorWorkflowReg, 'errorWorkflowReg');
|
|
433
395
|
const regErrorWorkflowRunTx = DBOS.registerWorkflow(errorWorkflowRunTx, 'errorWorkflowRunTx');
|
|
434
396
|
const regReadWorkflowReg = DBOS.registerWorkflow(readWorkflowReg, 'readWorkflowReg');
|