@voltro/protocol 0.84.0 → 0.86.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/CHANGELOG.md +1215 -0
- package/README.md +14 -0
- package/THIRD-PARTY-NOTICES.md +5 -89
- package/dist/apikey.d.ts +32 -34
- package/dist/approval.js +64 -0
- package/dist/approvalDescriptors.js +26 -0
- package/dist/auth.js +369 -0
- package/dist/boundedRing.js +27 -0
- package/dist/businessRule.js +11 -0
- package/dist/connection.js +73 -0
- package/dist/devActivity.js +4 -0
- package/dist/errorValue.js +21 -0
- package/dist/event.js +104 -0
- package/dist/idempotency.js +66 -0
- package/dist/identity.js +5 -9
- package/dist/index.d.ts +1494 -1366
- package/dist/index.js +42 -1411
- package/dist/inspect.d.ts +88 -88
- package/dist/inspect.js +14 -190
- package/dist/inspectBackfill.js +52 -0
- package/dist/inspectCommandRejection.js +21 -0
- package/dist/inspectProjection.js +133 -0
- package/dist/inspectStorageProjection.js +86 -0
- package/dist/inspectValueIdentity.js +4 -0
- package/dist/inspectWorkflowExecution.js +34 -0
- package/dist/inspectWorkflowQueuedStart.js +31 -0
- package/dist/jsonSchema.js +54 -0
- package/dist/jwt.d.ts +32 -34
- package/dist/outbox.js +10 -0
- package/dist/pairing.js +75 -0
- package/dist/plugin.js +61 -0
- package/dist/pluginNaming.js +32 -0
- package/dist/procedure.js +262 -0
- package/dist/publicApi.js +344 -0
- package/dist/reactivityChannel.js +34 -0
- package/dist/rest.d.ts +105 -57
- package/dist/rest.js +259 -437
- package/dist/restFormat.js +163 -0
- package/dist/rowEquality.js +31 -0
- package/dist/rowPatch.js +182 -0
- package/dist/schemaShape.js +67 -0
- package/dist/scopes.js +240 -0
- package/dist/serverErrorBus.js +15 -0
- package/dist/serviceCall.js +14 -0
- package/dist/session.d.ts +90 -96
- package/dist/session.js +13 -15
- package/dist/{sessionHeader-DriTZhmv.js → sessionHeader.js} +1 -1
- package/dist/storageAccess.js +14 -0
- package/dist/storeErrors.js +66 -0
- package/dist/storeScreenshots.js +90 -0
- package/dist/strictInput.js +27 -0
- package/dist/subscriptionEvent.js +37 -0
- package/dist/tenantSelection.js +40 -0
- package/dist/time.js +7 -0
- package/dist/undo.js +38 -0
- package/dist/validation.js +39 -0
- package/dist/webSocketGateway.js +10 -0
- package/dist/workflow.js +297 -0
- package/dist/workflowAccess.js +61 -0
- package/dist/workflowRestart.js +45 -0
- package/dist/workflowRetry.js +44 -0
- package/dist/workflowStartOutcome.js +30 -0
- package/package.json +4 -8
- package/dist/restFormat-EZ8k9ZcQ.js +0 -304
- package/dist/scopes-Di756ENh.js +0 -518
package/dist/undo.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineMutation as e, defineQuery as t } from "./procedure.js";
|
|
2
|
+
import { Schema as n } from "effect";
|
|
3
|
+
//#region src/undo.ts
|
|
4
|
+
var r = "__voltro.undo.log", i = "__voltro.undo.apply", a = "__voltro.undo.redo", o = n.Struct({
|
|
5
|
+
id: n.String,
|
|
6
|
+
tag: n.String,
|
|
7
|
+
label: n.NullOr(n.String),
|
|
8
|
+
undone: n.Boolean,
|
|
9
|
+
crossesAction: n.Boolean,
|
|
10
|
+
createdAt: n.String
|
|
11
|
+
}), s = class extends n.TaggedError()("UndoNotFound", { invocationId: n.String }) {}, c = class extends n.TaggedError()("UndoForbidden", { invocationId: n.String }) {}, l = class extends n.TaggedError()("UndoConflict", {
|
|
12
|
+
invocationId: n.String,
|
|
13
|
+
reason: n.Literals(["conflict", "action"])
|
|
14
|
+
}) {}, u = n.Union([
|
|
15
|
+
s,
|
|
16
|
+
c,
|
|
17
|
+
l
|
|
18
|
+
]), d = t({
|
|
19
|
+
name: r,
|
|
20
|
+
source: "_voltro_undo_log",
|
|
21
|
+
input: n.Struct({ limit: n.optional(n.Number) }),
|
|
22
|
+
output: n.Array(o),
|
|
23
|
+
openAccess: "subject-scoped by construction: lists only the calling subject's own undoable actions"
|
|
24
|
+
}), f = e({
|
|
25
|
+
name: i,
|
|
26
|
+
input: n.Struct({ invocationId: n.String }),
|
|
27
|
+
output: n.Struct({ ok: n.Boolean }),
|
|
28
|
+
error: u,
|
|
29
|
+
openAccess: "subject-scoped by construction: undo is per-actor — another subject's invocation fails typed with UndoForbidden"
|
|
30
|
+
}), p = e({
|
|
31
|
+
name: a,
|
|
32
|
+
input: n.Struct({ invocationId: n.String }),
|
|
33
|
+
output: n.Struct({ ok: n.Boolean }),
|
|
34
|
+
error: u,
|
|
35
|
+
openAccess: "subject-scoped by construction: redo is per-actor — another subject's invocation fails typed with UndoForbidden"
|
|
36
|
+
});
|
|
37
|
+
//#endregion
|
|
38
|
+
export { i as UNDO_APPLY_TAG, r as UNDO_LOG_TAG, a as UNDO_REDO_TAG, l as UndoConflict, c as UndoForbidden, o as UndoLogEntry, s as UndoNotFound, f as undoApplyDescriptor, d as undoLogQueryDescriptor, p as undoRedoDescriptor };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
//#region src/validation.ts
|
|
3
|
+
var t = e.Struct({
|
|
4
|
+
field: e.String,
|
|
5
|
+
message: e.String,
|
|
6
|
+
params: e.optional(e.Record(e.String, e.Unknown))
|
|
7
|
+
}), n = class extends e.TaggedError()("ValidationError", {
|
|
8
|
+
field: e.String,
|
|
9
|
+
message: e.String,
|
|
10
|
+
params: e.optional(e.Record(e.String, e.Unknown))
|
|
11
|
+
}) {}, r = class extends e.TaggedError()("ValidationErrors", { issues: e.Array(t).pipe(e.check(e.isMinLength(1))) }) {}, i = (e) => {
|
|
12
|
+
if (typeof e != "object" || !e) return [];
|
|
13
|
+
let t = e._tag;
|
|
14
|
+
if (t === "ValidationError") {
|
|
15
|
+
let t = e;
|
|
16
|
+
return [{
|
|
17
|
+
field: t.field,
|
|
18
|
+
message: t.message,
|
|
19
|
+
...t.params === void 0 ? {} : { params: t.params }
|
|
20
|
+
}];
|
|
21
|
+
}
|
|
22
|
+
if (t === "ValidationErrors") {
|
|
23
|
+
let t = e;
|
|
24
|
+
return Array.isArray(t.issues) ? t.issues : [];
|
|
25
|
+
}
|
|
26
|
+
if (t === "BusinessRuleViolation") {
|
|
27
|
+
let t = e;
|
|
28
|
+
if (typeof t.field != "string" || t.field.length === 0) return [];
|
|
29
|
+
let n = typeof t.message == "string" && t.message.length > 0 ? t.message : String(t.rule ?? "validation.invalid");
|
|
30
|
+
return [{
|
|
31
|
+
field: t.field,
|
|
32
|
+
message: n,
|
|
33
|
+
...t.params === void 0 ? {} : { params: t.params }
|
|
34
|
+
}];
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
export { n as ValidationError, r as ValidationErrors, t as ValidationIssue, i as fieldIssuesOf };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/webSocketGateway.ts
|
|
2
|
+
var e = 4001, t = /* @__PURE__ */ new Set(["/rpc", "/ws"]), n = (e) => {
|
|
3
|
+
for (let [t, n] of Object.entries(e.limits ?? {})) if (n !== void 0 && (!Number.isSafeInteger(n) || n <= 0)) throw Error(`defineWebSocket: limits.${t} must be a positive safe integer`);
|
|
4
|
+
if (!e.path.startsWith("/")) throw Error(`defineWebSocket: path must be absolute, got '${e.path}'`);
|
|
5
|
+
if (t.has(e.path)) throw Error(`defineWebSocket: '${e.path}' is the framework's own socket surface — mount the gateway elsewhere`);
|
|
6
|
+
if (e.auth !== "subject" && e.auth !== "public") throw Error(`defineWebSocket: auth must be 'subject' or 'public' (explicitly — an unauthenticated socket has to be a decision somebody wrote down), got '${String(e.auth)}'`);
|
|
7
|
+
return e;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { e as GATEWAY_CREDENTIAL_EXPIRED_CLOSE_CODE, n as defineWebSocket };
|
package/dist/workflow.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { strictInput as e } from "./strictInput.js";
|
|
2
|
+
import { defineAction as t, defineQuery as n, hasEnforcedGuard as r, workflowWireErrorUnion as i } from "./procedure.js";
|
|
3
|
+
import { getWorkflowAccess as a } from "./workflowAccess.js";
|
|
4
|
+
import { WorkflowStartTerminalOutcomeSchema as o } from "./workflowStartOutcome.js";
|
|
5
|
+
import { Schema as s } from "effect";
|
|
6
|
+
import { Rpc as c } from "effect/rpc";
|
|
7
|
+
//#region src/workflow.ts
|
|
8
|
+
var l = s.Literals([
|
|
9
|
+
"running",
|
|
10
|
+
"succeeded",
|
|
11
|
+
"failed",
|
|
12
|
+
"cancelled",
|
|
13
|
+
"suspended"
|
|
14
|
+
]), u = s.Struct({
|
|
15
|
+
id: s.String,
|
|
16
|
+
workflowName: s.String,
|
|
17
|
+
executionId: s.String,
|
|
18
|
+
status: s.Union([l, s.Literal("unknown")]),
|
|
19
|
+
output: s.optional(s.Unknown),
|
|
20
|
+
error: s.optional(s.Struct({
|
|
21
|
+
tag: s.NullOr(s.String),
|
|
22
|
+
message: s.String
|
|
23
|
+
}))
|
|
24
|
+
}), d = s.Struct({
|
|
25
|
+
operation: s.Literals(["cancel", "resume"]),
|
|
26
|
+
kind: s.Literals([
|
|
27
|
+
"accepted",
|
|
28
|
+
"confirmed",
|
|
29
|
+
"unknown",
|
|
30
|
+
"already-terminal"
|
|
31
|
+
]),
|
|
32
|
+
requestId: s.NullOr(s.NonEmptyString),
|
|
33
|
+
snapshot: u
|
|
34
|
+
}).pipe(s.check(s.makeFilter((e) => e.kind === "unknown" ? !0 : e.kind === "accepted" ? e.requestId !== null && (e.snapshot.status === "running" || e.snapshot.status === "suspended") : e.kind === "already-terminal" ? e.snapshot.status === "succeeded" || e.snapshot.status === "failed" || e.snapshot.status === "cancelled" : e.operation === "cancel" ? e.snapshot.status === "cancelled" : e.requestId !== null && e.snapshot.status === "running", { message: "Workflow control acknowledgement contradicts its observed state or receipt identity" }))), f = s.Struct({
|
|
35
|
+
operation: s.Literal("cancel"),
|
|
36
|
+
workflowName: s.NonEmptyString,
|
|
37
|
+
receiptId: s.NonEmptyString,
|
|
38
|
+
requestId: s.NullOr(s.NonEmptyString),
|
|
39
|
+
kind: s.Literals([
|
|
40
|
+
"accepted",
|
|
41
|
+
"confirmed",
|
|
42
|
+
"unknown",
|
|
43
|
+
"already-terminal"
|
|
44
|
+
]),
|
|
45
|
+
observation: s.Union([
|
|
46
|
+
s.Struct({ kind: s.Literal("pending") }),
|
|
47
|
+
s.Struct({
|
|
48
|
+
kind: s.Literal("admission"),
|
|
49
|
+
outcome: s.Union([o, s.Struct({
|
|
50
|
+
kind: s.Literal("rejected"),
|
|
51
|
+
failureId: s.NonEmptyString
|
|
52
|
+
})])
|
|
53
|
+
}),
|
|
54
|
+
s.Struct({
|
|
55
|
+
kind: s.Literal("execution"),
|
|
56
|
+
snapshot: u
|
|
57
|
+
}),
|
|
58
|
+
s.Struct({
|
|
59
|
+
kind: s.Literal("unavailable"),
|
|
60
|
+
reason: s.Literals([
|
|
61
|
+
"missing-request",
|
|
62
|
+
"missing-run",
|
|
63
|
+
"unconfirmed-generation",
|
|
64
|
+
"unbound-submission"
|
|
65
|
+
])
|
|
66
|
+
})
|
|
67
|
+
])
|
|
68
|
+
}).pipe(s.check(s.makeFilter((e) => {
|
|
69
|
+
let t = e.observation;
|
|
70
|
+
if (t.kind === "unavailable") return e.kind === "unknown" && (t.reason === "missing-request" ? e.requestId === null : e.requestId !== null);
|
|
71
|
+
if (e.requestId === null) return !1;
|
|
72
|
+
if (t.kind === "pending") return e.kind === "accepted";
|
|
73
|
+
if (t.kind === "admission") return e.kind === (t.outcome.kind === "cancelled" ? "confirmed" : "already-terminal");
|
|
74
|
+
let n = t.snapshot;
|
|
75
|
+
return n.workflowName !== e.workflowName || n.id.length === 0 || n.executionId.length === 0 ? !1 : e.kind === "accepted" ? n.status === "running" || n.status === "suspended" : e.kind === "confirmed" ? n.status === "cancelled" : e.kind === "already-terminal" && (n.status === "succeeded" || n.status === "failed" || n.status === "cancelled");
|
|
76
|
+
}, { message: "Queued cancellation acknowledgement contradicts its receipt or observation" }))), p = s.Literals([
|
|
77
|
+
"cancel",
|
|
78
|
+
"terminate",
|
|
79
|
+
"abandon"
|
|
80
|
+
]), m = s.Struct({
|
|
81
|
+
mode: s.String,
|
|
82
|
+
dueAt: s.NullOr(s.Number),
|
|
83
|
+
retryAfterMs: s.NullOr(s.Number),
|
|
84
|
+
intentId: s.NullOr(s.String)
|
|
85
|
+
}), h = s.Struct({
|
|
86
|
+
id: s.String,
|
|
87
|
+
workflowName: s.String,
|
|
88
|
+
executionId: s.NullOr(s.String),
|
|
89
|
+
status: s.Literals([
|
|
90
|
+
"running",
|
|
91
|
+
"succeeded",
|
|
92
|
+
"queued",
|
|
93
|
+
"dropped",
|
|
94
|
+
"skipped",
|
|
95
|
+
"replayed"
|
|
96
|
+
]),
|
|
97
|
+
deferral: s.optional(m)
|
|
98
|
+
}), g = (t, n) => {
|
|
99
|
+
let o = a(t);
|
|
100
|
+
return c.make(t.name, {
|
|
101
|
+
payload: e(t.payloadSchema, t.name, r(o.guards)),
|
|
102
|
+
success: h,
|
|
103
|
+
error: i({
|
|
104
|
+
errorSchema: t.errorSchema,
|
|
105
|
+
guards: o.guards
|
|
106
|
+
}, n)
|
|
107
|
+
});
|
|
108
|
+
}, _ = s.Struct({
|
|
109
|
+
id: s.String,
|
|
110
|
+
tag: s.String,
|
|
111
|
+
executionId: s.String,
|
|
112
|
+
status: l,
|
|
113
|
+
payload: s.Unknown,
|
|
114
|
+
workflowVersion: s.NullOr(s.String),
|
|
115
|
+
workflowPatches: s.NullOr(s.Unknown),
|
|
116
|
+
output: s.NullOr(s.Unknown),
|
|
117
|
+
subject: s.NullOr(s.Unknown),
|
|
118
|
+
source: s.NullOr(s.String),
|
|
119
|
+
errorTag: s.NullOr(s.String),
|
|
120
|
+
errorMessage: s.NullOr(s.String),
|
|
121
|
+
cancelled: s.Boolean,
|
|
122
|
+
startedAt: s.DateFromString,
|
|
123
|
+
completedAt: s.NullOr(s.DateFromString),
|
|
124
|
+
durationMs: s.NullOr(s.Number),
|
|
125
|
+
traceId: s.NullOr(s.String),
|
|
126
|
+
parentExecutionId: s.NullOr(s.String),
|
|
127
|
+
parentClosePolicy: s.NullOr(p)
|
|
128
|
+
}), v = s.Struct({
|
|
129
|
+
tag: s.optional(s.String),
|
|
130
|
+
status: s.optional(l),
|
|
131
|
+
limit: s.optional(s.Number)
|
|
132
|
+
}), y = s.Struct({
|
|
133
|
+
id: s.String,
|
|
134
|
+
runId: s.String,
|
|
135
|
+
stepName: s.String,
|
|
136
|
+
attempt: s.Number,
|
|
137
|
+
status: s.Literals([
|
|
138
|
+
"running",
|
|
139
|
+
"succeeded",
|
|
140
|
+
"failed"
|
|
141
|
+
]),
|
|
142
|
+
input: s.NullOr(s.Unknown),
|
|
143
|
+
retryPolicy: s.NullOr(s.Unknown),
|
|
144
|
+
output: s.NullOr(s.Unknown),
|
|
145
|
+
errorTag: s.NullOr(s.String),
|
|
146
|
+
errorMessage: s.NullOr(s.String),
|
|
147
|
+
errorCause: s.NullOr(s.Unknown),
|
|
148
|
+
startedAt: s.DateFromString,
|
|
149
|
+
completedAt: s.NullOr(s.DateFromString),
|
|
150
|
+
durationMs: s.NullOr(s.Number)
|
|
151
|
+
}), b = s.Struct({
|
|
152
|
+
id: s.String,
|
|
153
|
+
runId: s.String,
|
|
154
|
+
eventType: s.String,
|
|
155
|
+
payload: s.NullOr(s.Unknown),
|
|
156
|
+
occurredAt: s.DateFromString,
|
|
157
|
+
stepName: s.NullOr(s.String),
|
|
158
|
+
attempt: s.NullOr(s.Number)
|
|
159
|
+
}), x = s.Struct({
|
|
160
|
+
id: s.String,
|
|
161
|
+
name: s.String,
|
|
162
|
+
payload: s.Unknown,
|
|
163
|
+
source: s.String,
|
|
164
|
+
subject: s.NullOr(s.Unknown),
|
|
165
|
+
traceId: s.NullOr(s.String),
|
|
166
|
+
occurredAt: s.DateFromString
|
|
167
|
+
}), S = s.Struct({
|
|
168
|
+
id: s.String,
|
|
169
|
+
eventId: s.String,
|
|
170
|
+
eventName: s.String,
|
|
171
|
+
triggerId: s.String,
|
|
172
|
+
workflowName: s.String,
|
|
173
|
+
executionId: s.NullOr(s.String),
|
|
174
|
+
status: s.Literals([
|
|
175
|
+
"starting",
|
|
176
|
+
"started",
|
|
177
|
+
"skipped",
|
|
178
|
+
"failed"
|
|
179
|
+
]),
|
|
180
|
+
idempotencyKey: s.String,
|
|
181
|
+
skipped: s.Boolean,
|
|
182
|
+
errorMessage: s.NullOr(s.String),
|
|
183
|
+
createdAt: s.DateFromString,
|
|
184
|
+
completedAt: s.NullOr(s.DateFromString)
|
|
185
|
+
}), C = s.Struct({ id: s.String }), w = s.Struct({ runId: s.String }), T = s.Struct({
|
|
186
|
+
name: s.optional(s.String),
|
|
187
|
+
limit: s.optional(s.Number)
|
|
188
|
+
}), E = s.Struct({ eventId: s.String }), D = s.Struct({
|
|
189
|
+
requestId: s.NonEmptyString,
|
|
190
|
+
cause: s.optionalKey(s.Struct({
|
|
191
|
+
reason: s.NonEmptyString,
|
|
192
|
+
detail: s.optional(s.Record(s.String, s.Unknown))
|
|
193
|
+
}))
|
|
194
|
+
}), O = s.Struct({
|
|
195
|
+
workflowName: s.String,
|
|
196
|
+
executionId: s.String,
|
|
197
|
+
control: s.optional(D)
|
|
198
|
+
}), k = s.Struct({
|
|
199
|
+
workflowName: s.NonEmptyString,
|
|
200
|
+
executionId: s.NonEmptyString,
|
|
201
|
+
operation: s.Literals(["cancel", "resume"]),
|
|
202
|
+
requestId: s.NonEmptyString
|
|
203
|
+
}), A = s.Struct({
|
|
204
|
+
workflowName: s.NonEmptyString,
|
|
205
|
+
receiptId: s.NonEmptyString,
|
|
206
|
+
requestId: s.NonEmptyString
|
|
207
|
+
}), j = s.Struct({
|
|
208
|
+
...A.fields,
|
|
209
|
+
cause: D.fields.cause
|
|
210
|
+
}), M = t({
|
|
211
|
+
name: "__voltro.workflow.queued.cancel",
|
|
212
|
+
input: j,
|
|
213
|
+
output: f,
|
|
214
|
+
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
215
|
+
}), N = n({
|
|
216
|
+
name: "__voltro.workflow.queued.control",
|
|
217
|
+
input: A,
|
|
218
|
+
output: f,
|
|
219
|
+
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
220
|
+
}), P = n({
|
|
221
|
+
name: "__voltro.workflow.control",
|
|
222
|
+
input: k,
|
|
223
|
+
output: d,
|
|
224
|
+
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
225
|
+
}), F = s.Struct({
|
|
226
|
+
id: s.String,
|
|
227
|
+
signalName: s.String,
|
|
228
|
+
payload: s.optional(s.Unknown)
|
|
229
|
+
}), I = s.Struct({
|
|
230
|
+
id: s.String,
|
|
231
|
+
updateName: s.String,
|
|
232
|
+
payload: s.optional(s.Unknown),
|
|
233
|
+
timeoutMs: s.optional(s.Number)
|
|
234
|
+
}), L = s.Struct({
|
|
235
|
+
eventId: s.String,
|
|
236
|
+
updateId: s.String,
|
|
237
|
+
completedEventId: s.String,
|
|
238
|
+
result: s.Unknown
|
|
239
|
+
}), R = n({
|
|
240
|
+
name: "__voltro.workflow.run",
|
|
241
|
+
source: "_voltro_workflow_runs",
|
|
242
|
+
input: C,
|
|
243
|
+
output: s.Array(_),
|
|
244
|
+
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
245
|
+
}), z = n({
|
|
246
|
+
name: "__voltro.workflow.runs",
|
|
247
|
+
source: "_voltro_workflow_runs",
|
|
248
|
+
input: v,
|
|
249
|
+
output: s.Array(_),
|
|
250
|
+
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
251
|
+
}), B = n({
|
|
252
|
+
name: "__voltro.workflow.run.steps",
|
|
253
|
+
source: "_voltro_workflow_run_steps",
|
|
254
|
+
input: w,
|
|
255
|
+
output: s.Array(y),
|
|
256
|
+
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
257
|
+
}), V = n({
|
|
258
|
+
name: "__voltro.workflow.run.events",
|
|
259
|
+
source: "_voltro_workflow_run_events",
|
|
260
|
+
input: w,
|
|
261
|
+
output: s.Array(b),
|
|
262
|
+
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
263
|
+
}), H = n({
|
|
264
|
+
name: "__voltro.workflow.domainEvents",
|
|
265
|
+
source: "_voltro_workflow_events",
|
|
266
|
+
input: T,
|
|
267
|
+
output: s.Array(x),
|
|
268
|
+
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
269
|
+
}), U = n({
|
|
270
|
+
name: "__voltro.workflow.event.deliveries",
|
|
271
|
+
source: "_voltro_workflow_event_deliveries",
|
|
272
|
+
input: E,
|
|
273
|
+
output: s.Array(S),
|
|
274
|
+
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
275
|
+
}), W = t({
|
|
276
|
+
name: "__voltro.workflow.cancel",
|
|
277
|
+
input: O,
|
|
278
|
+
output: d,
|
|
279
|
+
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
280
|
+
}), G = t({
|
|
281
|
+
name: "__voltro.workflow.resume",
|
|
282
|
+
input: O,
|
|
283
|
+
output: d,
|
|
284
|
+
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
285
|
+
}), K = t({
|
|
286
|
+
name: "__voltro.workflow.signal",
|
|
287
|
+
input: F,
|
|
288
|
+
output: s.Struct({ eventId: s.String }),
|
|
289
|
+
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
290
|
+
}), q = t({
|
|
291
|
+
name: "__voltro.workflow.update",
|
|
292
|
+
input: I,
|
|
293
|
+
output: L,
|
|
294
|
+
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
295
|
+
});
|
|
296
|
+
//#endregion
|
|
297
|
+
export { O as WorkflowControlInputSchema, D as WorkflowControlIntentSchema, k as WorkflowControlReadInputSchema, d as WorkflowControlResultSchema, x as WorkflowDomainEventRowSchema, T as WorkflowDomainEventsInputSchema, E as WorkflowEventDeliveriesInputSchema, S as WorkflowEventDeliveryRowSchema, p as WorkflowParentClosePolicySchema, j as WorkflowQueuedCancelInputSchema, f as WorkflowQueuedCancellationResultSchema, A as WorkflowQueuedControlTargetSchema, b as WorkflowRunEventRowSchema, h as WorkflowRunHandleSchema, C as WorkflowRunRefSchema, _ as WorkflowRunRowSchema, u as WorkflowRunSnapshotSchema, l as WorkflowRunStatusSchema, y as WorkflowRunStepRowSchema, w as WorkflowRunTableRefSchema, v as WorkflowRunsInputSchema, F as WorkflowSignalInputSchema, m as WorkflowStartDeferralSchema, I as WorkflowUpdateInputSchema, L as WorkflowUpdateResultSchema, W as workflowCancelDescriptor, P as workflowControlQueryDescriptor, H as workflowDomainEventsQueryDescriptor, U as workflowEventDeliveriesQueryDescriptor, M as workflowQueuedCancelDescriptor, N as workflowQueuedControlQueryDescriptor, G as workflowResumeDescriptor, V as workflowRunEventsQueryDescriptor, R as workflowRunQueryDescriptor, B as workflowRunStepsQueryDescriptor, z as workflowRunsQueryDescriptor, K as workflowSignalDescriptor, g as workflowToRpc, q as workflowUpdateDescriptor };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { openAccessSpec as e } from "./scopes.js";
|
|
2
|
+
//#region src/workflowAccess.ts
|
|
3
|
+
var t = Symbol.for("@voltro/protocol/workflow-access"), n = {
|
|
4
|
+
guards: void 0,
|
|
5
|
+
openAccess: void 0,
|
|
6
|
+
internal: !1,
|
|
7
|
+
controls: void 0,
|
|
8
|
+
controlsOpenAccess: void 0
|
|
9
|
+
}, r = (t, n) => {
|
|
10
|
+
let r = n.guards;
|
|
11
|
+
if (r !== void 0 && r.length === 0) throw Error(`workflow("${t}"): \`guards: []\` is empty, so it enforces nothing —\n but it reads at the call site as if the start were protected. Omit the field
|
|
12
|
+
and decide with \`openAccess\` or \`internal\`, or list the scopes that may start it.`);
|
|
13
|
+
if (n.openAccess !== void 0) {
|
|
14
|
+
if (n.openAccess.trim() === "") throw Error(`workflow("${t}"): \`openAccess\` needs a REASON, not an empty string. It is\n the sentence a reviewer reads to decide whether anyone should really be able to
|
|
15
|
+
start durable work without an authorization check — e.g. \`openAccess: 'public
|
|
16
|
+
newsletter signup; the payload carries only the address being confirmed'\`.`);
|
|
17
|
+
if (r !== void 0 && r.length > 0) throw Error(`workflow("${t}"): \`openAccess\` and \`guards\` are two different access\n decisions, so declaring both says the start is protected AND open. Keep the
|
|
18
|
+
guards if a caller must hold a scope; drop them if anyone may start it.`);
|
|
19
|
+
if (n.internal === !0) throw Error(`workflow("${t}"): \`internal: true\` cannot be combined with \`openAccess\`.\n \`internal\` takes the start OFF the wire, and \`openAccess\` declares who may
|
|
20
|
+
start it over the wire — a decision about a door that does not exist. Drop
|
|
21
|
+
\`openAccess\` (an internal workflow needs no wire decision), or drop
|
|
22
|
+
\`internal: true\` if the workflow really is meant to be startable by clients.`);
|
|
23
|
+
}
|
|
24
|
+
let i = n.controls?.guards, a = n.controls?.openAccess;
|
|
25
|
+
if (i !== void 0 && i.length === 0) throw Error(`workflow("${t}"): \`controls: { guards: [] }\` is empty, so it enforces nothing —\n but it reads at the call site as if driving this run were protected. Omit
|
|
26
|
+
\`controls\` (wire control is then refused for everyone but \`admin:full\`), or
|
|
27
|
+
list the scopes a caller needs to cancel or signal a run.`);
|
|
28
|
+
if (a !== void 0) {
|
|
29
|
+
if (a.trim() === "") throw Error(`workflow("${t}"): \`controls: { openAccess }\` needs a REASON, not an empty string.\n It is the sentence a reviewer reads to decide whether anyone who can see this
|
|
30
|
+
run should be able to cancel or signal it — e.g. \`controls: { openAccess:
|
|
31
|
+
'the run belongs to the caller who started it; cancelling it affects nobody else' }\`.`);
|
|
32
|
+
if (i !== void 0 && i.length > 0) throw Error(`workflow("${t}"): \`controls\` takes \`guards\` OR \`openAccess\`, not both.\n Two answers to one question; keep the guards if a caller needs a scope.`);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
guards: n.openAccess === void 0 ? n.guards : [e(n.openAccess.trim())],
|
|
36
|
+
openAccess: n.openAccess?.trim(),
|
|
37
|
+
internal: n.internal === !0,
|
|
38
|
+
controls: a === void 0 ? i : [e(a.trim())],
|
|
39
|
+
controlsOpenAccess: a?.trim()
|
|
40
|
+
};
|
|
41
|
+
}, i = (e, n) => {
|
|
42
|
+
Object.defineProperty(e, t, {
|
|
43
|
+
value: n,
|
|
44
|
+
enumerable: !1
|
|
45
|
+
});
|
|
46
|
+
}, a = (e) => {
|
|
47
|
+
if (typeof e != "object" && typeof e != "function" || e === null) return n;
|
|
48
|
+
let r = e[t];
|
|
49
|
+
return r === void 0 ? n : r;
|
|
50
|
+
}, o = (e) => {
|
|
51
|
+
let t = a(e);
|
|
52
|
+
return {
|
|
53
|
+
name: e.name,
|
|
54
|
+
internal: t.internal,
|
|
55
|
+
guards: t.guards,
|
|
56
|
+
openAccess: t.openAccess,
|
|
57
|
+
controls: t.controls
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { t as WorkflowAccessProperty, i as attachWorkflowAccess, a as getWorkflowAccess, r as resolveWorkflowAccess, o as workflowAccessDescriptor };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
//#region src/workflowRestart.ts
|
|
3
|
+
var t = e.NonEmptyString.pipe(e.check(e.makeFilter((e) => e.trim().length > 0, { message: "A workflow restart identity must not be blank" }))), n = e.Union([e.Struct({ kind: e.Literal("failed-steps") }), e.Struct({
|
|
4
|
+
kind: e.Literal("from-step"),
|
|
5
|
+
stepName: t
|
|
6
|
+
})]), r = e.Struct({
|
|
7
|
+
requestId: t.pipe(e.check(e.isMaxLength(64))),
|
|
8
|
+
runId: t
|
|
9
|
+
}), i = e.Struct({
|
|
10
|
+
...r.fields,
|
|
11
|
+
selection: n
|
|
12
|
+
}), a = {
|
|
13
|
+
...i.fields,
|
|
14
|
+
workflowName: t,
|
|
15
|
+
executionId: t,
|
|
16
|
+
originalGeneration: t,
|
|
17
|
+
requestedAt: e.Number.pipe(e.check(e.isFinite())),
|
|
18
|
+
dispatchAttempts: e.Number.check(e.isInt(), e.isGreaterThanOrEqualTo(0))
|
|
19
|
+
}, o = e.Union([
|
|
20
|
+
e.Struct({
|
|
21
|
+
...r.fields,
|
|
22
|
+
kind: e.Literal("not-found")
|
|
23
|
+
}),
|
|
24
|
+
e.Struct({
|
|
25
|
+
...i.fields,
|
|
26
|
+
kind: e.Literal("withdrawn"),
|
|
27
|
+
workflowName: t,
|
|
28
|
+
withdrawnAt: e.Number.pipe(e.check(e.isFinite()))
|
|
29
|
+
}),
|
|
30
|
+
e.Struct({
|
|
31
|
+
...a,
|
|
32
|
+
kind: e.Literal("pending")
|
|
33
|
+
}),
|
|
34
|
+
e.Struct({
|
|
35
|
+
...a,
|
|
36
|
+
kind: e.Literal("entered"),
|
|
37
|
+
enteredGeneration: t
|
|
38
|
+
}).pipe(e.check(e.makeFilter((e) => e.enteredGeneration !== e.originalGeneration && e.dispatchAttempts > 0, { message: "Restart entry requires a dispatched command and a new generation" })))
|
|
39
|
+
]), s = e.Union([o, e.Struct({
|
|
40
|
+
...r.fields,
|
|
41
|
+
kind: e.Literal("not-reserved"),
|
|
42
|
+
reason: e.Literals(["admission", "journal"])
|
|
43
|
+
})]);
|
|
44
|
+
//#endregion
|
|
45
|
+
export { i as WorkflowRestartCommandSchema, o as WorkflowRestartObservationSchema, s as WorkflowRestartResultSchema, n as WorkflowRestartSelectionSchema, r as WorkflowRestartTargetSchema };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { WorkflowStartTerminalOutcomeSchema as e } from "./workflowStartOutcome.js";
|
|
2
|
+
import { Schema as t } from "effect";
|
|
3
|
+
//#region src/workflowRetry.ts
|
|
4
|
+
var n = t.NonEmptyString.pipe(t.check(t.makeFilter((e) => e.trim().length > 0, { message: "A workflow retry identity must not be blank" }))), r = t.Struct({
|
|
5
|
+
runId: n,
|
|
6
|
+
requestId: n
|
|
7
|
+
}), i = t.Struct({
|
|
8
|
+
...r.fields,
|
|
9
|
+
payloadOverride: t.optional(t.Unknown)
|
|
10
|
+
}), a = {
|
|
11
|
+
requestId: t.NonEmptyString,
|
|
12
|
+
originalRunId: t.NonEmptyString,
|
|
13
|
+
originalExecutionId: t.NonEmptyString,
|
|
14
|
+
workflowName: t.NonEmptyString
|
|
15
|
+
}, o = t.Union([
|
|
16
|
+
t.Struct({
|
|
17
|
+
...a,
|
|
18
|
+
kind: t.Literal("queued"),
|
|
19
|
+
nextAttemptAt: t.NullOr(t.Number)
|
|
20
|
+
}),
|
|
21
|
+
t.Struct({
|
|
22
|
+
...a,
|
|
23
|
+
kind: t.Literal("accepted"),
|
|
24
|
+
executionId: t.NonEmptyString
|
|
25
|
+
}).pipe(t.check(t.makeFilter((e) => e.executionId !== e.originalExecutionId, { message: "A retry cannot accept its original execution identity" }))),
|
|
26
|
+
t.Struct({
|
|
27
|
+
...a,
|
|
28
|
+
kind: t.Literal("unconfirmed"),
|
|
29
|
+
automaticRetryStopped: t.Boolean,
|
|
30
|
+
nextAttemptAt: t.NullOr(t.Number)
|
|
31
|
+
}).pipe(t.check(t.makeFilter((e) => !e.automaticRetryStopped || e.nextAttemptAt === null, { message: "Stopped automatic retries cannot have a next attempt" }))),
|
|
32
|
+
t.Struct({
|
|
33
|
+
...a,
|
|
34
|
+
kind: t.Literal("not-started"),
|
|
35
|
+
outcome: e
|
|
36
|
+
}),
|
|
37
|
+
t.Struct({
|
|
38
|
+
...a,
|
|
39
|
+
kind: t.Literal("rejected"),
|
|
40
|
+
failureId: t.NonEmptyString
|
|
41
|
+
})
|
|
42
|
+
]);
|
|
43
|
+
//#endregion
|
|
44
|
+
export { i as WorkflowRetryCommandSchema, o as WorkflowRetryObservationSchema, r as WorkflowRetryTargetSchema };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
//#region src/workflowStartOutcome.ts
|
|
3
|
+
var t = e.Union([
|
|
4
|
+
e.Struct({
|
|
5
|
+
kind: e.Literal("dropped"),
|
|
6
|
+
at: e.Number,
|
|
7
|
+
retryAfterMs: e.Number
|
|
8
|
+
}),
|
|
9
|
+
e.Struct({
|
|
10
|
+
kind: e.Literal("skipped"),
|
|
11
|
+
at: e.Number,
|
|
12
|
+
executionId: e.String,
|
|
13
|
+
runId: e.String
|
|
14
|
+
}),
|
|
15
|
+
e.Struct({
|
|
16
|
+
kind: e.Literal("superseded"),
|
|
17
|
+
at: e.Number
|
|
18
|
+
}),
|
|
19
|
+
e.Struct({
|
|
20
|
+
kind: e.Literal("cancelled"),
|
|
21
|
+
at: e.Number
|
|
22
|
+
}),
|
|
23
|
+
e.Struct({
|
|
24
|
+
kind: e.Literal("abandoned"),
|
|
25
|
+
at: e.Number,
|
|
26
|
+
failureId: e.NonEmptyString
|
|
27
|
+
})
|
|
28
|
+
]);
|
|
29
|
+
//#endregion
|
|
30
|
+
export { t as WorkflowStartTerminalOutcomeSchema };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.86.0",
|
|
4
4
|
"description": "The Voltro wire + plugin contract — defineQuery/Mutation/Action/Stream, definePlugin, sessions / JWT / API-keys, and the RPC protocol.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -63,16 +63,12 @@
|
|
|
63
63
|
"node": ">=24.0.0"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@
|
|
67
|
-
"@
|
|
68
|
-
"@voltro/database": "0.84.0",
|
|
69
|
-
"@voltro/logger": "0.84.0",
|
|
66
|
+
"@voltro/database": "0.86.0",
|
|
67
|
+
"@voltro/logger": "0.86.0",
|
|
70
68
|
"jose": "^6.2.12"
|
|
71
69
|
},
|
|
72
70
|
"peerDependencies": {
|
|
73
|
-
"
|
|
74
|
-
"@effect/rpc": "^0.76.2",
|
|
75
|
-
"effect": "^3.22.2"
|
|
71
|
+
"effect": "4.0.1"
|
|
76
72
|
},
|
|
77
73
|
"publishConfig": {
|
|
78
74
|
"access": "public"
|