gencow 0.1.143 → 0.1.145
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/core/index.js +375 -30
- package/dashboard/assets/index-CbDHShVr.css +1 -0
- package/dashboard/assets/{index-D0ZYKx4B.js → index-DX9dDxtT.js} +88 -78
- package/dashboard/index.html +2 -2
- package/lib/codegen/index.mjs +191 -68
- package/lib/jobs-command.mjs +40 -26
- package/package.json +2 -1
- package/server/index.js +4122 -458
- package/server/index.js.map +4 -4
- package/templates/auth.ts +31 -0
- package/dashboard/assets/index-C1IxULeS.css +0 -1
package/core/index.js
CHANGED
|
@@ -1374,6 +1374,11 @@ var require_node_cron = __commonJS({
|
|
|
1374
1374
|
}
|
|
1375
1375
|
});
|
|
1376
1376
|
|
|
1377
|
+
// ../core/src/context.ts
|
|
1378
|
+
function defineApi(api) {
|
|
1379
|
+
return api;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1377
1382
|
// ../core/src/document-types.ts
|
|
1378
1383
|
function normalizeCachePart(value) {
|
|
1379
1384
|
return value.trim() || "unknown";
|
|
@@ -1841,6 +1846,166 @@ function getRegisteredMutations() {
|
|
|
1841
1846
|
return [...mutationRegistry];
|
|
1842
1847
|
}
|
|
1843
1848
|
|
|
1849
|
+
// ../core/src/procedure.ts
|
|
1850
|
+
function hasStandardSchema(schema) {
|
|
1851
|
+
return !!schema && typeof schema === "object" && "~standard" in schema && !!schema["~standard"] && typeof schema["~standard"].validate === "function";
|
|
1852
|
+
}
|
|
1853
|
+
async function validateWithSchema(schema, value) {
|
|
1854
|
+
if (!schema) return value;
|
|
1855
|
+
if (hasStandardSchema(schema)) {
|
|
1856
|
+
const result = await schema["~standard"].validate(value);
|
|
1857
|
+
if ("issues" in result && result.issues.length > 0) {
|
|
1858
|
+
throw new Error(result.issues[0]?.message ?? "Validation failed");
|
|
1859
|
+
}
|
|
1860
|
+
if ("value" in result) {
|
|
1861
|
+
return result.value;
|
|
1862
|
+
}
|
|
1863
|
+
throw new Error("Validation failed");
|
|
1864
|
+
}
|
|
1865
|
+
throw new Error("procQuery/procMutation only supports Standard Schema v1 validators");
|
|
1866
|
+
}
|
|
1867
|
+
function composeMiddlewares(middlewares, handler, inputSchema, outputSchema, inputValidationIndex, outputValidationIndex) {
|
|
1868
|
+
return async (initialContext, initialInput) => {
|
|
1869
|
+
let idx = -1;
|
|
1870
|
+
const boundedInputValidationIndex = Math.min(Math.max(0, inputValidationIndex), middlewares.length);
|
|
1871
|
+
const boundedOutputValidationIndex = Math.min(Math.max(0, outputValidationIndex), middlewares.length);
|
|
1872
|
+
const run = async (currentIdx, context, input) => {
|
|
1873
|
+
if (currentIdx <= idx) {
|
|
1874
|
+
throw new Error("next() called multiple times in the same middleware");
|
|
1875
|
+
}
|
|
1876
|
+
idx = currentIdx;
|
|
1877
|
+
let currentInput = input;
|
|
1878
|
+
if (currentIdx === boundedInputValidationIndex) {
|
|
1879
|
+
currentInput = await validateWithSchema(inputSchema, currentInput);
|
|
1880
|
+
}
|
|
1881
|
+
if (currentIdx === middlewares.length) {
|
|
1882
|
+
const output2 = await handler({ context, input: currentInput });
|
|
1883
|
+
if (currentIdx === boundedOutputValidationIndex) {
|
|
1884
|
+
return await validateWithSchema(outputSchema, output2);
|
|
1885
|
+
}
|
|
1886
|
+
return output2;
|
|
1887
|
+
}
|
|
1888
|
+
const middleware = middlewares[currentIdx];
|
|
1889
|
+
const output = await middleware({
|
|
1890
|
+
context,
|
|
1891
|
+
input: currentInput,
|
|
1892
|
+
next: async (overrides) => {
|
|
1893
|
+
return await run(
|
|
1894
|
+
currentIdx + 1,
|
|
1895
|
+
overrides?.context ?? context,
|
|
1896
|
+
overrides?.input ?? currentInput
|
|
1897
|
+
);
|
|
1898
|
+
}
|
|
1899
|
+
});
|
|
1900
|
+
if (currentIdx === boundedOutputValidationIndex) {
|
|
1901
|
+
return await validateWithSchema(outputSchema, output);
|
|
1902
|
+
}
|
|
1903
|
+
return output;
|
|
1904
|
+
};
|
|
1905
|
+
return await run(0, initialContext, initialInput);
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
var GencowProcedureBuilderImpl = class _GencowProcedureBuilderImpl {
|
|
1909
|
+
constructor(kind, procedureName, middlewares = [], inputSchema, outputSchema, inputValidationIndex = -1, outputValidationIndex = -1, isPublic = false) {
|
|
1910
|
+
this.kind = kind;
|
|
1911
|
+
this.procedureName = procedureName;
|
|
1912
|
+
this.middlewares = middlewares;
|
|
1913
|
+
this.inputSchema = inputSchema;
|
|
1914
|
+
this.outputSchema = outputSchema;
|
|
1915
|
+
this.inputValidationIndex = inputValidationIndex;
|
|
1916
|
+
this.outputValidationIndex = outputValidationIndex;
|
|
1917
|
+
this.isPublic = isPublic;
|
|
1918
|
+
}
|
|
1919
|
+
name(name) {
|
|
1920
|
+
return new _GencowProcedureBuilderImpl(
|
|
1921
|
+
this.kind,
|
|
1922
|
+
name,
|
|
1923
|
+
this.middlewares,
|
|
1924
|
+
this.inputSchema,
|
|
1925
|
+
this.outputSchema,
|
|
1926
|
+
this.inputValidationIndex,
|
|
1927
|
+
this.outputValidationIndex,
|
|
1928
|
+
this.isPublic
|
|
1929
|
+
);
|
|
1930
|
+
}
|
|
1931
|
+
allowPublic() {
|
|
1932
|
+
return new _GencowProcedureBuilderImpl(
|
|
1933
|
+
this.kind,
|
|
1934
|
+
this.procedureName,
|
|
1935
|
+
this.middlewares,
|
|
1936
|
+
this.inputSchema,
|
|
1937
|
+
this.outputSchema,
|
|
1938
|
+
this.inputValidationIndex,
|
|
1939
|
+
this.outputValidationIndex,
|
|
1940
|
+
true
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
use(middleware) {
|
|
1944
|
+
return new _GencowProcedureBuilderImpl(
|
|
1945
|
+
this.kind,
|
|
1946
|
+
this.procedureName,
|
|
1947
|
+
[...this.middlewares, middleware],
|
|
1948
|
+
this.inputSchema,
|
|
1949
|
+
this.outputSchema,
|
|
1950
|
+
this.inputValidationIndex,
|
|
1951
|
+
this.outputValidationIndex,
|
|
1952
|
+
this.isPublic
|
|
1953
|
+
);
|
|
1954
|
+
}
|
|
1955
|
+
input(schema) {
|
|
1956
|
+
const nextInputValidationIndex = this.middlewares.length < 0 ? 0 : this.middlewares.length;
|
|
1957
|
+
return new _GencowProcedureBuilderImpl(
|
|
1958
|
+
this.kind,
|
|
1959
|
+
this.procedureName,
|
|
1960
|
+
this.middlewares,
|
|
1961
|
+
schema,
|
|
1962
|
+
this.outputSchema,
|
|
1963
|
+
nextInputValidationIndex,
|
|
1964
|
+
this.outputValidationIndex,
|
|
1965
|
+
this.isPublic
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1968
|
+
output(schema) {
|
|
1969
|
+
const nextOutputValidationIndex = this.middlewares.length < 0 ? 0 : this.middlewares.length;
|
|
1970
|
+
return new _GencowProcedureBuilderImpl(
|
|
1971
|
+
this.kind,
|
|
1972
|
+
this.procedureName,
|
|
1973
|
+
this.middlewares,
|
|
1974
|
+
this.inputSchema,
|
|
1975
|
+
schema,
|
|
1976
|
+
this.inputValidationIndex,
|
|
1977
|
+
nextOutputValidationIndex,
|
|
1978
|
+
this.isPublic
|
|
1979
|
+
);
|
|
1980
|
+
}
|
|
1981
|
+
handler(handler) {
|
|
1982
|
+
if (!this.procedureName) {
|
|
1983
|
+
throw new Error("Procedure name is required. Call .name(...) before .handler(...)");
|
|
1984
|
+
}
|
|
1985
|
+
const run = composeMiddlewares(
|
|
1986
|
+
this.middlewares,
|
|
1987
|
+
handler,
|
|
1988
|
+
this.inputSchema,
|
|
1989
|
+
this.outputSchema,
|
|
1990
|
+
this.inputValidationIndex,
|
|
1991
|
+
this.outputValidationIndex
|
|
1992
|
+
);
|
|
1993
|
+
return {
|
|
1994
|
+
kind: this.kind,
|
|
1995
|
+
name: this.procedureName,
|
|
1996
|
+
isPublic: this.isPublic,
|
|
1997
|
+
middlewares: [...this.middlewares],
|
|
1998
|
+
inputSchema: this.inputSchema,
|
|
1999
|
+
outputSchema: this.outputSchema,
|
|
2000
|
+
inputValidationIndex: this.inputValidationIndex,
|
|
2001
|
+
outputValidationIndex: this.outputValidationIndex,
|
|
2002
|
+
handler: run
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
var procQuery = new GencowProcedureBuilderImpl("query");
|
|
2007
|
+
var procMutation = new GencowProcedureBuilderImpl("mutation");
|
|
2008
|
+
|
|
1844
2009
|
// ../core/src/http-action.ts
|
|
1845
2010
|
if (!globalThis.__gencow_httpActionRegistry) globalThis.__gencow_httpActionRegistry = [];
|
|
1846
2011
|
var httpActionRegistry = globalThis.__gencow_httpActionRegistry;
|
|
@@ -2042,10 +2207,17 @@ function createScheduler(options) {
|
|
|
2042
2207
|
}
|
|
2043
2208
|
|
|
2044
2209
|
// ../core/src/workflow.ts
|
|
2045
|
-
import { sql as
|
|
2210
|
+
import { sql as sql3 } from "drizzle-orm";
|
|
2046
2211
|
|
|
2047
|
-
// ../core/src/
|
|
2212
|
+
// ../core/src/workflow-json.ts
|
|
2048
2213
|
import { sql } from "drizzle-orm";
|
|
2214
|
+
function workflowJsonb(value) {
|
|
2215
|
+
const json = JSON.stringify(value ?? {});
|
|
2216
|
+
return sql`${json}::text::jsonb`;
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
// ../core/src/workflows-api.ts
|
|
2220
|
+
import { sql as sql2 } from "drizzle-orm";
|
|
2049
2221
|
|
|
2050
2222
|
// ../core/src/v.ts
|
|
2051
2223
|
var GencowValidationError = class extends Error {
|
|
@@ -2203,6 +2375,7 @@ function mapWorkflowSummary(row) {
|
|
|
2203
2375
|
derivedStatus: deriveWorkflowStatus(row.status, row.current_step),
|
|
2204
2376
|
currentStep: row.current_step,
|
|
2205
2377
|
error: row.error,
|
|
2378
|
+
errorCode: row.error_code,
|
|
2206
2379
|
retryCount: row.retry_count,
|
|
2207
2380
|
maxRetries: row.max_retries,
|
|
2208
2381
|
maxDurationMs: Number(row.max_duration_ms),
|
|
@@ -2253,7 +2426,7 @@ function toWorkflowStatusFilter(status) {
|
|
|
2253
2426
|
async function ensureWorkflowRealtimeToken(db, workflowId, currentToken) {
|
|
2254
2427
|
if (currentToken && currentToken.trim() !== "") return currentToken;
|
|
2255
2428
|
const nextToken = createWorkflowRealtimeToken();
|
|
2256
|
-
const updateResult = await db.execute(
|
|
2429
|
+
const updateResult = await db.execute(sql2`
|
|
2257
2430
|
UPDATE _gencow_workflows
|
|
2258
2431
|
SET realtime_token = ${nextToken}
|
|
2259
2432
|
WHERE id = ${workflowId}
|
|
@@ -2262,7 +2435,7 @@ async function ensureWorkflowRealtimeToken(db, workflowId, currentToken) {
|
|
|
2262
2435
|
`);
|
|
2263
2436
|
const updatedToken = rowsFromResult(updateResult)[0]?.realtime_token ?? null;
|
|
2264
2437
|
if (updatedToken && updatedToken.trim() !== "") return updatedToken;
|
|
2265
|
-
const rereadResult = await db.execute(
|
|
2438
|
+
const rereadResult = await db.execute(sql2`
|
|
2266
2439
|
SELECT realtime_token
|
|
2267
2440
|
FROM _gencow_workflows
|
|
2268
2441
|
WHERE id = ${workflowId}
|
|
@@ -2272,7 +2445,7 @@ async function ensureWorkflowRealtimeToken(db, workflowId, currentToken) {
|
|
|
2272
2445
|
return rereadToken && rereadToken.trim() !== "" ? rereadToken : null;
|
|
2273
2446
|
}
|
|
2274
2447
|
async function loadWorkflowSignalTarget(db, workflowId) {
|
|
2275
|
-
const result = await db.execute(
|
|
2448
|
+
const result = await db.execute(sql2`
|
|
2276
2449
|
SELECT
|
|
2277
2450
|
id,
|
|
2278
2451
|
name,
|
|
@@ -2282,11 +2455,58 @@ async function loadWorkflowSignalTarget(db, workflowId) {
|
|
|
2282
2455
|
FROM _gencow_workflows
|
|
2283
2456
|
WHERE id = ${workflowId}
|
|
2284
2457
|
LIMIT 1
|
|
2285
|
-
|
|
2458
|
+
`);
|
|
2286
2459
|
return rowsFromResult(result)[0] ?? null;
|
|
2287
2460
|
}
|
|
2461
|
+
function isMissingWorkflowV2SignalSchemaError(error) {
|
|
2462
|
+
const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
|
|
2463
|
+
const cause = error && typeof error === "object" && "cause" in error ? error.cause : null;
|
|
2464
|
+
const causeCode = cause && typeof cause === "object" && "code" in cause ? String(cause.code) : "";
|
|
2465
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2466
|
+
return code === "42P01" || code === "42703" || causeCode === "42P01" || causeCode === "42703" || (code === "23503" || causeCode === "23503") && message.includes("_gencow_workflow_signals_v2") || message.includes('relation "_gencow_workflow_signals_v2" does not exist') || message.includes("relation _gencow_workflow_signals_v2 does not exist") || message.includes('relation "_gencow_workflow_runs_v2" does not exist') || message.includes("relation _gencow_workflow_runs_v2 does not exist");
|
|
2467
|
+
}
|
|
2468
|
+
async function tryRecordWorkflowV2Signal(options) {
|
|
2469
|
+
try {
|
|
2470
|
+
await options.db.execute(sql2`
|
|
2471
|
+
WITH inserted AS (
|
|
2472
|
+
INSERT INTO _gencow_workflow_signals_v2 (
|
|
2473
|
+
id,
|
|
2474
|
+
run_id,
|
|
2475
|
+
event_name,
|
|
2476
|
+
payload_json,
|
|
2477
|
+
idempotency_key
|
|
2478
|
+
)
|
|
2479
|
+
VALUES (
|
|
2480
|
+
${crypto.randomUUID()},
|
|
2481
|
+
${options.workflowId},
|
|
2482
|
+
${options.event},
|
|
2483
|
+
${workflowJsonb(options.payload)},
|
|
2484
|
+
${crypto.randomUUID()}
|
|
2485
|
+
)
|
|
2486
|
+
RETURNING run_id
|
|
2487
|
+
)
|
|
2488
|
+
UPDATE _gencow_workflow_runs_v2 run
|
|
2489
|
+
SET
|
|
2490
|
+
status = 'queued',
|
|
2491
|
+
runnable_at = NOW(),
|
|
2492
|
+
lease_owner = NULL,
|
|
2493
|
+
lease_expires_at = NULL,
|
|
2494
|
+
heartbeat_at = NULL,
|
|
2495
|
+
updated_at = NOW()
|
|
2496
|
+
FROM inserted
|
|
2497
|
+
WHERE run.id = inserted.run_id
|
|
2498
|
+
AND run.status = 'waiting'
|
|
2499
|
+
AND run.completed_at IS NULL
|
|
2500
|
+
AND run.cancel_requested_at IS NULL
|
|
2501
|
+
`);
|
|
2502
|
+
return true;
|
|
2503
|
+
} catch (error) {
|
|
2504
|
+
if (isMissingWorkflowV2SignalSchemaError(error)) return false;
|
|
2505
|
+
throw error;
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2288
2508
|
async function loadWorkflowSnapshot(db, workflowId, options) {
|
|
2289
|
-
const workflowResult = await db.execute(
|
|
2509
|
+
const workflowResult = await db.execute(sql2`
|
|
2290
2510
|
SELECT
|
|
2291
2511
|
id,
|
|
2292
2512
|
name,
|
|
@@ -2295,6 +2515,7 @@ async function loadWorkflowSnapshot(db, workflowId, options) {
|
|
|
2295
2515
|
current_step,
|
|
2296
2516
|
result,
|
|
2297
2517
|
error,
|
|
2518
|
+
error_code,
|
|
2298
2519
|
retry_count,
|
|
2299
2520
|
max_retries,
|
|
2300
2521
|
max_duration_ms,
|
|
@@ -2315,7 +2536,7 @@ async function loadWorkflowSnapshot(db, workflowId, options) {
|
|
|
2315
2536
|
}
|
|
2316
2537
|
const realtimeToken = await ensureWorkflowRealtimeToken(db, workflowId, row.realtime_token);
|
|
2317
2538
|
if (!realtimeToken) return null;
|
|
2318
|
-
const stepsResult = await db.execute(
|
|
2539
|
+
const stepsResult = await db.execute(sql2`
|
|
2319
2540
|
SELECT
|
|
2320
2541
|
step_name,
|
|
2321
2542
|
status,
|
|
@@ -2380,7 +2601,7 @@ function registerWorkflowsApi() {
|
|
|
2380
2601
|
};
|
|
2381
2602
|
}
|
|
2382
2603
|
const persistedPayload = serializeWorkflowValue(args.payload);
|
|
2383
|
-
await ctx.unsafeDb.execute(
|
|
2604
|
+
await ctx.unsafeDb.execute(sql2`
|
|
2384
2605
|
INSERT INTO _gencow_workflow_events (
|
|
2385
2606
|
id,
|
|
2386
2607
|
workflow_id,
|
|
@@ -2391,9 +2612,15 @@ function registerWorkflowsApi() {
|
|
|
2391
2612
|
${crypto.randomUUID()},
|
|
2392
2613
|
${workflow2.id},
|
|
2393
2614
|
${normalizedEvent},
|
|
2394
|
-
|
|
2615
|
+
${workflowJsonb(persistedPayload)}
|
|
2395
2616
|
)
|
|
2396
2617
|
`);
|
|
2618
|
+
await tryRecordWorkflowV2Signal({
|
|
2619
|
+
db: ctx.unsafeDb,
|
|
2620
|
+
workflowId: workflow2.id,
|
|
2621
|
+
event: normalizedEvent,
|
|
2622
|
+
payload: persistedPayload
|
|
2623
|
+
});
|
|
2397
2624
|
let scheduledJobId = null;
|
|
2398
2625
|
if (workflow2.status === "pending" && workflow2.current_step?.startsWith("wait:")) {
|
|
2399
2626
|
try {
|
|
@@ -2422,7 +2649,7 @@ function registerWorkflowsApi() {
|
|
|
2422
2649
|
const limit = normalizeListLimit(args.limit);
|
|
2423
2650
|
const requestedStatus = normalizeDerivedStatus(args.status);
|
|
2424
2651
|
const status = toWorkflowStatusFilter(requestedStatus);
|
|
2425
|
-
const result = status == null ? await ctx.unsafeDb.execute(
|
|
2652
|
+
const result = status == null ? await ctx.unsafeDb.execute(sql2`
|
|
2426
2653
|
SELECT
|
|
2427
2654
|
id,
|
|
2428
2655
|
name,
|
|
@@ -2431,6 +2658,7 @@ function registerWorkflowsApi() {
|
|
|
2431
2658
|
current_step,
|
|
2432
2659
|
result,
|
|
2433
2660
|
error,
|
|
2661
|
+
error_code,
|
|
2434
2662
|
retry_count,
|
|
2435
2663
|
max_retries,
|
|
2436
2664
|
max_duration_ms,
|
|
@@ -2442,7 +2670,7 @@ function registerWorkflowsApi() {
|
|
|
2442
2670
|
WHERE user_id = ${userId}
|
|
2443
2671
|
ORDER BY started_at DESC
|
|
2444
2672
|
LIMIT ${limit}
|
|
2445
|
-
`) : await ctx.unsafeDb.execute(
|
|
2673
|
+
`) : await ctx.unsafeDb.execute(sql2`
|
|
2446
2674
|
SELECT
|
|
2447
2675
|
id,
|
|
2448
2676
|
name,
|
|
@@ -2451,6 +2679,7 @@ function registerWorkflowsApi() {
|
|
|
2451
2679
|
current_step,
|
|
2452
2680
|
result,
|
|
2453
2681
|
error,
|
|
2682
|
+
error_code,
|
|
2454
2683
|
retry_count,
|
|
2455
2684
|
max_retries,
|
|
2456
2685
|
max_duration_ms,
|
|
@@ -2526,9 +2755,96 @@ function parseWorkflowDurationMs(raw, label = "workflow duration") {
|
|
|
2526
2755
|
}
|
|
2527
2756
|
return parseDurationString(raw, label);
|
|
2528
2757
|
}
|
|
2529
|
-
function normalizeMaxDurationMs(maxDuration) {
|
|
2758
|
+
function normalizeMaxDurationMs(maxDuration, label = "workflow() maxDuration") {
|
|
2530
2759
|
if (maxDuration == null) return DEFAULT_WORKFLOW_MAX_DURATION_MS;
|
|
2531
|
-
return parseWorkflowDurationMs(maxDuration,
|
|
2760
|
+
return parseWorkflowDurationMs(maxDuration, label);
|
|
2761
|
+
}
|
|
2762
|
+
function normalizeOptionalDurationMs(duration, label) {
|
|
2763
|
+
return duration == null ? null : parseWorkflowDurationMs(duration, label);
|
|
2764
|
+
}
|
|
2765
|
+
function normalizeConcurrency(concurrency) {
|
|
2766
|
+
if (concurrency == null) return null;
|
|
2767
|
+
if (!Number.isFinite(concurrency) || concurrency <= 0) {
|
|
2768
|
+
throw new Error(`workflow() concurrency must be a positive finite number, got "${concurrency}"`);
|
|
2769
|
+
}
|
|
2770
|
+
return Math.floor(concurrency);
|
|
2771
|
+
}
|
|
2772
|
+
function isMissingWorkflowV2SchemaError(error) {
|
|
2773
|
+
const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
|
|
2774
|
+
const cause = error && typeof error === "object" && "cause" in error ? error.cause : null;
|
|
2775
|
+
const causeCode = cause && typeof cause === "object" && "code" in cause ? String(cause.code) : "";
|
|
2776
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2777
|
+
return code === "42P01" || code === "42703" || causeCode === "42P01" || causeCode === "42703" || message.includes('relation "_gencow_workflow_runs_v2" does not exist') || message.includes("relation _gencow_workflow_runs_v2 does not exist") || message.includes('relation "_gencow_workflow_outbox_v2" does not exist') || message.includes("relation _gencow_workflow_outbox_v2 does not exist") || message.includes('column "max_active_duration_ms"') || message.includes('column "retry_count"') || message.includes('column "user_id"');
|
|
2778
|
+
}
|
|
2779
|
+
async function tryInsertWorkflowV2Run(options) {
|
|
2780
|
+
try {
|
|
2781
|
+
await options.db.execute(sql3`
|
|
2782
|
+
WITH inserted_run AS (
|
|
2783
|
+
INSERT INTO _gencow_workflow_runs_v2 (
|
|
2784
|
+
id,
|
|
2785
|
+
workflow_name,
|
|
2786
|
+
workflow_version,
|
|
2787
|
+
args_json,
|
|
2788
|
+
user_id,
|
|
2789
|
+
max_active_duration_ms,
|
|
2790
|
+
lifecycle_deadline_at,
|
|
2791
|
+
retry_count,
|
|
2792
|
+
max_retries,
|
|
2793
|
+
max_attempts
|
|
2794
|
+
)
|
|
2795
|
+
VALUES (
|
|
2796
|
+
${options.workflowId},
|
|
2797
|
+
${options.workflowName},
|
|
2798
|
+
${options.workflowVersion ?? null},
|
|
2799
|
+
${workflowJsonb(options.args)},
|
|
2800
|
+
${options.userId},
|
|
2801
|
+
${options.maxActiveDurationMs},
|
|
2802
|
+
CASE
|
|
2803
|
+
WHEN ${options.lifecycleTimeoutMs}::bigint IS NULL THEN NULL
|
|
2804
|
+
ELSE NOW() + (${options.lifecycleTimeoutMs}::bigint * INTERVAL '1 millisecond')
|
|
2805
|
+
END,
|
|
2806
|
+
0,
|
|
2807
|
+
${options.maxRetries},
|
|
2808
|
+
${options.maxRetries + 1}
|
|
2809
|
+
)
|
|
2810
|
+
RETURNING id
|
|
2811
|
+
),
|
|
2812
|
+
inserted_outbox AS (
|
|
2813
|
+
INSERT INTO _gencow_workflow_outbox_v2 (
|
|
2814
|
+
id,
|
|
2815
|
+
run_id,
|
|
2816
|
+
kind,
|
|
2817
|
+
available_at,
|
|
2818
|
+
status
|
|
2819
|
+
)
|
|
2820
|
+
SELECT
|
|
2821
|
+
'start:' || inserted_run.id,
|
|
2822
|
+
inserted_run.id,
|
|
2823
|
+
'wake_run',
|
|
2824
|
+
NOW(),
|
|
2825
|
+
'pending'
|
|
2826
|
+
FROM inserted_run
|
|
2827
|
+
ON CONFLICT (id) DO NOTHING
|
|
2828
|
+
RETURNING id
|
|
2829
|
+
)
|
|
2830
|
+
SELECT id
|
|
2831
|
+
FROM inserted_run
|
|
2832
|
+
`);
|
|
2833
|
+
return true;
|
|
2834
|
+
} catch (error) {
|
|
2835
|
+
if (isMissingWorkflowV2SchemaError(error)) return false;
|
|
2836
|
+
throw error;
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
async function tryDeleteWorkflowV2Run(db, workflowId) {
|
|
2840
|
+
try {
|
|
2841
|
+
await db.execute(sql3`
|
|
2842
|
+
DELETE FROM _gencow_workflow_runs_v2
|
|
2843
|
+
WHERE id = ${workflowId}
|
|
2844
|
+
`);
|
|
2845
|
+
} catch (error) {
|
|
2846
|
+
if (!isMissingWorkflowV2SchemaError(error)) throw error;
|
|
2847
|
+
}
|
|
2532
2848
|
}
|
|
2533
2849
|
function getWorkflowResumeActionName(name) {
|
|
2534
2850
|
return `${WORKFLOW_RESUME_ACTION_PREFIX}.${name}`;
|
|
@@ -2547,13 +2863,24 @@ function getRegisteredWorkflows() {
|
|
|
2547
2863
|
}
|
|
2548
2864
|
function workflow(name, options) {
|
|
2549
2865
|
registerWorkflowsApi();
|
|
2550
|
-
const
|
|
2866
|
+
const maxActiveDurationMs = normalizeMaxDurationMs(
|
|
2867
|
+
options.maxActiveDuration ?? options.maxDuration,
|
|
2868
|
+
options.maxActiveDuration == null ? "workflow() maxDuration" : "workflow() maxActiveDuration"
|
|
2869
|
+
);
|
|
2870
|
+
const lifecycleTimeoutMs = normalizeOptionalDurationMs(
|
|
2871
|
+
options.lifecycleTimeout,
|
|
2872
|
+
"workflow() lifecycleTimeout"
|
|
2873
|
+
);
|
|
2551
2874
|
const maxRetries = clampRetries(options.retries);
|
|
2552
2875
|
const def = {
|
|
2553
2876
|
name,
|
|
2554
2877
|
argsSchema: options.args,
|
|
2555
2878
|
isPublic: options.public === true,
|
|
2556
|
-
|
|
2879
|
+
version: options.version,
|
|
2880
|
+
maxDurationMs: maxActiveDurationMs,
|
|
2881
|
+
maxActiveDurationMs,
|
|
2882
|
+
lifecycleTimeoutMs,
|
|
2883
|
+
concurrency: normalizeConcurrency(options.concurrency),
|
|
2557
2884
|
maxRetries,
|
|
2558
2885
|
handler: options.handler
|
|
2559
2886
|
};
|
|
@@ -2567,7 +2894,7 @@ function workflow(name, options) {
|
|
|
2567
2894
|
const ownerId = ctx.auth.getUserIdentity()?.id ?? null;
|
|
2568
2895
|
const persistedArgs = serializeWorkflowValue(args ?? {});
|
|
2569
2896
|
const realtimeToken = createWorkflowRealtimeToken();
|
|
2570
|
-
await ctx.unsafeDb.execute(
|
|
2897
|
+
await ctx.unsafeDb.execute(sql3`
|
|
2571
2898
|
INSERT INTO _gencow_workflows (
|
|
2572
2899
|
id,
|
|
2573
2900
|
name,
|
|
@@ -2582,16 +2909,28 @@ function workflow(name, options) {
|
|
|
2582
2909
|
VALUES (
|
|
2583
2910
|
${workflowId},
|
|
2584
2911
|
${name},
|
|
2585
|
-
${
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2912
|
+
${workflowJsonb(persistedArgs)},
|
|
2913
|
+
${realtimeToken},
|
|
2914
|
+
'pending',
|
|
2915
|
+
0,
|
|
2916
|
+
${maxRetries},
|
|
2917
|
+
${maxActiveDurationMs},
|
|
2918
|
+
${ownerId}
|
|
2592
2919
|
)
|
|
2593
2920
|
`);
|
|
2921
|
+
let insertedWorkflowV2 = false;
|
|
2594
2922
|
try {
|
|
2923
|
+
insertedWorkflowV2 = await tryInsertWorkflowV2Run({
|
|
2924
|
+
db: ctx.unsafeDb,
|
|
2925
|
+
workflowId,
|
|
2926
|
+
workflowName: name,
|
|
2927
|
+
workflowVersion: options.version ?? null,
|
|
2928
|
+
args: persistedArgs,
|
|
2929
|
+
userId: ownerId,
|
|
2930
|
+
maxActiveDurationMs,
|
|
2931
|
+
lifecycleTimeoutMs,
|
|
2932
|
+
maxRetries
|
|
2933
|
+
});
|
|
2595
2934
|
const scheduledJobId = ctx.scheduler.runAfter(0, resumeAction, { workflowId });
|
|
2596
2935
|
return {
|
|
2597
2936
|
id: workflowId,
|
|
@@ -2600,10 +2939,13 @@ function workflow(name, options) {
|
|
|
2600
2939
|
scheduledJobId
|
|
2601
2940
|
};
|
|
2602
2941
|
} catch (error) {
|
|
2603
|
-
await ctx.unsafeDb.execute(
|
|
2942
|
+
await ctx.unsafeDb.execute(sql3`
|
|
2604
2943
|
DELETE FROM _gencow_workflows
|
|
2605
2944
|
WHERE id = ${workflowId}
|
|
2606
2945
|
`);
|
|
2946
|
+
if (insertedWorkflowV2) {
|
|
2947
|
+
await tryDeleteWorkflowV2Run(ctx.unsafeDb, workflowId);
|
|
2948
|
+
}
|
|
2607
2949
|
throw error;
|
|
2608
2950
|
}
|
|
2609
2951
|
}
|
|
@@ -2761,7 +3103,7 @@ function defineConfig(config) {
|
|
|
2761
3103
|
}
|
|
2762
3104
|
|
|
2763
3105
|
// ../core/src/rls.ts
|
|
2764
|
-
import { sql as
|
|
3106
|
+
import { sql as sql4 } from "drizzle-orm";
|
|
2765
3107
|
import { pgPolicy } from "drizzle-orm/pg-core";
|
|
2766
3108
|
var _ownerRlsRegistry = /* @__PURE__ */ new WeakMap();
|
|
2767
3109
|
function getOwnerRlsMeta(table) {
|
|
@@ -2777,13 +3119,13 @@ function ownerRls(userIdColumn, options) {
|
|
|
2777
3119
|
"[ownerRls] userIdColumn must have a .name property. Ensure you pass a valid Drizzle column reference (e.g. t.userId)."
|
|
2778
3120
|
);
|
|
2779
3121
|
}
|
|
2780
|
-
const isOwner =
|
|
3122
|
+
const isOwner = sql4`${userIdColumn} = current_setting('app.current_user_id', true)`;
|
|
2781
3123
|
const meta = {
|
|
2782
3124
|
columnName: colName,
|
|
2783
3125
|
readPublic: options?.read === "public"
|
|
2784
3126
|
};
|
|
2785
3127
|
const policies = [
|
|
2786
|
-
pgPolicy("rls-select", { for: "select", using: options?.read === "public" ?
|
|
3128
|
+
pgPolicy("rls-select", { for: "select", using: options?.read === "public" ? sql4`true` : isOwner }),
|
|
2787
3129
|
pgPolicy("rls-insert", { for: "insert", withCheck: isOwner }),
|
|
2788
3130
|
pgPolicy("rls-update", { for: "update", using: isOwner, withCheck: isOwner }),
|
|
2789
3131
|
pgPolicy("rls-delete", { for: "delete", using: isOwner })
|
|
@@ -2793,7 +3135,7 @@ function ownerRls(userIdColumn, options) {
|
|
|
2793
3135
|
|
|
2794
3136
|
// ../core/src/rls-db.ts
|
|
2795
3137
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2796
|
-
import { sql as
|
|
3138
|
+
import { sql as sql5 } from "drizzle-orm";
|
|
2797
3139
|
var gucNameRe = /^app\.[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*$/;
|
|
2798
3140
|
var RESERVED_VARS_KEYS = /* @__PURE__ */ new Set(["app.current_user_id", "app.current_user_role", "app.tenant_id"]);
|
|
2799
3141
|
function assertSafeGucName(key) {
|
|
@@ -2853,7 +3195,7 @@ async function applyRlsSessionVars(client, rls) {
|
|
|
2853
3195
|
await forEachSetConfig(rls, (name, value) => execSetConfig(client, name, value));
|
|
2854
3196
|
}
|
|
2855
3197
|
async function injectRlsVarsOnTx(tx, rls) {
|
|
2856
|
-
await forEachSetConfig(rls, (name, value) => tx.execute(
|
|
3198
|
+
await forEachSetConfig(rls, (name, value) => tx.execute(sql5`SELECT set_config(${name}, ${value}, true)`));
|
|
2857
3199
|
}
|
|
2858
3200
|
async function withRlsLeasedConnection(leased, rls, fn) {
|
|
2859
3201
|
try {
|
|
@@ -3393,6 +3735,7 @@ export {
|
|
|
3393
3735
|
createWorkflowRealtimeToken,
|
|
3394
3736
|
cronJobs,
|
|
3395
3737
|
crud,
|
|
3738
|
+
defineApi,
|
|
3396
3739
|
defineAuth,
|
|
3397
3740
|
defineConfig,
|
|
3398
3741
|
deregisterClient,
|
|
@@ -3424,6 +3767,8 @@ export {
|
|
|
3424
3767
|
parseArgs,
|
|
3425
3768
|
parseFilterNode,
|
|
3426
3769
|
parseWorkflowDurationMs,
|
|
3770
|
+
procMutation,
|
|
3771
|
+
procQuery,
|
|
3427
3772
|
query,
|
|
3428
3773
|
ragChunks,
|
|
3429
3774
|
ragCorpora,
|