@nodii/saga 0.6.0 → 0.7.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/admin-service.d.ts +19 -5
- package/dist/admin-service.d.ts.map +1 -1
- package/dist/admin-service.js +85 -8
- package/dist/admin-service.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +19 -1
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +156 -5
- package/dist/migrations/index.js.map +1 -1
- package/dist/saga.d.ts +40 -0
- package/dist/saga.d.ts.map +1 -1
- package/dist/saga.js +141 -6
- package/dist/saga.js.map +1 -1
- package/dist/state-store/postgres.d.ts +3 -1
- package/dist/state-store/postgres.d.ts.map +1 -1
- package/dist/state-store/postgres.js +32 -9
- package/dist/state-store/postgres.js.map +1 -1
- package/dist/test-doubles/in-memory-state-store.d.ts +3 -1
- package/dist/test-doubles/in-memory-state-store.d.ts.map +1 -1
- package/dist/test-doubles/in-memory-state-store.js +14 -0
- package/dist/test-doubles/in-memory-state-store.js.map +1 -1
- package/dist/types.d.ts +95 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +24 -0
- package/dist/types.js.map +1 -1
- package/package.json +4 -3
- package/src/migrations/001-saga-state.sql +139 -5
package/dist/admin-service.d.ts
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
|
-
import { type SagaAdminServiceOpts, type SagaStateRow } from "./types";
|
|
2
|
-
|
|
1
|
+
import { type SagaAdminAuthorizer, type SagaAdminServiceOpts, type SagaStateRow } from "./types";
|
|
2
|
+
/** Fields every admin RPC carries so the authorizer can gate the call. The
|
|
3
|
+
* caller resolves `actorPermissions` upstream from the verified actor
|
|
4
|
+
* envelope (`@nodii/grpc-auth`) and forwards it on the request. */
|
|
5
|
+
export interface AdminAuthFields {
|
|
6
|
+
/** Caller's effective permission set (resolved upstream). */
|
|
7
|
+
actorPermissions?: readonly string[];
|
|
8
|
+
/** Opaque actor id (audit + row-scoped checks). */
|
|
9
|
+
actorId?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface GetSagaRequest extends AdminAuthFields {
|
|
3
12
|
sagaId: string;
|
|
4
13
|
}
|
|
5
|
-
export interface ListSagasRequest {
|
|
14
|
+
export interface ListSagasRequest extends AdminAuthFields {
|
|
6
15
|
tenantId?: string;
|
|
7
16
|
status?: string;
|
|
8
17
|
type?: string;
|
|
9
18
|
limit?: number;
|
|
10
19
|
}
|
|
11
|
-
export interface WriteRpcRequest {
|
|
20
|
+
export interface WriteRpcRequest extends AdminAuthFields {
|
|
12
21
|
sagaId: string;
|
|
13
22
|
justification: string;
|
|
14
|
-
actorId?: string;
|
|
15
23
|
}
|
|
16
24
|
export interface RetryStepRequest extends WriteRpcRequest {
|
|
17
25
|
stepName: string;
|
|
@@ -51,5 +59,11 @@ export interface SagaAdminServer {
|
|
|
51
59
|
ok: true;
|
|
52
60
|
}>;
|
|
53
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Default admin authorizer (§ 5.10): pure permission-set membership over the
|
|
64
|
+
* locked SAGA_ADMIN_PERMISSIONS. No allow-all fallback (R1) — an op whose
|
|
65
|
+
* required permission is absent from `actorPermissions` is denied.
|
|
66
|
+
*/
|
|
67
|
+
export declare const defaultSagaAdminAuthorizer: SagaAdminAuthorizer;
|
|
54
68
|
export declare function createSagaAdminServer(opts: SagaAdminServiceOpts): SagaAdminServer;
|
|
55
69
|
//# sourceMappingURL=admin-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-service.d.ts","sourceRoot":"","sources":["../src/admin-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"admin-service.d.ts","sourceRoot":"","sources":["../src/admin-service.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,KAAK,mBAAmB,EAExB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EAKlB,MAAM,SAAS,CAAC;AAEjB;;oEAEoE;AACpE,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAe,SAAQ,eAAe;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAuB,SAAQ,eAAe;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,8BAA+B,SAAQ,eAAe;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACrE,SAAS,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC,CAAC;IACrE,SAAS,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACxD,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACtD,eAAe,CAAC,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACpE,uBAAuB,CACrB,GAAG,EAAE,8BAA8B,GAClC,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACzB,UAAU,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;CAC3D;AAED;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,mBACc,CAAC;AAExD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,oBAAoB,GACzB,eAAe,CA6OjB"}
|
package/dist/admin-service.js
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
// SagaAdminService factory — § 5.10.
|
|
2
|
-
// surface; the real gRPC proto + codegen + Pane wiring lands in
|
|
3
|
-
// saga-v0.1.x. Stubs for the 4 write RPCs throw NotImplementedError AFTER
|
|
4
|
-
// validating `justification` — that keeps the audit shape correct in the
|
|
5
|
-
// rejection path.
|
|
1
|
+
// SagaAdminService factory — § 5.10.
|
|
6
2
|
//
|
|
7
|
-
//
|
|
8
|
-
// pluggable
|
|
9
|
-
|
|
3
|
+
// Every RPC is gated on the locked SAGA_ADMIN_PERMISSIONS (§ 5.10) via the
|
|
4
|
+
// pluggable SagaAdminAuthorizer (default = permission-set membership over
|
|
5
|
+
// the caller's `actorPermissions`). Administrative saga operations are
|
|
6
|
+
// privileged — force-compensate / cancel / retry / skip / manual-resolve
|
|
7
|
+
// directly mutate saga state outside the normal step flow — so an
|
|
8
|
+
// unauthorized caller is rejected with SagaAdminUnauthorized BEFORE any
|
|
9
|
+
// state write, and an `*_unauthorized` audit row records the denied attempt.
|
|
10
|
+
//
|
|
11
|
+
// Read RPCs (`GetSaga`, `ListSagas`) are gated on READ; the 5 write RPCs map
|
|
12
|
+
// to RETRY / SKIP / COMPENSATE / MANUAL_RESOLVE per the locked permission
|
|
13
|
+
// catalog. Write RPCs additionally validate `justification` (min 20 chars).
|
|
14
|
+
import { JustificationRequired, MIN_JUSTIFICATION_CHARS, SAGA_ADMIN_PERMISSIONS, SagaAdminUnauthorized, } from "./types";
|
|
15
|
+
/**
|
|
16
|
+
* Default admin authorizer (§ 5.10): pure permission-set membership over the
|
|
17
|
+
* locked SAGA_ADMIN_PERMISSIONS. No allow-all fallback (R1) — an op whose
|
|
18
|
+
* required permission is absent from `actorPermissions` is denied.
|
|
19
|
+
*/
|
|
20
|
+
export const defaultSagaAdminAuthorizer = (ctx) => ctx.actorPermissions.includes(ctx.requiredPermission);
|
|
10
21
|
export function createSagaAdminServer(opts) {
|
|
11
22
|
const { stateStore, audit } = opts;
|
|
23
|
+
const authorizer = opts.authorizer ?? defaultSagaAdminAuthorizer;
|
|
12
24
|
const validateJustification = (justification) => {
|
|
13
25
|
if (typeof justification !== "string" ||
|
|
14
26
|
justification.length < MIN_JUSTIFICATION_CHARS) {
|
|
@@ -25,13 +37,45 @@ export function createSagaAdminServer(opts) {
|
|
|
25
37
|
payload,
|
|
26
38
|
});
|
|
27
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* Authorize an admin op against its locked permission. Emits an
|
|
42
|
+
* `*_unauthorized` audit row and throws SagaAdminUnauthorized on denial,
|
|
43
|
+
* BEFORE any state mutation. The authorizer is async-tolerant so a remote
|
|
44
|
+
* RBAC resolver can be plugged in.
|
|
45
|
+
*/
|
|
46
|
+
const authorize = async (method, requiredPermission, sagaId, fields) => {
|
|
47
|
+
const ctx = {
|
|
48
|
+
method,
|
|
49
|
+
requiredPermission,
|
|
50
|
+
sagaId,
|
|
51
|
+
actorPermissions: fields.actorPermissions ?? [],
|
|
52
|
+
actorId: fields.actorId,
|
|
53
|
+
};
|
|
54
|
+
let allowed;
|
|
55
|
+
try {
|
|
56
|
+
allowed = await authorizer(ctx);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// A throwing authorizer is treated as a hard denial (fail-closed, R1).
|
|
60
|
+
allowed = false;
|
|
61
|
+
}
|
|
62
|
+
if (!allowed) {
|
|
63
|
+
await emitAdminAudit(`${auditActionFor(method)}_unauthorized`, sagaId, {
|
|
64
|
+
required_permission: requiredPermission,
|
|
65
|
+
actor_id: fields.actorId ?? null,
|
|
66
|
+
});
|
|
67
|
+
throw new SagaAdminUnauthorized(method, requiredPermission);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
28
70
|
return {
|
|
29
71
|
async GetSaga(req) {
|
|
72
|
+
await authorize("GetSaga", SAGA_ADMIN_PERMISSIONS.READ, req.sagaId, req);
|
|
30
73
|
await emitAdminAudit("saga_admin_get_saga", req.sagaId);
|
|
31
74
|
const saga = await stateStore.getSaga(req.sagaId);
|
|
32
75
|
return { saga };
|
|
33
76
|
},
|
|
34
77
|
async ListSagas(req) {
|
|
78
|
+
await authorize("ListSagas", SAGA_ADMIN_PERMISSIONS.READ, req.tenantId ?? "*", req);
|
|
35
79
|
await emitAdminAudit("saga_admin_list_sagas", req.tenantId ?? "*");
|
|
36
80
|
// Per § 5.5 list() is optional on SagaStateStore — stores that omit it
|
|
37
81
|
// return []. Production-bound stores (Drizzle) MUST implement it.
|
|
@@ -46,6 +90,7 @@ export function createSagaAdminServer(opts) {
|
|
|
46
90
|
return { sagas: rows.slice(0, limit) };
|
|
47
91
|
},
|
|
48
92
|
async RetryStep(req) {
|
|
93
|
+
await authorize("RetryStep", SAGA_ADMIN_PERMISSIONS.RETRY, req.sagaId, req);
|
|
49
94
|
validateJustification(req.justification);
|
|
50
95
|
await emitAdminAudit("saga_admin_retry_step", req.sagaId, {
|
|
51
96
|
step: req.stepName,
|
|
@@ -66,6 +111,7 @@ export function createSagaAdminServer(opts) {
|
|
|
66
111
|
return { ok: true };
|
|
67
112
|
},
|
|
68
113
|
async SkipStep(req) {
|
|
114
|
+
await authorize("SkipStep", SAGA_ADMIN_PERMISSIONS.SKIP, req.sagaId, req);
|
|
69
115
|
validateJustification(req.justification);
|
|
70
116
|
await emitAdminAudit("saga_admin_skip_step", req.sagaId, {
|
|
71
117
|
step: req.stepName,
|
|
@@ -92,6 +138,7 @@ export function createSagaAdminServer(opts) {
|
|
|
92
138
|
return { ok: true };
|
|
93
139
|
},
|
|
94
140
|
async ForceCompensate(req) {
|
|
141
|
+
await authorize("ForceCompensate", SAGA_ADMIN_PERMISSIONS.COMPENSATE, req.sagaId, req);
|
|
95
142
|
validateJustification(req.justification);
|
|
96
143
|
await emitAdminAudit("saga_admin_force_compensate", req.sagaId, {
|
|
97
144
|
from_step: req.fromStep,
|
|
@@ -111,6 +158,7 @@ export function createSagaAdminServer(opts) {
|
|
|
111
158
|
return { ok: true };
|
|
112
159
|
},
|
|
113
160
|
async MarkManuallyCompensated(req) {
|
|
161
|
+
await authorize("MarkManuallyCompensated", SAGA_ADMIN_PERMISSIONS.MANUAL_RESOLVE, req.sagaId, req);
|
|
114
162
|
validateJustification(req.justification);
|
|
115
163
|
await emitAdminAudit("saga_admin_mark_manually_compensated", req.sagaId, {
|
|
116
164
|
resolution_ref: req.resolutionRef,
|
|
@@ -141,6 +189,10 @@ export function createSagaAdminServer(opts) {
|
|
|
141
189
|
return { ok: true };
|
|
142
190
|
},
|
|
143
191
|
async CancelSaga(req) {
|
|
192
|
+
// Cancel is a terminal compensate-class admin op; the locked
|
|
193
|
+
// permission catalog (§ 5.10) has no dedicated CANCEL string, so it
|
|
194
|
+
// is gated on COMPENSATE (same privilege tier as force-compensate).
|
|
195
|
+
await authorize("CancelSaga", SAGA_ADMIN_PERMISSIONS.COMPENSATE, req.sagaId, req);
|
|
144
196
|
validateJustification(req.justification);
|
|
145
197
|
await emitAdminAudit("saga_admin_cancel", req.sagaId, {
|
|
146
198
|
reason: req.reason,
|
|
@@ -158,4 +210,29 @@ export function createSagaAdminServer(opts) {
|
|
|
158
210
|
},
|
|
159
211
|
};
|
|
160
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Map an RPC method name → its audit-action stem, so the unauthorized
|
|
215
|
+
* audit row reuses the same `saga_admin_*` family as the success path
|
|
216
|
+
* (e.g. `RetryStep` → `saga_admin_retry_step` → `saga_admin_retry_step_unauthorized`).
|
|
217
|
+
*/
|
|
218
|
+
function auditActionFor(method) {
|
|
219
|
+
switch (method) {
|
|
220
|
+
case "GetSaga":
|
|
221
|
+
return "saga_admin_get_saga";
|
|
222
|
+
case "ListSagas":
|
|
223
|
+
return "saga_admin_list_sagas";
|
|
224
|
+
case "RetryStep":
|
|
225
|
+
return "saga_admin_retry_step";
|
|
226
|
+
case "SkipStep":
|
|
227
|
+
return "saga_admin_skip_step";
|
|
228
|
+
case "ForceCompensate":
|
|
229
|
+
return "saga_admin_force_compensate";
|
|
230
|
+
case "MarkManuallyCompensated":
|
|
231
|
+
return "saga_admin_mark_manually_compensated";
|
|
232
|
+
case "CancelSaga":
|
|
233
|
+
return "saga_admin_cancel";
|
|
234
|
+
default:
|
|
235
|
+
return `saga_admin_${method.toLowerCase()}`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
161
238
|
//# sourceMappingURL=admin-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-service.js","sourceRoot":"","sources":["../src/admin-service.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"admin-service.js","sourceRoot":"","sources":["../src/admin-service.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ export { emitSagaSignal, awaitSagaSignal, awaitChildren, awaitAnySibling, getSib
|
|
|
8
8
|
export { sagaCallable, SAGA_CALLABLE_METADATA, readSagaCallableConfig, readSagaCallableRegistry, _resetDecoratorRegistryForTests, } from "./decorator";
|
|
9
9
|
export type { SagaCallableMethod } from "./decorator";
|
|
10
10
|
export { sagaContext } from "./interceptor";
|
|
11
|
-
export { createSagaAdminServer } from "./admin-service";
|
|
12
|
-
export type { CancelSagaRequest, ForceCompensateRequest, GetSagaRequest, ListSagasRequest, MarkManuallyCompensatedRequest, RetryStepRequest, SagaAdminServer, SkipStepRequest, WriteRpcRequest, } from "./admin-service";
|
|
11
|
+
export { createSagaAdminServer, defaultSagaAdminAuthorizer, } from "./admin-service";
|
|
12
|
+
export type { AdminAuthFields, CancelSagaRequest, ForceCompensateRequest, GetSagaRequest, ListSagasRequest, MarkManuallyCompensatedRequest, RetryStepRequest, SagaAdminServer, SkipStepRequest, WriteRpcRequest, } from "./admin-service";
|
|
13
13
|
export { PostgresSagaStateStore, type PostgresSagaStateStoreOpts, } from "./state-store/postgres";
|
|
14
14
|
export { RedisSignalBus, type RedisSignalBusOpts, } from "./signal-bus/redis-stream";
|
|
15
15
|
export { beginAsyncStep, completeAsyncStep, getCompletedAsyncOutput, SagaPausedSentinel, } from "./async-step";
|
|
@@ -19,6 +19,8 @@ export { createParticipantWorker, ParticipantOutboxTableInvalid, ParticipantWork
|
|
|
19
19
|
export type { CreateParticipantWorkerOpts, ParticipantBeginEvent, ParticipantHandler, ParticipantHandlerCtx, ParticipantSqlClient, ParticipantSqlTx, ParticipantWorkerHandle, } from "./participant-worker";
|
|
20
20
|
export { applySagaMigrations, getSagaStateMigrationSQL } from "./migrations";
|
|
21
21
|
export { uuidv7, isUuidv7 } from "./uuid";
|
|
22
|
-
export { MIN_JUSTIFICATION_CHARS, SAGA_ADMIN_PERMISSIONS, SagaError, SagaIdempotencyRequired, SagaNotInitialized, SagaContextRequired, SagaStepFailed, SagaCompensationFailed, JustificationRequired, SagaTypeNotRegistered, NoActiveSaga, NotImplementedError, } from "./types";
|
|
23
|
-
export type { CompensationLogEntry, CreateSagaOpts, IdempotencyShim, ResumeContext, SagaAdminPermission, SagaAdminServiceOpts, SagaCallableConfig, SagaContextInterceptorOpts, SagaHandle, SagaInterceptorCall, SagaSignalBus, SagaStateRow, SagaStateStore, SagaStatus, StepOpts, TelemetryAuditEmitter, } from "./types";
|
|
22
|
+
export { MIN_JUSTIFICATION_CHARS, SAGA_ADMIN_PERMISSIONS, SagaError, SagaIdempotencyRequired, SagaNotInitialized, SagaContextRequired, SagaStepFailed, SagaCompensationFailed, JustificationRequired, SagaAdminUnauthorized, SagaTypeNotRegistered, NoActiveSaga, NotImplementedError, } from "./types";
|
|
23
|
+
export type { CompensationLogEntry, CreateSagaOpts, IdempotencyShim, ResumeContext, SagaAdminAuthContext, SagaAdminAuthorizer, SagaAdminPermission, SagaAdminServiceOpts, SagaCallableConfig, SagaContextInterceptorOpts, SagaHandle, SagaInterceptorCall, SagaSignalBus, SagaStateRow, SagaStateStore, SagaStatus, StepOpts, TelemetryAuditEmitter, UndoStackEntry, } from "./types";
|
|
24
|
+
export { recoverUndoStack } from "./saga";
|
|
25
|
+
export type { UndoResolver, RecoverUndoStackResult } from "./saga";
|
|
24
26
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,QAAQ,SAAS,CAAC;AAC/B,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACjE,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjE,OAAO,EACL,UAAU,EACV,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,WAAW,GACZ,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,eAAe,EACf,aAAa,EACb,eAAe,EACf,WAAW,GACZ,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,QAAQ,SAAS,CAAC;AAC/B,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACjE,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjE,OAAO,EACL,UAAU,EACV,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,WAAW,GACZ,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,eAAe,EACf,aAAa,EACb,eAAe,EACf,WAAW,GACZ,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAChB,8BAA8B,EAC9B,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,sBAAsB,EACtB,KAAK,0BAA0B,GAChC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,cAAc,EACd,KAAK,kBAAkB,GACxB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGjE,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,2BAA2B,EAC3B,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE1C,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EAEtB,SAAS,EACT,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,EACZ,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,0BAA0B,EAC1B,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,UAAU,EACV,QAAQ,EACR,qBAAqB,EACrB,cAAc,GACf,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ export { registerSaga, getRegisteredSaga, hasRegisteredSaga, _resetRegistryForTe
|
|
|
21
21
|
export { emitSagaSignal, awaitSagaSignal, awaitChildren, awaitAnySibling, getSiblings, } from "./signals";
|
|
22
22
|
export { sagaCallable, SAGA_CALLABLE_METADATA, readSagaCallableConfig, readSagaCallableRegistry, _resetDecoratorRegistryForTests, } from "./decorator";
|
|
23
23
|
export { sagaContext } from "./interceptor";
|
|
24
|
-
export { createSagaAdminServer } from "./admin-service";
|
|
24
|
+
export { createSagaAdminServer, defaultSagaAdminAuthorizer, } from "./admin-service";
|
|
25
25
|
// Production-bound state-store + signal-bus.
|
|
26
26
|
export { PostgresSagaStateStore, } from "./state-store/postgres";
|
|
27
27
|
export { RedisSignalBus, } from "./signal-bus/redis-stream";
|
|
@@ -35,5 +35,6 @@ export { applySagaMigrations, getSagaStateMigrationSQL } from "./migrations";
|
|
|
35
35
|
export { uuidv7, isUuidv7 } from "./uuid";
|
|
36
36
|
export { MIN_JUSTIFICATION_CHARS, SAGA_ADMIN_PERMISSIONS,
|
|
37
37
|
// Typed-error hierarchy.
|
|
38
|
-
SagaError, SagaIdempotencyRequired, SagaNotInitialized, SagaContextRequired, SagaStepFailed, SagaCompensationFailed, JustificationRequired, SagaTypeNotRegistered, NoActiveSaga, NotImplementedError, } from "./types";
|
|
38
|
+
SagaError, SagaIdempotencyRequired, SagaNotInitialized, SagaContextRequired, SagaStepFailed, SagaCompensationFailed, JustificationRequired, SagaAdminUnauthorized, SagaTypeNotRegistered, NoActiveSaga, NotImplementedError, } from "./types";
|
|
39
|
+
export { recoverUndoStack } from "./saga";
|
|
39
40
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,mEAAmE;AACnE,6CAA6C;AAC7C,EAAE;AACF,2CAA2C;AAC3C,uEAAuE;AACvE,+EAA+E;AAC/E,sDAAsD;AACtD,mCAAmC;AACnC,0EAA0E;AAC1E,4EAA4E;AAC5E,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAEhF,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC;AAC/B,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAGjE,OAAO,EACL,UAAU,EACV,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,WAAW,GACZ,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,eAAe,EACf,aAAa,EACb,eAAe,EACf,WAAW,GACZ,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,mEAAmE;AACnE,6CAA6C;AAC7C,EAAE;AACF,2CAA2C;AAC3C,uEAAuE;AACvE,+EAA+E;AAC/E,sDAAsD;AACtD,mCAAmC;AACnC,0EAA0E;AAC1E,4EAA4E;AAC5E,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAEhF,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC;AAC/B,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAGjE,OAAO,EACL,UAAU,EACV,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,WAAW,GACZ,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,eAAe,EACf,aAAa,EACb,eAAe,EACf,WAAW,GACZ,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,iBAAiB,CAAC;AAczB,6CAA6C;AAC7C,OAAO,EACL,sBAAsB,GAEvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,cAAc,GAEf,MAAM,2BAA2B,CAAC;AAEnC,iCAAiC;AACjC,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAG3C,uDAAuD;AACvD,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAW9B,cAAc;AACd,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE1C,OAAO,EACL,uBAAuB,EACvB,sBAAsB;AACtB,yBAAyB;AACzB,SAAS,EACT,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,EACZ,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAwBjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
type SqlClient = {
|
|
2
2
|
unsafe: (sql: string, args?: unknown[]) => Promise<any>;
|
|
3
3
|
};
|
|
4
|
+
/**
|
|
5
|
+
* Return the canonical A1 RLS DDL for `saga_state`. Returns the inlined
|
|
6
|
+
* constant verbatim — no runtime `@nodii/db-rls` call (see `SAGA_STATE_RLS_DDL`).
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateSagaStateRlsDdl(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Return the canonical A1 RLS DDL for `saga_outbox`. Inlined constant verbatim —
|
|
11
|
+
* no runtime `@nodii/db-rls` call (see `SAGA_OUTBOX_RLS_DDL`).
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateSagaOutboxRlsDdl(): string;
|
|
4
14
|
export declare function getSagaStateMigrationSQL(): string;
|
|
5
15
|
/**
|
|
6
16
|
* Apply the saga_state + saga_outbox migrations idempotently. Safe to call
|
|
@@ -13,7 +23,15 @@ export declare function getSagaStateMigrationSQL(): string;
|
|
|
13
23
|
* whitespace blocks are skipped.
|
|
14
24
|
*/
|
|
15
25
|
export declare function applySagaMigrations(sql: SqlClient): Promise<void>;
|
|
16
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Internal — exported only for unit-test coverage on the splitter.
|
|
28
|
+
*
|
|
29
|
+
* Dollar-quote-aware: a `$$`-delimited body (e.g. the D412 `DO $$ ... $$;`
|
|
30
|
+
* guarded TEXT→UUID migration) may contain inner lines ending in `;` that
|
|
31
|
+
* must NOT terminate the statement. We track an open `$tag$` ... `$tag$`
|
|
32
|
+
* span and only honor a trailing `;` as a statement boundary when no dollar
|
|
33
|
+
* quote is open. (postgres.js sends each split statement via `.unsafe`.)
|
|
34
|
+
*/
|
|
17
35
|
export declare function splitStatements(ddl: string): string[];
|
|
18
36
|
export {};
|
|
19
37
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAOA,KAAK,SAAS,GAAG;IAEf,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACzD,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAOA,KAAK,SAAS,GAAG;IAEf,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACzD,CAAC;AA8CF;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AA8CD;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAgGD,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAMvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBrD"}
|
package/dist/migrations/index.js
CHANGED
|
@@ -4,6 +4,106 @@
|
|
|
4
4
|
// per-service migration runner. The SQL is shipped alongside this module
|
|
5
5
|
// as `001-saga-state.sql` so a service that uses a different runner
|
|
6
6
|
// (Flyway, golang-migrate, alembic, etc.) can copy it directly.
|
|
7
|
+
/**
|
|
8
|
+
* Canonical A1 RLS DDL for `saga_state` (D412 / 08-rls v7 § 4) — INLINED as a
|
|
9
|
+
* string constant so the runtime + published `@nodii/saga` package carry NO
|
|
10
|
+
* dependency on `@nodii/db-rls` (a runtime/peer dep would pull the whole RLS
|
|
11
|
+
* pooling + migrate-gen surface into saga and trigger a version cascade).
|
|
12
|
+
*
|
|
13
|
+
* saga_state does full CRUD, so it carries all four verbs: 1 owner policy + 4
|
|
14
|
+
* authenticated policies + 4 services policies, plus the per-role GRANTs.
|
|
15
|
+
* Postgres has no `CREATE POLICY IF NOT EXISTS`, so each `CREATE POLICY` is
|
|
16
|
+
* preceded by a matching `DROP POLICY IF EXISTS ... ;` to keep the migration
|
|
17
|
+
* idempotent. ENABLE + GRANT are already idempotent.
|
|
18
|
+
*
|
|
19
|
+
* This block is byte-equal to `@nodii/db-rls`'s
|
|
20
|
+
* `withRls("saga_state", { allow: ["SELECT","INSERT","UPDATE","DELETE"] })` +
|
|
21
|
+
* `generateTableGrants(...)` output (with the DROP-before-CREATE
|
|
22
|
+
* interleaving + grants reordered after ENABLE). The byte-equality is asserted
|
|
23
|
+
* in `tests/rls.integration.test.ts` against the live db-rls generators
|
|
24
|
+
* (db-rls is a DEV/test-only dependency that guards canonical drift — it is
|
|
25
|
+
* NEVER imported by shipped saga source). The vendored copy in
|
|
26
|
+
* `001-saga-state.sql` is byte-equal to this constant (sans the DO-block ALTER
|
|
27
|
+
* which lives above the table DDL there).
|
|
28
|
+
*/
|
|
29
|
+
const SAGA_STATE_RLS_DDL = `ALTER TABLE saga_state ENABLE ROW LEVEL SECURITY;
|
|
30
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON saga_state TO nodii_services;
|
|
31
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON saga_state TO authenticated;
|
|
32
|
+
DROP POLICY IF EXISTS saga_state_owner_policy ON saga_state;
|
|
33
|
+
CREATE POLICY saga_state_owner_policy ON saga_state FOR ALL TO nodii_owner USING (true) WITH CHECK (true);
|
|
34
|
+
DROP POLICY IF EXISTS saga_state_auth_select_policy ON saga_state;
|
|
35
|
+
CREATE POLICY saga_state_auth_select_policy ON saga_state FOR SELECT TO authenticated USING (tenant_id IN (SELECT auth.user_tenant_ids(auth.get_user_id())));
|
|
36
|
+
DROP POLICY IF EXISTS saga_state_auth_insert_policy ON saga_state;
|
|
37
|
+
CREATE POLICY saga_state_auth_insert_policy ON saga_state FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT auth.user_tenant_ids(auth.get_user_id())));
|
|
38
|
+
DROP POLICY IF EXISTS saga_state_auth_update_policy ON saga_state;
|
|
39
|
+
CREATE POLICY saga_state_auth_update_policy ON saga_state FOR UPDATE TO authenticated USING (tenant_id IN (SELECT auth.user_tenant_ids(auth.get_user_id()))) WITH CHECK (tenant_id IN (SELECT auth.user_tenant_ids(auth.get_user_id())));
|
|
40
|
+
DROP POLICY IF EXISTS saga_state_auth_delete_policy ON saga_state;
|
|
41
|
+
CREATE POLICY saga_state_auth_delete_policy ON saga_state FOR DELETE TO authenticated USING (tenant_id IN (SELECT auth.user_tenant_ids(auth.get_user_id())));
|
|
42
|
+
DROP POLICY IF EXISTS saga_state_svc_select_policy ON saga_state;
|
|
43
|
+
CREATE POLICY saga_state_svc_select_policy ON saga_state FOR SELECT TO nodii_services USING (tenant_id = auth.get_tenant_id());
|
|
44
|
+
DROP POLICY IF EXISTS saga_state_svc_insert_policy ON saga_state;
|
|
45
|
+
CREATE POLICY saga_state_svc_insert_policy ON saga_state FOR INSERT TO nodii_services WITH CHECK (tenant_id = auth.get_tenant_id());
|
|
46
|
+
DROP POLICY IF EXISTS saga_state_svc_update_policy ON saga_state;
|
|
47
|
+
CREATE POLICY saga_state_svc_update_policy ON saga_state FOR UPDATE TO nodii_services USING (tenant_id = auth.get_tenant_id()) WITH CHECK (tenant_id = auth.get_tenant_id());
|
|
48
|
+
DROP POLICY IF EXISTS saga_state_svc_delete_policy ON saga_state;
|
|
49
|
+
CREATE POLICY saga_state_svc_delete_policy ON saga_state FOR DELETE TO nodii_services USING (tenant_id = auth.get_tenant_id());`;
|
|
50
|
+
/**
|
|
51
|
+
* Return the canonical A1 RLS DDL for `saga_state`. Returns the inlined
|
|
52
|
+
* constant verbatim — no runtime `@nodii/db-rls` call (see `SAGA_STATE_RLS_DDL`).
|
|
53
|
+
*/
|
|
54
|
+
export function generateSagaStateRlsDdl() {
|
|
55
|
+
return SAGA_STATE_RLS_DDL;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Canonical A1 RLS DDL for `saga_outbox` (D412 / 08-rls v7 § 1.1 — "every table
|
|
59
|
+
* with a `tenant_id` column gets the 5-policy A1 RLS, no exceptions"). The
|
|
60
|
+
* Pattern-2 outbox is tenant-scoped (every begin/completion row carries the
|
|
61
|
+
* saga's `tenant_id`) and `readOutboxForSaga` filters by `saga_id` ONLY — so RLS
|
|
62
|
+
* is the sole tenant-isolation boundary on this table, exactly as for
|
|
63
|
+
* `saga_state`.
|
|
64
|
+
*
|
|
65
|
+
* Identical structure to `SAGA_STATE_RLS_DDL` (1 owner + 4 authenticated + 4
|
|
66
|
+
* services policies + per-role GRANTs), retargeted to `saga_outbox`; byte-equal
|
|
67
|
+
* to `withRls("saga_outbox", { allow: [...] })` + `generateTableGrants(...)`,
|
|
68
|
+
* asserted in `tests/rls.integration.test.ts`.
|
|
69
|
+
*
|
|
70
|
+
* `saga_outbox.tenant_id` is `UUID` but — unlike `saga_state` — intentionally
|
|
71
|
+
* NULLABLE: the participant-worker writes a NULL tenant for tenant-less sagas
|
|
72
|
+
* (participant-worker.ts) and `createSaga` permits an absent tenant. Under these
|
|
73
|
+
* policies a NULL-tenant row is invisible to every non-owner role
|
|
74
|
+
* (`NULL = auth.get_tenant_id()` and `NULL IN (...)` are both NULL/false), i.e.
|
|
75
|
+
* owner-only — a safe, non-leaking outcome. A1 mandates the 5-policy RLS on
|
|
76
|
+
* every `tenant_id` table; it does NOT mandate NOT NULL (that is a `saga_state`
|
|
77
|
+
* choice), so this is fully A1-conformant.
|
|
78
|
+
*/
|
|
79
|
+
const SAGA_OUTBOX_RLS_DDL = `ALTER TABLE saga_outbox ENABLE ROW LEVEL SECURITY;
|
|
80
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON saga_outbox TO nodii_services;
|
|
81
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON saga_outbox TO authenticated;
|
|
82
|
+
DROP POLICY IF EXISTS saga_outbox_owner_policy ON saga_outbox;
|
|
83
|
+
CREATE POLICY saga_outbox_owner_policy ON saga_outbox FOR ALL TO nodii_owner USING (true) WITH CHECK (true);
|
|
84
|
+
DROP POLICY IF EXISTS saga_outbox_auth_select_policy ON saga_outbox;
|
|
85
|
+
CREATE POLICY saga_outbox_auth_select_policy ON saga_outbox FOR SELECT TO authenticated USING (tenant_id IN (SELECT auth.user_tenant_ids(auth.get_user_id())));
|
|
86
|
+
DROP POLICY IF EXISTS saga_outbox_auth_insert_policy ON saga_outbox;
|
|
87
|
+
CREATE POLICY saga_outbox_auth_insert_policy ON saga_outbox FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT auth.user_tenant_ids(auth.get_user_id())));
|
|
88
|
+
DROP POLICY IF EXISTS saga_outbox_auth_update_policy ON saga_outbox;
|
|
89
|
+
CREATE POLICY saga_outbox_auth_update_policy ON saga_outbox FOR UPDATE TO authenticated USING (tenant_id IN (SELECT auth.user_tenant_ids(auth.get_user_id()))) WITH CHECK (tenant_id IN (SELECT auth.user_tenant_ids(auth.get_user_id())));
|
|
90
|
+
DROP POLICY IF EXISTS saga_outbox_auth_delete_policy ON saga_outbox;
|
|
91
|
+
CREATE POLICY saga_outbox_auth_delete_policy ON saga_outbox FOR DELETE TO authenticated USING (tenant_id IN (SELECT auth.user_tenant_ids(auth.get_user_id())));
|
|
92
|
+
DROP POLICY IF EXISTS saga_outbox_svc_select_policy ON saga_outbox;
|
|
93
|
+
CREATE POLICY saga_outbox_svc_select_policy ON saga_outbox FOR SELECT TO nodii_services USING (tenant_id = auth.get_tenant_id());
|
|
94
|
+
DROP POLICY IF EXISTS saga_outbox_svc_insert_policy ON saga_outbox;
|
|
95
|
+
CREATE POLICY saga_outbox_svc_insert_policy ON saga_outbox FOR INSERT TO nodii_services WITH CHECK (tenant_id = auth.get_tenant_id());
|
|
96
|
+
DROP POLICY IF EXISTS saga_outbox_svc_update_policy ON saga_outbox;
|
|
97
|
+
CREATE POLICY saga_outbox_svc_update_policy ON saga_outbox FOR UPDATE TO nodii_services USING (tenant_id = auth.get_tenant_id()) WITH CHECK (tenant_id = auth.get_tenant_id());
|
|
98
|
+
DROP POLICY IF EXISTS saga_outbox_svc_delete_policy ON saga_outbox;
|
|
99
|
+
CREATE POLICY saga_outbox_svc_delete_policy ON saga_outbox FOR DELETE TO nodii_services USING (tenant_id = auth.get_tenant_id());`;
|
|
100
|
+
/**
|
|
101
|
+
* Return the canonical A1 RLS DDL for `saga_outbox`. Inlined constant verbatim —
|
|
102
|
+
* no runtime `@nodii/db-rls` call (see `SAGA_OUTBOX_RLS_DDL`).
|
|
103
|
+
*/
|
|
104
|
+
export function generateSagaOutboxRlsDdl() {
|
|
105
|
+
return SAGA_OUTBOX_RLS_DDL;
|
|
106
|
+
}
|
|
7
107
|
/**
|
|
8
108
|
* Canonical saga_state + saga_outbox migration. Kept inline here (NOT
|
|
9
109
|
* read from disk at runtime) so the published artifact + the synthetic-
|
|
@@ -15,7 +115,7 @@
|
|
|
15
115
|
const SAGA_STATE_MIGRATION_SQL = `
|
|
16
116
|
CREATE TABLE IF NOT EXISTS saga_state (
|
|
17
117
|
id TEXT PRIMARY KEY,
|
|
18
|
-
tenant_id
|
|
118
|
+
tenant_id UUID NOT NULL,
|
|
19
119
|
type TEXT NOT NULL,
|
|
20
120
|
status TEXT NOT NULL,
|
|
21
121
|
current_step TEXT,
|
|
@@ -34,6 +134,7 @@ CREATE TABLE IF NOT EXISTS saga_state (
|
|
|
34
134
|
failure_reason TEXT,
|
|
35
135
|
failure_step TEXT,
|
|
36
136
|
compensation_log JSONB NOT NULL DEFAULT '[]'::jsonb,
|
|
137
|
+
undo_stack JSONB NOT NULL DEFAULT '[]'::jsonb,
|
|
37
138
|
last_admin_action TEXT,
|
|
38
139
|
last_admin_actor_id TEXT,
|
|
39
140
|
last_admin_at TIMESTAMPTZ,
|
|
@@ -41,6 +142,21 @@ CREATE TABLE IF NOT EXISTS saga_state (
|
|
|
41
142
|
cancel_reason TEXT
|
|
42
143
|
);
|
|
43
144
|
ALTER TABLE saga_state ADD COLUMN IF NOT EXISTS reaper_grace_ms BIGINT;
|
|
145
|
+
ALTER TABLE saga_state ADD COLUMN IF NOT EXISTS undo_stack JSONB NOT NULL DEFAULT '[]'::jsonb;
|
|
146
|
+
DO $$
|
|
147
|
+
BEGIN
|
|
148
|
+
IF EXISTS (
|
|
149
|
+
SELECT 1 FROM information_schema.columns
|
|
150
|
+
WHERE table_schema = current_schema()
|
|
151
|
+
AND table_name = 'saga_state'
|
|
152
|
+
AND column_name = 'tenant_id'
|
|
153
|
+
AND data_type <> 'uuid'
|
|
154
|
+
) THEN
|
|
155
|
+
EXECUTE 'ALTER TABLE saga_state ALTER COLUMN tenant_id TYPE uuid USING tenant_id::uuid';
|
|
156
|
+
END IF;
|
|
157
|
+
END
|
|
158
|
+
$$;
|
|
159
|
+
ALTER TABLE saga_state ALTER COLUMN tenant_id SET NOT NULL;
|
|
44
160
|
CREATE UNIQUE INDEX IF NOT EXISTS saga_state_trigger_trace_id_uniq
|
|
45
161
|
ON saga_state (trigger_trace_id);
|
|
46
162
|
CREATE INDEX IF NOT EXISTS saga_state_dead_letter_idx
|
|
@@ -57,13 +173,29 @@ CREATE TABLE IF NOT EXISTS saga_outbox (
|
|
|
57
173
|
step_name TEXT NOT NULL,
|
|
58
174
|
event_type TEXT NOT NULL,
|
|
59
175
|
payload JSONB NOT NULL,
|
|
60
|
-
tenant_id
|
|
176
|
+
tenant_id UUID,
|
|
61
177
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
62
178
|
delivered_at TIMESTAMPTZ
|
|
63
179
|
);
|
|
64
180
|
CREATE INDEX IF NOT EXISTS saga_outbox_saga_idx ON saga_outbox (saga_id, created_at);
|
|
65
181
|
CREATE INDEX IF NOT EXISTS saga_outbox_undelivered_idx ON saga_outbox (delivered_at)
|
|
66
182
|
WHERE delivered_at IS NULL;
|
|
183
|
+
DO $$
|
|
184
|
+
BEGIN
|
|
185
|
+
IF EXISTS (
|
|
186
|
+
SELECT 1 FROM information_schema.columns
|
|
187
|
+
WHERE table_schema = current_schema()
|
|
188
|
+
AND table_name = 'saga_outbox'
|
|
189
|
+
AND column_name = 'tenant_id'
|
|
190
|
+
AND data_type <> 'uuid'
|
|
191
|
+
) THEN
|
|
192
|
+
EXECUTE 'ALTER TABLE saga_outbox ALTER COLUMN tenant_id TYPE uuid USING tenant_id::uuid';
|
|
193
|
+
EXECUTE 'UPDATE saga_outbox o SET tenant_id = s.tenant_id FROM saga_state s WHERE o.saga_id = s.id AND o.tenant_id IS NULL';
|
|
194
|
+
END IF;
|
|
195
|
+
END
|
|
196
|
+
$$;
|
|
197
|
+
${generateSagaStateRlsDdl()}
|
|
198
|
+
${generateSagaOutboxRlsDdl()}
|
|
67
199
|
`.trim();
|
|
68
200
|
export function getSagaStateMigrationSQL() {
|
|
69
201
|
return SAGA_STATE_MIGRATION_SQL;
|
|
@@ -85,16 +217,35 @@ export async function applySagaMigrations(sql) {
|
|
|
85
217
|
await sql.unsafe(stmt);
|
|
86
218
|
}
|
|
87
219
|
}
|
|
88
|
-
/**
|
|
220
|
+
/**
|
|
221
|
+
* Internal — exported only for unit-test coverage on the splitter.
|
|
222
|
+
*
|
|
223
|
+
* Dollar-quote-aware: a `$$`-delimited body (e.g. the D412 `DO $$ ... $$;`
|
|
224
|
+
* guarded TEXT→UUID migration) may contain inner lines ending in `;` that
|
|
225
|
+
* must NOT terminate the statement. We track an open `$tag$` ... `$tag$`
|
|
226
|
+
* span and only honor a trailing `;` as a statement boundary when no dollar
|
|
227
|
+
* quote is open. (postgres.js sends each split statement via `.unsafe`.)
|
|
228
|
+
*/
|
|
89
229
|
export function splitStatements(ddl) {
|
|
90
230
|
const out = [];
|
|
91
231
|
let buf = "";
|
|
232
|
+
let openTag = null;
|
|
92
233
|
for (const rawLine of ddl.split("\n")) {
|
|
93
234
|
const line = rawLine.trim();
|
|
94
|
-
|
|
235
|
+
// Skip comment-only / blank lines, but only when we're not inside a
|
|
236
|
+
// dollar-quoted body (a `--` inside `$$` is body text, not a comment).
|
|
237
|
+
if (openTag === null && (!line || line.startsWith("--")))
|
|
95
238
|
continue;
|
|
96
239
|
buf = buf ? `${buf}\n${rawLine}` : rawLine;
|
|
97
|
-
|
|
240
|
+
// Toggle dollar-quote state across every `$tag$` token on this line.
|
|
241
|
+
for (const m of rawLine.matchAll(/\$[A-Za-z0-9_]*\$/g)) {
|
|
242
|
+
const tag = m[0];
|
|
243
|
+
if (openTag === null)
|
|
244
|
+
openTag = tag;
|
|
245
|
+
else if (openTag === tag)
|
|
246
|
+
openTag = null;
|
|
247
|
+
}
|
|
248
|
+
if (openTag === null && line.endsWith(";")) {
|
|
98
249
|
const cleaned = buf.trim();
|
|
99
250
|
if (cleaned)
|
|
100
251
|
out.push(cleaned);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,oEAAoE;AACpE,gEAAgE;AAOhE;;;;;;;GAOG;AACH,MAAM,wBAAwB,GAAG
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,oEAAoE;AACpE,gEAAgE;AAOhE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;gIAoBqG,CAAC;AAEjI;;;GAGG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;kIAoBsG,CAAC;AAEnI;;;GAGG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkF/B,uBAAuB,EAAE;EACzB,wBAAwB,EAAE;CAC3B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,UAAU,wBAAwB;IACtC,OAAO,wBAAwB,CAAC;AAClC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAc;IACtD,MAAM,GAAG,GAAG,wBAAwB,EAAE,CAAC;IACvC,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,OAAO,GAAkB,IAAI,CAAC;IAClC,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5B,oEAAoE;QACpE,uEAAuE;QACvE,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAAE,SAAS;QACnE,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QAC3C,qEAAqE;QACrE,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC;YACvD,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACjB,IAAI,OAAO,KAAK,IAAI;gBAAE,OAAO,GAAG,GAAG,CAAC;iBAC/B,IAAI,OAAO,KAAK,GAAG;gBAAE,OAAO,GAAG,IAAI,CAAC;QAC3C,CAAC;QACD,IAAI,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;YAC3B,IAAI,OAAO;gBAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/B,GAAG,GAAG,EAAE,CAAC;QACX,CAAC;IACH,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,EAAE;QAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/saga.d.ts
CHANGED
|
@@ -45,4 +45,44 @@ export declare function stepParallel<O>(steps: {
|
|
|
45
45
|
export declare function cancel(sagaId: string, reason: string): Promise<void>;
|
|
46
46
|
/** Read-only accessor for the active saga handle in scope. */
|
|
47
47
|
export declare function currentSaga(): SagaHandle | null;
|
|
48
|
+
/**
|
|
49
|
+
* Resolves the undo function for a step on the RECOVERY path. A fresh
|
|
50
|
+
* process has lost the in-memory undo closures, so it must re-bind them
|
|
51
|
+
* from its registered saga handlers. Given the persisted step name +
|
|
52
|
+
* output, return the undo callable, or `null` if the step has no
|
|
53
|
+
* compensation (logged as `skipped_no_undo`).
|
|
54
|
+
*/
|
|
55
|
+
export type UndoResolver = (stepName: string, output: unknown) => ((output: unknown) => Promise<void>) | null;
|
|
56
|
+
/** Outcome of {@link recoverUndoStack}. */
|
|
57
|
+
export interface RecoverUndoStackResult {
|
|
58
|
+
/** Number of durable undo entries found for the saga. */
|
|
59
|
+
loaded: number;
|
|
60
|
+
/** Steps whose undo ran successfully. */
|
|
61
|
+
undone: string[];
|
|
62
|
+
/** Steps that had no undo (logged skipped_no_undo). */
|
|
63
|
+
skipped: string[];
|
|
64
|
+
/** Steps whose undo threw (logged undo_failed). */
|
|
65
|
+
failed: string[];
|
|
66
|
+
/** Terminal status the saga settled on after recovery. */
|
|
67
|
+
status: "compensated" | "compensated_with_errors" | "compensation_failed";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* § 5.8 RECOVERY — reload the durable undo stack from the state store and
|
|
71
|
+
* run the pending compensations in reverse on a FRESH process.
|
|
72
|
+
*
|
|
73
|
+
* This is the entrypoint that makes undo-stack durability meaningful: after
|
|
74
|
+
* a process restart mid-saga, the in-memory undo map is gone, but the
|
|
75
|
+
* `saga_state.undo_stack` column persisted every completed step's
|
|
76
|
+
* compensation (step name + output + mode). A recovery worker (or the
|
|
77
|
+
* reaper) calls this with an {@link UndoResolver} that re-binds each step's
|
|
78
|
+
* undo fn from the registered saga handler; the function then walks the
|
|
79
|
+
* stack in reverse, runs each undo, appends to `compensation_log`, sets the
|
|
80
|
+
* terminal status, and clears the durable stack.
|
|
81
|
+
*
|
|
82
|
+
* Mirrors the in-process {@link runCompensation} semantics: a strict-mode
|
|
83
|
+
* undo failure halts with `compensation_failed` (leaving the remaining
|
|
84
|
+
* entries durable for a later re-attempt); best-effort failures continue
|
|
85
|
+
* and settle on `compensated_with_errors`.
|
|
86
|
+
*/
|
|
87
|
+
export declare function recoverUndoStack(sagaId: string, resolve: UndoResolver): Promise<RecoverUndoStackResult>;
|
|
48
88
|
//# sourceMappingURL=saga.d.ts.map
|
package/dist/saga.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"saga.d.ts","sourceRoot":"","sources":["../src/saga.ts"],"names":[],"mappings":"AAoBA,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,UAAU,EAEf,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"saga.d.ts","sourceRoot":"","sources":["../src/saga.ts"],"names":[],"mappings":"AAoBA,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,UAAU,EAEf,KAAK,QAAQ,EAId,MAAM,SAAS,CAAC;AAwGjB;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAuFZ;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAGZ;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CA6GzE;AAeD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;CAAE,EAAE,GAC3C,OAAO,CAAC,CAAC,EAAE,CAAC,CAEd;AA4FD,oEAAoE;AACpE,wBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1E;AAED,8DAA8D;AAC9D,wBAAgB,WAAW,IAAI,UAAU,GAAG,IAAI,CAE/C;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,KACZ,CAAC,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;AAEjD,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,mDAAmD;IACnD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0DAA0D;IAC1D,MAAM,EAAE,aAAa,GAAG,yBAAyB,GAAG,qBAAqB,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,sBAAsB,CAAC,CAgFjC"}
|