@voyantjs/action-ledger 0.52.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/canary.d.ts +17 -0
- package/dist/canary.d.ts.map +1 -0
- package/dist/canary.js +77 -0
- package/dist/capability.d.ts +73 -0
- package/dist/capability.d.ts.map +1 -0
- package/dist/capability.js +206 -0
- package/dist/fingerprint.d.ts +26 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +55 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/request-context.d.ts +136 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +237 -0
- package/dist/route-schemas.d.ts +739 -0
- package/dist/route-schemas.d.ts.map +1 -0
- package/dist/route-schemas.js +427 -0
- package/dist/routes.d.ts +1596 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +271 -0
- package/dist/schema.d.ts +1759 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +237 -0
- package/dist/service.d.ts +317 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +1025 -0
- package/dist/timeline.d.ts +67 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +79 -0
- package/package.json +105 -0
package/dist/service.js
ADDED
|
@@ -0,0 +1,1025 @@
|
|
|
1
|
+
import { newId } from "@voyantjs/db";
|
|
2
|
+
import { and, desc, eq, gte, inArray, lt, lte, or, sql } from "drizzle-orm";
|
|
3
|
+
import { actionApprovals, actionDelegations, actionLedgerEntries, actionLedgerPayloads, actionLedgerRelayOutbox, actionMutationDetails, actionSensitiveReadDetails, } from "./schema.js";
|
|
4
|
+
const DEFAULT_LIST_LIMIT = 50;
|
|
5
|
+
const MAX_LIST_LIMIT = 200;
|
|
6
|
+
export class ActionLedgerIdempotencyConflictError extends Error {
|
|
7
|
+
existingActionId;
|
|
8
|
+
constructor(existingActionId) {
|
|
9
|
+
super("Action ledger idempotency key was reused with a different fingerprint");
|
|
10
|
+
this.name = "ActionLedgerIdempotencyConflictError";
|
|
11
|
+
this.existingActionId = existingActionId;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class ActionApprovalDecisionConflictError extends Error {
|
|
15
|
+
approvalId;
|
|
16
|
+
currentStatus;
|
|
17
|
+
constructor(approvalId, currentStatus) {
|
|
18
|
+
super(`Action approval ${approvalId} is already ${currentStatus}`);
|
|
19
|
+
this.name = "ActionApprovalDecisionConflictError";
|
|
20
|
+
this.approvalId = approvalId;
|
|
21
|
+
this.currentStatus = currentStatus;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class ActionApprovalDecisionStatusError extends Error {
|
|
25
|
+
status;
|
|
26
|
+
constructor(status) {
|
|
27
|
+
super(`Action approval decision status must be terminal, received ${status}`);
|
|
28
|
+
this.name = "ActionApprovalDecisionStatusError";
|
|
29
|
+
this.status = status;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class ActionLedgerReversalTargetError extends Error {
|
|
33
|
+
actionId;
|
|
34
|
+
reason;
|
|
35
|
+
constructor(actionId, reason) {
|
|
36
|
+
super(`Action ledger entry ${actionId} cannot be reversed: ${reason}`);
|
|
37
|
+
this.name = "ActionLedgerReversalTargetError";
|
|
38
|
+
this.actionId = actionId;
|
|
39
|
+
this.reason = reason;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const approvalDecisionStatusValues = [
|
|
43
|
+
"approved",
|
|
44
|
+
"denied",
|
|
45
|
+
"expired",
|
|
46
|
+
"cancelled",
|
|
47
|
+
"superseded",
|
|
48
|
+
];
|
|
49
|
+
const approvalDecisionStatusSet = new Set(approvalDecisionStatusValues);
|
|
50
|
+
export const actionLedgerService = {
|
|
51
|
+
async appendEntry(db, input) {
|
|
52
|
+
const existing = await findExistingIdempotentEntry(db, input);
|
|
53
|
+
if (existing) {
|
|
54
|
+
assertSameFingerprint(existing, input.idempotencyFingerprint ?? null);
|
|
55
|
+
return { entry: existing, replayed: true };
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
return await withOptionalTransaction(db, (tx) => insertEntry(tx, input));
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const racedExisting = await findExistingIdempotentEntry(db, input);
|
|
62
|
+
if (racedExisting) {
|
|
63
|
+
assertSameFingerprint(racedExisting, input.idempotencyFingerprint ?? null);
|
|
64
|
+
return { entry: racedExisting, replayed: true };
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
async requestApproval(db, input) {
|
|
70
|
+
return withOptionalTransaction(db, async (tx) => {
|
|
71
|
+
const approvalId = newId("action_approvals");
|
|
72
|
+
const requestedActionResult = await actionLedgerService.appendEntry(tx, {
|
|
73
|
+
...input.requestedAction,
|
|
74
|
+
status: "awaiting_approval",
|
|
75
|
+
approvalId,
|
|
76
|
+
});
|
|
77
|
+
if (!requestedActionResult.entry.approvalId) {
|
|
78
|
+
throw new Error("Action approval requested action is missing its approval id");
|
|
79
|
+
}
|
|
80
|
+
const existingApproval = await findApprovalForRequestedAction(tx, requestedActionResult.entry.id);
|
|
81
|
+
if (existingApproval) {
|
|
82
|
+
return {
|
|
83
|
+
requestedAction: requestedActionResult.entry,
|
|
84
|
+
approval: existingApproval,
|
|
85
|
+
replayed: requestedActionResult.replayed,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const [approval] = await tx
|
|
89
|
+
.insert(actionApprovals)
|
|
90
|
+
.values({
|
|
91
|
+
id: requestedActionResult.entry.approvalId,
|
|
92
|
+
requestedActionId: requestedActionResult.entry.id,
|
|
93
|
+
status: "pending",
|
|
94
|
+
requestedByPrincipalId: input.approval.requestedByPrincipalId ?? requestedActionResult.entry.principalId,
|
|
95
|
+
assignedToPrincipalId: input.approval.assignedToPrincipalId ?? null,
|
|
96
|
+
delegatedFromPrincipalId: input.approval.delegatedFromPrincipalId ?? null,
|
|
97
|
+
policyName: input.approval.policyName,
|
|
98
|
+
policyVersion: input.approval.policyVersion,
|
|
99
|
+
targetSnapshotRef: input.approval.targetSnapshotRef ?? null,
|
|
100
|
+
riskSnapshot: input.approval.riskSnapshot ?? requestedActionResult.entry.evaluatedRisk,
|
|
101
|
+
reasonCode: input.approval.reasonCode ?? null,
|
|
102
|
+
expiresAt: input.approval.expiresAt ? parseCursorDate(input.approval.expiresAt) : null,
|
|
103
|
+
})
|
|
104
|
+
.returning();
|
|
105
|
+
if (!approval) {
|
|
106
|
+
throw new Error("Action approval insert did not return an approval");
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
requestedAction: requestedActionResult.entry,
|
|
110
|
+
approval,
|
|
111
|
+
replayed: requestedActionResult.replayed,
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
async decideApproval(db, input) {
|
|
116
|
+
assertApprovalDecisionStatus(input.status);
|
|
117
|
+
return withOptionalTransaction(db, async (tx) => {
|
|
118
|
+
const approval = await findApprovalById(tx, input.id);
|
|
119
|
+
if (!approval)
|
|
120
|
+
return null;
|
|
121
|
+
if (approval.status !== "pending") {
|
|
122
|
+
throw new ActionApprovalDecisionConflictError(approval.id, approval.status);
|
|
123
|
+
}
|
|
124
|
+
const decidedAt = input.decidedAt ? parseCursorDate(input.decidedAt) : new Date();
|
|
125
|
+
const [updatedApproval] = await tx
|
|
126
|
+
.update(actionApprovals)
|
|
127
|
+
.set({
|
|
128
|
+
status: input.status,
|
|
129
|
+
decidedByPrincipalId: input.decidedByPrincipalId,
|
|
130
|
+
decidedAt,
|
|
131
|
+
})
|
|
132
|
+
.where(and(eq(actionApprovals.id, input.id), eq(actionApprovals.status, "pending")))
|
|
133
|
+
.returning();
|
|
134
|
+
if (!updatedApproval) {
|
|
135
|
+
const current = await findApprovalById(tx, input.id);
|
|
136
|
+
if (!current)
|
|
137
|
+
return null;
|
|
138
|
+
throw new ActionApprovalDecisionConflictError(current.id, current.status);
|
|
139
|
+
}
|
|
140
|
+
const decisionAction = await actionLedgerService.appendEntry(tx, {
|
|
141
|
+
...input.decisionAction,
|
|
142
|
+
actionKind: input.status === "approved" ? "approve" : "reject",
|
|
143
|
+
status: input.status,
|
|
144
|
+
evaluatedRisk: input.decisionAction.evaluatedRisk ?? updatedApproval.riskSnapshot,
|
|
145
|
+
targetType: input.decisionAction.targetType ?? "action_approval",
|
|
146
|
+
targetId: input.decisionAction.targetId ?? updatedApproval.id,
|
|
147
|
+
causationActionId: updatedApproval.requestedActionId,
|
|
148
|
+
approvalId: updatedApproval.id,
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
approval: updatedApproval,
|
|
152
|
+
decisionAction: decisionAction.entry,
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
},
|
|
156
|
+
async recordReversal(db, input) {
|
|
157
|
+
return withOptionalTransaction(db, async (tx) => {
|
|
158
|
+
const original = await actionLedgerService.getEntry(tx, input.originalActionId);
|
|
159
|
+
if (!original)
|
|
160
|
+
return null;
|
|
161
|
+
if (!original.mutationDetail) {
|
|
162
|
+
throw new ActionLedgerReversalTargetError(input.originalActionId, "missing_mutation_detail");
|
|
163
|
+
}
|
|
164
|
+
if (original.mutationDetail.reversalKind === "none") {
|
|
165
|
+
throw new ActionLedgerReversalTargetError(input.originalActionId, "not_reversible");
|
|
166
|
+
}
|
|
167
|
+
const reversalResult = await actionLedgerService.appendEntry(tx, {
|
|
168
|
+
...input.reversalAction,
|
|
169
|
+
causationActionId: original.entry.id,
|
|
170
|
+
mutationDetail: {
|
|
171
|
+
commandInputRef: input.reversalAction.mutationDetail?.commandInputRef ?? null,
|
|
172
|
+
commandResultRef: input.reversalAction.mutationDetail?.commandResultRef ?? null,
|
|
173
|
+
summary: input.reversalAction.mutationDetail?.summary ?? null,
|
|
174
|
+
reversalKind: input.reversalAction.mutationDetail?.reversalKind ?? "none",
|
|
175
|
+
reversalCommandId: input.reversalAction.mutationDetail?.reversalCommandId ?? null,
|
|
176
|
+
reversalCommandVersion: input.reversalAction.mutationDetail?.reversalCommandVersion ?? null,
|
|
177
|
+
reversalArgsRef: input.reversalAction.mutationDetail?.reversalArgsRef ?? null,
|
|
178
|
+
reversalStateProjection: input.reversalAction.mutationDetail?.reversalStateProjection ?? null,
|
|
179
|
+
reversalOutcomeProjection: input.reversalAction.mutationDetail?.reversalOutcomeProjection ?? null,
|
|
180
|
+
reversesActionId: original.entry.id,
|
|
181
|
+
reversedByActionIdProjection: input.reversalAction.mutationDetail?.reversedByActionIdProjection ?? null,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
const defaultProjection = input.reversalAction.status === "failed"
|
|
185
|
+
? { reversalState: "failed", reversalOutcome: "failed" }
|
|
186
|
+
: { reversalState: "completed", reversalOutcome: "full" };
|
|
187
|
+
await tx
|
|
188
|
+
.update(actionMutationDetails)
|
|
189
|
+
.set({
|
|
190
|
+
reversalStateProjection: input.projection?.reversalState ?? defaultProjection.reversalState,
|
|
191
|
+
reversalOutcomeProjection: input.projection?.reversalOutcome ?? defaultProjection.reversalOutcome,
|
|
192
|
+
reversedByActionIdProjection: reversalResult.entry.id,
|
|
193
|
+
})
|
|
194
|
+
.where(eq(actionMutationDetails.actionId, original.entry.id));
|
|
195
|
+
return {
|
|
196
|
+
originalAction: original.entry,
|
|
197
|
+
originalMutationDetail: original.mutationDetail,
|
|
198
|
+
reversalAction: reversalResult.entry,
|
|
199
|
+
replayed: reversalResult.replayed,
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
|
+
async listEntries(db, input = {}) {
|
|
204
|
+
const limit = normalizeListLimit(input.limit);
|
|
205
|
+
const predicate = buildActionLedgerEntriesPredicate(input);
|
|
206
|
+
let query = db.select().from(actionLedgerEntries).$dynamic();
|
|
207
|
+
if (predicate) {
|
|
208
|
+
query = query.where(predicate);
|
|
209
|
+
}
|
|
210
|
+
const rows = await query
|
|
211
|
+
.orderBy(desc(actionLedgerEntries.occurredAt), desc(actionLedgerEntries.id))
|
|
212
|
+
.limit(limit + 1);
|
|
213
|
+
const entries = rows.slice(0, limit);
|
|
214
|
+
return {
|
|
215
|
+
entries,
|
|
216
|
+
nextCursor: rows.length > limit && entries.length > 0
|
|
217
|
+
? toActionLedgerListCursor(entries[entries.length - 1])
|
|
218
|
+
: null,
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
async listRelayOutbox(db, input = {}) {
|
|
222
|
+
const limit = normalizeListLimit(input.limit);
|
|
223
|
+
const predicate = buildActionLedgerRelayOutboxPredicate(input);
|
|
224
|
+
let query = db.select().from(actionLedgerRelayOutbox).$dynamic();
|
|
225
|
+
if (predicate) {
|
|
226
|
+
query = query.where(predicate);
|
|
227
|
+
}
|
|
228
|
+
const rows = await query
|
|
229
|
+
.orderBy(desc(actionLedgerRelayOutbox.createdAt), desc(actionLedgerRelayOutbox.id))
|
|
230
|
+
.limit(limit + 1);
|
|
231
|
+
const visibleRows = rows.slice(0, limit);
|
|
232
|
+
return {
|
|
233
|
+
rows: visibleRows,
|
|
234
|
+
nextCursor: rows.length > limit && visibleRows.length > 0
|
|
235
|
+
? toActionLedgerRelayOutboxListCursor(visibleRows[visibleRows.length - 1])
|
|
236
|
+
: null,
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
async listApprovals(db, input = {}) {
|
|
240
|
+
const limit = normalizeListLimit(input.limit);
|
|
241
|
+
const predicate = buildActionApprovalsPredicate(input);
|
|
242
|
+
let query = db.select().from(actionApprovals).$dynamic();
|
|
243
|
+
if (predicate) {
|
|
244
|
+
query = query.where(predicate);
|
|
245
|
+
}
|
|
246
|
+
const rows = await query
|
|
247
|
+
.orderBy(desc(actionApprovals.createdAt), desc(actionApprovals.id))
|
|
248
|
+
.limit(limit + 1);
|
|
249
|
+
const approvals = rows.slice(0, limit);
|
|
250
|
+
return {
|
|
251
|
+
approvals,
|
|
252
|
+
nextCursor: rows.length > limit && approvals.length > 0
|
|
253
|
+
? toActionApprovalListCursor(approvals[approvals.length - 1])
|
|
254
|
+
: null,
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
async listDelegations(db, input = {}) {
|
|
258
|
+
const limit = normalizeListLimit(input.limit);
|
|
259
|
+
const predicate = buildActionDelegationsPredicate(input);
|
|
260
|
+
let query = db.select().from(actionDelegations).$dynamic();
|
|
261
|
+
if (predicate) {
|
|
262
|
+
query = query.where(predicate);
|
|
263
|
+
}
|
|
264
|
+
const rows = await query
|
|
265
|
+
.orderBy(desc(actionDelegations.createdAt), desc(actionDelegations.id))
|
|
266
|
+
.limit(limit + 1);
|
|
267
|
+
const delegations = rows.slice(0, limit);
|
|
268
|
+
return {
|
|
269
|
+
delegations,
|
|
270
|
+
nextCursor: rows.length > limit && delegations.length > 0
|
|
271
|
+
? toActionDelegationListCursor(delegations[delegations.length - 1])
|
|
272
|
+
: null,
|
|
273
|
+
};
|
|
274
|
+
},
|
|
275
|
+
async getApproval(db, id) {
|
|
276
|
+
const [approval] = await db
|
|
277
|
+
.select()
|
|
278
|
+
.from(actionApprovals)
|
|
279
|
+
.where(eq(actionApprovals.id, id))
|
|
280
|
+
.limit(1);
|
|
281
|
+
if (!approval)
|
|
282
|
+
return null;
|
|
283
|
+
return {
|
|
284
|
+
approval,
|
|
285
|
+
requestedAction: await actionLedgerService.getEntry(db, approval.requestedActionId),
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
async validateApprovedAction(db, input) {
|
|
289
|
+
const result = await actionLedgerService.getApproval(db, input.approvalId);
|
|
290
|
+
if (!result) {
|
|
291
|
+
return { ok: false, reason: "not_found" };
|
|
292
|
+
}
|
|
293
|
+
if (result.approval.status !== "approved") {
|
|
294
|
+
return {
|
|
295
|
+
ok: false,
|
|
296
|
+
reason: "not_approved",
|
|
297
|
+
approval: result.approval,
|
|
298
|
+
status: result.approval.status,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const now = input.now ? parseCursorDate(input.now) : new Date();
|
|
302
|
+
if (result.approval.expiresAt && result.approval.expiresAt < now) {
|
|
303
|
+
return {
|
|
304
|
+
ok: false,
|
|
305
|
+
reason: "expired",
|
|
306
|
+
approval: result.approval,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
const requestedAction = result.requestedAction?.entry;
|
|
310
|
+
if (!requestedAction ||
|
|
311
|
+
requestedAction.actionName !== input.actionName ||
|
|
312
|
+
requestedAction.actionVersion !== input.actionVersion ||
|
|
313
|
+
(input.requestedActionKind && requestedAction.actionKind !== input.requestedActionKind) ||
|
|
314
|
+
(input.requestedActionStatus &&
|
|
315
|
+
!(Array.isArray(input.requestedActionStatus)
|
|
316
|
+
? input.requestedActionStatus
|
|
317
|
+
: [input.requestedActionStatus]).includes(requestedAction.status)) ||
|
|
318
|
+
requestedAction.targetType !== input.targetType ||
|
|
319
|
+
requestedAction.targetId !== input.targetId ||
|
|
320
|
+
requestedAction.routeOrToolName !== (input.routeOrToolName ?? null) ||
|
|
321
|
+
requestedAction.approvalId !== result.approval.id) {
|
|
322
|
+
return {
|
|
323
|
+
ok: false,
|
|
324
|
+
reason: "mismatched_action",
|
|
325
|
+
approval: result.approval,
|
|
326
|
+
requestedAction: requestedAction ?? undefined,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
const existingExecution = await actionLedgerService.listEntries(db, {
|
|
330
|
+
actionName: input.actionName,
|
|
331
|
+
actionKind: input.executionActionKind,
|
|
332
|
+
targetType: input.targetType,
|
|
333
|
+
targetId: input.targetId,
|
|
334
|
+
causationActionId: requestedAction.id,
|
|
335
|
+
approvalId: result.approval.id,
|
|
336
|
+
status: input.executionStatus ?? "succeeded",
|
|
337
|
+
limit: 1,
|
|
338
|
+
});
|
|
339
|
+
if (existingExecution.entries.length > 0) {
|
|
340
|
+
return {
|
|
341
|
+
ok: false,
|
|
342
|
+
reason: "already_executed",
|
|
343
|
+
approval: result.approval,
|
|
344
|
+
requestedAction,
|
|
345
|
+
existingActionId: existingExecution.entries[0]?.id,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (!requestedAction.idempotencyFingerprint) {
|
|
349
|
+
return {
|
|
350
|
+
ok: false,
|
|
351
|
+
reason: "missing_fingerprint",
|
|
352
|
+
approval: result.approval,
|
|
353
|
+
requestedAction,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
if (input.idempotencyFingerprint !== requestedAction.idempotencyFingerprint) {
|
|
357
|
+
return {
|
|
358
|
+
ok: false,
|
|
359
|
+
reason: "fingerprint_mismatch",
|
|
360
|
+
approval: result.approval,
|
|
361
|
+
requestedAction,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (input.principalType &&
|
|
365
|
+
input.principalId &&
|
|
366
|
+
(requestedAction.principalType !== input.principalType ||
|
|
367
|
+
requestedAction.principalId !== input.principalId)) {
|
|
368
|
+
return {
|
|
369
|
+
ok: false,
|
|
370
|
+
reason: "principal_mismatch",
|
|
371
|
+
approval: result.approval,
|
|
372
|
+
requestedAction,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
ok: true,
|
|
377
|
+
approval: result.approval,
|
|
378
|
+
requestedAction,
|
|
379
|
+
idempotencyFingerprint: requestedAction.idempotencyFingerprint,
|
|
380
|
+
};
|
|
381
|
+
},
|
|
382
|
+
async getDelegation(db, id) {
|
|
383
|
+
const [delegation] = await db
|
|
384
|
+
.select()
|
|
385
|
+
.from(actionDelegations)
|
|
386
|
+
.where(eq(actionDelegations.id, id))
|
|
387
|
+
.limit(1);
|
|
388
|
+
if (!delegation)
|
|
389
|
+
return null;
|
|
390
|
+
return { delegation };
|
|
391
|
+
},
|
|
392
|
+
async claimRelayOutbox(db, input = {}) {
|
|
393
|
+
const limit = normalizeListLimit(input.limit);
|
|
394
|
+
const dueAt = input.dueAt ? parseCursorDate(input.dueAt) : new Date();
|
|
395
|
+
const organizationId = input.organizationId ?? null;
|
|
396
|
+
const result = await db.execute(sql `
|
|
397
|
+
WITH due AS (
|
|
398
|
+
SELECT id
|
|
399
|
+
FROM action_ledger_outbox
|
|
400
|
+
WHERE relay_status IN ('pending', 'failed')
|
|
401
|
+
AND (${organizationId}::text IS NULL OR organization_id = ${organizationId})
|
|
402
|
+
AND (next_retry_at IS NULL OR next_retry_at <= ${dueAt})
|
|
403
|
+
ORDER BY created_at ASC, id ASC
|
|
404
|
+
LIMIT ${limit}
|
|
405
|
+
FOR UPDATE SKIP LOCKED
|
|
406
|
+
)
|
|
407
|
+
UPDATE action_ledger_outbox AS outbox
|
|
408
|
+
SET relay_status = 'processing',
|
|
409
|
+
attempt_count = outbox.attempt_count + 1,
|
|
410
|
+
last_error = NULL,
|
|
411
|
+
processed_at = NULL
|
|
412
|
+
FROM due
|
|
413
|
+
WHERE outbox.id = due.id
|
|
414
|
+
RETURNING
|
|
415
|
+
outbox.id,
|
|
416
|
+
outbox.action_id,
|
|
417
|
+
outbox.organization_id,
|
|
418
|
+
outbox.relay_status,
|
|
419
|
+
outbox.payload_ref,
|
|
420
|
+
outbox.attempt_count,
|
|
421
|
+
outbox.next_retry_at,
|
|
422
|
+
outbox.last_error,
|
|
423
|
+
outbox.created_at,
|
|
424
|
+
outbox.processed_at
|
|
425
|
+
`);
|
|
426
|
+
const rows = ("rows" in result ? result.rows : result);
|
|
427
|
+
return {
|
|
428
|
+
rows: rows.map(actionLedgerRelayOutboxFromSqlRow),
|
|
429
|
+
};
|
|
430
|
+
},
|
|
431
|
+
async markRelayOutboxSucceeded(db, input) {
|
|
432
|
+
const [row] = await db
|
|
433
|
+
.update(actionLedgerRelayOutbox)
|
|
434
|
+
.set({
|
|
435
|
+
relayStatus: "succeeded",
|
|
436
|
+
nextRetryAt: null,
|
|
437
|
+
lastError: null,
|
|
438
|
+
processedAt: input.processedAt ? parseCursorDate(input.processedAt) : new Date(),
|
|
439
|
+
})
|
|
440
|
+
.where(and(eq(actionLedgerRelayOutbox.id, input.id), eq(actionLedgerRelayOutbox.relayStatus, "processing")))
|
|
441
|
+
.returning();
|
|
442
|
+
return row ?? null;
|
|
443
|
+
},
|
|
444
|
+
async markRelayOutboxFailed(db, input) {
|
|
445
|
+
const deadLetter = input.deadLetter ?? false;
|
|
446
|
+
const [row] = await db
|
|
447
|
+
.update(actionLedgerRelayOutbox)
|
|
448
|
+
.set({
|
|
449
|
+
relayStatus: deadLetter ? "dead_letter" : "failed",
|
|
450
|
+
nextRetryAt: deadLetter
|
|
451
|
+
? null
|
|
452
|
+
: input.nextRetryAt
|
|
453
|
+
? parseCursorDate(input.nextRetryAt)
|
|
454
|
+
: null,
|
|
455
|
+
lastError: input.lastError,
|
|
456
|
+
processedAt: deadLetter
|
|
457
|
+
? input.processedAt
|
|
458
|
+
? parseCursorDate(input.processedAt)
|
|
459
|
+
: new Date()
|
|
460
|
+
: null,
|
|
461
|
+
})
|
|
462
|
+
.where(and(eq(actionLedgerRelayOutbox.id, input.id), eq(actionLedgerRelayOutbox.relayStatus, "processing")))
|
|
463
|
+
.returning();
|
|
464
|
+
return row ?? null;
|
|
465
|
+
},
|
|
466
|
+
async getEntry(db, id) {
|
|
467
|
+
const [entry] = await db
|
|
468
|
+
.select()
|
|
469
|
+
.from(actionLedgerEntries)
|
|
470
|
+
.where(eq(actionLedgerEntries.id, id))
|
|
471
|
+
.limit(1);
|
|
472
|
+
if (!entry)
|
|
473
|
+
return null;
|
|
474
|
+
const [[mutationDetail], [sensitiveReadDetail], payloads, relayOutbox] = await Promise.all([
|
|
475
|
+
db
|
|
476
|
+
.select()
|
|
477
|
+
.from(actionMutationDetails)
|
|
478
|
+
.where(eq(actionMutationDetails.actionId, id))
|
|
479
|
+
.limit(1),
|
|
480
|
+
db
|
|
481
|
+
.select()
|
|
482
|
+
.from(actionSensitiveReadDetails)
|
|
483
|
+
.where(eq(actionSensitiveReadDetails.actionId, id))
|
|
484
|
+
.limit(1),
|
|
485
|
+
db.select().from(actionLedgerPayloads).where(eq(actionLedgerPayloads.actionId, id)),
|
|
486
|
+
db.select().from(actionLedgerRelayOutbox).where(eq(actionLedgerRelayOutbox.actionId, id)),
|
|
487
|
+
]);
|
|
488
|
+
return {
|
|
489
|
+
entry,
|
|
490
|
+
mutationDetail: mutationDetail ?? null,
|
|
491
|
+
sensitiveReadDetail: sensitiveReadDetail ?? null,
|
|
492
|
+
payloads,
|
|
493
|
+
relayOutbox,
|
|
494
|
+
};
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
const activeTransactionDbs = new WeakSet();
|
|
498
|
+
function withOptionalTransaction(db, callback) {
|
|
499
|
+
if (activeTransactionDbs.has(db)) {
|
|
500
|
+
return callback(db);
|
|
501
|
+
}
|
|
502
|
+
const maybeTransactional = db;
|
|
503
|
+
if (typeof maybeTransactional.transaction === "function") {
|
|
504
|
+
return maybeTransactional.transaction(async (tx) => {
|
|
505
|
+
activeTransactionDbs.add(tx);
|
|
506
|
+
try {
|
|
507
|
+
return await callback(tx);
|
|
508
|
+
}
|
|
509
|
+
finally {
|
|
510
|
+
activeTransactionDbs.delete(tx);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
return callback(db);
|
|
515
|
+
}
|
|
516
|
+
function actionLedgerRelayOutboxFromSqlRow(row) {
|
|
517
|
+
return {
|
|
518
|
+
id: row.id,
|
|
519
|
+
actionId: row.action_id,
|
|
520
|
+
organizationId: row.organization_id,
|
|
521
|
+
relayStatus: row.relay_status,
|
|
522
|
+
payloadRef: row.payload_ref,
|
|
523
|
+
attemptCount: Number(row.attempt_count),
|
|
524
|
+
nextRetryAt: row.next_retry_at ? parseCursorDate(row.next_retry_at) : null,
|
|
525
|
+
lastError: row.last_error,
|
|
526
|
+
createdAt: parseCursorDate(row.created_at),
|
|
527
|
+
processedAt: row.processed_at ? parseCursorDate(row.processed_at) : null,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
async function insertEntry(db, input) {
|
|
531
|
+
const { enqueueRelay, mutationDetail, payloads, sensitiveReadDetail, ...entryInput } = input;
|
|
532
|
+
const [entry] = await db
|
|
533
|
+
.insert(actionLedgerEntries)
|
|
534
|
+
.values({
|
|
535
|
+
...entryInput,
|
|
536
|
+
occurredAt: input.occurredAt,
|
|
537
|
+
})
|
|
538
|
+
.returning();
|
|
539
|
+
if (!entry) {
|
|
540
|
+
throw new Error("Action ledger insert did not return an entry");
|
|
541
|
+
}
|
|
542
|
+
if (mutationDetail) {
|
|
543
|
+
await db.insert(actionMutationDetails).values({
|
|
544
|
+
actionId: entry.id,
|
|
545
|
+
...mutationDetail,
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
if (sensitiveReadDetail) {
|
|
549
|
+
await db.insert(actionSensitiveReadDetails).values({
|
|
550
|
+
actionId: entry.id,
|
|
551
|
+
...sensitiveReadDetail,
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
if (payloads && payloads.length > 0) {
|
|
555
|
+
await db.insert(actionLedgerPayloads).values(payloads.map((payload) => ({
|
|
556
|
+
actionId: entry.id,
|
|
557
|
+
...payload,
|
|
558
|
+
})));
|
|
559
|
+
}
|
|
560
|
+
if (enqueueRelay) {
|
|
561
|
+
const payloadRef = typeof enqueueRelay === "object" ? enqueueRelay.payloadRef : null;
|
|
562
|
+
await db.insert(actionLedgerRelayOutbox).values({
|
|
563
|
+
actionId: entry.id,
|
|
564
|
+
organizationId: entry.organizationId,
|
|
565
|
+
payloadRef: payloadRef ?? null,
|
|
566
|
+
relayStatus: "pending",
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
return { entry, replayed: false };
|
|
570
|
+
}
|
|
571
|
+
async function findExistingIdempotentEntry(db, input) {
|
|
572
|
+
if (!input.idempotencyScope || !input.idempotencyKey)
|
|
573
|
+
return null;
|
|
574
|
+
const [existing] = await db
|
|
575
|
+
.select()
|
|
576
|
+
.from(actionLedgerEntries)
|
|
577
|
+
.where(and(eq(actionLedgerEntries.idempotencyScope, input.idempotencyScope), eq(actionLedgerEntries.actionName, input.actionName), eq(actionLedgerEntries.targetType, input.targetType), eq(actionLedgerEntries.targetId, input.targetId), eq(actionLedgerEntries.idempotencyKey, input.idempotencyKey)))
|
|
578
|
+
.limit(1);
|
|
579
|
+
return existing ?? null;
|
|
580
|
+
}
|
|
581
|
+
async function findApprovalForRequestedAction(db, requestedActionId) {
|
|
582
|
+
const [approval] = await db
|
|
583
|
+
.select()
|
|
584
|
+
.from(actionApprovals)
|
|
585
|
+
.where(eq(actionApprovals.requestedActionId, requestedActionId))
|
|
586
|
+
.limit(1);
|
|
587
|
+
return approval ?? null;
|
|
588
|
+
}
|
|
589
|
+
async function findApprovalById(db, id) {
|
|
590
|
+
const [approval] = await db
|
|
591
|
+
.select()
|
|
592
|
+
.from(actionApprovals)
|
|
593
|
+
.where(eq(actionApprovals.id, id))
|
|
594
|
+
.limit(1);
|
|
595
|
+
return approval ?? null;
|
|
596
|
+
}
|
|
597
|
+
function assertApprovalDecisionStatus(status) {
|
|
598
|
+
if (approvalDecisionStatusSet.has(status))
|
|
599
|
+
return;
|
|
600
|
+
throw new ActionApprovalDecisionStatusError(status);
|
|
601
|
+
}
|
|
602
|
+
function assertSameFingerprint(entry, fingerprint) {
|
|
603
|
+
if (entry.idempotencyFingerprint !== fingerprint) {
|
|
604
|
+
throw new ActionLedgerIdempotencyConflictError(entry.id);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function normalizeListLimit(limit) {
|
|
608
|
+
if (limit === undefined)
|
|
609
|
+
return DEFAULT_LIST_LIMIT;
|
|
610
|
+
if (!Number.isFinite(limit))
|
|
611
|
+
return DEFAULT_LIST_LIMIT;
|
|
612
|
+
return Math.min(Math.max(Math.trunc(limit), 1), MAX_LIST_LIMIT);
|
|
613
|
+
}
|
|
614
|
+
function toActionLedgerListCursor(entry) {
|
|
615
|
+
return {
|
|
616
|
+
occurredAt: serializeCursorDate(entry.occurredAt),
|
|
617
|
+
id: entry.id,
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
function toActionLedgerRelayOutboxListCursor(row) {
|
|
621
|
+
return {
|
|
622
|
+
createdAt: serializeCursorDate(row.createdAt),
|
|
623
|
+
id: row.id,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
function toActionApprovalListCursor(row) {
|
|
627
|
+
return {
|
|
628
|
+
createdAt: serializeCursorDate(row.createdAt),
|
|
629
|
+
id: row.id,
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function toActionDelegationListCursor(row) {
|
|
633
|
+
return {
|
|
634
|
+
createdAt: serializeCursorDate(row.createdAt),
|
|
635
|
+
id: row.id,
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
function serializeCursorDate(value) {
|
|
639
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
640
|
+
if (Number.isNaN(date.getTime())) {
|
|
641
|
+
throw new Error("Action ledger cursor occurredAt must be a valid timestamp");
|
|
642
|
+
}
|
|
643
|
+
return date.toISOString();
|
|
644
|
+
}
|
|
645
|
+
function parseCursorDate(value) {
|
|
646
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
647
|
+
if (Number.isNaN(date.getTime())) {
|
|
648
|
+
throw new Error("Action ledger cursor occurredAt must be a valid timestamp");
|
|
649
|
+
}
|
|
650
|
+
return date;
|
|
651
|
+
}
|
|
652
|
+
function riskCondition(value) {
|
|
653
|
+
if (value === undefined)
|
|
654
|
+
return undefined;
|
|
655
|
+
if (Array.isArray(value)) {
|
|
656
|
+
if (value.length === 0)
|
|
657
|
+
return undefined;
|
|
658
|
+
return inArray(actionLedgerEntries.evaluatedRisk, value);
|
|
659
|
+
}
|
|
660
|
+
return eq(actionLedgerEntries.evaluatedRisk, value);
|
|
661
|
+
}
|
|
662
|
+
function statusCondition(value) {
|
|
663
|
+
if (value === undefined)
|
|
664
|
+
return undefined;
|
|
665
|
+
if (Array.isArray(value)) {
|
|
666
|
+
if (value.length === 0)
|
|
667
|
+
return undefined;
|
|
668
|
+
return inArray(actionLedgerEntries.status, value);
|
|
669
|
+
}
|
|
670
|
+
return eq(actionLedgerEntries.status, value);
|
|
671
|
+
}
|
|
672
|
+
function approvalStatusCondition(value) {
|
|
673
|
+
if (value === undefined)
|
|
674
|
+
return undefined;
|
|
675
|
+
if (Array.isArray(value)) {
|
|
676
|
+
if (value.length === 0)
|
|
677
|
+
return undefined;
|
|
678
|
+
return inArray(actionApprovals.status, value);
|
|
679
|
+
}
|
|
680
|
+
return eq(actionApprovals.status, value);
|
|
681
|
+
}
|
|
682
|
+
function approvalRiskCondition(value) {
|
|
683
|
+
if (value === undefined)
|
|
684
|
+
return undefined;
|
|
685
|
+
if (Array.isArray(value)) {
|
|
686
|
+
if (value.length === 0)
|
|
687
|
+
return undefined;
|
|
688
|
+
return inArray(actionApprovals.riskSnapshot, value);
|
|
689
|
+
}
|
|
690
|
+
return eq(actionApprovals.riskSnapshot, value);
|
|
691
|
+
}
|
|
692
|
+
function reversalKindCondition(value) {
|
|
693
|
+
if (value === undefined)
|
|
694
|
+
return undefined;
|
|
695
|
+
if (Array.isArray(value)) {
|
|
696
|
+
if (value.length === 0)
|
|
697
|
+
return undefined;
|
|
698
|
+
return inArray(actionMutationDetails.reversalKind, value);
|
|
699
|
+
}
|
|
700
|
+
return eq(actionMutationDetails.reversalKind, value);
|
|
701
|
+
}
|
|
702
|
+
function reversalStateCondition(value) {
|
|
703
|
+
if (value === undefined)
|
|
704
|
+
return undefined;
|
|
705
|
+
if (Array.isArray(value)) {
|
|
706
|
+
if (value.length === 0)
|
|
707
|
+
return undefined;
|
|
708
|
+
return inArray(actionMutationDetails.reversalStateProjection, value);
|
|
709
|
+
}
|
|
710
|
+
return eq(actionMutationDetails.reversalStateProjection, value);
|
|
711
|
+
}
|
|
712
|
+
function reversalOutcomeCondition(value) {
|
|
713
|
+
if (value === undefined)
|
|
714
|
+
return undefined;
|
|
715
|
+
if (Array.isArray(value)) {
|
|
716
|
+
if (value.length === 0)
|
|
717
|
+
return undefined;
|
|
718
|
+
return inArray(actionMutationDetails.reversalOutcomeProjection, value);
|
|
719
|
+
}
|
|
720
|
+
return eq(actionMutationDetails.reversalOutcomeProjection, value);
|
|
721
|
+
}
|
|
722
|
+
function relayStatusCondition(value) {
|
|
723
|
+
if (value === undefined)
|
|
724
|
+
return undefined;
|
|
725
|
+
if (Array.isArray(value)) {
|
|
726
|
+
if (value.length === 0)
|
|
727
|
+
return undefined;
|
|
728
|
+
return inArray(actionLedgerRelayOutbox.relayStatus, value);
|
|
729
|
+
}
|
|
730
|
+
return eq(actionLedgerRelayOutbox.relayStatus, value);
|
|
731
|
+
}
|
|
732
|
+
function mutationDetailExists(condition) {
|
|
733
|
+
return sql `EXISTS (
|
|
734
|
+
SELECT 1
|
|
735
|
+
FROM ${actionMutationDetails}
|
|
736
|
+
WHERE ${actionMutationDetails.actionId} = ${actionLedgerEntries.id}
|
|
737
|
+
AND ${condition}
|
|
738
|
+
)`;
|
|
739
|
+
}
|
|
740
|
+
function sensitiveReadDetailExists(condition) {
|
|
741
|
+
return sql `EXISTS (
|
|
742
|
+
SELECT 1
|
|
743
|
+
FROM ${actionSensitiveReadDetails}
|
|
744
|
+
WHERE ${actionSensitiveReadDetails.actionId} = ${actionLedgerEntries.id}
|
|
745
|
+
AND ${condition}
|
|
746
|
+
)`;
|
|
747
|
+
}
|
|
748
|
+
function buildCursorCondition(cursor) {
|
|
749
|
+
const occurredAt = parseCursorDate(cursor.occurredAt);
|
|
750
|
+
const tieBreaker = and(eq(actionLedgerEntries.occurredAt, occurredAt), lt(actionLedgerEntries.id, cursor.id));
|
|
751
|
+
return or(lt(actionLedgerEntries.occurredAt, occurredAt), tieBreaker);
|
|
752
|
+
}
|
|
753
|
+
function buildRelayOutboxCursorCondition(cursor) {
|
|
754
|
+
const createdAt = parseCursorDate(cursor.createdAt);
|
|
755
|
+
const tieBreaker = and(eq(actionLedgerRelayOutbox.createdAt, createdAt), lt(actionLedgerRelayOutbox.id, cursor.id));
|
|
756
|
+
return or(lt(actionLedgerRelayOutbox.createdAt, createdAt), tieBreaker);
|
|
757
|
+
}
|
|
758
|
+
function buildApprovalCursorCondition(cursor) {
|
|
759
|
+
const createdAt = parseCursorDate(cursor.createdAt);
|
|
760
|
+
const tieBreaker = and(eq(actionApprovals.createdAt, createdAt), lt(actionApprovals.id, cursor.id));
|
|
761
|
+
return or(lt(actionApprovals.createdAt, createdAt), tieBreaker);
|
|
762
|
+
}
|
|
763
|
+
function buildDelegationCursorCondition(cursor) {
|
|
764
|
+
const createdAt = parseCursorDate(cursor.createdAt);
|
|
765
|
+
const tieBreaker = and(eq(actionDelegations.createdAt, createdAt), lt(actionDelegations.id, cursor.id));
|
|
766
|
+
return or(lt(actionDelegations.createdAt, createdAt), tieBreaker);
|
|
767
|
+
}
|
|
768
|
+
function buildActionDelegationsPredicate(input) {
|
|
769
|
+
const conditions = [];
|
|
770
|
+
if (input.rootPrincipalType) {
|
|
771
|
+
conditions.push(eq(actionDelegations.rootPrincipalType, input.rootPrincipalType));
|
|
772
|
+
}
|
|
773
|
+
if (input.rootPrincipalId) {
|
|
774
|
+
conditions.push(eq(actionDelegations.rootPrincipalId, input.rootPrincipalId));
|
|
775
|
+
}
|
|
776
|
+
if (input.parentPrincipalType) {
|
|
777
|
+
conditions.push(eq(actionDelegations.parentPrincipalType, input.parentPrincipalType));
|
|
778
|
+
}
|
|
779
|
+
if (input.parentPrincipalId) {
|
|
780
|
+
conditions.push(eq(actionDelegations.parentPrincipalId, input.parentPrincipalId));
|
|
781
|
+
}
|
|
782
|
+
if (input.childPrincipalType) {
|
|
783
|
+
conditions.push(eq(actionDelegations.childPrincipalType, input.childPrincipalType));
|
|
784
|
+
}
|
|
785
|
+
if (input.childPrincipalId) {
|
|
786
|
+
conditions.push(eq(actionDelegations.childPrincipalId, input.childPrincipalId));
|
|
787
|
+
}
|
|
788
|
+
if (input.grantSource)
|
|
789
|
+
conditions.push(eq(actionDelegations.grantSource, input.grantSource));
|
|
790
|
+
if (input.capabilityScopeRef) {
|
|
791
|
+
conditions.push(eq(actionDelegations.capabilityScopeRef, input.capabilityScopeRef));
|
|
792
|
+
}
|
|
793
|
+
if (input.budgetScopeRef) {
|
|
794
|
+
conditions.push(eq(actionDelegations.budgetScopeRef, input.budgetScopeRef));
|
|
795
|
+
}
|
|
796
|
+
if (input.expiresAtFrom) {
|
|
797
|
+
conditions.push(gte(actionDelegations.expiresAt, parseCursorDate(input.expiresAtFrom)));
|
|
798
|
+
}
|
|
799
|
+
if (input.expiresAtTo) {
|
|
800
|
+
conditions.push(lte(actionDelegations.expiresAt, parseCursorDate(input.expiresAtTo)));
|
|
801
|
+
}
|
|
802
|
+
if (input.createdAtFrom) {
|
|
803
|
+
conditions.push(gte(actionDelegations.createdAt, parseCursorDate(input.createdAtFrom)));
|
|
804
|
+
}
|
|
805
|
+
if (input.createdAtTo) {
|
|
806
|
+
conditions.push(lte(actionDelegations.createdAt, parseCursorDate(input.createdAtTo)));
|
|
807
|
+
}
|
|
808
|
+
if (input.cursor) {
|
|
809
|
+
conditions.push(buildDelegationCursorCondition(input.cursor));
|
|
810
|
+
}
|
|
811
|
+
if (conditions.length === 0)
|
|
812
|
+
return undefined;
|
|
813
|
+
if (conditions.length === 1)
|
|
814
|
+
return conditions[0];
|
|
815
|
+
return and(...conditions);
|
|
816
|
+
}
|
|
817
|
+
function buildActionApprovalsPredicate(input) {
|
|
818
|
+
const conditions = [];
|
|
819
|
+
if (input.requestedActionId) {
|
|
820
|
+
conditions.push(eq(actionApprovals.requestedActionId, input.requestedActionId));
|
|
821
|
+
}
|
|
822
|
+
const entryStatusCondition = approvalStatusCondition(input.status);
|
|
823
|
+
if (entryStatusCondition)
|
|
824
|
+
conditions.push(entryStatusCondition);
|
|
825
|
+
if (input.requestedByPrincipalId) {
|
|
826
|
+
conditions.push(eq(actionApprovals.requestedByPrincipalId, input.requestedByPrincipalId));
|
|
827
|
+
}
|
|
828
|
+
if (input.assignedToPrincipalId) {
|
|
829
|
+
conditions.push(eq(actionApprovals.assignedToPrincipalId, input.assignedToPrincipalId));
|
|
830
|
+
}
|
|
831
|
+
if (input.decidedByPrincipalId) {
|
|
832
|
+
conditions.push(eq(actionApprovals.decidedByPrincipalId, input.decidedByPrincipalId));
|
|
833
|
+
}
|
|
834
|
+
if (input.delegatedFromPrincipalId) {
|
|
835
|
+
conditions.push(eq(actionApprovals.delegatedFromPrincipalId, input.delegatedFromPrincipalId));
|
|
836
|
+
}
|
|
837
|
+
if (input.policyName)
|
|
838
|
+
conditions.push(eq(actionApprovals.policyName, input.policyName));
|
|
839
|
+
if (input.policyVersion)
|
|
840
|
+
conditions.push(eq(actionApprovals.policyVersion, input.policyVersion));
|
|
841
|
+
const riskSnapshotCondition = approvalRiskCondition(input.riskSnapshot);
|
|
842
|
+
if (riskSnapshotCondition)
|
|
843
|
+
conditions.push(riskSnapshotCondition);
|
|
844
|
+
if (input.reasonCode)
|
|
845
|
+
conditions.push(eq(actionApprovals.reasonCode, input.reasonCode));
|
|
846
|
+
if (input.expiresAtFrom) {
|
|
847
|
+
conditions.push(gte(actionApprovals.expiresAt, parseCursorDate(input.expiresAtFrom)));
|
|
848
|
+
}
|
|
849
|
+
if (input.expiresAtTo) {
|
|
850
|
+
conditions.push(lte(actionApprovals.expiresAt, parseCursorDate(input.expiresAtTo)));
|
|
851
|
+
}
|
|
852
|
+
if (input.decidedAtFrom) {
|
|
853
|
+
conditions.push(gte(actionApprovals.decidedAt, parseCursorDate(input.decidedAtFrom)));
|
|
854
|
+
}
|
|
855
|
+
if (input.decidedAtTo) {
|
|
856
|
+
conditions.push(lte(actionApprovals.decidedAt, parseCursorDate(input.decidedAtTo)));
|
|
857
|
+
}
|
|
858
|
+
if (input.createdAtFrom) {
|
|
859
|
+
conditions.push(gte(actionApprovals.createdAt, parseCursorDate(input.createdAtFrom)));
|
|
860
|
+
}
|
|
861
|
+
if (input.createdAtTo) {
|
|
862
|
+
conditions.push(lte(actionApprovals.createdAt, parseCursorDate(input.createdAtTo)));
|
|
863
|
+
}
|
|
864
|
+
if (input.cursor) {
|
|
865
|
+
conditions.push(buildApprovalCursorCondition(input.cursor));
|
|
866
|
+
}
|
|
867
|
+
if (conditions.length === 0)
|
|
868
|
+
return undefined;
|
|
869
|
+
if (conditions.length === 1)
|
|
870
|
+
return conditions[0];
|
|
871
|
+
return and(...conditions);
|
|
872
|
+
}
|
|
873
|
+
function buildActionLedgerRelayOutboxPredicate(input) {
|
|
874
|
+
const conditions = [];
|
|
875
|
+
if (input.actionId)
|
|
876
|
+
conditions.push(eq(actionLedgerRelayOutbox.actionId, input.actionId));
|
|
877
|
+
if (input.organizationId) {
|
|
878
|
+
conditions.push(eq(actionLedgerRelayOutbox.organizationId, input.organizationId));
|
|
879
|
+
}
|
|
880
|
+
const entryRelayStatusCondition = relayStatusCondition(input.relayStatus);
|
|
881
|
+
if (entryRelayStatusCondition)
|
|
882
|
+
conditions.push(entryRelayStatusCondition);
|
|
883
|
+
if (input.dueBefore) {
|
|
884
|
+
conditions.push(lte(actionLedgerRelayOutbox.nextRetryAt, parseCursorDate(input.dueBefore)));
|
|
885
|
+
}
|
|
886
|
+
if (input.createdAtFrom) {
|
|
887
|
+
conditions.push(gte(actionLedgerRelayOutbox.createdAt, parseCursorDate(input.createdAtFrom)));
|
|
888
|
+
}
|
|
889
|
+
if (input.createdAtTo) {
|
|
890
|
+
conditions.push(lte(actionLedgerRelayOutbox.createdAt, parseCursorDate(input.createdAtTo)));
|
|
891
|
+
}
|
|
892
|
+
if (input.processedAtFrom) {
|
|
893
|
+
conditions.push(gte(actionLedgerRelayOutbox.processedAt, parseCursorDate(input.processedAtFrom)));
|
|
894
|
+
}
|
|
895
|
+
if (input.processedAtTo) {
|
|
896
|
+
conditions.push(lte(actionLedgerRelayOutbox.processedAt, parseCursorDate(input.processedAtTo)));
|
|
897
|
+
}
|
|
898
|
+
if (input.cursor) {
|
|
899
|
+
conditions.push(buildRelayOutboxCursorCondition(input.cursor));
|
|
900
|
+
}
|
|
901
|
+
if (conditions.length === 0)
|
|
902
|
+
return undefined;
|
|
903
|
+
if (conditions.length === 1)
|
|
904
|
+
return conditions[0];
|
|
905
|
+
return and(...conditions);
|
|
906
|
+
}
|
|
907
|
+
function buildActionLedgerEntriesPredicate(input) {
|
|
908
|
+
const conditions = [];
|
|
909
|
+
if (input.actionName)
|
|
910
|
+
conditions.push(eq(actionLedgerEntries.actionName, input.actionName));
|
|
911
|
+
if (input.actionKind)
|
|
912
|
+
conditions.push(eq(actionLedgerEntries.actionKind, input.actionKind));
|
|
913
|
+
if (input.actorType)
|
|
914
|
+
conditions.push(eq(actionLedgerEntries.actorType, input.actorType));
|
|
915
|
+
if (input.principalType) {
|
|
916
|
+
conditions.push(eq(actionLedgerEntries.principalType, input.principalType));
|
|
917
|
+
}
|
|
918
|
+
if (input.principalId)
|
|
919
|
+
conditions.push(eq(actionLedgerEntries.principalId, input.principalId));
|
|
920
|
+
if (input.apiTokenId)
|
|
921
|
+
conditions.push(eq(actionLedgerEntries.apiTokenId, input.apiTokenId));
|
|
922
|
+
if (input.sessionId)
|
|
923
|
+
conditions.push(eq(actionLedgerEntries.sessionId, input.sessionId));
|
|
924
|
+
if (input.callerType)
|
|
925
|
+
conditions.push(eq(actionLedgerEntries.callerType, input.callerType));
|
|
926
|
+
if (input.organizationId) {
|
|
927
|
+
conditions.push(eq(actionLedgerEntries.organizationId, input.organizationId));
|
|
928
|
+
}
|
|
929
|
+
if (input.targetType)
|
|
930
|
+
conditions.push(eq(actionLedgerEntries.targetType, input.targetType));
|
|
931
|
+
if (input.targetId)
|
|
932
|
+
conditions.push(eq(actionLedgerEntries.targetId, input.targetId));
|
|
933
|
+
if (input.targetIds && input.targetIds.length > 0) {
|
|
934
|
+
conditions.push(inArray(actionLedgerEntries.targetId, input.targetIds));
|
|
935
|
+
}
|
|
936
|
+
if (input.routeOrToolName) {
|
|
937
|
+
conditions.push(eq(actionLedgerEntries.routeOrToolName, input.routeOrToolName));
|
|
938
|
+
}
|
|
939
|
+
if (input.workflowRunId) {
|
|
940
|
+
conditions.push(eq(actionLedgerEntries.workflowRunId, input.workflowRunId));
|
|
941
|
+
}
|
|
942
|
+
if (input.workflowStepId) {
|
|
943
|
+
conditions.push(eq(actionLedgerEntries.workflowStepId, input.workflowStepId));
|
|
944
|
+
}
|
|
945
|
+
if (input.correlationId) {
|
|
946
|
+
conditions.push(eq(actionLedgerEntries.correlationId, input.correlationId));
|
|
947
|
+
}
|
|
948
|
+
if (input.causationActionId) {
|
|
949
|
+
conditions.push(eq(actionLedgerEntries.causationActionId, input.causationActionId));
|
|
950
|
+
}
|
|
951
|
+
if (input.capabilityId)
|
|
952
|
+
conditions.push(eq(actionLedgerEntries.capabilityId, input.capabilityId));
|
|
953
|
+
if (input.capabilityVersion) {
|
|
954
|
+
conditions.push(eq(actionLedgerEntries.capabilityVersion, input.capabilityVersion));
|
|
955
|
+
}
|
|
956
|
+
if (input.authorizationSource) {
|
|
957
|
+
conditions.push(eq(actionLedgerEntries.authorizationSource, input.authorizationSource));
|
|
958
|
+
}
|
|
959
|
+
if (input.approvalId)
|
|
960
|
+
conditions.push(eq(actionLedgerEntries.approvalId, input.approvalId));
|
|
961
|
+
if (input.amendsActionId) {
|
|
962
|
+
conditions.push(eq(actionLedgerEntries.amendsActionId, input.amendsActionId));
|
|
963
|
+
}
|
|
964
|
+
if (input.idempotencyScope) {
|
|
965
|
+
conditions.push(eq(actionLedgerEntries.idempotencyScope, input.idempotencyScope));
|
|
966
|
+
}
|
|
967
|
+
if (input.idempotencyKey) {
|
|
968
|
+
conditions.push(eq(actionLedgerEntries.idempotencyKey, input.idempotencyKey));
|
|
969
|
+
}
|
|
970
|
+
const evaluatedRiskCondition = riskCondition(input.evaluatedRisk);
|
|
971
|
+
if (evaluatedRiskCondition)
|
|
972
|
+
conditions.push(evaluatedRiskCondition);
|
|
973
|
+
const entryStatusCondition = statusCondition(input.status);
|
|
974
|
+
if (entryStatusCondition)
|
|
975
|
+
conditions.push(entryStatusCondition);
|
|
976
|
+
const entryReversalKindCondition = reversalKindCondition(input.reversalKind);
|
|
977
|
+
if (entryReversalKindCondition) {
|
|
978
|
+
conditions.push(mutationDetailExists(entryReversalKindCondition));
|
|
979
|
+
}
|
|
980
|
+
const entryReversalStateCondition = reversalStateCondition(input.reversalState);
|
|
981
|
+
if (entryReversalStateCondition) {
|
|
982
|
+
conditions.push(mutationDetailExists(entryReversalStateCondition));
|
|
983
|
+
}
|
|
984
|
+
const entryReversalOutcomeCondition = reversalOutcomeCondition(input.reversalOutcome);
|
|
985
|
+
if (entryReversalOutcomeCondition) {
|
|
986
|
+
conditions.push(mutationDetailExists(entryReversalOutcomeCondition));
|
|
987
|
+
}
|
|
988
|
+
if (input.reversesActionId) {
|
|
989
|
+
conditions.push(mutationDetailExists(eq(actionMutationDetails.reversesActionId, input.reversesActionId)));
|
|
990
|
+
}
|
|
991
|
+
if (input.reversedByActionId) {
|
|
992
|
+
conditions.push(mutationDetailExists(eq(actionMutationDetails.reversedByActionIdProjection, input.reversedByActionId)));
|
|
993
|
+
}
|
|
994
|
+
if (input.sensitiveReasonCode) {
|
|
995
|
+
conditions.push(sensitiveReadDetailExists(eq(actionSensitiveReadDetails.reasonCode, input.sensitiveReasonCode)));
|
|
996
|
+
}
|
|
997
|
+
if (input.decisionPolicy) {
|
|
998
|
+
conditions.push(sensitiveReadDetailExists(eq(actionSensitiveReadDetails.decisionPolicy, input.decisionPolicy)));
|
|
999
|
+
}
|
|
1000
|
+
if (input.occurredAtFrom) {
|
|
1001
|
+
conditions.push(gte(actionLedgerEntries.occurredAt, parseCursorDate(input.occurredAtFrom)));
|
|
1002
|
+
}
|
|
1003
|
+
if (input.occurredAtTo) {
|
|
1004
|
+
conditions.push(lte(actionLedgerEntries.occurredAt, parseCursorDate(input.occurredAtTo)));
|
|
1005
|
+
}
|
|
1006
|
+
if (input.cursor) {
|
|
1007
|
+
conditions.push(buildCursorCondition(input.cursor));
|
|
1008
|
+
}
|
|
1009
|
+
if (conditions.length === 0)
|
|
1010
|
+
return undefined;
|
|
1011
|
+
if (conditions.length === 1)
|
|
1012
|
+
return conditions[0];
|
|
1013
|
+
return and(...conditions);
|
|
1014
|
+
}
|
|
1015
|
+
export const __test__ = {
|
|
1016
|
+
buildActionApprovalsPredicate,
|
|
1017
|
+
buildActionDelegationsPredicate,
|
|
1018
|
+
buildActionLedgerEntriesPredicate,
|
|
1019
|
+
buildActionLedgerRelayOutboxPredicate,
|
|
1020
|
+
normalizeListLimit,
|
|
1021
|
+
toActionApprovalListCursor,
|
|
1022
|
+
toActionDelegationListCursor,
|
|
1023
|
+
toActionLedgerListCursor,
|
|
1024
|
+
toActionLedgerRelayOutboxListCursor,
|
|
1025
|
+
};
|