gencow 0.1.144 → 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 +79 -73
- package/package.json +1 -1
- package/server/index.js +686 -309
- package/server/index.js.map +4 -4
package/core/index.js
CHANGED
|
@@ -2207,10 +2207,17 @@ function createScheduler(options) {
|
|
|
2207
2207
|
}
|
|
2208
2208
|
|
|
2209
2209
|
// ../core/src/workflow.ts
|
|
2210
|
-
import { sql as
|
|
2210
|
+
import { sql as sql3 } from "drizzle-orm";
|
|
2211
2211
|
|
|
2212
|
-
// ../core/src/
|
|
2212
|
+
// ../core/src/workflow-json.ts
|
|
2213
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";
|
|
2214
2221
|
|
|
2215
2222
|
// ../core/src/v.ts
|
|
2216
2223
|
var GencowValidationError = class extends Error {
|
|
@@ -2419,7 +2426,7 @@ function toWorkflowStatusFilter(status) {
|
|
|
2419
2426
|
async function ensureWorkflowRealtimeToken(db, workflowId, currentToken) {
|
|
2420
2427
|
if (currentToken && currentToken.trim() !== "") return currentToken;
|
|
2421
2428
|
const nextToken = createWorkflowRealtimeToken();
|
|
2422
|
-
const updateResult = await db.execute(
|
|
2429
|
+
const updateResult = await db.execute(sql2`
|
|
2423
2430
|
UPDATE _gencow_workflows
|
|
2424
2431
|
SET realtime_token = ${nextToken}
|
|
2425
2432
|
WHERE id = ${workflowId}
|
|
@@ -2428,7 +2435,7 @@ async function ensureWorkflowRealtimeToken(db, workflowId, currentToken) {
|
|
|
2428
2435
|
`);
|
|
2429
2436
|
const updatedToken = rowsFromResult(updateResult)[0]?.realtime_token ?? null;
|
|
2430
2437
|
if (updatedToken && updatedToken.trim() !== "") return updatedToken;
|
|
2431
|
-
const rereadResult = await db.execute(
|
|
2438
|
+
const rereadResult = await db.execute(sql2`
|
|
2432
2439
|
SELECT realtime_token
|
|
2433
2440
|
FROM _gencow_workflows
|
|
2434
2441
|
WHERE id = ${workflowId}
|
|
@@ -2438,7 +2445,7 @@ async function ensureWorkflowRealtimeToken(db, workflowId, currentToken) {
|
|
|
2438
2445
|
return rereadToken && rereadToken.trim() !== "" ? rereadToken : null;
|
|
2439
2446
|
}
|
|
2440
2447
|
async function loadWorkflowSignalTarget(db, workflowId) {
|
|
2441
|
-
const result = await db.execute(
|
|
2448
|
+
const result = await db.execute(sql2`
|
|
2442
2449
|
SELECT
|
|
2443
2450
|
id,
|
|
2444
2451
|
name,
|
|
@@ -2460,7 +2467,7 @@ function isMissingWorkflowV2SignalSchemaError(error) {
|
|
|
2460
2467
|
}
|
|
2461
2468
|
async function tryRecordWorkflowV2Signal(options) {
|
|
2462
2469
|
try {
|
|
2463
|
-
await options.db.execute(
|
|
2470
|
+
await options.db.execute(sql2`
|
|
2464
2471
|
WITH inserted AS (
|
|
2465
2472
|
INSERT INTO _gencow_workflow_signals_v2 (
|
|
2466
2473
|
id,
|
|
@@ -2473,7 +2480,7 @@ async function tryRecordWorkflowV2Signal(options) {
|
|
|
2473
2480
|
${crypto.randomUUID()},
|
|
2474
2481
|
${options.workflowId},
|
|
2475
2482
|
${options.event},
|
|
2476
|
-
${
|
|
2483
|
+
${workflowJsonb(options.payload)},
|
|
2477
2484
|
${crypto.randomUUID()}
|
|
2478
2485
|
)
|
|
2479
2486
|
RETURNING run_id
|
|
@@ -2499,7 +2506,7 @@ async function tryRecordWorkflowV2Signal(options) {
|
|
|
2499
2506
|
}
|
|
2500
2507
|
}
|
|
2501
2508
|
async function loadWorkflowSnapshot(db, workflowId, options) {
|
|
2502
|
-
const workflowResult = await db.execute(
|
|
2509
|
+
const workflowResult = await db.execute(sql2`
|
|
2503
2510
|
SELECT
|
|
2504
2511
|
id,
|
|
2505
2512
|
name,
|
|
@@ -2529,7 +2536,7 @@ async function loadWorkflowSnapshot(db, workflowId, options) {
|
|
|
2529
2536
|
}
|
|
2530
2537
|
const realtimeToken = await ensureWorkflowRealtimeToken(db, workflowId, row.realtime_token);
|
|
2531
2538
|
if (!realtimeToken) return null;
|
|
2532
|
-
const stepsResult = await db.execute(
|
|
2539
|
+
const stepsResult = await db.execute(sql2`
|
|
2533
2540
|
SELECT
|
|
2534
2541
|
step_name,
|
|
2535
2542
|
status,
|
|
@@ -2594,7 +2601,7 @@ function registerWorkflowsApi() {
|
|
|
2594
2601
|
};
|
|
2595
2602
|
}
|
|
2596
2603
|
const persistedPayload = serializeWorkflowValue(args.payload);
|
|
2597
|
-
await ctx.unsafeDb.execute(
|
|
2604
|
+
await ctx.unsafeDb.execute(sql2`
|
|
2598
2605
|
INSERT INTO _gencow_workflow_events (
|
|
2599
2606
|
id,
|
|
2600
2607
|
workflow_id,
|
|
@@ -2605,7 +2612,7 @@ function registerWorkflowsApi() {
|
|
|
2605
2612
|
${crypto.randomUUID()},
|
|
2606
2613
|
${workflow2.id},
|
|
2607
2614
|
${normalizedEvent},
|
|
2608
|
-
${
|
|
2615
|
+
${workflowJsonb(persistedPayload)}
|
|
2609
2616
|
)
|
|
2610
2617
|
`);
|
|
2611
2618
|
await tryRecordWorkflowV2Signal({
|
|
@@ -2642,7 +2649,7 @@ function registerWorkflowsApi() {
|
|
|
2642
2649
|
const limit = normalizeListLimit(args.limit);
|
|
2643
2650
|
const requestedStatus = normalizeDerivedStatus(args.status);
|
|
2644
2651
|
const status = toWorkflowStatusFilter(requestedStatus);
|
|
2645
|
-
const result = status == null ? await ctx.unsafeDb.execute(
|
|
2652
|
+
const result = status == null ? await ctx.unsafeDb.execute(sql2`
|
|
2646
2653
|
SELECT
|
|
2647
2654
|
id,
|
|
2648
2655
|
name,
|
|
@@ -2663,7 +2670,7 @@ function registerWorkflowsApi() {
|
|
|
2663
2670
|
WHERE user_id = ${userId}
|
|
2664
2671
|
ORDER BY started_at DESC
|
|
2665
2672
|
LIMIT ${limit}
|
|
2666
|
-
`) : await ctx.unsafeDb.execute(
|
|
2673
|
+
`) : await ctx.unsafeDb.execute(sql2`
|
|
2667
2674
|
SELECT
|
|
2668
2675
|
id,
|
|
2669
2676
|
name,
|
|
@@ -2769,61 +2776,60 @@ function isMissingWorkflowV2SchemaError(error) {
|
|
|
2769
2776
|
const message = error instanceof Error ? error.message : String(error);
|
|
2770
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"');
|
|
2771
2778
|
}
|
|
2772
|
-
async function tryInsertWorkflowV2WakeOutbox(db, workflowId) {
|
|
2773
|
-
try {
|
|
2774
|
-
await db.execute(sql2`
|
|
2775
|
-
INSERT INTO _gencow_workflow_outbox_v2 (
|
|
2776
|
-
id,
|
|
2777
|
-
run_id,
|
|
2778
|
-
kind,
|
|
2779
|
-
available_at,
|
|
2780
|
-
status
|
|
2781
|
-
)
|
|
2782
|
-
VALUES (
|
|
2783
|
-
${`start:${workflowId}`},
|
|
2784
|
-
${workflowId},
|
|
2785
|
-
'wake_run',
|
|
2786
|
-
NOW(),
|
|
2787
|
-
'pending'
|
|
2788
|
-
)
|
|
2789
|
-
ON CONFLICT (id) DO NOTHING
|
|
2790
|
-
`);
|
|
2791
|
-
} catch (error) {
|
|
2792
|
-
if (!isMissingWorkflowV2SchemaError(error)) throw error;
|
|
2793
|
-
}
|
|
2794
|
-
}
|
|
2795
2779
|
async function tryInsertWorkflowV2Run(options) {
|
|
2796
2780
|
try {
|
|
2797
|
-
await options.db.execute(
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
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
|
|
2824
2829
|
)
|
|
2830
|
+
SELECT id
|
|
2831
|
+
FROM inserted_run
|
|
2825
2832
|
`);
|
|
2826
|
-
await tryInsertWorkflowV2WakeOutbox(options.db, options.workflowId);
|
|
2827
2833
|
return true;
|
|
2828
2834
|
} catch (error) {
|
|
2829
2835
|
if (isMissingWorkflowV2SchemaError(error)) return false;
|
|
@@ -2832,7 +2838,7 @@ async function tryInsertWorkflowV2Run(options) {
|
|
|
2832
2838
|
}
|
|
2833
2839
|
async function tryDeleteWorkflowV2Run(db, workflowId) {
|
|
2834
2840
|
try {
|
|
2835
|
-
await db.execute(
|
|
2841
|
+
await db.execute(sql3`
|
|
2836
2842
|
DELETE FROM _gencow_workflow_runs_v2
|
|
2837
2843
|
WHERE id = ${workflowId}
|
|
2838
2844
|
`);
|
|
@@ -2888,7 +2894,7 @@ function workflow(name, options) {
|
|
|
2888
2894
|
const ownerId = ctx.auth.getUserIdentity()?.id ?? null;
|
|
2889
2895
|
const persistedArgs = serializeWorkflowValue(args ?? {});
|
|
2890
2896
|
const realtimeToken = createWorkflowRealtimeToken();
|
|
2891
|
-
await ctx.unsafeDb.execute(
|
|
2897
|
+
await ctx.unsafeDb.execute(sql3`
|
|
2892
2898
|
INSERT INTO _gencow_workflows (
|
|
2893
2899
|
id,
|
|
2894
2900
|
name,
|
|
@@ -2903,7 +2909,7 @@ function workflow(name, options) {
|
|
|
2903
2909
|
VALUES (
|
|
2904
2910
|
${workflowId},
|
|
2905
2911
|
${name},
|
|
2906
|
-
${
|
|
2912
|
+
${workflowJsonb(persistedArgs)},
|
|
2907
2913
|
${realtimeToken},
|
|
2908
2914
|
'pending',
|
|
2909
2915
|
0,
|
|
@@ -2933,7 +2939,7 @@ function workflow(name, options) {
|
|
|
2933
2939
|
scheduledJobId
|
|
2934
2940
|
};
|
|
2935
2941
|
} catch (error) {
|
|
2936
|
-
await ctx.unsafeDb.execute(
|
|
2942
|
+
await ctx.unsafeDb.execute(sql3`
|
|
2937
2943
|
DELETE FROM _gencow_workflows
|
|
2938
2944
|
WHERE id = ${workflowId}
|
|
2939
2945
|
`);
|
|
@@ -3097,7 +3103,7 @@ function defineConfig(config) {
|
|
|
3097
3103
|
}
|
|
3098
3104
|
|
|
3099
3105
|
// ../core/src/rls.ts
|
|
3100
|
-
import { sql as
|
|
3106
|
+
import { sql as sql4 } from "drizzle-orm";
|
|
3101
3107
|
import { pgPolicy } from "drizzle-orm/pg-core";
|
|
3102
3108
|
var _ownerRlsRegistry = /* @__PURE__ */ new WeakMap();
|
|
3103
3109
|
function getOwnerRlsMeta(table) {
|
|
@@ -3113,13 +3119,13 @@ function ownerRls(userIdColumn, options) {
|
|
|
3113
3119
|
"[ownerRls] userIdColumn must have a .name property. Ensure you pass a valid Drizzle column reference (e.g. t.userId)."
|
|
3114
3120
|
);
|
|
3115
3121
|
}
|
|
3116
|
-
const isOwner =
|
|
3122
|
+
const isOwner = sql4`${userIdColumn} = current_setting('app.current_user_id', true)`;
|
|
3117
3123
|
const meta = {
|
|
3118
3124
|
columnName: colName,
|
|
3119
3125
|
readPublic: options?.read === "public"
|
|
3120
3126
|
};
|
|
3121
3127
|
const policies = [
|
|
3122
|
-
pgPolicy("rls-select", { for: "select", using: options?.read === "public" ?
|
|
3128
|
+
pgPolicy("rls-select", { for: "select", using: options?.read === "public" ? sql4`true` : isOwner }),
|
|
3123
3129
|
pgPolicy("rls-insert", { for: "insert", withCheck: isOwner }),
|
|
3124
3130
|
pgPolicy("rls-update", { for: "update", using: isOwner, withCheck: isOwner }),
|
|
3125
3131
|
pgPolicy("rls-delete", { for: "delete", using: isOwner })
|
|
@@ -3129,7 +3135,7 @@ function ownerRls(userIdColumn, options) {
|
|
|
3129
3135
|
|
|
3130
3136
|
// ../core/src/rls-db.ts
|
|
3131
3137
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3132
|
-
import { sql as
|
|
3138
|
+
import { sql as sql5 } from "drizzle-orm";
|
|
3133
3139
|
var gucNameRe = /^app\.[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*$/;
|
|
3134
3140
|
var RESERVED_VARS_KEYS = /* @__PURE__ */ new Set(["app.current_user_id", "app.current_user_role", "app.tenant_id"]);
|
|
3135
3141
|
function assertSafeGucName(key) {
|
|
@@ -3189,7 +3195,7 @@ async function applyRlsSessionVars(client, rls) {
|
|
|
3189
3195
|
await forEachSetConfig(rls, (name, value) => execSetConfig(client, name, value));
|
|
3190
3196
|
}
|
|
3191
3197
|
async function injectRlsVarsOnTx(tx, rls) {
|
|
3192
|
-
await forEachSetConfig(rls, (name, value) => tx.execute(
|
|
3198
|
+
await forEachSetConfig(rls, (name, value) => tx.execute(sql5`SELECT set_config(${name}, ${value}, true)`));
|
|
3193
3199
|
}
|
|
3194
3200
|
async function withRlsLeasedConnection(leased, rls, fn) {
|
|
3195
3201
|
try {
|