@dudousxd/nestjs-durable-core 0.28.0 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +64 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +64 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -277,6 +277,28 @@ interface StateStore {
|
|
|
277
277
|
transaction?<T>(work: (tx: StoreTransaction) => Promise<T>): Promise<T>;
|
|
278
278
|
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
279
279
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
280
|
+
/**
|
|
281
|
+
* The LATEST checkpoint for `runId` whose `name` equals `name` exactly (highest `seq` wins), or
|
|
282
|
+
* `undefined` if none. A targeted read that avoids fetching + deserializing every checkpoint just to
|
|
283
|
+
* keep one match — the store does the filter (`WHERE name = … ORDER BY seq DESC LIMIT 1`). Preserves
|
|
284
|
+
* the "last in seq order wins" semantics the engine relies on for `getEvent` (a re-published key
|
|
285
|
+
* overwrites the prior value at a higher seq, so the highest-seq match is the current value).
|
|
286
|
+
*
|
|
287
|
+
* Optional: a store that omits it still works — the engine falls back to {@link listCheckpoints}
|
|
288
|
+
* plus an in-JS filter that produces the identical result.
|
|
289
|
+
*/
|
|
290
|
+
getLatestCheckpointByName?(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
291
|
+
/**
|
|
292
|
+
* All checkpoints for `runId` whose `name` starts with ANY of `prefixes`, ordered by `seq` ascending
|
|
293
|
+
* (same order as {@link listCheckpoints}). A targeted read that avoids scanning every checkpoint just
|
|
294
|
+
* to keep the prefix matches — the store does the filter (`WHERE name LIKE 'prefix%' …`). Used by the
|
|
295
|
+
* run-tree to find a parent's child edges (`signal:child:` / `spawn:` checkpoints) without loading the
|
|
296
|
+
* whole history. An empty `prefixes` array matches nothing.
|
|
297
|
+
*
|
|
298
|
+
* Optional: a store that omits it still works — the engine falls back to {@link listCheckpoints}
|
|
299
|
+
* plus an in-JS prefix scan that produces the identical result.
|
|
300
|
+
*/
|
|
301
|
+
listCheckpointsByNamePrefix?(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
280
302
|
}
|
|
281
303
|
/** Typed, queryable per-run data — exact values for `eq`/`ne`, numbers/strings for range ops. */
|
|
282
304
|
type SearchAttributes = Record<string, string | number | boolean>;
|
|
@@ -1792,6 +1814,8 @@ declare class CodecStateStore implements StateStore {
|
|
|
1792
1814
|
} | null>;
|
|
1793
1815
|
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
1794
1816
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
1817
|
+
getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
1818
|
+
listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
1795
1819
|
}
|
|
1796
1820
|
|
|
1797
1821
|
/** Token prefix for every waiter on `name` — used by `listSignalWaiters` to fan an event out. */
|
|
@@ -1982,6 +2006,13 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
1982
2006
|
private readonly attributeIndex;
|
|
1983
2007
|
/** Test/observability hook: how many candidate runs the last attribute query's index produced. */
|
|
1984
2008
|
lastAttributeCandidates: number;
|
|
2009
|
+
/**
|
|
2010
|
+
* Test/observability hook: how many checkpoint rows the LAST checkpoint read materialized (the rows
|
|
2011
|
+
* actually returned to the caller). `listCheckpoints` returns all rows for the run; the targeted
|
|
2012
|
+
* {@link getLatestCheckpointByName} / {@link listCheckpointsByNamePrefix} return only the matches —
|
|
2013
|
+
* so this lets the benchmark contrast "fetch N, scan in JS" against "fetch the k that match".
|
|
2014
|
+
*/
|
|
2015
|
+
lastCheckpointRowsRead: number;
|
|
1985
2016
|
private key;
|
|
1986
2017
|
/** Drop then re-add a run's normalized attribute rows so the index tracks its current attributes. */
|
|
1987
2018
|
private reindexAttributes;
|
|
@@ -2016,6 +2047,8 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
2016
2047
|
private candidatesForFilter;
|
|
2017
2048
|
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
2018
2049
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
2050
|
+
getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
2051
|
+
listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
2019
2052
|
}
|
|
2020
2053
|
|
|
2021
2054
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -277,6 +277,28 @@ interface StateStore {
|
|
|
277
277
|
transaction?<T>(work: (tx: StoreTransaction) => Promise<T>): Promise<T>;
|
|
278
278
|
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
279
279
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
280
|
+
/**
|
|
281
|
+
* The LATEST checkpoint for `runId` whose `name` equals `name` exactly (highest `seq` wins), or
|
|
282
|
+
* `undefined` if none. A targeted read that avoids fetching + deserializing every checkpoint just to
|
|
283
|
+
* keep one match — the store does the filter (`WHERE name = … ORDER BY seq DESC LIMIT 1`). Preserves
|
|
284
|
+
* the "last in seq order wins" semantics the engine relies on for `getEvent` (a re-published key
|
|
285
|
+
* overwrites the prior value at a higher seq, so the highest-seq match is the current value).
|
|
286
|
+
*
|
|
287
|
+
* Optional: a store that omits it still works — the engine falls back to {@link listCheckpoints}
|
|
288
|
+
* plus an in-JS filter that produces the identical result.
|
|
289
|
+
*/
|
|
290
|
+
getLatestCheckpointByName?(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
291
|
+
/**
|
|
292
|
+
* All checkpoints for `runId` whose `name` starts with ANY of `prefixes`, ordered by `seq` ascending
|
|
293
|
+
* (same order as {@link listCheckpoints}). A targeted read that avoids scanning every checkpoint just
|
|
294
|
+
* to keep the prefix matches — the store does the filter (`WHERE name LIKE 'prefix%' …`). Used by the
|
|
295
|
+
* run-tree to find a parent's child edges (`signal:child:` / `spawn:` checkpoints) without loading the
|
|
296
|
+
* whole history. An empty `prefixes` array matches nothing.
|
|
297
|
+
*
|
|
298
|
+
* Optional: a store that omits it still works — the engine falls back to {@link listCheckpoints}
|
|
299
|
+
* plus an in-JS prefix scan that produces the identical result.
|
|
300
|
+
*/
|
|
301
|
+
listCheckpointsByNamePrefix?(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
280
302
|
}
|
|
281
303
|
/** Typed, queryable per-run data — exact values for `eq`/`ne`, numbers/strings for range ops. */
|
|
282
304
|
type SearchAttributes = Record<string, string | number | boolean>;
|
|
@@ -1792,6 +1814,8 @@ declare class CodecStateStore implements StateStore {
|
|
|
1792
1814
|
} | null>;
|
|
1793
1815
|
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
1794
1816
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
1817
|
+
getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
1818
|
+
listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
1795
1819
|
}
|
|
1796
1820
|
|
|
1797
1821
|
/** Token prefix for every waiter on `name` — used by `listSignalWaiters` to fan an event out. */
|
|
@@ -1982,6 +2006,13 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
1982
2006
|
private readonly attributeIndex;
|
|
1983
2007
|
/** Test/observability hook: how many candidate runs the last attribute query's index produced. */
|
|
1984
2008
|
lastAttributeCandidates: number;
|
|
2009
|
+
/**
|
|
2010
|
+
* Test/observability hook: how many checkpoint rows the LAST checkpoint read materialized (the rows
|
|
2011
|
+
* actually returned to the caller). `listCheckpoints` returns all rows for the run; the targeted
|
|
2012
|
+
* {@link getLatestCheckpointByName} / {@link listCheckpointsByNamePrefix} return only the matches —
|
|
2013
|
+
* so this lets the benchmark contrast "fetch N, scan in JS" against "fetch the k that match".
|
|
2014
|
+
*/
|
|
2015
|
+
lastCheckpointRowsRead: number;
|
|
1985
2016
|
private key;
|
|
1986
2017
|
/** Drop then re-add a run's normalized attribute rows so the index tracks its current attributes. */
|
|
1987
2018
|
private reindexAttributes;
|
|
@@ -2016,6 +2047,8 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
2016
2047
|
private candidatesForFilter;
|
|
2017
2048
|
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
2018
2049
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
2050
|
+
getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
2051
|
+
listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
2019
2052
|
}
|
|
2020
2053
|
|
|
2021
2054
|
/**
|
package/dist/index.js
CHANGED
|
@@ -2039,7 +2039,12 @@ var WorkflowEngine = class {
|
|
|
2039
2039
|
for (const w of await this.store.listSignalWaiters("child:")) {
|
|
2040
2040
|
if (w.runId === parentRunId) childIds.add(w.token.slice("child:".length));
|
|
2041
2041
|
}
|
|
2042
|
-
|
|
2042
|
+
const prefixes = [
|
|
2043
|
+
"signal:child:",
|
|
2044
|
+
"spawn:"
|
|
2045
|
+
];
|
|
2046
|
+
const childCheckpoints = this.store.listCheckpointsByNamePrefix ? await this.store.listCheckpointsByNamePrefix(parentRunId, prefixes) : (await this.store.listCheckpoints(parentRunId)).filter((cp) => prefixes.some((p) => cp.name.startsWith(p)));
|
|
2047
|
+
for (const cp of childCheckpoints) {
|
|
2043
2048
|
if (cp.name.startsWith("signal:child:")) childIds.add(cp.name.slice("signal:child:".length));
|
|
2044
2049
|
if (cp.name.startsWith("spawn:") && typeof cp.output === "string") childIds.add(cp.output);
|
|
2045
2050
|
}
|
|
@@ -2830,9 +2835,12 @@ var WorkflowEngine = class {
|
|
|
2830
2835
|
*/
|
|
2831
2836
|
async getEvent(runId, key) {
|
|
2832
2837
|
const name = `event:${key}`;
|
|
2833
|
-
|
|
2838
|
+
if (this.store.getLatestCheckpointByName) {
|
|
2839
|
+
const latest2 = await this.store.getLatestCheckpointByName(runId, name);
|
|
2840
|
+
return latest2?.output;
|
|
2841
|
+
}
|
|
2834
2842
|
let latest;
|
|
2835
|
-
for (const cp of
|
|
2843
|
+
for (const cp of await this.store.listCheckpoints(runId)) if (cp.name === name) latest = cp.output;
|
|
2836
2844
|
return latest;
|
|
2837
2845
|
}
|
|
2838
2846
|
/**
|
|
@@ -3285,6 +3293,19 @@ var CodecStateStore = class {
|
|
|
3285
3293
|
async listCheckpoints(runId) {
|
|
3286
3294
|
return (await this.inner.listCheckpoints(runId)).map((c) => this.decCp(c));
|
|
3287
3295
|
}
|
|
3296
|
+
async getLatestCheckpointByName(runId, name) {
|
|
3297
|
+
if (this.inner.getLatestCheckpointByName) {
|
|
3298
|
+
const cp = await this.inner.getLatestCheckpointByName(runId, name);
|
|
3299
|
+
return cp ? this.decCp(cp) : void 0;
|
|
3300
|
+
}
|
|
3301
|
+
let latest;
|
|
3302
|
+
for (const cp of await this.inner.listCheckpoints(runId)) if (cp.name === name) latest = cp;
|
|
3303
|
+
return latest ? this.decCp(latest) : void 0;
|
|
3304
|
+
}
|
|
3305
|
+
async listCheckpointsByNamePrefix(runId, prefixes) {
|
|
3306
|
+
const matches = this.inner.listCheckpointsByNamePrefix ? await this.inner.listCheckpointsByNamePrefix(runId, prefixes) : (await this.inner.listCheckpoints(runId)).filter((cp) => prefixes.some((p) => cp.name.startsWith(p)));
|
|
3307
|
+
return matches.map((c) => this.decCp(c));
|
|
3308
|
+
}
|
|
3288
3309
|
};
|
|
3289
3310
|
|
|
3290
3311
|
// src/metrics.ts
|
|
@@ -3309,11 +3330,16 @@ function collectMetrics(engine) {
|
|
|
3309
3330
|
};
|
|
3310
3331
|
const wf = /* @__PURE__ */ __name((name) => {
|
|
3311
3332
|
const k = name ?? "unknown";
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3333
|
+
let entry = m.byWorkflow[k];
|
|
3334
|
+
if (!entry) {
|
|
3335
|
+
entry = {
|
|
3336
|
+
started: 0,
|
|
3337
|
+
completed: 0,
|
|
3338
|
+
failed: 0
|
|
3339
|
+
};
|
|
3340
|
+
m.byWorkflow[k] = entry;
|
|
3341
|
+
}
|
|
3342
|
+
return entry;
|
|
3317
3343
|
}, "wf");
|
|
3318
3344
|
const stop = engine.subscribe((e) => {
|
|
3319
3345
|
switch (e.type) {
|
|
@@ -3560,6 +3586,13 @@ var InMemoryStateStore = class {
|
|
|
3560
3586
|
attributeIndex = /* @__PURE__ */ new Map();
|
|
3561
3587
|
/** Test/observability hook: how many candidate runs the last attribute query's index produced. */
|
|
3562
3588
|
lastAttributeCandidates = 0;
|
|
3589
|
+
/**
|
|
3590
|
+
* Test/observability hook: how many checkpoint rows the LAST checkpoint read materialized (the rows
|
|
3591
|
+
* actually returned to the caller). `listCheckpoints` returns all rows for the run; the targeted
|
|
3592
|
+
* {@link getLatestCheckpointByName} / {@link listCheckpointsByNamePrefix} return only the matches —
|
|
3593
|
+
* so this lets the benchmark contrast "fetch N, scan in JS" against "fetch the k that match".
|
|
3594
|
+
*/
|
|
3595
|
+
lastCheckpointRowsRead = 0;
|
|
3563
3596
|
key(runId, seq) {
|
|
3564
3597
|
return `${runId}:${seq}`;
|
|
3565
3598
|
}
|
|
@@ -3766,11 +3799,33 @@ var InMemoryStateStore = class {
|
|
|
3766
3799
|
}));
|
|
3767
3800
|
}
|
|
3768
3801
|
async listCheckpoints(runId) {
|
|
3769
|
-
|
|
3802
|
+
const rows = [
|
|
3770
3803
|
...this.checkpoints.values()
|
|
3771
3804
|
].filter((cp) => cp.runId === runId).sort((a, b) => a.seq - b.seq).map((cp) => ({
|
|
3772
3805
|
...cp
|
|
3773
3806
|
}));
|
|
3807
|
+
this.lastCheckpointRowsRead = rows.length;
|
|
3808
|
+
return rows;
|
|
3809
|
+
}
|
|
3810
|
+
async getLatestCheckpointByName(runId, name) {
|
|
3811
|
+
let latest;
|
|
3812
|
+
for (const cp of this.checkpoints.values()) {
|
|
3813
|
+
if (cp.runId !== runId || cp.name !== name) continue;
|
|
3814
|
+
if (!latest || cp.seq > latest.seq) latest = cp;
|
|
3815
|
+
}
|
|
3816
|
+
this.lastCheckpointRowsRead = latest ? 1 : 0;
|
|
3817
|
+
return latest ? {
|
|
3818
|
+
...latest
|
|
3819
|
+
} : void 0;
|
|
3820
|
+
}
|
|
3821
|
+
async listCheckpointsByNamePrefix(runId, prefixes) {
|
|
3822
|
+
const rows = [
|
|
3823
|
+
...this.checkpoints.values()
|
|
3824
|
+
].filter((cp) => cp.runId === runId && prefixes.some((p) => cp.name.startsWith(p))).sort((a, b) => a.seq - b.seq).map((cp) => ({
|
|
3825
|
+
...cp
|
|
3826
|
+
}));
|
|
3827
|
+
this.lastCheckpointRowsRead = rows.length;
|
|
3828
|
+
return rows;
|
|
3774
3829
|
}
|
|
3775
3830
|
};
|
|
3776
3831
|
|