@shware/workflow 9.0.0 → 9.0.2
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/engine/time-scale.cjs +33 -11
- package/dist/engine/time-scale.cjs.map +1 -1
- package/dist/engine/time-scale.d.cts +12 -6
- package/dist/engine/time-scale.d.cts.map +1 -1
- package/dist/engine/time-scale.d.mts +12 -6
- package/dist/engine/time-scale.d.mts.map +1 -1
- package/dist/engine/time-scale.mjs +33 -11
- package/dist/engine/time-scale.mjs.map +1 -1
- package/dist/postgres-js.cjs +11 -1
- package/dist/postgres-js.cjs.map +1 -1
- package/dist/postgres-js.d.cts +6 -0
- package/dist/postgres-js.d.cts.map +1 -1
- package/dist/postgres-js.d.mts +6 -0
- package/dist/postgres-js.d.mts.map +1 -1
- package/dist/postgres-js.mjs +11 -2
- package/dist/postgres-js.mjs.map +1 -1
- package/dist/store/sql.cjs.map +1 -1
- package/dist/store/sql.d.cts +7 -4
- package/dist/store/sql.d.cts.map +1 -1
- package/dist/store/sql.d.mts +7 -4
- package/dist/store/sql.d.mts.map +1 -1
- package/dist/store/sql.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -10,8 +10,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
10
10
|
* timeouts, goal and `performed(... within)` windows inside the workflow), so
|
|
11
11
|
* the journey keeps its shape, just faster. What does not scale: wall-clock
|
|
12
12
|
* `timeWindow`s (they are times of day, not durations). Segment definitions
|
|
13
|
-
* live in the store rather than the workflow body;
|
|
14
|
-
* scales them as they are read
|
|
13
|
+
* and trigger routes live in the store rather than the workflow body;
|
|
14
|
+
* `scaleSegmentWindows` scales them as they are read — one segment for an
|
|
15
|
+
* event-driven evaluation, all of them for the scheduled sweep, a route's
|
|
16
|
+
* profile gate at ingest — so a segment's `within` keeps pace everywhere.
|
|
15
17
|
*
|
|
16
18
|
* The walk follows the IR's own structure — the fields that hold a Duration
|
|
17
19
|
* are known — so a message prop or an event payload that happens to look like
|
|
@@ -105,20 +107,40 @@ function scale(duration, factor) {
|
|
|
105
107
|
};
|
|
106
108
|
}
|
|
107
109
|
/**
|
|
108
|
-
* A store whose segment definitions come back scaled by `factor` — what
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
110
|
+
* A store whose segment definitions come back scaled by `factor` — what the journey host leases
|
|
111
|
+
* in fast-forward, so a segment's windows (`performed(e.login, { within: '7 days' })`) shrink
|
|
112
|
+
* with the journeys that reference it, and with the segment triggers it routes. Every read that
|
|
113
|
+
* hands out a stored condition is scaled: `getSegmentCondition` (event-driven evaluation),
|
|
114
|
+
* `listSegments` (the scheduled sweep, which narrows candidates in SQL and evaluates with those
|
|
115
|
+
* definitions) and `findTriggers` (the profile gate an event trigger checks before it starts a
|
|
116
|
+
* journey, which `scaleDurations` scales inside the journey too). Every other method is the
|
|
117
|
+
* store's own.
|
|
112
118
|
*/
|
|
113
119
|
function scaleSegmentWindows(store, factor) {
|
|
114
120
|
if (!(factor > 0) || factor === 1) return store;
|
|
115
121
|
return new Proxy(store, { get(target, property) {
|
|
116
|
-
if (property === "getSegmentCondition") return async (name) => {
|
|
117
|
-
const condition = await target.getSegmentCondition(name);
|
|
118
|
-
return condition === void 0 ? void 0 : scaleCondition(condition, factor);
|
|
119
|
-
};
|
|
120
122
|
const value = Reflect.get(target, property, target);
|
|
121
|
-
|
|
123
|
+
if (typeof value !== "function") return value;
|
|
124
|
+
switch (property) {
|
|
125
|
+
case "getSegmentCondition": return async (name) => {
|
|
126
|
+
const condition = await target.getSegmentCondition(name);
|
|
127
|
+
return condition === void 0 ? void 0 : scaleCondition(condition, factor);
|
|
128
|
+
};
|
|
129
|
+
case "listSegments": return async () => {
|
|
130
|
+
return (await value.call(target)).map(({ id, condition }) => ({
|
|
131
|
+
id,
|
|
132
|
+
condition: scaleCondition(condition, factor)
|
|
133
|
+
}));
|
|
134
|
+
};
|
|
135
|
+
case "findTriggers": return async (event) => {
|
|
136
|
+
const scaled = (route) => ({
|
|
137
|
+
...route,
|
|
138
|
+
filter: route.filter === null ? null : scaleCondition(route.filter, factor)
|
|
139
|
+
});
|
|
140
|
+
return (await value.call(target, event)).map(scaled);
|
|
141
|
+
};
|
|
142
|
+
default: return value.bind(target);
|
|
143
|
+
}
|
|
122
144
|
} });
|
|
123
145
|
}
|
|
124
146
|
exports.scaleCondition = scaleCondition;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-scale.cjs","names":[],"sources":["../../src/engine/time-scale.ts"],"sourcesContent":["import type { ConditionIR, DurationIR, NodeIR, WorkflowIR } from '../ir';\n\n/**\n * Debug time scale: divide every duration in a workflow's IR by a factor so a\n * journey written in days plays out in seconds — with a factor of 3600, a\n * `delay('2 days')` sleeps 48 s, a `waitUntil(..., { timeout: '3 days' })`\n * gives up after 72 s, and a goal `within: '30 days'` closes after 12 min.\n *\n * Everything that is a Duration scales together (delays, random delays, wait\n * timeouts, goal and `performed(... within)` windows inside the workflow), so\n * the journey keeps its shape, just faster. What does not scale: wall-clock\n * `timeWindow`s (they are times of day, not durations). Segment definitions\n * live in the store rather than the workflow body
|
|
1
|
+
{"version":3,"file":"time-scale.cjs","names":[],"sources":["../../src/engine/time-scale.ts"],"sourcesContent":["import type { ConditionIR, DurationIR, NodeIR, WorkflowIR } from '../ir';\n\n/**\n * Debug time scale: divide every duration in a workflow's IR by a factor so a\n * journey written in days plays out in seconds — with a factor of 3600, a\n * `delay('2 days')` sleeps 48 s, a `waitUntil(..., { timeout: '3 days' })`\n * gives up after 72 s, and a goal `within: '30 days'` closes after 12 min.\n *\n * Everything that is a Duration scales together (delays, random delays, wait\n * timeouts, goal and `performed(... within)` windows inside the workflow), so\n * the journey keeps its shape, just faster. What does not scale: wall-clock\n * `timeWindow`s (they are times of day, not durations). Segment definitions\n * and trigger routes live in the store rather than the workflow body;\n * `scaleSegmentWindows` scales them as they are read — one segment for an\n * event-driven evaluation, all of them for the scheduled sweep, a route's\n * profile gate at ingest — so a segment's `within` keeps pace everywhere.\n *\n * The walk follows the IR's own structure — the fields that hold a Duration\n * are known — so a message prop or an event payload that happens to look like\n * one (`{ value, ms }`) is left alone. The source text is kept so the studio\n * still shows what was written.\n */\nexport function scaleDurations(ir: WorkflowIR, factor: number): WorkflowIR {\n if (!(factor > 0) || factor === 1) return ir;\n return {\n ...ir,\n trigger:\n ir.trigger.type === 'event'\n ? {\n ...ir.trigger,\n ...(ir.trigger.filter === undefined\n ? {}\n : { filter: scaleCondition(ir.trigger.filter, factor) }),\n }\n : ir.trigger,\n ...(ir.goal === undefined\n ? {}\n : {\n goal: {\n ...ir.goal,\n condition: scaleCondition(ir.goal.condition, factor),\n ...(ir.goal.within === undefined ? {} : { within: scale(ir.goal.within, factor) }),\n },\n }),\n ...(ir.exitWhen === undefined ? {} : { exitWhen: scaleCondition(ir.exitWhen, factor) }),\n flow: scaleFlow(ir.flow, factor),\n };\n}\n\n/** A condition with its `performed(... within)` windows divided by `factor`. */\nexport function scaleCondition(condition: ConditionIR, factor: number): ConditionIR {\n if (!(factor > 0) || factor === 1) return condition;\n switch (condition.type) {\n case 'and':\n case 'or':\n return {\n ...condition,\n conditions: condition.conditions.map((child) => scaleCondition(child, factor)),\n };\n case 'not':\n return { ...condition, condition: scaleCondition(condition.condition, factor) };\n case 'performed':\n return condition.within === undefined\n ? condition\n : { ...condition, within: scale(condition.within, factor) };\n default:\n return condition;\n }\n}\n\nfunction scaleFlow(nodes: readonly NodeIR[], factor: number): NodeIR[] {\n return nodes.map((node) => scaleNode(node, factor));\n}\n\nfunction scaleNode(node: NodeIR, factor: number): NodeIR {\n switch (node.type) {\n case 'delay':\n return { ...node, duration: scale(node.duration, factor) };\n case 'random_delay':\n return { ...node, min: scale(node.min, factor), max: scale(node.max, factor) };\n case 'wait_until':\n return {\n ...node,\n condition: scaleCondition(node.condition, factor),\n timeout: scale(node.timeout, factor),\n onTimeout: Array.isArray(node.onTimeout)\n ? scaleFlow(node.onTimeout, factor)\n : node.onTimeout,\n };\n case 'branch':\n return {\n ...node,\n cases: node.cases.map((branch) => ({\n ...branch,\n condition: scaleCondition(branch.condition, factor),\n flow: scaleFlow(branch.flow, factor),\n })),\n ...(node.otherwise === undefined ? {} : { otherwise: scaleFlow(node.otherwise, factor) }),\n };\n case 'filter':\n return { ...node, condition: scaleCondition(node.condition, factor) };\n case 'cohort':\n return {\n ...node,\n arms: node.arms.map((arm) => ({ ...arm, flow: scaleFlow(arm.flow, factor) })),\n };\n default:\n return node;\n }\n}\n\nfunction scale(duration: DurationIR, factor: number): DurationIR {\n return { value: duration.value, ms: Math.max(0, Math.round(duration.ms / factor)) };\n}\n\n/**\n * A store whose segment definitions come back scaled by `factor` — what the journey host leases\n * in fast-forward, so a segment's windows (`performed(e.login, { within: '7 days' })`) shrink\n * with the journeys that reference it, and with the segment triggers it routes. Every read that\n * hands out a stored condition is scaled: `getSegmentCondition` (event-driven evaluation),\n * `listSegments` (the scheduled sweep, which narrows candidates in SQL and evaluates with those\n * definitions) and `findTriggers` (the profile gate an event trigger checks before it starts a\n * journey, which `scaleDurations` scales inside the journey too). Every other method is the\n * store's own.\n */\nexport function scaleSegmentWindows<\n S extends { getSegmentCondition(name: string): Promise<ConditionIR | undefined> },\n>(store: S, factor: number): S {\n if (!(factor > 0) || factor === 1) return store;\n return new Proxy(store, {\n get(target, property) {\n const value: unknown = Reflect.get(target, property, target);\n if (typeof value !== 'function') return value;\n switch (property) {\n case 'getSegmentCondition':\n return async (name: string) => {\n const condition = await target.getSegmentCondition(name);\n return condition === undefined ? undefined : scaleCondition(condition, factor);\n };\n case 'listSegments':\n return async () => {\n const segments = (await value.call(target)) as { id: string; condition: ConditionIR }[];\n return segments.map(({ id, condition }) => ({\n id,\n condition: scaleCondition(condition, factor),\n }));\n };\n case 'findTriggers':\n return async (event: string) => {\n // Only the profile gate: `where` is payload-only, so it holds no window to scale\n const scaled = (route: { filter: ConditionIR | null }) => ({\n ...route,\n filter: route.filter === null ? null : scaleCondition(route.filter, factor),\n });\n return ((await value.call(target, event)) as { filter: ConditionIR | null }[]).map(\n scaled\n );\n };\n default:\n return value.bind(target);\n }\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -9,8 +9,10 @@ import { ConditionIR, WorkflowIR } from "../ir.cjs";
|
|
|
9
9
|
* timeouts, goal and `performed(... within)` windows inside the workflow), so
|
|
10
10
|
* the journey keeps its shape, just faster. What does not scale: wall-clock
|
|
11
11
|
* `timeWindow`s (they are times of day, not durations). Segment definitions
|
|
12
|
-
* live in the store rather than the workflow body;
|
|
13
|
-
* scales them as they are read
|
|
12
|
+
* and trigger routes live in the store rather than the workflow body;
|
|
13
|
+
* `scaleSegmentWindows` scales them as they are read — one segment for an
|
|
14
|
+
* event-driven evaluation, all of them for the scheduled sweep, a route's
|
|
15
|
+
* profile gate at ingest — so a segment's `within` keeps pace everywhere.
|
|
14
16
|
*
|
|
15
17
|
* The walk follows the IR's own structure — the fields that hold a Duration
|
|
16
18
|
* are known — so a message prop or an event payload that happens to look like
|
|
@@ -21,10 +23,14 @@ export declare function scaleDurations(ir: WorkflowIR, factor: number): Workflow
|
|
|
21
23
|
/** A condition with its `performed(... within)` windows divided by `factor`. */
|
|
22
24
|
export declare function scaleCondition(condition: ConditionIR, factor: number): ConditionIR;
|
|
23
25
|
/**
|
|
24
|
-
* A store whose segment definitions come back scaled by `factor` — what
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* A store whose segment definitions come back scaled by `factor` — what the journey host leases
|
|
27
|
+
* in fast-forward, so a segment's windows (`performed(e.login, { within: '7 days' })`) shrink
|
|
28
|
+
* with the journeys that reference it, and with the segment triggers it routes. Every read that
|
|
29
|
+
* hands out a stored condition is scaled: `getSegmentCondition` (event-driven evaluation),
|
|
30
|
+
* `listSegments` (the scheduled sweep, which narrows candidates in SQL and evaluates with those
|
|
31
|
+
* definitions) and `findTriggers` (the profile gate an event trigger checks before it starts a
|
|
32
|
+
* journey, which `scaleDurations` scales inside the journey too). Every other method is the
|
|
33
|
+
* store's own.
|
|
28
34
|
*/
|
|
29
35
|
export declare function scaleSegmentWindows<S extends {
|
|
30
36
|
getSegmentCondition(name: string): Promise<ConditionIR | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-scale.d.cts","names":[],"sources":["../../src/engine/time-scale.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"time-scale.d.cts","names":[],"sources":["../../src/engine/time-scale.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;wBAsBgB,eAAe,IAAI,YAAY,iBAAiB;;wBA4BhD,eAAe,WAAW,aAAa,iBAAiB;;;;;;;;;;;wBA2ExD,oBACd;EAAY,oBAAoB,eAAe,QAAQ;GACvD,OAAO,GAAG,iBAAiB"}
|
|
@@ -9,8 +9,10 @@ import { ConditionIR, WorkflowIR } from "../ir.mjs";
|
|
|
9
9
|
* timeouts, goal and `performed(... within)` windows inside the workflow), so
|
|
10
10
|
* the journey keeps its shape, just faster. What does not scale: wall-clock
|
|
11
11
|
* `timeWindow`s (they are times of day, not durations). Segment definitions
|
|
12
|
-
* live in the store rather than the workflow body;
|
|
13
|
-
* scales them as they are read
|
|
12
|
+
* and trigger routes live in the store rather than the workflow body;
|
|
13
|
+
* `scaleSegmentWindows` scales them as they are read — one segment for an
|
|
14
|
+
* event-driven evaluation, all of them for the scheduled sweep, a route's
|
|
15
|
+
* profile gate at ingest — so a segment's `within` keeps pace everywhere.
|
|
14
16
|
*
|
|
15
17
|
* The walk follows the IR's own structure — the fields that hold a Duration
|
|
16
18
|
* are known — so a message prop or an event payload that happens to look like
|
|
@@ -21,10 +23,14 @@ export declare function scaleDurations(ir: WorkflowIR, factor: number): Workflow
|
|
|
21
23
|
/** A condition with its `performed(... within)` windows divided by `factor`. */
|
|
22
24
|
export declare function scaleCondition(condition: ConditionIR, factor: number): ConditionIR;
|
|
23
25
|
/**
|
|
24
|
-
* A store whose segment definitions come back scaled by `factor` — what
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* A store whose segment definitions come back scaled by `factor` — what the journey host leases
|
|
27
|
+
* in fast-forward, so a segment's windows (`performed(e.login, { within: '7 days' })`) shrink
|
|
28
|
+
* with the journeys that reference it, and with the segment triggers it routes. Every read that
|
|
29
|
+
* hands out a stored condition is scaled: `getSegmentCondition` (event-driven evaluation),
|
|
30
|
+
* `listSegments` (the scheduled sweep, which narrows candidates in SQL and evaluates with those
|
|
31
|
+
* definitions) and `findTriggers` (the profile gate an event trigger checks before it starts a
|
|
32
|
+
* journey, which `scaleDurations` scales inside the journey too). Every other method is the
|
|
33
|
+
* store's own.
|
|
28
34
|
*/
|
|
29
35
|
export declare function scaleSegmentWindows<S extends {
|
|
30
36
|
getSegmentCondition(name: string): Promise<ConditionIR | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-scale.d.mts","names":[],"sources":["../../src/engine/time-scale.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"time-scale.d.mts","names":[],"sources":["../../src/engine/time-scale.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;wBAsBgB,eAAe,IAAI,YAAY,iBAAiB;;wBA4BhD,eAAe,WAAW,aAAa,iBAAiB;;;;;;;;;;;wBA2ExD,oBACd;EAAY,oBAAoB,eAAe,QAAQ;GACvD,OAAO,GAAG,iBAAiB"}
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
* timeouts, goal and `performed(... within)` windows inside the workflow), so
|
|
10
10
|
* the journey keeps its shape, just faster. What does not scale: wall-clock
|
|
11
11
|
* `timeWindow`s (they are times of day, not durations). Segment definitions
|
|
12
|
-
* live in the store rather than the workflow body;
|
|
13
|
-
* scales them as they are read
|
|
12
|
+
* and trigger routes live in the store rather than the workflow body;
|
|
13
|
+
* `scaleSegmentWindows` scales them as they are read — one segment for an
|
|
14
|
+
* event-driven evaluation, all of them for the scheduled sweep, a route's
|
|
15
|
+
* profile gate at ingest — so a segment's `within` keeps pace everywhere.
|
|
14
16
|
*
|
|
15
17
|
* The walk follows the IR's own structure — the fields that hold a Duration
|
|
16
18
|
* are known — so a message prop or an event payload that happens to look like
|
|
@@ -104,20 +106,40 @@ function scale(duration, factor) {
|
|
|
104
106
|
};
|
|
105
107
|
}
|
|
106
108
|
/**
|
|
107
|
-
* A store whose segment definitions come back scaled by `factor` — what
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
109
|
+
* A store whose segment definitions come back scaled by `factor` — what the journey host leases
|
|
110
|
+
* in fast-forward, so a segment's windows (`performed(e.login, { within: '7 days' })`) shrink
|
|
111
|
+
* with the journeys that reference it, and with the segment triggers it routes. Every read that
|
|
112
|
+
* hands out a stored condition is scaled: `getSegmentCondition` (event-driven evaluation),
|
|
113
|
+
* `listSegments` (the scheduled sweep, which narrows candidates in SQL and evaluates with those
|
|
114
|
+
* definitions) and `findTriggers` (the profile gate an event trigger checks before it starts a
|
|
115
|
+
* journey, which `scaleDurations` scales inside the journey too). Every other method is the
|
|
116
|
+
* store's own.
|
|
111
117
|
*/
|
|
112
118
|
function scaleSegmentWindows(store, factor) {
|
|
113
119
|
if (!(factor > 0) || factor === 1) return store;
|
|
114
120
|
return new Proxy(store, { get(target, property) {
|
|
115
|
-
if (property === "getSegmentCondition") return async (name) => {
|
|
116
|
-
const condition = await target.getSegmentCondition(name);
|
|
117
|
-
return condition === void 0 ? void 0 : scaleCondition(condition, factor);
|
|
118
|
-
};
|
|
119
121
|
const value = Reflect.get(target, property, target);
|
|
120
|
-
|
|
122
|
+
if (typeof value !== "function") return value;
|
|
123
|
+
switch (property) {
|
|
124
|
+
case "getSegmentCondition": return async (name) => {
|
|
125
|
+
const condition = await target.getSegmentCondition(name);
|
|
126
|
+
return condition === void 0 ? void 0 : scaleCondition(condition, factor);
|
|
127
|
+
};
|
|
128
|
+
case "listSegments": return async () => {
|
|
129
|
+
return (await value.call(target)).map(({ id, condition }) => ({
|
|
130
|
+
id,
|
|
131
|
+
condition: scaleCondition(condition, factor)
|
|
132
|
+
}));
|
|
133
|
+
};
|
|
134
|
+
case "findTriggers": return async (event) => {
|
|
135
|
+
const scaled = (route) => ({
|
|
136
|
+
...route,
|
|
137
|
+
filter: route.filter === null ? null : scaleCondition(route.filter, factor)
|
|
138
|
+
});
|
|
139
|
+
return (await value.call(target, event)).map(scaled);
|
|
140
|
+
};
|
|
141
|
+
default: return value.bind(target);
|
|
142
|
+
}
|
|
121
143
|
} });
|
|
122
144
|
}
|
|
123
145
|
export { scaleCondition, scaleDurations, scaleSegmentWindows };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-scale.mjs","names":[],"sources":["../../src/engine/time-scale.ts"],"sourcesContent":["import type { ConditionIR, DurationIR, NodeIR, WorkflowIR } from '../ir';\n\n/**\n * Debug time scale: divide every duration in a workflow's IR by a factor so a\n * journey written in days plays out in seconds — with a factor of 3600, a\n * `delay('2 days')` sleeps 48 s, a `waitUntil(..., { timeout: '3 days' })`\n * gives up after 72 s, and a goal `within: '30 days'` closes after 12 min.\n *\n * Everything that is a Duration scales together (delays, random delays, wait\n * timeouts, goal and `performed(... within)` windows inside the workflow), so\n * the journey keeps its shape, just faster. What does not scale: wall-clock\n * `timeWindow`s (they are times of day, not durations). Segment definitions\n * live in the store rather than the workflow body
|
|
1
|
+
{"version":3,"file":"time-scale.mjs","names":[],"sources":["../../src/engine/time-scale.ts"],"sourcesContent":["import type { ConditionIR, DurationIR, NodeIR, WorkflowIR } from '../ir';\n\n/**\n * Debug time scale: divide every duration in a workflow's IR by a factor so a\n * journey written in days plays out in seconds — with a factor of 3600, a\n * `delay('2 days')` sleeps 48 s, a `waitUntil(..., { timeout: '3 days' })`\n * gives up after 72 s, and a goal `within: '30 days'` closes after 12 min.\n *\n * Everything that is a Duration scales together (delays, random delays, wait\n * timeouts, goal and `performed(... within)` windows inside the workflow), so\n * the journey keeps its shape, just faster. What does not scale: wall-clock\n * `timeWindow`s (they are times of day, not durations). Segment definitions\n * and trigger routes live in the store rather than the workflow body;\n * `scaleSegmentWindows` scales them as they are read — one segment for an\n * event-driven evaluation, all of them for the scheduled sweep, a route's\n * profile gate at ingest — so a segment's `within` keeps pace everywhere.\n *\n * The walk follows the IR's own structure — the fields that hold a Duration\n * are known — so a message prop or an event payload that happens to look like\n * one (`{ value, ms }`) is left alone. The source text is kept so the studio\n * still shows what was written.\n */\nexport function scaleDurations(ir: WorkflowIR, factor: number): WorkflowIR {\n if (!(factor > 0) || factor === 1) return ir;\n return {\n ...ir,\n trigger:\n ir.trigger.type === 'event'\n ? {\n ...ir.trigger,\n ...(ir.trigger.filter === undefined\n ? {}\n : { filter: scaleCondition(ir.trigger.filter, factor) }),\n }\n : ir.trigger,\n ...(ir.goal === undefined\n ? {}\n : {\n goal: {\n ...ir.goal,\n condition: scaleCondition(ir.goal.condition, factor),\n ...(ir.goal.within === undefined ? {} : { within: scale(ir.goal.within, factor) }),\n },\n }),\n ...(ir.exitWhen === undefined ? {} : { exitWhen: scaleCondition(ir.exitWhen, factor) }),\n flow: scaleFlow(ir.flow, factor),\n };\n}\n\n/** A condition with its `performed(... within)` windows divided by `factor`. */\nexport function scaleCondition(condition: ConditionIR, factor: number): ConditionIR {\n if (!(factor > 0) || factor === 1) return condition;\n switch (condition.type) {\n case 'and':\n case 'or':\n return {\n ...condition,\n conditions: condition.conditions.map((child) => scaleCondition(child, factor)),\n };\n case 'not':\n return { ...condition, condition: scaleCondition(condition.condition, factor) };\n case 'performed':\n return condition.within === undefined\n ? condition\n : { ...condition, within: scale(condition.within, factor) };\n default:\n return condition;\n }\n}\n\nfunction scaleFlow(nodes: readonly NodeIR[], factor: number): NodeIR[] {\n return nodes.map((node) => scaleNode(node, factor));\n}\n\nfunction scaleNode(node: NodeIR, factor: number): NodeIR {\n switch (node.type) {\n case 'delay':\n return { ...node, duration: scale(node.duration, factor) };\n case 'random_delay':\n return { ...node, min: scale(node.min, factor), max: scale(node.max, factor) };\n case 'wait_until':\n return {\n ...node,\n condition: scaleCondition(node.condition, factor),\n timeout: scale(node.timeout, factor),\n onTimeout: Array.isArray(node.onTimeout)\n ? scaleFlow(node.onTimeout, factor)\n : node.onTimeout,\n };\n case 'branch':\n return {\n ...node,\n cases: node.cases.map((branch) => ({\n ...branch,\n condition: scaleCondition(branch.condition, factor),\n flow: scaleFlow(branch.flow, factor),\n })),\n ...(node.otherwise === undefined ? {} : { otherwise: scaleFlow(node.otherwise, factor) }),\n };\n case 'filter':\n return { ...node, condition: scaleCondition(node.condition, factor) };\n case 'cohort':\n return {\n ...node,\n arms: node.arms.map((arm) => ({ ...arm, flow: scaleFlow(arm.flow, factor) })),\n };\n default:\n return node;\n }\n}\n\nfunction scale(duration: DurationIR, factor: number): DurationIR {\n return { value: duration.value, ms: Math.max(0, Math.round(duration.ms / factor)) };\n}\n\n/**\n * A store whose segment definitions come back scaled by `factor` — what the journey host leases\n * in fast-forward, so a segment's windows (`performed(e.login, { within: '7 days' })`) shrink\n * with the journeys that reference it, and with the segment triggers it routes. Every read that\n * hands out a stored condition is scaled: `getSegmentCondition` (event-driven evaluation),\n * `listSegments` (the scheduled sweep, which narrows candidates in SQL and evaluates with those\n * definitions) and `findTriggers` (the profile gate an event trigger checks before it starts a\n * journey, which `scaleDurations` scales inside the journey too). Every other method is the\n * store's own.\n */\nexport function scaleSegmentWindows<\n S extends { getSegmentCondition(name: string): Promise<ConditionIR | undefined> },\n>(store: S, factor: number): S {\n if (!(factor > 0) || factor === 1) return store;\n return new Proxy(store, {\n get(target, property) {\n const value: unknown = Reflect.get(target, property, target);\n if (typeof value !== 'function') return value;\n switch (property) {\n case 'getSegmentCondition':\n return async (name: string) => {\n const condition = await target.getSegmentCondition(name);\n return condition === undefined ? undefined : scaleCondition(condition, factor);\n };\n case 'listSegments':\n return async () => {\n const segments = (await value.call(target)) as { id: string; condition: ConditionIR }[];\n return segments.map(({ id, condition }) => ({\n id,\n condition: scaleCondition(condition, factor),\n }));\n };\n case 'findTriggers':\n return async (event: string) => {\n // Only the profile gate: `where` is payload-only, so it holds no window to scale\n const scaled = (route: { filter: ConditionIR | null }) => ({\n ...route,\n filter: route.filter === null ? null : scaleCondition(route.filter, factor),\n });\n return ((await value.call(target, event)) as { filter: ConditionIR | null }[]).map(\n scaled\n );\n };\n default:\n return value.bind(target);\n }\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,eAAe,IAAgB,QAA4B;CACzE,IAAI,EAAE,SAAS,MAAM,WAAW,GAAG,OAAO;CAC1C,OAAO;EACL,GAAG;EACH,SACE,GAAG,QAAQ,SAAS,UAChB;GACE,GAAG,GAAG;GACN,GAAI,GAAG,QAAQ,WAAW,KAAA,IACtB,CAAC,IACD,EAAE,QAAQ,eAAe,GAAG,QAAQ,QAAQ,MAAM,EAAE;EAC1D,IACA,GAAG;EACT,GAAI,GAAG,SAAS,KAAA,IACZ,CAAC,IACD,EACE,MAAM;GACJ,GAAG,GAAG;GACN,WAAW,eAAe,GAAG,KAAK,WAAW,MAAM;GACnD,GAAI,GAAG,KAAK,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,GAAG,KAAK,QAAQ,MAAM,EAAE;EAClF,EACF;EACJ,GAAI,GAAG,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,eAAe,GAAG,UAAU,MAAM,EAAE;EACrF,MAAM,UAAU,GAAG,MAAM,MAAM;CACjC;AACF;;AAGA,SAAgB,eAAe,WAAwB,QAA6B;CAClF,IAAI,EAAE,SAAS,MAAM,WAAW,GAAG,OAAO;CAC1C,QAAQ,UAAU,MAAlB;EACE,KAAK;EACL,KAAK,MACH,OAAO;GACL,GAAG;GACH,YAAY,UAAU,WAAW,KAAK,UAAU,eAAe,OAAO,MAAM,CAAC;EAC/E;EACF,KAAK,OACH,OAAO;GAAE,GAAG;GAAW,WAAW,eAAe,UAAU,WAAW,MAAM;EAAE;EAChF,KAAK,aACH,OAAO,UAAU,WAAW,KAAA,IACxB,YACA;GAAE,GAAG;GAAW,QAAQ,MAAM,UAAU,QAAQ,MAAM;EAAE;EAC9D,SACE,OAAO;CACX;AACF;AAEA,SAAS,UAAU,OAA0B,QAA0B;CACrE,OAAO,MAAM,KAAK,SAAS,UAAU,MAAM,MAAM,CAAC;AACpD;AAEA,SAAS,UAAU,MAAc,QAAwB;CACvD,QAAQ,KAAK,MAAb;EACE,KAAK,SACH,OAAO;GAAE,GAAG;GAAM,UAAU,MAAM,KAAK,UAAU,MAAM;EAAE;EAC3D,KAAK,gBACH,OAAO;GAAE,GAAG;GAAM,KAAK,MAAM,KAAK,KAAK,MAAM;GAAG,KAAK,MAAM,KAAK,KAAK,MAAM;EAAE;EAC/E,KAAK,cACH,OAAO;GACL,GAAG;GACH,WAAW,eAAe,KAAK,WAAW,MAAM;GAChD,SAAS,MAAM,KAAK,SAAS,MAAM;GACnC,WAAW,MAAM,QAAQ,KAAK,SAAS,IACnC,UAAU,KAAK,WAAW,MAAM,IAChC,KAAK;EACX;EACF,KAAK,UACH,OAAO;GACL,GAAG;GACH,OAAO,KAAK,MAAM,KAAK,YAAY;IACjC,GAAG;IACH,WAAW,eAAe,OAAO,WAAW,MAAM;IAClD,MAAM,UAAU,OAAO,MAAM,MAAM;GACrC,EAAE;GACF,GAAI,KAAK,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,UAAU,KAAK,WAAW,MAAM,EAAE;EACzF;EACF,KAAK,UACH,OAAO;GAAE,GAAG;GAAM,WAAW,eAAe,KAAK,WAAW,MAAM;EAAE;EACtE,KAAK,UACH,OAAO;GACL,GAAG;GACH,MAAM,KAAK,KAAK,KAAK,SAAS;IAAE,GAAG;IAAK,MAAM,UAAU,IAAI,MAAM,MAAM;GAAE,EAAE;EAC9E;EACF,SACE,OAAO;CACX;AACF;AAEA,SAAS,MAAM,UAAsB,QAA4B;CAC/D,OAAO;EAAE,OAAO,SAAS;EAAO,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,KAAK,MAAM,CAAC;CAAE;AACpF;;;;;;;;;;;AAYA,SAAgB,oBAEd,OAAU,QAAmB;CAC7B,IAAI,EAAE,SAAS,MAAM,WAAW,GAAG,OAAO;CAC1C,OAAO,IAAI,MAAM,OAAO,EACtB,IAAI,QAAQ,UAAU;EACpB,MAAM,QAAiB,QAAQ,IAAI,QAAQ,UAAU,MAAM;EAC3D,IAAI,OAAO,UAAU,YAAY,OAAO;EACxC,QAAQ,UAAR;GACE,KAAK,uBACH,OAAO,OAAO,SAAiB;IAC7B,MAAM,YAAY,MAAM,OAAO,oBAAoB,IAAI;IACvD,OAAO,cAAc,KAAA,IAAY,KAAA,IAAY,eAAe,WAAW,MAAM;GAC/E;GACF,KAAK,gBACH,OAAO,YAAY;IAEjB,QAAO,MADiB,MAAM,KAAK,MAAM,EAAA,CACzB,KAAK,EAAE,IAAI,iBAAiB;KAC1C;KACA,WAAW,eAAe,WAAW,MAAM;IAC7C,EAAE;GACJ;GACF,KAAK,gBACH,OAAO,OAAO,UAAkB;IAE9B,MAAM,UAAU,WAA2C;KACzD,GAAG;KACH,QAAQ,MAAM,WAAW,OAAO,OAAO,eAAe,MAAM,QAAQ,MAAM;IAC5E;IACA,QAAS,MAAM,MAAM,KAAK,QAAQ,KAAK,EAAA,CAAwC,IAC7E,MACF;GACF;GACF,SACE,OAAO,MAAM,KAAK,MAAM;EAC5B;CACF,EACF,CAAC;AACH"}
|
package/dist/postgres-js.cjs
CHANGED
|
@@ -9,10 +9,19 @@ postgres = require_runtime.__toESM(postgres, 1);
|
|
|
9
9
|
* (`postgres` is an optional peer dependency; import this subpath only when
|
|
10
10
|
* you use it). On Cloudflare the connection string is Hyperdrive's.
|
|
11
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* A JS array as a Postgres array literal (`{"a","b"}`), one level deep. `sql.unsafe` sends an
|
|
14
|
+
* array parameter as `String(array)` (`a,b`, and `''` for an empty one), which `$1::text[]`
|
|
15
|
+
* rejects with "malformed array literal"; the literal form parses whatever the column type.
|
|
16
|
+
*/
|
|
17
|
+
function pgArrayLiteral(items) {
|
|
18
|
+
const element = (item) => item === null || item === void 0 ? "NULL" : `"${String(item).replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
|
|
19
|
+
return `{${items.map(element).join(",")}}`;
|
|
20
|
+
}
|
|
12
21
|
/** postgres.js → the store's minimal client. Inside `begin` every statement runs on the transaction client; the store never nests, so an inner `transaction` continues on the same one. */
|
|
13
22
|
function postgresJsClient(sql, inTransaction = false) {
|
|
14
23
|
return {
|
|
15
|
-
query: (text, params = []) => sql.unsafe(text, params),
|
|
24
|
+
query: (text, params = []) => sql.unsafe(text, params.map((param) => Array.isArray(param) ? pgArrayLiteral(param) : param)),
|
|
16
25
|
transaction: (fn) => inTransaction ? fn(postgresJsClient(sql, true)) : sql.begin((tx) => fn(postgresJsClient(tx, true)))
|
|
17
26
|
};
|
|
18
27
|
}
|
|
@@ -35,6 +44,7 @@ function postgresJsStore(connectionString, options = {}) {
|
|
|
35
44
|
close: () => sql.end({ timeout: 5 })
|
|
36
45
|
};
|
|
37
46
|
}
|
|
47
|
+
exports.pgArrayLiteral = pgArrayLiteral;
|
|
38
48
|
exports.postgresJsClient = postgresJsClient;
|
|
39
49
|
exports.postgresJsStore = postgresJsStore;
|
|
40
50
|
|
package/dist/postgres-js.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-js.cjs","names":["PostgresJourneyStore"],"sources":["../src/postgres-js.ts"],"sourcesContent":["import postgres from 'postgres';\nimport type { StoreLease } from './store/index';\nimport {\n PostgresJourneyStore,\n type PostgresJourneyStoreOptions,\n type SqlClientLike,\n} from './store/postgres';\n\n/**\n * `@shware/workflow/postgres-js` — the Postgres store over postgres.js\n * (`postgres` is an optional peer dependency; import this subpath only when\n * you use it). On Cloudflare the connection string is Hyperdrive's.\n */\n\n/** postgres.js → the store's minimal client. Inside `begin` every statement runs on the transaction client; the store never nests, so an inner `transaction` continues on the same one. */\nexport function postgresJsClient(\n sql: postgres.Sql | postgres.TransactionSql,\n inTransaction = false\n): SqlClientLike {\n return {\n query: <T>(text: string, params: readonly unknown[] = []) =>\n sql.unsafe(text
|
|
1
|
+
{"version":3,"file":"postgres-js.cjs","names":["PostgresJourneyStore"],"sources":["../src/postgres-js.ts"],"sourcesContent":["import postgres from 'postgres';\nimport type { StoreLease } from './store/index';\nimport {\n PostgresJourneyStore,\n type PostgresJourneyStoreOptions,\n type SqlClientLike,\n} from './store/postgres';\n\n/**\n * `@shware/workflow/postgres-js` — the Postgres store over postgres.js\n * (`postgres` is an optional peer dependency; import this subpath only when\n * you use it). On Cloudflare the connection string is Hyperdrive's.\n */\n\n/**\n * A JS array as a Postgres array literal (`{\"a\",\"b\"}`), one level deep. `sql.unsafe` sends an\n * array parameter as `String(array)` (`a,b`, and `''` for an empty one), which `$1::text[]`\n * rejects with \"malformed array literal\"; the literal form parses whatever the column type.\n */\nexport function pgArrayLiteral(\n items: readonly (string | number | bigint | boolean | null | undefined)[]\n): string {\n const element = (item: (typeof items)[number]) =>\n item === null || item === undefined\n ? 'NULL'\n : `\"${String(item).replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"')}\"`;\n return `{${items.map(element).join(',')}}`;\n}\n\n/** postgres.js → the store's minimal client. Inside `begin` every statement runs on the transaction client; the store never nests, so an inner `transaction` continues on the same one. */\nexport function postgresJsClient(\n sql: postgres.Sql | postgres.TransactionSql,\n inTransaction = false\n): SqlClientLike {\n return {\n query: <T>(text: string, params: readonly unknown[] = []) =>\n sql.unsafe(\n text,\n params.map((param) =>\n Array.isArray(param)\n ? pgArrayLiteral(param as Parameters<typeof pgArrayLiteral>[0])\n : param\n ) as never\n ) as unknown as Promise<T[]>,\n transaction: <T>(fn: (tx: SqlClientLike) => Promise<T>) =>\n inTransaction\n ? fn(postgresJsClient(sql, true))\n : ((sql as postgres.Sql).begin((tx) => fn(postgresJsClient(tx, true))) as Promise<T>),\n };\n}\n\nexport interface PostgresJsStoreOptions extends PostgresJourneyStoreOptions {\n /**\n * postgres.js options. Defaults suit a Worker with Hyperdrive: one\n * connection per request/run, closed after 2 s idle, no type fetch on\n * connect, prepared statements (Hyperdrive caches them).\n */\n connection?: postgres.Options<Record<string, never>>;\n}\n\n/**\n * Open a Postgres-backed store for one request or workflow run. The lease's\n * `close` ends the client — a Worker cannot share a socket across requests,\n * and Hyperdrive keeps the real pool next to the database.\n */\nexport function postgresJsStore(\n connectionString: string,\n options: PostgresJsStoreOptions = {}\n): Required<StoreLease> {\n const { connection, ...storeOptions } = options;\n const sql = postgres(connectionString, {\n max: 1,\n // A journey run lives for days: between steps it sleeps or waits, and a run holding its\n // connection through that holds one per sleeping instance — hundreds of them exhaust the\n // database. Idle connections close and the next query opens a fresh one.\n idle_timeout: 2,\n fetch_types: false,\n prepare: true,\n ...connection,\n });\n return {\n store: new PostgresJourneyStore(postgresJsClient(sql), storeOptions),\n close: () => sql.end({ timeout: 5 }),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,SAAgB,eACd,OACQ;CACR,MAAM,WAAW,SACf,SAAS,QAAQ,SAAS,KAAA,IACtB,SACA,IAAI,OAAO,IAAI,CAAC,CAAC,WAAW,MAAM,MAAM,CAAC,CAAC,WAAW,MAAK,MAAK,EAAE;CACvE,OAAO,IAAI,MAAM,IAAI,OAAO,CAAC,CAAC,KAAK,GAAG,EAAE;AAC1C;;AAGA,SAAgB,iBACd,KACA,gBAAgB,OACD;CACf,OAAO;EACL,QAAW,MAAc,SAA6B,CAAC,MACrD,IAAI,OACF,MACA,OAAO,KAAK,UACV,MAAM,QAAQ,KAAK,IACf,eAAe,KAA6C,IAC5D,KACN,CACF;EACF,cAAiB,OACf,gBACI,GAAG,iBAAiB,KAAK,IAAI,CAAC,IAC5B,IAAqB,OAAO,OAAO,GAAG,iBAAiB,IAAI,IAAI,CAAC,CAAC;CAC3E;AACF;;;;;;AAgBA,SAAgB,gBACd,kBACA,UAAkC,CAAC,GACb;CACtB,MAAM,EAAE,YAAY,GAAG,iBAAiB;CACxC,MAAM,OAAA,GAAM,SAAA,QAAA,CAAS,kBAAkB;EACrC,KAAK;EAIL,cAAc;EACd,aAAa;EACb,SAAS;EACT,GAAG;CACL,CAAC;CACD,OAAO;EACL,OAAO,IAAIA,uBAAAA,qBAAqB,iBAAiB,GAAG,GAAG,YAAY;EACnE,aAAa,IAAI,IAAI,EAAE,SAAS,EAAE,CAAC;CACrC;AACF"}
|
package/dist/postgres-js.d.cts
CHANGED
|
@@ -7,6 +7,12 @@ import postgres from "postgres";
|
|
|
7
7
|
* (`postgres` is an optional peer dependency; import this subpath only when
|
|
8
8
|
* you use it). On Cloudflare the connection string is Hyperdrive's.
|
|
9
9
|
*/
|
|
10
|
+
/**
|
|
11
|
+
* A JS array as a Postgres array literal (`{"a","b"}`), one level deep. `sql.unsafe` sends an
|
|
12
|
+
* array parameter as `String(array)` (`a,b`, and `''` for an empty one), which `$1::text[]`
|
|
13
|
+
* rejects with "malformed array literal"; the literal form parses whatever the column type.
|
|
14
|
+
*/
|
|
15
|
+
export declare function pgArrayLiteral(items: readonly (string | number | bigint | boolean | null | undefined)[]): string;
|
|
10
16
|
/** postgres.js → the store's minimal client. Inside `begin` every statement runs on the transaction client; the store never nests, so an inner `transaction` continues on the same one. */
|
|
11
17
|
export declare function postgresJsClient(sql: postgres.Sql | postgres.TransactionSql, inTransaction?: boolean): SqlClientLike;
|
|
12
18
|
export interface PostgresJsStoreOptions extends PostgresJourneyStoreOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-js.d.cts","names":[],"sources":["../src/postgres-js.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"postgres-js.d.cts","names":[],"sources":["../src/postgres-js.ts"],"mappings":";;;;;;;;;;;;;;wBAmBgB,eACd;;wBAUc,iBACd,KAAK,SAAS,MAAM,SAAS,gBAC7B,0BACC;iBAkBc,+BAA+B;;;;;;EAM9C,aAAa,SAAS,QAAQ;;;;;;;wBAQhB,gBACd,0BACA,UAAS,yBACR,SAAS"}
|
package/dist/postgres-js.d.mts
CHANGED
|
@@ -7,6 +7,12 @@ import postgres from "postgres";
|
|
|
7
7
|
* (`postgres` is an optional peer dependency; import this subpath only when
|
|
8
8
|
* you use it). On Cloudflare the connection string is Hyperdrive's.
|
|
9
9
|
*/
|
|
10
|
+
/**
|
|
11
|
+
* A JS array as a Postgres array literal (`{"a","b"}`), one level deep. `sql.unsafe` sends an
|
|
12
|
+
* array parameter as `String(array)` (`a,b`, and `''` for an empty one), which `$1::text[]`
|
|
13
|
+
* rejects with "malformed array literal"; the literal form parses whatever the column type.
|
|
14
|
+
*/
|
|
15
|
+
export declare function pgArrayLiteral(items: readonly (string | number | bigint | boolean | null | undefined)[]): string;
|
|
10
16
|
/** postgres.js → the store's minimal client. Inside `begin` every statement runs on the transaction client; the store never nests, so an inner `transaction` continues on the same one. */
|
|
11
17
|
export declare function postgresJsClient(sql: postgres.Sql | postgres.TransactionSql, inTransaction?: boolean): SqlClientLike;
|
|
12
18
|
export interface PostgresJsStoreOptions extends PostgresJourneyStoreOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-js.d.mts","names":[],"sources":["../src/postgres-js.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"postgres-js.d.mts","names":[],"sources":["../src/postgres-js.ts"],"mappings":";;;;;;;;;;;;;;wBAmBgB,eACd;;wBAUc,iBACd,KAAK,SAAS,MAAM,SAAS,gBAC7B,0BACC;iBAkBc,+BAA+B;;;;;;EAM9C,aAAa,SAAS,QAAQ;;;;;;;wBAQhB,gBACd,0BACA,UAAS,yBACR,SAAS"}
|
package/dist/postgres-js.mjs
CHANGED
|
@@ -6,10 +6,19 @@ import postgres from "postgres";
|
|
|
6
6
|
* (`postgres` is an optional peer dependency; import this subpath only when
|
|
7
7
|
* you use it). On Cloudflare the connection string is Hyperdrive's.
|
|
8
8
|
*/
|
|
9
|
+
/**
|
|
10
|
+
* A JS array as a Postgres array literal (`{"a","b"}`), one level deep. `sql.unsafe` sends an
|
|
11
|
+
* array parameter as `String(array)` (`a,b`, and `''` for an empty one), which `$1::text[]`
|
|
12
|
+
* rejects with "malformed array literal"; the literal form parses whatever the column type.
|
|
13
|
+
*/
|
|
14
|
+
function pgArrayLiteral(items) {
|
|
15
|
+
const element = (item) => item === null || item === void 0 ? "NULL" : `"${String(item).replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
|
|
16
|
+
return `{${items.map(element).join(",")}}`;
|
|
17
|
+
}
|
|
9
18
|
/** postgres.js → the store's minimal client. Inside `begin` every statement runs on the transaction client; the store never nests, so an inner `transaction` continues on the same one. */
|
|
10
19
|
function postgresJsClient(sql, inTransaction = false) {
|
|
11
20
|
return {
|
|
12
|
-
query: (text, params = []) => sql.unsafe(text, params),
|
|
21
|
+
query: (text, params = []) => sql.unsafe(text, params.map((param) => Array.isArray(param) ? pgArrayLiteral(param) : param)),
|
|
13
22
|
transaction: (fn) => inTransaction ? fn(postgresJsClient(sql, true)) : sql.begin((tx) => fn(postgresJsClient(tx, true)))
|
|
14
23
|
};
|
|
15
24
|
}
|
|
@@ -32,6 +41,6 @@ function postgresJsStore(connectionString, options = {}) {
|
|
|
32
41
|
close: () => sql.end({ timeout: 5 })
|
|
33
42
|
};
|
|
34
43
|
}
|
|
35
|
-
export { postgresJsClient, postgresJsStore };
|
|
44
|
+
export { pgArrayLiteral, postgresJsClient, postgresJsStore };
|
|
36
45
|
|
|
37
46
|
//# sourceMappingURL=postgres-js.mjs.map
|
package/dist/postgres-js.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-js.mjs","names":[],"sources":["../src/postgres-js.ts"],"sourcesContent":["import postgres from 'postgres';\nimport type { StoreLease } from './store/index';\nimport {\n PostgresJourneyStore,\n type PostgresJourneyStoreOptions,\n type SqlClientLike,\n} from './store/postgres';\n\n/**\n * `@shware/workflow/postgres-js` — the Postgres store over postgres.js\n * (`postgres` is an optional peer dependency; import this subpath only when\n * you use it). On Cloudflare the connection string is Hyperdrive's.\n */\n\n/** postgres.js → the store's minimal client. Inside `begin` every statement runs on the transaction client; the store never nests, so an inner `transaction` continues on the same one. */\nexport function postgresJsClient(\n sql: postgres.Sql | postgres.TransactionSql,\n inTransaction = false\n): SqlClientLike {\n return {\n query: <T>(text: string, params: readonly unknown[] = []) =>\n sql.unsafe(text
|
|
1
|
+
{"version":3,"file":"postgres-js.mjs","names":[],"sources":["../src/postgres-js.ts"],"sourcesContent":["import postgres from 'postgres';\nimport type { StoreLease } from './store/index';\nimport {\n PostgresJourneyStore,\n type PostgresJourneyStoreOptions,\n type SqlClientLike,\n} from './store/postgres';\n\n/**\n * `@shware/workflow/postgres-js` — the Postgres store over postgres.js\n * (`postgres` is an optional peer dependency; import this subpath only when\n * you use it). On Cloudflare the connection string is Hyperdrive's.\n */\n\n/**\n * A JS array as a Postgres array literal (`{\"a\",\"b\"}`), one level deep. `sql.unsafe` sends an\n * array parameter as `String(array)` (`a,b`, and `''` for an empty one), which `$1::text[]`\n * rejects with \"malformed array literal\"; the literal form parses whatever the column type.\n */\nexport function pgArrayLiteral(\n items: readonly (string | number | bigint | boolean | null | undefined)[]\n): string {\n const element = (item: (typeof items)[number]) =>\n item === null || item === undefined\n ? 'NULL'\n : `\"${String(item).replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"')}\"`;\n return `{${items.map(element).join(',')}}`;\n}\n\n/** postgres.js → the store's minimal client. Inside `begin` every statement runs on the transaction client; the store never nests, so an inner `transaction` continues on the same one. */\nexport function postgresJsClient(\n sql: postgres.Sql | postgres.TransactionSql,\n inTransaction = false\n): SqlClientLike {\n return {\n query: <T>(text: string, params: readonly unknown[] = []) =>\n sql.unsafe(\n text,\n params.map((param) =>\n Array.isArray(param)\n ? pgArrayLiteral(param as Parameters<typeof pgArrayLiteral>[0])\n : param\n ) as never\n ) as unknown as Promise<T[]>,\n transaction: <T>(fn: (tx: SqlClientLike) => Promise<T>) =>\n inTransaction\n ? fn(postgresJsClient(sql, true))\n : ((sql as postgres.Sql).begin((tx) => fn(postgresJsClient(tx, true))) as Promise<T>),\n };\n}\n\nexport interface PostgresJsStoreOptions extends PostgresJourneyStoreOptions {\n /**\n * postgres.js options. Defaults suit a Worker with Hyperdrive: one\n * connection per request/run, closed after 2 s idle, no type fetch on\n * connect, prepared statements (Hyperdrive caches them).\n */\n connection?: postgres.Options<Record<string, never>>;\n}\n\n/**\n * Open a Postgres-backed store for one request or workflow run. The lease's\n * `close` ends the client — a Worker cannot share a socket across requests,\n * and Hyperdrive keeps the real pool next to the database.\n */\nexport function postgresJsStore(\n connectionString: string,\n options: PostgresJsStoreOptions = {}\n): Required<StoreLease> {\n const { connection, ...storeOptions } = options;\n const sql = postgres(connectionString, {\n max: 1,\n // A journey run lives for days: between steps it sleeps or waits, and a run holding its\n // connection through that holds one per sleeping instance — hundreds of them exhaust the\n // database. Idle connections close and the next query opens a fresh one.\n idle_timeout: 2,\n fetch_types: false,\n prepare: true,\n ...connection,\n });\n return {\n store: new PostgresJourneyStore(postgresJsClient(sql), storeOptions),\n close: () => sql.end({ timeout: 5 }),\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAmBA,SAAgB,eACd,OACQ;CACR,MAAM,WAAW,SACf,SAAS,QAAQ,SAAS,KAAA,IACtB,SACA,IAAI,OAAO,IAAI,CAAC,CAAC,WAAW,MAAM,MAAM,CAAC,CAAC,WAAW,MAAK,MAAK,EAAE;CACvE,OAAO,IAAI,MAAM,IAAI,OAAO,CAAC,CAAC,KAAK,GAAG,EAAE;AAC1C;;AAGA,SAAgB,iBACd,KACA,gBAAgB,OACD;CACf,OAAO;EACL,QAAW,MAAc,SAA6B,CAAC,MACrD,IAAI,OACF,MACA,OAAO,KAAK,UACV,MAAM,QAAQ,KAAK,IACf,eAAe,KAA6C,IAC5D,KACN,CACF;EACF,cAAiB,OACf,gBACI,GAAG,iBAAiB,KAAK,IAAI,CAAC,IAC5B,IAAqB,OAAO,OAAO,GAAG,iBAAiB,IAAI,IAAI,CAAC,CAAC;CAC3E;AACF;;;;;;AAgBA,SAAgB,gBACd,kBACA,UAAkC,CAAC,GACb;CACtB,MAAM,EAAE,YAAY,GAAG,iBAAiB;CACxC,MAAM,MAAM,SAAS,kBAAkB;EACrC,KAAK;EAIL,cAAc;EACd,aAAa;EACb,SAAS;EACT,GAAG;CACL,CAAC;CACD,OAAO;EACL,OAAO,IAAI,qBAAqB,iBAAiB,GAAG,GAAG,YAAY;EACnE,aAAa,IAAI,IAAI,EAAE,SAAS,EAAE,CAAC;CACrC;AACF"}
|
package/dist/store/sql.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql.cjs","names":["Buffer"],"sources":["../../src/store/sql.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\n\n/**\n * The smallest SQL client this store needs: parameterized queries (`$1`, `$2`,\n * …) and a transaction scope. It is deliberately not postgres.js, pg or\n * drizzle — the app wraps whichever it already uses in a few lines:\n *\n * const client: SqlClientLike = {\n * query: (text, params) =>
|
|
1
|
+
{"version":3,"file":"sql.cjs","names":["Buffer"],"sources":["../../src/store/sql.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\n\n/**\n * The smallest SQL client this store needs: parameterized queries (`$1`, `$2`,\n * …) and a transaction scope. It is deliberately not postgres.js, pg or\n * drizzle — the app wraps whichever it already uses in a few lines:\n *\n * const client: SqlClientLike = {\n * query: (text, params) => pg.query(text, params).then((r) => r.rows), // pg\n * transaction: (fn) => withTransaction((tx) => fn(wrap(tx))),\n * };\n *\n * For postgres.js use `postgresJsClient` (`@shware/workflow/postgres-js`): its\n * `unsafe` sends a JS array parameter as `String(array)`, which the store's\n * `= ANY($1::text[])` queries reject, so that wrapper writes arrays as Postgres\n * array literals. jsonb columns may come back parsed (postgres.js, pg) or as\n * strings (some drivers); both are accepted.\n */\nexport interface SqlClientLike {\n query<T = Record<string, unknown>>(text: string, params?: readonly unknown[]): Promise<T[]>;\n transaction<T>(fn: (tx: SqlClientLike) => Promise<T>): Promise<T>;\n}\n\n/**\n * UUID v7 (time-ordered) for event rows, generated here so the store has no\n * dependency: 48-bit ms timestamp, version 7, RFC 4122 variant, 74 random bits.\n */\nexport function uuidv7(now = Date.now()): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n bytes[0] = (now / 2 ** 40) & 0xff;\n bytes[1] = (now / 2 ** 32) & 0xff;\n bytes[2] = (now / 2 ** 24) & 0xff;\n bytes[3] = (now / 2 ** 16) & 0xff;\n bytes[4] = (now / 2 ** 8) & 0xff;\n bytes[5] = now & 0xff;\n bytes[6] = (bytes[6] & 0x0f) | 0x70;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Buffer.from(bytes).toString('hex');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n"],"mappings":";;;;;;;AA2BA,SAAgB,OAAO,MAAM,KAAK,IAAI,GAAW;CAC/C,MAAM,wBAAQ,IAAI,WAAW,EAAE;CAC/B,OAAO,gBAAgB,KAAK;CAC5B,MAAM,KAAM,MAAM,KAAK,KAAM;CAC7B,MAAM,KAAM,MAAM,KAAK,KAAM;CAC7B,MAAM,KAAM,MAAM,KAAK,KAAM;CAC7B,MAAM,KAAM,MAAM,KAAK,KAAM;CAC7B,MAAM,KAAM,MAAM,MAAU;CAC5B,MAAM,KAAK,MAAM;CACjB,MAAM,KAAM,MAAM,KAAK,KAAQ;CAC/B,MAAM,KAAM,MAAM,KAAK,KAAQ;CAC/B,MAAM,MAAMA,YAAAA,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,KAAK;CAC7C,OAAO,GAAG,IAAI,MAAM,GAAG,CAAC,EAAE,GAAG,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,IAAI,MAAM,IAAI,EAAE,EAAE,GAAG,IAAI,MAAM,IAAI,EAAE,EAAE,GAAG,IAAI,MAAM,EAAE;AACzG"}
|
package/dist/store/sql.d.cts
CHANGED
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
* drizzle — the app wraps whichever it already uses in a few lines:
|
|
5
5
|
*
|
|
6
6
|
* const client: SqlClientLike = {
|
|
7
|
-
* query: (text, params) =>
|
|
8
|
-
* transaction: (fn) =>
|
|
7
|
+
* query: (text, params) => pg.query(text, params).then((r) => r.rows), // pg
|
|
8
|
+
* transaction: (fn) => withTransaction((tx) => fn(wrap(tx))),
|
|
9
9
|
* };
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* For postgres.js use `postgresJsClient` (`@shware/workflow/postgres-js`): its
|
|
12
|
+
* `unsafe` sends a JS array parameter as `String(array)`, which the store's
|
|
13
|
+
* `= ANY($1::text[])` queries reject, so that wrapper writes arrays as Postgres
|
|
14
|
+
* array literals. jsonb columns may come back parsed (postgres.js, pg) or as
|
|
15
|
+
* strings (some drivers); both are accepted.
|
|
13
16
|
*/
|
|
14
17
|
export interface SqlClientLike {
|
|
15
18
|
query<T = Record<string, unknown>>(text: string, params?: readonly unknown[]): Promise<T[]>;
|
package/dist/store/sql.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql.d.cts","names":[],"sources":["../../src/store/sql.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"sql.d.cts","names":[],"sources":["../../src/store/sql.ts"],"mappings":";;;;;;;;;;;;;;;;iBAkBiB;EACf,MAAM,IAAI,yBAAyB,cAAc,8BAA8B,QAAQ;EACvF,YAAY,GAAG,KAAK,IAAI,kBAAkB,QAAQ,KAAK,QAAQ;;;;;;wBAOjD,OAAO"}
|
package/dist/store/sql.d.mts
CHANGED
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
* drizzle — the app wraps whichever it already uses in a few lines:
|
|
5
5
|
*
|
|
6
6
|
* const client: SqlClientLike = {
|
|
7
|
-
* query: (text, params) =>
|
|
8
|
-
* transaction: (fn) =>
|
|
7
|
+
* query: (text, params) => pg.query(text, params).then((r) => r.rows), // pg
|
|
8
|
+
* transaction: (fn) => withTransaction((tx) => fn(wrap(tx))),
|
|
9
9
|
* };
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* For postgres.js use `postgresJsClient` (`@shware/workflow/postgres-js`): its
|
|
12
|
+
* `unsafe` sends a JS array parameter as `String(array)`, which the store's
|
|
13
|
+
* `= ANY($1::text[])` queries reject, so that wrapper writes arrays as Postgres
|
|
14
|
+
* array literals. jsonb columns may come back parsed (postgres.js, pg) or as
|
|
15
|
+
* strings (some drivers); both are accepted.
|
|
13
16
|
*/
|
|
14
17
|
export interface SqlClientLike {
|
|
15
18
|
query<T = Record<string, unknown>>(text: string, params?: readonly unknown[]): Promise<T[]>;
|
package/dist/store/sql.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql.d.mts","names":[],"sources":["../../src/store/sql.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"sql.d.mts","names":[],"sources":["../../src/store/sql.ts"],"mappings":";;;;;;;;;;;;;;;;iBAkBiB;EACf,MAAM,IAAI,yBAAyB,cAAc,8BAA8B,QAAQ;EACvF,YAAY,GAAG,KAAK,IAAI,kBAAkB,QAAQ,KAAK,QAAQ;;;;;;wBAOjD,OAAO"}
|
package/dist/store/sql.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql.mjs","names":[],"sources":["../../src/store/sql.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\n\n/**\n * The smallest SQL client this store needs: parameterized queries (`$1`, `$2`,\n * …) and a transaction scope. It is deliberately not postgres.js, pg or\n * drizzle — the app wraps whichever it already uses in a few lines:\n *\n * const client: SqlClientLike = {\n * query: (text, params) =>
|
|
1
|
+
{"version":3,"file":"sql.mjs","names":[],"sources":["../../src/store/sql.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\n\n/**\n * The smallest SQL client this store needs: parameterized queries (`$1`, `$2`,\n * …) and a transaction scope. It is deliberately not postgres.js, pg or\n * drizzle — the app wraps whichever it already uses in a few lines:\n *\n * const client: SqlClientLike = {\n * query: (text, params) => pg.query(text, params).then((r) => r.rows), // pg\n * transaction: (fn) => withTransaction((tx) => fn(wrap(tx))),\n * };\n *\n * For postgres.js use `postgresJsClient` (`@shware/workflow/postgres-js`): its\n * `unsafe` sends a JS array parameter as `String(array)`, which the store's\n * `= ANY($1::text[])` queries reject, so that wrapper writes arrays as Postgres\n * array literals. jsonb columns may come back parsed (postgres.js, pg) or as\n * strings (some drivers); both are accepted.\n */\nexport interface SqlClientLike {\n query<T = Record<string, unknown>>(text: string, params?: readonly unknown[]): Promise<T[]>;\n transaction<T>(fn: (tx: SqlClientLike) => Promise<T>): Promise<T>;\n}\n\n/**\n * UUID v7 (time-ordered) for event rows, generated here so the store has no\n * dependency: 48-bit ms timestamp, version 7, RFC 4122 variant, 74 random bits.\n */\nexport function uuidv7(now = Date.now()): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n bytes[0] = (now / 2 ** 40) & 0xff;\n bytes[1] = (now / 2 ** 32) & 0xff;\n bytes[2] = (now / 2 ** 24) & 0xff;\n bytes[3] = (now / 2 ** 16) & 0xff;\n bytes[4] = (now / 2 ** 8) & 0xff;\n bytes[5] = now & 0xff;\n bytes[6] = (bytes[6] & 0x0f) | 0x70;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Buffer.from(bytes).toString('hex');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n"],"mappings":";;;;;;AA2BA,SAAgB,OAAO,MAAM,KAAK,IAAI,GAAW;CAC/C,MAAM,wBAAQ,IAAI,WAAW,EAAE;CAC/B,OAAO,gBAAgB,KAAK;CAC5B,MAAM,KAAM,MAAM,KAAK,KAAM;CAC7B,MAAM,KAAM,MAAM,KAAK,KAAM;CAC7B,MAAM,KAAM,MAAM,KAAK,KAAM;CAC7B,MAAM,KAAM,MAAM,KAAK,KAAM;CAC7B,MAAM,KAAM,MAAM,MAAU;CAC5B,MAAM,KAAK,MAAM;CACjB,MAAM,KAAM,MAAM,KAAK,KAAQ;CAC/B,MAAM,KAAM,MAAM,KAAK,KAAQ;CAC/B,MAAM,MAAM,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,KAAK;CAC7C,OAAO,GAAG,IAAI,MAAM,GAAG,CAAC,EAAE,GAAG,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,IAAI,MAAM,IAAI,EAAE,EAAE,GAAG,IAAI,MAAM,IAAI,EAAE,EAAE,GAAG,IAAI,MAAM,EAAE;AACzG"}
|