@dudousxd/nestjs-durable-store-mikro-orm 0.22.0 → 0.24.0
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 +155 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -5
- package/dist/index.d.ts +49 -5
- package/dist/index.js +143 -24
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import * as _mikro_orm_core from '@mikro-orm/core';
|
|
2
|
-
import { EntitySchema, MikroORM } from '@mikro-orm/core';
|
|
3
|
-
import { StepKind, RunStatus, StateStore, WorkflowRun, RetentionPolicy, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, SignalWaiter } from '@dudousxd/nestjs-durable-core';
|
|
2
|
+
import { EntityRepository, EntityRepositoryType, EntitySchema, MikroORM } from '@mikro-orm/core';
|
|
3
|
+
import { StepKind, RunStatus, StateStore, WorkflowRun, RetentionPolicy, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, SignalWaiter } from '@dudousxd/nestjs-durable-core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Custom repositories for the durable entities, so a host app can `@InjectRepository`/inject a
|
|
7
|
+
* `WorkflowRunRepository` by type instead of repeating `em.find(WorkflowRunEntity, …)` with the
|
|
8
|
+
* entity class as the first argument everywhere.
|
|
9
|
+
*
|
|
10
|
+
* They are wired per-schema inside `durableEntities()` (not with a decorator), because that factory
|
|
11
|
+
* can be called more than once with different column naming and each call must produce schemas that
|
|
12
|
+
* still point at these classes. Each entity also declares `[EntityRepositoryType]`, which is what
|
|
13
|
+
* makes `em.getRepository(WorkflowRunEntity)` resolve to the specific type rather than the base
|
|
14
|
+
* `EntityRepository`.
|
|
15
|
+
*
|
|
16
|
+
* The bodies are intentionally empty: the durable engine's own queries go through
|
|
17
|
+
* `MikroOrmStateStore`, and a durable-owned query added here would be a second, unversioned read
|
|
18
|
+
* path over the same tables. They exist as an injectable, correctly-typed handle for host code.
|
|
19
|
+
*/
|
|
20
|
+
declare class WorkflowRunRepository extends EntityRepository<WorkflowRunEntity> {
|
|
21
|
+
}
|
|
22
|
+
declare class StepCheckpointRepository extends EntityRepository<StepCheckpointEntity> {
|
|
23
|
+
}
|
|
24
|
+
declare class RunAttributeRepository extends EntityRepository<RunAttributeEntity> {
|
|
25
|
+
}
|
|
26
|
+
declare class SignalWaiterRepository extends EntityRepository<SignalWaiterEntity> {
|
|
27
|
+
}
|
|
28
|
+
declare class BufferedSignalRepository extends EntityRepository<BufferedSignalEntity> {
|
|
29
|
+
}
|
|
30
|
+
declare class BufferedEventRepository extends EntityRepository<BufferedEventEntity> {
|
|
31
|
+
}
|
|
4
32
|
|
|
5
33
|
/**
|
|
6
34
|
* How property names map to physical column names. The durable tables are adapter-agnostic: a run
|
|
@@ -16,6 +44,7 @@ import { StepKind, RunStatus, StateStore, WorkflowRun, RetentionPolicy, StepChec
|
|
|
16
44
|
*/
|
|
17
45
|
type DurableColumnNaming = 'snake_case' | 'preserve' | ((property: string) => string);
|
|
18
46
|
declare class WorkflowRunEntity {
|
|
47
|
+
[EntityRepositoryType]?: WorkflowRunRepository;
|
|
19
48
|
id: string;
|
|
20
49
|
workflow: string;
|
|
21
50
|
workflowVersion: string;
|
|
@@ -37,6 +66,7 @@ declare class WorkflowRunEntity {
|
|
|
37
66
|
updatedAt: Date;
|
|
38
67
|
}
|
|
39
68
|
declare class StepCheckpointEntity {
|
|
69
|
+
[EntityRepositoryType]?: StepCheckpointRepository;
|
|
40
70
|
runId: string;
|
|
41
71
|
seq: number;
|
|
42
72
|
name: string;
|
|
@@ -62,18 +92,21 @@ declare class StepCheckpointEntity {
|
|
|
62
92
|
* `numValue`, strings/booleans in `strValue` (booleans as "true"/"false"); see normalizeAttributeRows.
|
|
63
93
|
*/
|
|
64
94
|
declare class RunAttributeEntity {
|
|
95
|
+
[EntityRepositoryType]?: RunAttributeRepository;
|
|
65
96
|
runId: string;
|
|
66
97
|
key: string;
|
|
67
98
|
strValue?: string | null;
|
|
68
99
|
numValue?: number | null;
|
|
69
100
|
}
|
|
70
101
|
declare class SignalWaiterEntity {
|
|
102
|
+
[EntityRepositoryType]?: SignalWaiterRepository;
|
|
71
103
|
token: string;
|
|
72
104
|
runId: string;
|
|
73
105
|
seq: number;
|
|
74
106
|
parallelGroup?: string | null;
|
|
75
107
|
}
|
|
76
108
|
declare class BufferedSignalEntity {
|
|
109
|
+
[EntityRepositoryType]?: BufferedSignalRepository;
|
|
77
110
|
id: number;
|
|
78
111
|
token: string;
|
|
79
112
|
payload?: unknown;
|
|
@@ -82,6 +115,7 @@ declare class BufferedSignalEntity {
|
|
|
82
115
|
* caller-minted `id` (a uuid, not autoincrement — `removeBufferedEvent` targets it directly), not by
|
|
83
116
|
* `runId`: events are name-based pub/sub, not tied to any one run. */
|
|
84
117
|
declare class BufferedEventEntity {
|
|
118
|
+
[EntityRepositoryType]?: BufferedEventRepository;
|
|
85
119
|
id: string;
|
|
86
120
|
name: string;
|
|
87
121
|
payload?: unknown;
|
|
@@ -162,15 +196,25 @@ declare class MikroOrmStateStore implements StateStore {
|
|
|
162
196
|
* predicates only raw SQL can express — the `tag` JSON LIKE and one EXISTS per attribute filter.
|
|
163
197
|
* Shared so a facet count and the page it labels are taken over the SAME set. */
|
|
164
198
|
private runQueryBuilder;
|
|
199
|
+
/** `tags LIKE '%"etl"%'` — the quoted token, so `etl` doesn't match `etl-foo`. See the note in
|
|
200
|
+
* {@link listRuns} for why this is raw SQL rather than a MikroORM condition. */
|
|
201
|
+
private tagLikeSql;
|
|
165
202
|
/** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
|
|
166
203
|
* console can show whole-set counts next to a bounded page instead of downloading every run to
|
|
167
204
|
* count them in the browser. */
|
|
168
205
|
runFacets(query: RunFacetQuery): Promise<RunFacetRow[]>;
|
|
206
|
+
/** Distinct values of one filter axis over the same predicates {@link listRuns} pages. A run-table
|
|
207
|
+
* column is a `GROUP BY` like {@link runFacets}, exact over the whole matching set; the tag and
|
|
208
|
+
* attribute axes live outside the row (json array / side table) and are counted from a bounded
|
|
209
|
+
* page of runs instead — see {@link RunValueFacetOptions.scan}. */
|
|
210
|
+
runValueFacets(axis: RunValueAxis, query: RunFacetQuery, opts?: RunValueFacetOptions): Promise<RunValueFacetRow[]>;
|
|
169
211
|
/** One attribute predicate as a raw EXISTS subquery on the side-table, correlated to the outer run
|
|
170
212
|
* alias. `<>` (ne) also excludes runs where the attribute is absent (missing-key-never-matches):
|
|
171
213
|
* EXISTS already requires a present row, so EXISTS(... <> ...) is exactly ne-with-present. Numeric
|
|
172
|
-
* operands compare the num column, everything else the str column.
|
|
173
|
-
*
|
|
214
|
+
* operands compare the num column, everything else the str column. `in` contributes one comparison
|
|
215
|
+
* per member, ORed inside the single EXISTS — one subquery either way, and an `in` over no values
|
|
216
|
+
* yields no comparison and so matches nothing. Identifiers are quoted per driver and operands are
|
|
217
|
+
* inlined as literals (string operands are escaped by doubling quotes). */
|
|
174
218
|
private attributeExistsSql;
|
|
175
219
|
/** Resolve the side-table's actual column names from MikroORM metadata, so the raw EXISTS matches
|
|
176
220
|
* the active naming strategy (underscore vs camelCase) rather than guessing. The run PK column is
|
|
@@ -254,4 +298,4 @@ declare function durableManagedTables(): string[];
|
|
|
254
298
|
*/
|
|
255
299
|
declare function ensureMikroOrmDurableSchema(orm: MikroORM): Promise<void>;
|
|
256
300
|
|
|
257
|
-
export { BufferedEventEntity, BufferedSignalEntity, type DurableColumnNaming, ENTITIES, MikroOrmStateStore, type MikroOrmStateStoreOptions, RunAttributeEntity, SignalWaiterEntity, StepCheckpointEntity, WorkflowRunEntity, durableEntities, durableManagedTables, ensureMikroOrmDurableSchema };
|
|
301
|
+
export { BufferedEventEntity, BufferedEventRepository, BufferedSignalEntity, BufferedSignalRepository, type DurableColumnNaming, ENTITIES, MikroOrmStateStore, type MikroOrmStateStoreOptions, RunAttributeEntity, RunAttributeRepository, SignalWaiterEntity, SignalWaiterRepository, StepCheckpointEntity, StepCheckpointRepository, WorkflowRunEntity, WorkflowRunRepository, durableEntities, durableManagedTables, ensureMikroOrmDurableSchema };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import * as _mikro_orm_core from '@mikro-orm/core';
|
|
2
|
-
import { EntitySchema, MikroORM } from '@mikro-orm/core';
|
|
3
|
-
import { StepKind, RunStatus, StateStore, WorkflowRun, RetentionPolicy, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, SignalWaiter } from '@dudousxd/nestjs-durable-core';
|
|
2
|
+
import { EntityRepository, EntityRepositoryType, EntitySchema, MikroORM } from '@mikro-orm/core';
|
|
3
|
+
import { StepKind, RunStatus, StateStore, WorkflowRun, RetentionPolicy, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, SignalWaiter } from '@dudousxd/nestjs-durable-core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Custom repositories for the durable entities, so a host app can `@InjectRepository`/inject a
|
|
7
|
+
* `WorkflowRunRepository` by type instead of repeating `em.find(WorkflowRunEntity, …)` with the
|
|
8
|
+
* entity class as the first argument everywhere.
|
|
9
|
+
*
|
|
10
|
+
* They are wired per-schema inside `durableEntities()` (not with a decorator), because that factory
|
|
11
|
+
* can be called more than once with different column naming and each call must produce schemas that
|
|
12
|
+
* still point at these classes. Each entity also declares `[EntityRepositoryType]`, which is what
|
|
13
|
+
* makes `em.getRepository(WorkflowRunEntity)` resolve to the specific type rather than the base
|
|
14
|
+
* `EntityRepository`.
|
|
15
|
+
*
|
|
16
|
+
* The bodies are intentionally empty: the durable engine's own queries go through
|
|
17
|
+
* `MikroOrmStateStore`, and a durable-owned query added here would be a second, unversioned read
|
|
18
|
+
* path over the same tables. They exist as an injectable, correctly-typed handle for host code.
|
|
19
|
+
*/
|
|
20
|
+
declare class WorkflowRunRepository extends EntityRepository<WorkflowRunEntity> {
|
|
21
|
+
}
|
|
22
|
+
declare class StepCheckpointRepository extends EntityRepository<StepCheckpointEntity> {
|
|
23
|
+
}
|
|
24
|
+
declare class RunAttributeRepository extends EntityRepository<RunAttributeEntity> {
|
|
25
|
+
}
|
|
26
|
+
declare class SignalWaiterRepository extends EntityRepository<SignalWaiterEntity> {
|
|
27
|
+
}
|
|
28
|
+
declare class BufferedSignalRepository extends EntityRepository<BufferedSignalEntity> {
|
|
29
|
+
}
|
|
30
|
+
declare class BufferedEventRepository extends EntityRepository<BufferedEventEntity> {
|
|
31
|
+
}
|
|
4
32
|
|
|
5
33
|
/**
|
|
6
34
|
* How property names map to physical column names. The durable tables are adapter-agnostic: a run
|
|
@@ -16,6 +44,7 @@ import { StepKind, RunStatus, StateStore, WorkflowRun, RetentionPolicy, StepChec
|
|
|
16
44
|
*/
|
|
17
45
|
type DurableColumnNaming = 'snake_case' | 'preserve' | ((property: string) => string);
|
|
18
46
|
declare class WorkflowRunEntity {
|
|
47
|
+
[EntityRepositoryType]?: WorkflowRunRepository;
|
|
19
48
|
id: string;
|
|
20
49
|
workflow: string;
|
|
21
50
|
workflowVersion: string;
|
|
@@ -37,6 +66,7 @@ declare class WorkflowRunEntity {
|
|
|
37
66
|
updatedAt: Date;
|
|
38
67
|
}
|
|
39
68
|
declare class StepCheckpointEntity {
|
|
69
|
+
[EntityRepositoryType]?: StepCheckpointRepository;
|
|
40
70
|
runId: string;
|
|
41
71
|
seq: number;
|
|
42
72
|
name: string;
|
|
@@ -62,18 +92,21 @@ declare class StepCheckpointEntity {
|
|
|
62
92
|
* `numValue`, strings/booleans in `strValue` (booleans as "true"/"false"); see normalizeAttributeRows.
|
|
63
93
|
*/
|
|
64
94
|
declare class RunAttributeEntity {
|
|
95
|
+
[EntityRepositoryType]?: RunAttributeRepository;
|
|
65
96
|
runId: string;
|
|
66
97
|
key: string;
|
|
67
98
|
strValue?: string | null;
|
|
68
99
|
numValue?: number | null;
|
|
69
100
|
}
|
|
70
101
|
declare class SignalWaiterEntity {
|
|
102
|
+
[EntityRepositoryType]?: SignalWaiterRepository;
|
|
71
103
|
token: string;
|
|
72
104
|
runId: string;
|
|
73
105
|
seq: number;
|
|
74
106
|
parallelGroup?: string | null;
|
|
75
107
|
}
|
|
76
108
|
declare class BufferedSignalEntity {
|
|
109
|
+
[EntityRepositoryType]?: BufferedSignalRepository;
|
|
77
110
|
id: number;
|
|
78
111
|
token: string;
|
|
79
112
|
payload?: unknown;
|
|
@@ -82,6 +115,7 @@ declare class BufferedSignalEntity {
|
|
|
82
115
|
* caller-minted `id` (a uuid, not autoincrement — `removeBufferedEvent` targets it directly), not by
|
|
83
116
|
* `runId`: events are name-based pub/sub, not tied to any one run. */
|
|
84
117
|
declare class BufferedEventEntity {
|
|
118
|
+
[EntityRepositoryType]?: BufferedEventRepository;
|
|
85
119
|
id: string;
|
|
86
120
|
name: string;
|
|
87
121
|
payload?: unknown;
|
|
@@ -162,15 +196,25 @@ declare class MikroOrmStateStore implements StateStore {
|
|
|
162
196
|
* predicates only raw SQL can express — the `tag` JSON LIKE and one EXISTS per attribute filter.
|
|
163
197
|
* Shared so a facet count and the page it labels are taken over the SAME set. */
|
|
164
198
|
private runQueryBuilder;
|
|
199
|
+
/** `tags LIKE '%"etl"%'` — the quoted token, so `etl` doesn't match `etl-foo`. See the note in
|
|
200
|
+
* {@link listRuns} for why this is raw SQL rather than a MikroORM condition. */
|
|
201
|
+
private tagLikeSql;
|
|
165
202
|
/** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
|
|
166
203
|
* console can show whole-set counts next to a bounded page instead of downloading every run to
|
|
167
204
|
* count them in the browser. */
|
|
168
205
|
runFacets(query: RunFacetQuery): Promise<RunFacetRow[]>;
|
|
206
|
+
/** Distinct values of one filter axis over the same predicates {@link listRuns} pages. A run-table
|
|
207
|
+
* column is a `GROUP BY` like {@link runFacets}, exact over the whole matching set; the tag and
|
|
208
|
+
* attribute axes live outside the row (json array / side table) and are counted from a bounded
|
|
209
|
+
* page of runs instead — see {@link RunValueFacetOptions.scan}. */
|
|
210
|
+
runValueFacets(axis: RunValueAxis, query: RunFacetQuery, opts?: RunValueFacetOptions): Promise<RunValueFacetRow[]>;
|
|
169
211
|
/** One attribute predicate as a raw EXISTS subquery on the side-table, correlated to the outer run
|
|
170
212
|
* alias. `<>` (ne) also excludes runs where the attribute is absent (missing-key-never-matches):
|
|
171
213
|
* EXISTS already requires a present row, so EXISTS(... <> ...) is exactly ne-with-present. Numeric
|
|
172
|
-
* operands compare the num column, everything else the str column.
|
|
173
|
-
*
|
|
214
|
+
* operands compare the num column, everything else the str column. `in` contributes one comparison
|
|
215
|
+
* per member, ORed inside the single EXISTS — one subquery either way, and an `in` over no values
|
|
216
|
+
* yields no comparison and so matches nothing. Identifiers are quoted per driver and operands are
|
|
217
|
+
* inlined as literals (string operands are escaped by doubling quotes). */
|
|
174
218
|
private attributeExistsSql;
|
|
175
219
|
/** Resolve the side-table's actual column names from MikroORM metadata, so the raw EXISTS matches
|
|
176
220
|
* the active naming strategy (underscore vs camelCase) rather than guessing. The run PK column is
|
|
@@ -254,4 +298,4 @@ declare function durableManagedTables(): string[];
|
|
|
254
298
|
*/
|
|
255
299
|
declare function ensureMikroOrmDurableSchema(orm: MikroORM): Promise<void>;
|
|
256
300
|
|
|
257
|
-
export { BufferedEventEntity, BufferedSignalEntity, type DurableColumnNaming, ENTITIES, MikroOrmStateStore, type MikroOrmStateStoreOptions, RunAttributeEntity, SignalWaiterEntity, StepCheckpointEntity, WorkflowRunEntity, durableEntities, durableManagedTables, ensureMikroOrmDurableSchema };
|
|
301
|
+
export { BufferedEventEntity, BufferedEventRepository, BufferedSignalEntity, BufferedSignalRepository, type DurableColumnNaming, ENTITIES, MikroOrmStateStore, type MikroOrmStateStoreOptions, RunAttributeEntity, RunAttributeRepository, SignalWaiterEntity, SignalWaiterRepository, StepCheckpointEntity, StepCheckpointRepository, WorkflowRunEntity, WorkflowRunRepository, durableEntities, durableManagedTables, ensureMikroOrmDurableSchema };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,42 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
4
|
// src/entities.ts
|
|
5
|
-
import { EntitySchema } from "@mikro-orm/core";
|
|
5
|
+
import { EntityRepositoryType, EntitySchema } from "@mikro-orm/core";
|
|
6
|
+
|
|
7
|
+
// src/repositories.ts
|
|
8
|
+
import { EntityRepository } from "@mikro-orm/core";
|
|
9
|
+
var WorkflowRunRepository = class extends EntityRepository {
|
|
10
|
+
static {
|
|
11
|
+
__name(this, "WorkflowRunRepository");
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var StepCheckpointRepository = class extends EntityRepository {
|
|
15
|
+
static {
|
|
16
|
+
__name(this, "StepCheckpointRepository");
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var RunAttributeRepository = class extends EntityRepository {
|
|
20
|
+
static {
|
|
21
|
+
__name(this, "RunAttributeRepository");
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var SignalWaiterRepository = class extends EntityRepository {
|
|
25
|
+
static {
|
|
26
|
+
__name(this, "SignalWaiterRepository");
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var BufferedSignalRepository = class extends EntityRepository {
|
|
30
|
+
static {
|
|
31
|
+
__name(this, "BufferedSignalRepository");
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var BufferedEventRepository = class extends EntityRepository {
|
|
35
|
+
static {
|
|
36
|
+
__name(this, "BufferedEventRepository");
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/entities.ts
|
|
6
41
|
function snakeCase(property) {
|
|
7
42
|
return property.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
8
43
|
}
|
|
@@ -17,6 +52,7 @@ var WorkflowRunEntity = class {
|
|
|
17
52
|
static {
|
|
18
53
|
__name(this, "WorkflowRunEntity");
|
|
19
54
|
}
|
|
55
|
+
[EntityRepositoryType];
|
|
20
56
|
id;
|
|
21
57
|
workflow;
|
|
22
58
|
workflowVersion;
|
|
@@ -41,6 +77,7 @@ var StepCheckpointEntity = class {
|
|
|
41
77
|
static {
|
|
42
78
|
__name(this, "StepCheckpointEntity");
|
|
43
79
|
}
|
|
80
|
+
[EntityRepositoryType];
|
|
44
81
|
runId;
|
|
45
82
|
seq;
|
|
46
83
|
name;
|
|
@@ -63,6 +100,7 @@ var RunAttributeEntity = class {
|
|
|
63
100
|
static {
|
|
64
101
|
__name(this, "RunAttributeEntity");
|
|
65
102
|
}
|
|
103
|
+
[EntityRepositoryType];
|
|
66
104
|
runId;
|
|
67
105
|
key;
|
|
68
106
|
strValue;
|
|
@@ -72,6 +110,7 @@ var SignalWaiterEntity = class {
|
|
|
72
110
|
static {
|
|
73
111
|
__name(this, "SignalWaiterEntity");
|
|
74
112
|
}
|
|
113
|
+
[EntityRepositoryType];
|
|
75
114
|
token;
|
|
76
115
|
runId;
|
|
77
116
|
seq;
|
|
@@ -81,6 +120,7 @@ var BufferedSignalEntity = class {
|
|
|
81
120
|
static {
|
|
82
121
|
__name(this, "BufferedSignalEntity");
|
|
83
122
|
}
|
|
123
|
+
[EntityRepositoryType];
|
|
84
124
|
id;
|
|
85
125
|
token;
|
|
86
126
|
payload;
|
|
@@ -89,6 +129,7 @@ var BufferedEventEntity = class {
|
|
|
89
129
|
static {
|
|
90
130
|
__name(this, "BufferedEventEntity");
|
|
91
131
|
}
|
|
132
|
+
[EntityRepositoryType];
|
|
92
133
|
id;
|
|
93
134
|
name;
|
|
94
135
|
payload;
|
|
@@ -106,6 +147,9 @@ function durableEntities(options = {}) {
|
|
|
106
147
|
const workflowRuns = new EntitySchema({
|
|
107
148
|
class: WorkflowRunEntity,
|
|
108
149
|
tableName: "durable_workflow_runs",
|
|
150
|
+
// Wired per schema rather than once on the class: `durableEntities` can be called again with a
|
|
151
|
+
// different `naming`, and each call builds a distinct EntitySchema over the same class.
|
|
152
|
+
repository: /* @__PURE__ */ __name(() => WorkflowRunRepository, "repository"),
|
|
109
153
|
// Global filter: confines every read to the tenant's namespace when the store is scoped.
|
|
110
154
|
// The `cond` returns `{}` (no-op) when the `namespace` arg is `undefined`, so an unscoped
|
|
111
155
|
// (operator / control-plane) store sees all rows unchanged — existing behaviour is preserved.
|
|
@@ -269,6 +313,7 @@ function durableEntities(options = {}) {
|
|
|
269
313
|
const stepCheckpoints = new EntitySchema({
|
|
270
314
|
class: StepCheckpointEntity,
|
|
271
315
|
tableName: "durable_step_checkpoints",
|
|
316
|
+
repository: /* @__PURE__ */ __name(() => StepCheckpointRepository, "repository"),
|
|
272
317
|
properties: {
|
|
273
318
|
runId: {
|
|
274
319
|
type: "string",
|
|
@@ -357,6 +402,7 @@ function durableEntities(options = {}) {
|
|
|
357
402
|
const runAttributes = new EntitySchema({
|
|
358
403
|
class: RunAttributeEntity,
|
|
359
404
|
tableName: "durable_run_attributes",
|
|
405
|
+
repository: /* @__PURE__ */ __name(() => RunAttributeRepository, "repository"),
|
|
360
406
|
// The search-attribute EXISTS join (see the class docstring) pushes equality/range predicates down
|
|
361
407
|
// onto `(key, numValue)` / `(key, strValue)`; mirror the Prisma adapter so those scans stay indexed.
|
|
362
408
|
indexes: [
|
|
@@ -402,6 +448,7 @@ function durableEntities(options = {}) {
|
|
|
402
448
|
const signalWaiters = new EntitySchema({
|
|
403
449
|
class: SignalWaiterEntity,
|
|
404
450
|
tableName: "durable_signal_waiters",
|
|
451
|
+
repository: /* @__PURE__ */ __name(() => SignalWaiterRepository, "repository"),
|
|
405
452
|
properties: {
|
|
406
453
|
token: {
|
|
407
454
|
type: "string",
|
|
@@ -429,6 +476,7 @@ function durableEntities(options = {}) {
|
|
|
429
476
|
const bufferedSignals = new EntitySchema({
|
|
430
477
|
class: BufferedSignalEntity,
|
|
431
478
|
tableName: "durable_buffered_signals",
|
|
479
|
+
repository: /* @__PURE__ */ __name(() => BufferedSignalRepository, "repository"),
|
|
432
480
|
properties: {
|
|
433
481
|
id: {
|
|
434
482
|
type: "integer",
|
|
@@ -451,6 +499,7 @@ function durableEntities(options = {}) {
|
|
|
451
499
|
const bufferedEvents = new EntitySchema({
|
|
452
500
|
class: BufferedEventEntity,
|
|
453
501
|
tableName: "durable_buffered_events",
|
|
502
|
+
repository: /* @__PURE__ */ __name(() => BufferedEventRepository, "repository"),
|
|
454
503
|
// `(name, publishedAt)` serves both `listBufferedEvents`'s name-scoped scan and its oldest-first
|
|
455
504
|
// ordering in one index.
|
|
456
505
|
indexes: [
|
|
@@ -498,7 +547,7 @@ __name(durableEntities, "durableEntities");
|
|
|
498
547
|
var ENTITIES = durableEntities();
|
|
499
548
|
|
|
500
549
|
// src/mikro-orm-state-store.ts
|
|
501
|
-
import {
|
|
550
|
+
import { RUN_VALUE_FACET_SCAN, attributePredicateOperands, axisIsRunColumn, mergeRunFacetRows, mergeRunValueFacetRows, normalizeAttributeRows, parseDuration, runValueFacetsFromRuns } from "@dudousxd/nestjs-durable-core";
|
|
502
551
|
import { raw } from "@mikro-orm/core";
|
|
503
552
|
|
|
504
553
|
// src/schema.ts
|
|
@@ -982,20 +1031,47 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
982
1031
|
* applied by the QueryBuilder path in both callers. */
|
|
983
1032
|
runWhere(query) {
|
|
984
1033
|
const where = {};
|
|
1034
|
+
const and = [];
|
|
985
1035
|
if (query.workflow) where.workflow = query.workflow;
|
|
1036
|
+
if (query.workflows) and.push({
|
|
1037
|
+
workflow: {
|
|
1038
|
+
$in: query.workflows
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
986
1041
|
if (query.namespace !== void 0) where.namespace = query.namespace;
|
|
1042
|
+
if (query.namespaces) and.push({
|
|
1043
|
+
namespace: {
|
|
1044
|
+
$in: query.namespaces
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
987
1047
|
if (query.origin !== void 0) where.origin = query.origin;
|
|
1048
|
+
if (query.origins) {
|
|
1049
|
+
const named = query.origins.filter((o) => o !== null);
|
|
1050
|
+
const branches = [];
|
|
1051
|
+
if (named.length) branches.push({
|
|
1052
|
+
origin: {
|
|
1053
|
+
$in: named
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
if (query.origins.some((o) => o === null)) branches.push({
|
|
1057
|
+
origin: null
|
|
1058
|
+
});
|
|
1059
|
+
and.push(branches.length ? {
|
|
1060
|
+
$or: branches
|
|
1061
|
+
} : {
|
|
1062
|
+
origin: {
|
|
1063
|
+
$in: []
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
988
1067
|
if (query.status && query.statuses) {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
status: {
|
|
995
|
-
$in: query.statuses
|
|
996
|
-
}
|
|
1068
|
+
and.push({
|
|
1069
|
+
status: query.status
|
|
1070
|
+
}, {
|
|
1071
|
+
status: {
|
|
1072
|
+
$in: query.statuses
|
|
997
1073
|
}
|
|
998
|
-
|
|
1074
|
+
});
|
|
999
1075
|
} else if (query.status) {
|
|
1000
1076
|
where.status = query.status;
|
|
1001
1077
|
} else if (query.statuses) {
|
|
@@ -1003,6 +1079,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1003
1079
|
$in: query.statuses
|
|
1004
1080
|
};
|
|
1005
1081
|
}
|
|
1082
|
+
if (and.length) where.$and = and;
|
|
1006
1083
|
return where;
|
|
1007
1084
|
}
|
|
1008
1085
|
async listRuns(query) {
|
|
@@ -1011,7 +1088,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1011
1088
|
const orderBy = {
|
|
1012
1089
|
createdAt: "desc"
|
|
1013
1090
|
};
|
|
1014
|
-
if (query.tag || query.attributes?.length) {
|
|
1091
|
+
if (query.tag || query.tags || query.attributes?.length) {
|
|
1015
1092
|
const qb = this.runQueryBuilder(em, query, where).orderBy(orderBy);
|
|
1016
1093
|
if (query.limit != null) qb.limit(query.limit);
|
|
1017
1094
|
if (query.offset != null) qb.offset(query.offset);
|
|
@@ -1037,17 +1114,23 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1037
1114
|
const quote = this.idQuote(em);
|
|
1038
1115
|
const cols = this.attributeColumns(em);
|
|
1039
1116
|
const qb = em.createQueryBuilder(WorkflowRunEntity, "r").where(where);
|
|
1040
|
-
if (query.tag)
|
|
1041
|
-
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
qb.andWhere(`${colExpr} LIKE '${pattern}'`);
|
|
1117
|
+
if (query.tag) qb.andWhere(this.tagLikeSql(em, query.tag, quote));
|
|
1118
|
+
if (query.tags) {
|
|
1119
|
+
const branches = query.tags.map((t) => this.tagLikeSql(em, t, quote));
|
|
1120
|
+
qb.andWhere(branches.length ? `(${branches.join(" OR ")})` : "1 = 0");
|
|
1045
1121
|
}
|
|
1046
1122
|
for (const f of query.attributes ?? []) {
|
|
1047
1123
|
qb.andWhere(this.attributeExistsSql(f, quote, "r", cols));
|
|
1048
1124
|
}
|
|
1049
1125
|
return qb;
|
|
1050
1126
|
}
|
|
1127
|
+
/** `tags LIKE '%"etl"%'` — the quoted token, so `etl` doesn't match `etl-foo`. See the note in
|
|
1128
|
+
* {@link listRuns} for why this is raw SQL rather than a MikroORM condition. */
|
|
1129
|
+
tagLikeSql(em, tag, quote) {
|
|
1130
|
+
const pattern = `%"${tag.replace(/'/g, "''")}"%`;
|
|
1131
|
+
const colExpr = this.jsonAsText(em, `${quote("r")}.${quote(this.tagsColumn(em))}`);
|
|
1132
|
+
return `${colExpr} LIKE '${pattern}'`;
|
|
1133
|
+
}
|
|
1051
1134
|
/** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
|
|
1052
1135
|
* console can show whole-set counts next to a bounded page instead of downloading every run to
|
|
1053
1136
|
* count them in the browser. */
|
|
@@ -1068,20 +1151,50 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1068
1151
|
count: Number(r.count)
|
|
1069
1152
|
})));
|
|
1070
1153
|
}
|
|
1154
|
+
/** Distinct values of one filter axis over the same predicates {@link listRuns} pages. A run-table
|
|
1155
|
+
* column is a `GROUP BY` like {@link runFacets}, exact over the whole matching set; the tag and
|
|
1156
|
+
* attribute axes live outside the row (json array / side table) and are counted from a bounded
|
|
1157
|
+
* page of runs instead — see {@link RunValueFacetOptions.scan}. */
|
|
1158
|
+
async runValueFacets(axis, query, opts) {
|
|
1159
|
+
if (!axisIsRunColumn(axis)) {
|
|
1160
|
+
const runs = await this.listRuns({
|
|
1161
|
+
...query,
|
|
1162
|
+
limit: opts?.scan ?? RUN_VALUE_FACET_SCAN
|
|
1163
|
+
});
|
|
1164
|
+
return runValueFacetsFromRuns(runs, axis, opts);
|
|
1165
|
+
}
|
|
1166
|
+
const em = this.fork();
|
|
1167
|
+
const meta = em.getMetadata().get(WorkflowRunEntity);
|
|
1168
|
+
const col = meta.properties[axis.field]?.fieldNames?.[0] ?? axis.field;
|
|
1169
|
+
const rows = await this.runQueryBuilder(em, query, this.runWhere(query)).select([
|
|
1170
|
+
`r.${col}`
|
|
1171
|
+
]).addSelect(raw("count(*) as count")).groupBy([
|
|
1172
|
+
`r.${col}`
|
|
1173
|
+
]).execute();
|
|
1174
|
+
return mergeRunValueFacetRows(rows.map((row) => ({
|
|
1175
|
+
value: row[axis.field] ?? null,
|
|
1176
|
+
count: Number(row.count)
|
|
1177
|
+
})), opts);
|
|
1178
|
+
}
|
|
1071
1179
|
/** One attribute predicate as a raw EXISTS subquery on the side-table, correlated to the outer run
|
|
1072
1180
|
* alias. `<>` (ne) also excludes runs where the attribute is absent (missing-key-never-matches):
|
|
1073
1181
|
* EXISTS already requires a present row, so EXISTS(... <> ...) is exactly ne-with-present. Numeric
|
|
1074
|
-
* operands compare the num column, everything else the str column.
|
|
1075
|
-
*
|
|
1182
|
+
* operands compare the num column, everything else the str column. `in` contributes one comparison
|
|
1183
|
+
* per member, ORed inside the single EXISTS — one subquery either way, and an `in` over no values
|
|
1184
|
+
* yields no comparison and so matches nothing. Identifiers are quoted per driver and operands are
|
|
1185
|
+
* inlined as literals (string operands are escaped by doubling quotes). */
|
|
1076
1186
|
attributeExistsSql(f, quote, alias, cols) {
|
|
1077
|
-
const
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1187
|
+
const comparisons = attributePredicateOperands(f).map(({ column, comparator, operand }) => {
|
|
1188
|
+
const col = column === "numValue" ? cols.numValue : cols.strValue;
|
|
1189
|
+
const literal = typeof operand === "number" ? String(operand) : `'${String(operand).replace(/'/g, "''")}'`;
|
|
1190
|
+
return `${quote("a")}.${quote(col)} ${comparator} ${literal}`;
|
|
1191
|
+
});
|
|
1192
|
+
if (comparisons.length === 0) return "1 = 0";
|
|
1081
1193
|
const a = quote(cols.table);
|
|
1082
1194
|
const sub = quote("a");
|
|
1083
1195
|
const outerId = `${quote(alias)}.${quote(cols.runPk)}`;
|
|
1084
|
-
|
|
1196
|
+
const match = comparisons.length === 1 ? comparisons[0] : `(${comparisons.join(" OR ")})`;
|
|
1197
|
+
return `EXISTS (SELECT 1 FROM ${a} ${sub} WHERE ${sub}.${quote(cols.runId)} = ${outerId} AND ${sub}.${quote(cols.key)} = '${f.key.replace(/'/g, "''")}' AND ${match})`;
|
|
1085
1198
|
}
|
|
1086
1199
|
/** Resolve the side-table's actual column names from MikroORM metadata, so the raw EXISTS matches
|
|
1087
1200
|
* the active naming strategy (underscore vs camelCase) rather than guessing. The run PK column is
|
|
@@ -1361,13 +1474,19 @@ function fromCheckpointEntity(e) {
|
|
|
1361
1474
|
__name(fromCheckpointEntity, "fromCheckpointEntity");
|
|
1362
1475
|
export {
|
|
1363
1476
|
BufferedEventEntity,
|
|
1477
|
+
BufferedEventRepository,
|
|
1364
1478
|
BufferedSignalEntity,
|
|
1479
|
+
BufferedSignalRepository,
|
|
1365
1480
|
ENTITIES,
|
|
1366
1481
|
MikroOrmStateStore,
|
|
1367
1482
|
RunAttributeEntity,
|
|
1483
|
+
RunAttributeRepository,
|
|
1368
1484
|
SignalWaiterEntity,
|
|
1485
|
+
SignalWaiterRepository,
|
|
1369
1486
|
StepCheckpointEntity,
|
|
1487
|
+
StepCheckpointRepository,
|
|
1370
1488
|
WorkflowRunEntity,
|
|
1489
|
+
WorkflowRunRepository,
|
|
1371
1490
|
durableEntities,
|
|
1372
1491
|
durableManagedTables,
|
|
1373
1492
|
ensureMikroOrmDurableSchema
|