@voltro/protocol 0.80.0 → 0.82.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 +1610 -6
- package/dist/apikey.d.ts +10 -2
- package/dist/index.d.ts +949 -76
- package/dist/index.js +904 -634
- package/dist/inspect.d.ts +67 -1
- package/dist/inspect.js +95 -61
- package/dist/jwt.d.ts +9 -1
- package/dist/rest.d.ts +121 -19
- package/dist/rest.js +255 -214
- package/dist/{restFormat-ChSVyufA.js → restFormat-EZ8k9ZcQ.js} +77 -60
- package/dist/{scopes-BbIyym8y.js → scopes-Di756ENh.js} +153 -102
- package/dist/session.d.ts +31 -0
- package/dist/session.js +49 -48
- package/dist/sessionHeader-DriTZhmv.js +7 -0
- package/package.json +3 -3
package/dist/inspect.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { ParseOptions } from 'effect/SchemaAST';
|
|
|
2
2
|
import { Schema } from 'effect';
|
|
3
3
|
|
|
4
4
|
export declare const decodeInspectBackfillInput: (u: unknown, overrideOptions?: ParseOptions) => {
|
|
5
|
-
readonly from: string;
|
|
6
5
|
readonly name: string;
|
|
7
6
|
readonly limit: number;
|
|
7
|
+
readonly from: string;
|
|
8
8
|
readonly to: string;
|
|
9
9
|
readonly requestId: string;
|
|
10
10
|
readonly confirm: boolean;
|
|
@@ -78,6 +78,72 @@ export declare type InspectReplicaSelection = {
|
|
|
78
78
|
readonly replicaIds: ReadonlyArray<string>;
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
+
export declare type InspectWorkflowExecutionObservation = typeof InspectWorkflowExecutionObservationSchema.Type;
|
|
82
|
+
|
|
83
|
+
/** A projection of engine evidence, not a run-row or recorder-entry claim.
|
|
84
|
+
* Deliberately excludes payloads, outputs, raw causes and journal identifiers. */
|
|
85
|
+
export declare const InspectWorkflowExecutionObservationSchema: Schema.Union<[Schema.Struct<{
|
|
86
|
+
kind: Schema.Literal<["not-observed"]>;
|
|
87
|
+
workflowName: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
88
|
+
executionId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
89
|
+
}>, Schema.Struct<{
|
|
90
|
+
kind: Schema.Literal<["pending"]>;
|
|
91
|
+
workflowName: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
92
|
+
executionId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
93
|
+
}>, Schema.Struct<{
|
|
94
|
+
kind: Schema.Literal<["delivery-failed"]>;
|
|
95
|
+
workflowName: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
96
|
+
executionId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
97
|
+
}>, Schema.Struct<{
|
|
98
|
+
kind: Schema.Literal<["suspended"]>;
|
|
99
|
+
workflowName: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
100
|
+
executionId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
101
|
+
}>, Schema.Struct<{
|
|
102
|
+
kind: Schema.Literal<["completed"]>;
|
|
103
|
+
outcome: Schema.Literal<["succeeded", "failed", "cancelled"]>;
|
|
104
|
+
workflowName: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
105
|
+
executionId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
106
|
+
}>]>;
|
|
107
|
+
|
|
108
|
+
export declare type InspectWorkflowExecutionTarget = typeof InspectWorkflowExecutionTargetSchema.Type;
|
|
109
|
+
|
|
110
|
+
/** Read an existing execution identity; this is never a start command. */
|
|
111
|
+
export declare const InspectWorkflowExecutionTargetSchema: Schema.Struct<{
|
|
112
|
+
workflowName: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
113
|
+
executionId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
114
|
+
}>;
|
|
115
|
+
|
|
116
|
+
export declare type InspectWorkflowQueuedStartObservation = typeof InspectWorkflowQueuedStartObservationSchema.Type;
|
|
117
|
+
|
|
118
|
+
/** Read-only admission evidence. Accepted means an execution exists, not that it succeeded. */
|
|
119
|
+
export declare const InspectWorkflowQueuedStartObservationSchema: Schema.Union<[Schema.Struct<{
|
|
120
|
+
kind: Schema.Literal<["pending"]>;
|
|
121
|
+
workflowName: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
122
|
+
receiptId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
123
|
+
tenantId: Schema.NullOr<Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>>;
|
|
124
|
+
}>, Schema.Struct<{
|
|
125
|
+
kind: Schema.Literal<["accepted"]>;
|
|
126
|
+
executionId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
127
|
+
workflowName: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
128
|
+
receiptId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
129
|
+
tenantId: Schema.NullOr<Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>>;
|
|
130
|
+
}>, Schema.Struct<{
|
|
131
|
+
kind: Schema.Literal<["not-executed"]>;
|
|
132
|
+
reason: Schema.Literal<["dropped", "skipped", "superseded", "cancelled", "abandoned", "rejected"]>;
|
|
133
|
+
workflowName: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
134
|
+
receiptId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
135
|
+
tenantId: Schema.NullOr<Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>>;
|
|
136
|
+
}>]>;
|
|
137
|
+
|
|
138
|
+
export declare type InspectWorkflowQueuedStartTarget = typeof InspectWorkflowQueuedStartTargetSchema.Type;
|
|
139
|
+
|
|
140
|
+
/** A caller receipt, never an execution or a reusable admission slot. */
|
|
141
|
+
export declare const InspectWorkflowQueuedStartTargetSchema: Schema.Struct<{
|
|
142
|
+
workflowName: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
143
|
+
receiptId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
|
|
144
|
+
tenantId: Schema.NullOr<Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>>;
|
|
145
|
+
}>;
|
|
146
|
+
|
|
81
147
|
export declare class InvalidReplicaSelection extends Error {
|
|
82
148
|
readonly name = "InvalidReplicaSelection";
|
|
83
149
|
}
|
package/dist/inspect.js
CHANGED
|
@@ -1,85 +1,119 @@
|
|
|
1
1
|
import { Schema as e } from "effect";
|
|
2
|
-
//#region src/
|
|
3
|
-
var t = e.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
//#region src/inspectWorkflowQueuedStart.ts
|
|
3
|
+
var t = e.NonEmptyString.pipe(e.maxLength(1024), e.filter((e) => e.trim().length > 0 && !/[\u0000-\u001f\u007f]/.test(e))), n = e.Struct({
|
|
4
|
+
workflowName: t,
|
|
5
|
+
receiptId: t,
|
|
6
|
+
tenantId: e.NullOr(t)
|
|
7
|
+
}), r = n.fields, i = e.Union(e.Struct({
|
|
8
|
+
...r,
|
|
9
|
+
kind: e.Literal("pending")
|
|
10
|
+
}), e.Struct({
|
|
11
|
+
...r,
|
|
12
|
+
kind: e.Literal("accepted"),
|
|
13
|
+
executionId: t
|
|
14
|
+
}), e.Struct({
|
|
15
|
+
...r,
|
|
16
|
+
kind: e.Literal("not-executed"),
|
|
17
|
+
reason: e.Literal("dropped", "skipped", "superseded", "cancelled", "abandoned", "rejected")
|
|
18
|
+
})), a = e.String.pipe(e.filter((e) => Number.isFinite(Date.parse(e)))), o = e.String.pipe(e.minLength(1), e.maxLength(256)), s = e.Struct({
|
|
19
|
+
id: o,
|
|
20
|
+
name: o,
|
|
21
|
+
from: a,
|
|
22
|
+
to: a,
|
|
8
23
|
occurrences: e.Array(e.Number),
|
|
9
24
|
status: e.Literal("queued", "running", "completed", "interrupted"),
|
|
10
25
|
owner: e.NullOr(e.String),
|
|
11
|
-
createdAt:
|
|
12
|
-
updatedAt:
|
|
26
|
+
createdAt: a,
|
|
27
|
+
updatedAt: a,
|
|
13
28
|
cursor: e.Number,
|
|
14
29
|
activeIndex: e.NullOr(e.Number),
|
|
15
30
|
results: e.Array(e.Struct({
|
|
16
|
-
runId:
|
|
31
|
+
runId: o,
|
|
17
32
|
status: e.Literal("succeeded", "failed", "skipped", "missed")
|
|
18
33
|
})),
|
|
19
34
|
error: e.NullOr(e.String)
|
|
20
|
-
}),
|
|
21
|
-
requestId:
|
|
22
|
-
name:
|
|
23
|
-
from:
|
|
24
|
-
to:
|
|
35
|
+
}), c = e.Struct({
|
|
36
|
+
requestId: o,
|
|
37
|
+
name: o,
|
|
38
|
+
from: a,
|
|
39
|
+
to: a,
|
|
25
40
|
confirm: e.Boolean,
|
|
26
41
|
limit: e.Number.pipe(e.int(), e.between(1, 1e4))
|
|
27
|
-
}),
|
|
42
|
+
}), l = e.decodeUnknownSync(c), u = e.Union(e.Struct({
|
|
28
43
|
kind: e.Literal("accepted"),
|
|
29
|
-
request:
|
|
44
|
+
request: s
|
|
30
45
|
}), e.Struct({
|
|
31
46
|
kind: e.Literal("refused"),
|
|
32
|
-
name:
|
|
47
|
+
name: o,
|
|
33
48
|
occurrences: e.Number,
|
|
34
49
|
reason: e.String
|
|
35
|
-
})),
|
|
50
|
+
})), d = class extends Error {
|
|
36
51
|
name = "ScheduleBackfillRequestConflict";
|
|
37
|
-
},
|
|
52
|
+
}, f = e.NonEmptyString.pipe(e.maxLength(1024), e.filter((e) => e.trim().length > 0 && !/[\u0000-\u001f\u007f]/.test(e))), p = e.Struct({
|
|
53
|
+
workflowName: f,
|
|
54
|
+
executionId: f
|
|
55
|
+
}), m = p.fields, h = e.Union(e.Struct({
|
|
56
|
+
...m,
|
|
57
|
+
kind: e.Literal("not-observed")
|
|
58
|
+
}), e.Struct({
|
|
59
|
+
...m,
|
|
60
|
+
kind: e.Literal("pending")
|
|
61
|
+
}), e.Struct({
|
|
62
|
+
...m,
|
|
63
|
+
kind: e.Literal("delivery-failed")
|
|
64
|
+
}), e.Struct({
|
|
65
|
+
...m,
|
|
66
|
+
kind: e.Literal("suspended")
|
|
67
|
+
}), e.Struct({
|
|
68
|
+
...m,
|
|
69
|
+
kind: e.Literal("completed"),
|
|
70
|
+
outcome: e.Literal("succeeded", "failed", "cancelled")
|
|
71
|
+
})), g = (e) => typeof e == "object" && e && !Array.isArray(e) ? e : void 0, _ = (e) => Array.isArray(e) && e.every((e) => g(e) !== void 0) ? e : void 0, v = (e) => typeof e == "number" && Number.isFinite(e) ? e : 0, y = /* @__PURE__ */ new Set([
|
|
38
72
|
"counter",
|
|
39
73
|
"gauge",
|
|
40
74
|
"histogram",
|
|
41
75
|
"summary",
|
|
42
76
|
"frequency"
|
|
43
|
-
]),
|
|
44
|
-
let t =
|
|
45
|
-
return t?.every((e) => typeof e.name == "string" &&
|
|
46
|
-
},
|
|
47
|
-
let t =
|
|
77
|
+
]), b = (e) => {
|
|
78
|
+
let t = _(e);
|
|
79
|
+
return t?.every((e) => typeof e.name == "string" && y.has(String(e.type))) ? t : void 0;
|
|
80
|
+
}, x = (e) => {
|
|
81
|
+
let t = g(e), n = g(t?.data), r = _(n?.results);
|
|
48
82
|
if (!t || !n?.selection || !r) return e;
|
|
49
83
|
let i = r.filter((e) => e.status === "answered").map((e) => ({
|
|
50
84
|
replicaId: String(e.replicaId),
|
|
51
|
-
observation:
|
|
52
|
-
data:
|
|
85
|
+
observation: g(e.observation),
|
|
86
|
+
data: g(e.observation)?.data
|
|
53
87
|
}));
|
|
54
88
|
if (i.length === 0) throw Error("None of the selected replicas answered. Check replica availability and retry.");
|
|
55
89
|
let a = i.map((e) => ({
|
|
56
90
|
...e,
|
|
57
|
-
object:
|
|
58
|
-
})), o = (e) => a.reduce((t, n) => t +
|
|
91
|
+
object: g(e.data)
|
|
92
|
+
})), o = (e) => a.reduce((t, n) => t + v(n.object?.[e]), 0), s = Math.max(...i.map((e) => v(e.observation?.capturedAt))), c = {
|
|
59
93
|
capturedAt: s,
|
|
60
94
|
bufferSize: o("bufferSize"),
|
|
61
95
|
bufferCapacity: o("bufferCapacity")
|
|
62
|
-
},
|
|
63
|
-
if (a.every((e) =>
|
|
64
|
-
let e = a.flatMap((e) =>
|
|
96
|
+
}, l;
|
|
97
|
+
if (a.every((e) => _(e.object?.records))) {
|
|
98
|
+
let e = a.flatMap((e) => _(e.object?.records).map((t) => ({
|
|
65
99
|
...t,
|
|
66
100
|
fields: {
|
|
67
|
-
...
|
|
101
|
+
...g(t.fields),
|
|
68
102
|
"voltro.replica": e.replicaId
|
|
69
103
|
}
|
|
70
|
-
}))).sort((e, t) =>
|
|
71
|
-
|
|
72
|
-
...
|
|
104
|
+
}))).sort((e, t) => v(e.ts) - v(t.ts));
|
|
105
|
+
l = {
|
|
106
|
+
...c,
|
|
73
107
|
returned: e.length,
|
|
74
108
|
records: e
|
|
75
109
|
};
|
|
76
|
-
} else if (a.every((e) =>
|
|
110
|
+
} else if (a.every((e) => _(e.object?.traces))) {
|
|
77
111
|
let e = /* @__PURE__ */ new Map();
|
|
78
|
-
for (let t of a) for (let n of
|
|
79
|
-
let r = String(n.traceId), i = (
|
|
112
|
+
for (let t of a) for (let n of _(t.object?.traces)) {
|
|
113
|
+
let r = String(n.traceId), i = (_(n.spans) ?? []).map((e) => ({
|
|
80
114
|
...e,
|
|
81
115
|
attributes: {
|
|
82
|
-
...
|
|
116
|
+
...g(e.attributes),
|
|
83
117
|
"voltro.replica": t.replicaId
|
|
84
118
|
}
|
|
85
119
|
})), a = e.get(r) ?? [];
|
|
@@ -89,7 +123,7 @@ var t = e.String.pipe(e.filter((e) => Number.isFinite(Date.parse(e)))), n = e.St
|
|
|
89
123
|
}), e.set(r, a);
|
|
90
124
|
}
|
|
91
125
|
let t = [...e.values()].map((e) => {
|
|
92
|
-
let t = e.flatMap((e) =>
|
|
126
|
+
let t = e.flatMap((e) => _(e.spans) ?? []).sort((e, t) => v(e.startMs) - v(t.startMs)), n = [...e].sort((e, t) => v(e.startMs) - v(t.startMs))[0], r = Math.min(...e.map((e) => v(e.startMs))), i = Math.max(...e.map((e) => v(e.startMs) + v(e.durationMs)));
|
|
93
127
|
return {
|
|
94
128
|
...n,
|
|
95
129
|
startMs: r,
|
|
@@ -97,65 +131,65 @@ var t = e.String.pipe(e.filter((e) => Number.isFinite(Date.parse(e)))), n = e.St
|
|
|
97
131
|
spanCount: t.length,
|
|
98
132
|
hasError: e.some((e) => e.hasError === !0),
|
|
99
133
|
spans: t,
|
|
100
|
-
...e.some((e) => e.deliveryCount !== void 0) ? { deliveryCount: e.reduce((e, t) => e +
|
|
101
|
-
...e.some((e) => e.lastDeliveryMs !== void 0) ? { lastDeliveryMs: Math.max(...e.map((e) =>
|
|
134
|
+
...e.some((e) => e.deliveryCount !== void 0) ? { deliveryCount: e.reduce((e, t) => e + v(t.deliveryCount), 0) } : {},
|
|
135
|
+
...e.some((e) => e.lastDeliveryMs !== void 0) ? { lastDeliveryMs: Math.max(...e.map((e) => v(e.lastDeliveryMs))) } : {}
|
|
102
136
|
};
|
|
103
137
|
}).sort((e, t) => t.startMs - e.startMs);
|
|
104
|
-
|
|
105
|
-
...
|
|
138
|
+
l = {
|
|
139
|
+
...c,
|
|
106
140
|
returned: t.length,
|
|
107
141
|
traces: t
|
|
108
142
|
};
|
|
109
|
-
} else if (a.every((e) =>
|
|
143
|
+
} else if (a.every((e) => _(e.object?.buckets) && typeof e.object?.windowMs == "number")) l = {
|
|
110
144
|
capturedAt: s,
|
|
111
|
-
windowMs: Math.max(...a.map((e) =>
|
|
145
|
+
windowMs: Math.max(...a.map((e) => v(e.object?.windowMs))),
|
|
112
146
|
totalSamples: o("totalSamples"),
|
|
113
|
-
buckets: a.flatMap((e) =>
|
|
147
|
+
buckets: a.flatMap((e) => _(e.object?.buckets).map((t) => ({
|
|
114
148
|
...t,
|
|
115
149
|
tag: `${String(t.tag)} [${e.replicaId}]`
|
|
116
150
|
})))
|
|
117
151
|
};
|
|
118
|
-
else if (i.every((e) =>
|
|
152
|
+
else if (i.every((e) => b(e.data))) l = i.flatMap((e) => b(e.data).map((t) => ({
|
|
119
153
|
...t,
|
|
120
154
|
labels: {
|
|
121
|
-
...
|
|
155
|
+
...g(t.labels),
|
|
122
156
|
voltro_replica: e.replicaId
|
|
123
157
|
}
|
|
124
158
|
})));
|
|
125
|
-
else if (a.every((e) =>
|
|
159
|
+
else if (a.every((e) => b(e.object?.samples))) l = { samples: a.flatMap((e) => b(e.object?.samples).map((t) => ({
|
|
126
160
|
...t,
|
|
127
161
|
labels: {
|
|
128
|
-
...
|
|
162
|
+
...g(t.labels),
|
|
129
163
|
voltro_replica: e.replicaId
|
|
130
164
|
}
|
|
131
165
|
}))) };
|
|
132
|
-
else if (r.length === 1 && i.length === 1)
|
|
166
|
+
else if (r.length === 1 && i.length === 1) l = i[0].data;
|
|
133
167
|
else throw Error("This view must compare observations per replica; its values cannot be safely combined.");
|
|
134
168
|
return {
|
|
135
169
|
...t,
|
|
136
|
-
data:
|
|
170
|
+
data: l
|
|
137
171
|
};
|
|
138
|
-
},
|
|
172
|
+
}, S = class extends Error {
|
|
139
173
|
name = "InvalidReplicaSelection";
|
|
140
|
-
},
|
|
174
|
+
}, C = (e) => {
|
|
141
175
|
let t = e.getAll("replicas"), n = e.getAll("replica");
|
|
142
176
|
if (t.length !== 0 || n.length !== 0) {
|
|
143
|
-
if (t.length > 1 || t.length === 1 && t[0] !== "all" || t.length > 0 && n.length > 0) throw new
|
|
177
|
+
if (t.length > 1 || t.length === 1 && t[0] !== "all" || t.length > 0 && n.length > 0) throw new S("Choose all replicas or explicit replica IDs, not both.");
|
|
144
178
|
if (t[0] === "all") return { mode: "all" };
|
|
145
|
-
if (n.some((e) => e.trim().length === 0 || e.length > 512 || /[\u0000-\u001f\u007f]/.test(e))) throw new
|
|
179
|
+
if (n.some((e) => e.trim().length === 0 || e.length > 512 || /[\u0000-\u001f\u007f]/.test(e))) throw new S("Replica IDs must be nonempty and contain no control characters.");
|
|
146
180
|
return {
|
|
147
181
|
mode: "selected",
|
|
148
182
|
replicaIds: [...new Set(n)].sort()
|
|
149
183
|
};
|
|
150
184
|
}
|
|
151
|
-
},
|
|
185
|
+
}, w = (e, t) => {
|
|
152
186
|
let n = e.indexOf("#"), r = n < 0 ? "" : e.slice(n), i = n < 0 ? e : e.slice(0, n), a = i.indexOf("?"), o = a < 0 ? i : i.slice(0, a), s = new URLSearchParams(a < 0 ? "" : i.slice(a + 1));
|
|
153
187
|
if (s.delete("replica"), s.delete("replicas"), t.mode === "all") s.set("replicas", "all");
|
|
154
188
|
else {
|
|
155
|
-
if (t.replicaIds.length === 0) throw new
|
|
189
|
+
if (t.replicaIds.length === 0) throw new S("Select at least one replica.");
|
|
156
190
|
for (let e of [...new Set(t.replicaIds)].sort()) s.append("replica", e);
|
|
157
191
|
}
|
|
158
|
-
return
|
|
192
|
+
return C(s), `${o}?${s}${r}`;
|
|
159
193
|
};
|
|
160
194
|
//#endregion
|
|
161
|
-
export {
|
|
195
|
+
export { c as InspectBackfillInputSchema, s as InspectBackfillReceiptSchema, u as InspectBackfillResultSchema, h as InspectWorkflowExecutionObservationSchema, p as InspectWorkflowExecutionTargetSchema, i as InspectWorkflowQueuedStartObservationSchema, n as InspectWorkflowQueuedStartTargetSchema, S as InvalidReplicaSelection, d as ScheduleBackfillRequestConflict, l as decodeInspectBackfillInput, C as parseReplicaSelection, x as projectReplicaObservation, w as withReplicaSelection };
|
package/dist/jwt.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ declare interface AuthStrategy {
|
|
|
26
26
|
readonly resolve: (input: AuthStrategyInput) => Promise<StrategyResolution> | StrategyResolution;
|
|
27
27
|
/**
|
|
28
28
|
* The bearer-token PREFIX this strategy claims, when it gates on one
|
|
29
|
-
* (`'sk_'`, `'
|
|
29
|
+
* (`'sk_'`, `'acme_'`). Declared so a collision is DETECTABLE.
|
|
30
30
|
*
|
|
31
31
|
* Two strategies claiming the same prefix is not a harmless duplicate: the
|
|
32
32
|
* chain is first-match-wins, so whichever runs first decides the Subject —
|
|
@@ -339,6 +339,14 @@ declare const Subject: Schema.Union<[Schema.Struct<{
|
|
|
339
339
|
tenantId: typeof Schema.Null;
|
|
340
340
|
scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
341
341
|
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
342
|
+
}>, Schema.Struct<{
|
|
343
|
+
type: Schema.Literal<["service"]>;
|
|
344
|
+
id: typeof Schema.String;
|
|
345
|
+
/** The calling app's name, as the callee lists it in `serviceCallers`. */
|
|
346
|
+
service: typeof Schema.String;
|
|
347
|
+
tenantId: typeof Schema.Null;
|
|
348
|
+
scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
349
|
+
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
342
350
|
}>]>;
|
|
343
351
|
|
|
344
352
|
declare type Subject = typeof Subject.Type;
|
package/dist/rest.d.ts
CHANGED
|
@@ -30,6 +30,9 @@ declare interface ActionProcedureDescriptor<Name extends string, Input extends S
|
|
|
30
30
|
* `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
|
|
31
31
|
* the only two shapes `security.defaultDeny` accepts. */
|
|
32
32
|
readonly openAccess: string | undefined;
|
|
33
|
+
/** Only these apps of the project may call this procedure — see `service` on
|
|
34
|
+
* the definer's options. Absent → the app's own clients call it. */
|
|
35
|
+
readonly service: ServiceSpec | undefined;
|
|
33
36
|
/** Opt this action into a public REST endpoint (innovation/11). */
|
|
34
37
|
readonly publicApi: PublicApiSpec | undefined;
|
|
35
38
|
/** Opt this action into the auto-synthesized agent toolset (innovation/07). */
|
|
@@ -138,7 +141,7 @@ export declare const collectPublicApiRoutes: (entries: ReadonlyArray<PublicApiEn
|
|
|
138
141
|
* because that is where the normalised decision lands, and because every
|
|
139
142
|
* enforcement path reads the descriptor's array and nothing else.
|
|
140
143
|
*/
|
|
141
|
-
declare type DeclaredAccess<Input = unknown> = ReadonlyArray<AnyGuardSpec<Input> | OpenAccessSpec>;
|
|
144
|
+
declare type DeclaredAccess<Input = unknown> = ReadonlyArray<AnyGuardSpec<Input> | OpenAccessSpec | ServiceAccessSpec>;
|
|
142
145
|
|
|
143
146
|
/** The `_tag` literals a descriptor's `error:` union declares (empty for none). */
|
|
144
147
|
export declare const declaredErrorTags: (error: Schema.Schema.All | undefined) => ReadonlySet<string>;
|
|
@@ -178,15 +181,16 @@ export declare const deriveResource: (tag: string, spec: PublicApiSpec, descript
|
|
|
178
181
|
|
|
179
182
|
/**
|
|
180
183
|
* Dispatch a request across REST routes that SHARE a path. Every REST route
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
184
|
+
* gates its own method, returning a precise 405 BEFORE any side effect when the
|
|
185
|
+
* method isn't its own — so probing the group in order and taking the FIRST
|
|
186
|
+
* non-405 result yields the route that owns the request's method. This is what
|
|
187
|
+
* lets the standard GET + POST on one resource path coexist: `rpcServer.ts` in
|
|
188
|
+
* `@voltro/runtime` mounts ONE dispatcher per
|
|
189
|
+
* path (the underlying router rejects two mounts on the same
|
|
190
|
+
* `(method, path)`). A single-route group returns that route's result directly
|
|
191
|
+
* (its own 405 included); when no route in the group owns the method, the last
|
|
192
|
+
* 405 stands — its `Allow` names every method the path serves, because
|
|
193
|
+
* `restRoutesToHttpRoutes` gives each route its path's full set.
|
|
190
194
|
*
|
|
191
195
|
* `group` must be non-empty (the serve pipeline only builds a dispatcher for a
|
|
192
196
|
* path that has at least one route).
|
|
@@ -283,14 +287,24 @@ declare interface IdempotencyStore {
|
|
|
283
287
|
/** Current record for (scope, key), or null. Read-side (inspect/dashboard). */
|
|
284
288
|
readonly get: (scope: string, key: string) => Promise<IdempotencyRecord | null>;
|
|
285
289
|
/**
|
|
286
|
-
* Atomically claim (scope, key) IF absent OR stale
|
|
290
|
+
* Atomically claim (scope, key) IF absent OR stale — a COMPLETED record older
|
|
291
|
+
* than `ttlMs` (the replay window), or an IN-FLIGHT one whose lease
|
|
292
|
+
* (`inFlightLeaseMs`, renewed by the live handler) lapsed:
|
|
287
293
|
* write a fresh `in_flight` row and return `'claimed'`. Otherwise return the
|
|
288
294
|
* EXISTING (still-fresh) record. The atomicity here is the whole game — two
|
|
289
295
|
* concurrent same-key requests both hit this, exactly one gets `'claimed'`,
|
|
290
296
|
* the loser reads the winner's record. In SQL this is one
|
|
291
297
|
* `INSERT … ON CONFLICT DO UPDATE … WHERE existing.createdAt < now - ttl`.
|
|
292
298
|
*/
|
|
293
|
-
readonly claim: (scope: string, key: string, now: number, ttlMs: number) => Promise<'claimed' | IdempotencyRecord>;
|
|
299
|
+
readonly claim: (scope: string, key: string, now: number, ttlMs: number, inFlightLeaseMs: number) => Promise<'claimed' | IdempotencyRecord>;
|
|
300
|
+
/**
|
|
301
|
+
* Extend a RUNNING claim's lease: bump its timestamp while it is still
|
|
302
|
+
* `in_flight` (a completed or released claim is left alone). The server calls
|
|
303
|
+
* it while the handler runs; a process that dies stops calling it, and its
|
|
304
|
+
* claim goes stale after `inFlightLeaseMs` instead of after the whole replay
|
|
305
|
+
* window.
|
|
306
|
+
*/
|
|
307
|
+
readonly renew: (scope: string, key: string, now: number) => Promise<void>;
|
|
294
308
|
/** Flip the claim to `completed` and store the response. */
|
|
295
309
|
readonly complete: (scope: string, key: string, response: IdempotencyResponse, now: number) => Promise<void>;
|
|
296
310
|
/** Drop the claim (handler errored → a retry may re-process). */
|
|
@@ -370,6 +384,9 @@ declare interface MutationProcedureDescriptor<Name extends string, Input extends
|
|
|
370
384
|
* `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
|
|
371
385
|
* the only two shapes `security.defaultDeny` accepts. */
|
|
372
386
|
readonly openAccess: string | undefined;
|
|
387
|
+
/** Only these apps of the project may call this procedure — see `service` on
|
|
388
|
+
* the definer's options. Absent → the app's own clients call it. */
|
|
389
|
+
readonly service: ServiceSpec | undefined;
|
|
373
390
|
/** Opt this mutation into a public REST endpoint (innovation/11). */
|
|
374
391
|
readonly publicApi: PublicApiSpec | undefined;
|
|
375
392
|
/** Opt this mutation into the auto-synthesized agent toolset (innovation/07). */
|
|
@@ -423,7 +440,7 @@ declare interface NestedTargetFields<Input = unknown> {
|
|
|
423
440
|
/** Guard WHICH cached query entries this target patches: only entries whose
|
|
424
441
|
* CURRENT value satisfies the predicate. Pure + browser-safe. Prevents a
|
|
425
442
|
* patch from bleeding across sibling subscriptions that share a source table
|
|
426
|
-
* (the guard
|
|
443
|
+
* (the guard an app would otherwise hand-write as `roadmap.id === input.roadmapId`). Absent →
|
|
427
444
|
* every entry matching the target `table` is patched. */
|
|
428
445
|
readonly match?: ((value: unknown, input: Input) => boolean) | undefined;
|
|
429
446
|
}
|
|
@@ -504,11 +521,17 @@ declare interface PluginErrorSchema {
|
|
|
504
521
|
|
|
505
522
|
/** A public raw-HTTP route a plugin serves on the framework listener. */
|
|
506
523
|
declare interface PluginHttpRoute {
|
|
507
|
-
/** HTTP method, or `'*'` for any (the handler decides).
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
524
|
+
/** HTTP method, or `'*'` for any (the handler decides). The server mounts the
|
|
525
|
+
* path for exactly the methods its routes declare; `'*'` mounts it for every
|
|
526
|
+
* method the router knows (34), so keep that for a route that really serves
|
|
527
|
+
* them — see `answersOtherMethods` for one that only wants to refuse them. */
|
|
511
528
|
readonly method: '*' | 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
|
|
529
|
+
/** `true`: the handler gates its own method, so a request with a method the
|
|
530
|
+
* path does not declare is still handed to it — to answer with a precise
|
|
531
|
+
* `405` and `Allow` instead of the router's `404`. The path is still mounted
|
|
532
|
+
* only for `method`. A REST route sets it. Without it, a route that declares
|
|
533
|
+
* one method never sees another. */
|
|
534
|
+
readonly answersOtherMethods?: boolean | undefined;
|
|
512
535
|
/** Absolute path, e.g. `/_voltro/storage`. Matched byte for byte: the
|
|
513
536
|
* router folds case and a trailing slash to FIND the route, the dispatcher
|
|
514
537
|
* then asks whether the request's path is one the route declared — so
|
|
@@ -776,7 +799,8 @@ export declare const problemTypeOf: (tag: string) => string;
|
|
|
776
799
|
* and the status `errorStatus` assigns (`422` by default, the status the
|
|
777
800
|
* OpenAPI projection already documents for a typed error); a plugin's
|
|
778
801
|
* cross-cutting error answers under the status the plugin names. Anything
|
|
779
|
-
* else is rethrown for the desugar's shared mapping (`ScopeError` → 403/401,
|
|
802
|
+
* else is rethrown for the desugar's shared mapping (`ScopeError` → 403/401,
|
|
803
|
+
* a resource guard's `NoSuchResource` → 404, a
|
|
780
804
|
* `status`-carrying error → itself, the rest → 500). The body is the tagged
|
|
781
805
|
* `{ error, …fields }` form; `projectedFailureFor` speaks the profile.
|
|
782
806
|
*/
|
|
@@ -963,6 +987,13 @@ declare interface PublicApiSpec {
|
|
|
963
987
|
* optional, the rest inherited. See `PublicApiProfileConfig`.
|
|
964
988
|
*/
|
|
965
989
|
readonly format?: PublicApiProfileConfig;
|
|
990
|
+
/**
|
|
991
|
+
* GET only: how long a CDN may keep a successful answer, and the tags it can
|
|
992
|
+
* purge it by (`Cache-Tag`, `Surrogate-Key`). A request with a credential is
|
|
993
|
+
* always answered `private` — see `RestCache`. `tags` receives the answer as
|
|
994
|
+
* it goes on the wire.
|
|
995
|
+
*/
|
|
996
|
+
readonly cache?: RestCache;
|
|
966
997
|
}
|
|
967
998
|
|
|
968
999
|
/**
|
|
@@ -1052,6 +1083,9 @@ declare interface QueryProcedureDescriptor<Name extends string, Input extends Sc
|
|
|
1052
1083
|
* `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
|
|
1053
1084
|
* the only two shapes `security.defaultDeny` accepts. */
|
|
1054
1085
|
readonly openAccess: string | undefined;
|
|
1086
|
+
/** Only these apps of the project may call this procedure — see `service` on
|
|
1087
|
+
* the definer's options. Absent → the app's own clients call it. */
|
|
1088
|
+
readonly service: ServiceSpec | undefined;
|
|
1055
1089
|
/** Opt this query into a public REST endpoint (innovation/11). */
|
|
1056
1090
|
readonly publicApi: PublicApiSpec | undefined;
|
|
1057
1091
|
/** Opt this query into the auto-synthesized agent toolset (innovation/07). */
|
|
@@ -1153,6 +1187,27 @@ export declare interface RestByteResponse {
|
|
|
1153
1187
|
readonly headers?: Readonly<Record<string, string | ReadonlyArray<string>>>;
|
|
1154
1188
|
}
|
|
1155
1189
|
|
|
1190
|
+
/**
|
|
1191
|
+
* Caching for a GET route's successful (200) answers.
|
|
1192
|
+
*
|
|
1193
|
+
* An anonymous request's answer is `public` for `maxAge` seconds (`max-age`
|
|
1194
|
+
* and `s-maxage`), `stale-while-revalidate` when set, and carries `tags` as
|
|
1195
|
+
* `Cache-Tag` (comma-separated) and `Surrogate-Key` (space-separated) — the
|
|
1196
|
+
* headers CDNs purge by. A request that carries a credential (`Authorization`
|
|
1197
|
+
* or a cookie) gets `private, no-cache` and no tags, whatever is declared:
|
|
1198
|
+
* what a key or a session may see must never land in a shared cache. Every
|
|
1199
|
+
* such answer says `Vary: Authorization, Cookie`. Errors are not affected.
|
|
1200
|
+
*/
|
|
1201
|
+
export declare interface RestCache {
|
|
1202
|
+
/** Seconds a cache may serve the answer without asking again. */
|
|
1203
|
+
readonly maxAge: number;
|
|
1204
|
+
/** Seconds after that a stale answer may still be served while the cache refetches. */
|
|
1205
|
+
readonly staleWhileRevalidate?: number;
|
|
1206
|
+
/** Purge tags from the answer as it goes on the wire, and the input.
|
|
1207
|
+
* A tag with whitespace or a comma is dropped (no header can carry it). */
|
|
1208
|
+
readonly tags?: (output: unknown, input: unknown) => ReadonlyArray<string>;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1156
1211
|
export declare type RestDates = 'iso' | 'epoch-ms';
|
|
1157
1212
|
|
|
1158
1213
|
export declare type RestEnvelope = 'none' | 'data' | 'jsonapi';
|
|
@@ -1244,6 +1299,11 @@ export declare interface RestGuardRejection {
|
|
|
1244
1299
|
readonly message: string;
|
|
1245
1300
|
}
|
|
1246
1301
|
|
|
1302
|
+
/** What a REST handler may return: the decoded output, a `response(...)` with
|
|
1303
|
+
* its own status and headers, an `sse(...)` stream, or `bytes(...)` for a
|
|
1304
|
+
* non-JSON body (a file, an export, an XML document). */
|
|
1305
|
+
export declare type RestHandlerResult<O> = O | RestResponse<O> | RestStreamResponse | RestByteResponse;
|
|
1306
|
+
|
|
1247
1307
|
export declare type RestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
|
|
1248
1308
|
|
|
1249
1309
|
export declare type RestNaming = 'camelCase' | 'snake_case';
|
|
@@ -1307,7 +1367,7 @@ export declare interface RestRouteDescriptor<I, O> {
|
|
|
1307
1367
|
/** Output schema. The handler returns the decoded `Type`; the desugar
|
|
1308
1368
|
* encodes it to JSON for the `200` response. */
|
|
1309
1369
|
readonly output: Schema.Schema<O, any, never>;
|
|
1310
|
-
readonly handler: (input: I, ctx: RestRouteContext) => Promise<
|
|
1370
|
+
readonly handler: (input: I, ctx: RestRouteContext) => Promise<RestHandlerResult<O>> | RestHandlerResult<O>;
|
|
1311
1371
|
readonly summary?: string;
|
|
1312
1372
|
readonly description?: string;
|
|
1313
1373
|
readonly example?: RestRouteExample;
|
|
@@ -1364,6 +1424,9 @@ export declare interface RestRouteDescriptor<I, O> {
|
|
|
1364
1424
|
* of the JSON), so it is correct across content-encodings — the
|
|
1365
1425
|
* transport's compression varies the bytes, not the representation. */
|
|
1366
1426
|
readonly etag?: boolean;
|
|
1427
|
+
/** GET only: how long a shared cache (a CDN) may keep a successful answer,
|
|
1428
|
+
* and the tags it can be purged by. See {@link RestCache}. */
|
|
1429
|
+
readonly cache?: RestCache;
|
|
1367
1430
|
}
|
|
1368
1431
|
|
|
1369
1432
|
/** Intentional non-2xx REST control flow with an application-defined JSON
|
|
@@ -1490,6 +1553,37 @@ export declare const routeAcceptsPath: (route: {
|
|
|
1490
1553
|
readonly match?: "prefix" | "exact" | undefined;
|
|
1491
1554
|
}, path: string) => boolean;
|
|
1492
1555
|
|
|
1556
|
+
/**
|
|
1557
|
+
* The runtime-erased form of a procedure's `service: { callers }` — "only these
|
|
1558
|
+
* apps of the project may call this, and nobody else".
|
|
1559
|
+
*
|
|
1560
|
+
* A guard entry for the reason `OpenAccessSpec` is one: every enforcement path
|
|
1561
|
+
* is handed the `guards` array and nothing else, so a decision that lived on a
|
|
1562
|
+
* separate descriptor field would be enforced by the paths somebody remembered
|
|
1563
|
+
* and skipped by the rest.
|
|
1564
|
+
*
|
|
1565
|
+
* It admits exactly a `service` subject whose name is listed. `admin:full` does
|
|
1566
|
+
* NOT bypass it: a service procedure is a machine contract between two apps,
|
|
1567
|
+
* and a browser session holding the admin scope is not one of the parties.
|
|
1568
|
+
*/
|
|
1569
|
+
declare interface ServiceAccessSpec {
|
|
1570
|
+
/** The calling apps' names, as the callee declares them in `serviceCallers`.
|
|
1571
|
+
* Non-empty by construction — the definers refuse an empty list. */
|
|
1572
|
+
readonly service: ReadonlyArray<string>;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
/**
|
|
1576
|
+
* A service procedure's declaration: which apps of the project may call it.
|
|
1577
|
+
*
|
|
1578
|
+
* service: { callers: ['worker'] }
|
|
1579
|
+
*
|
|
1580
|
+
* The names are the CALLING apps' names as this app lists them in
|
|
1581
|
+
* `serviceCallers` — the same name the caller's credential resolves to.
|
|
1582
|
+
*/
|
|
1583
|
+
declare interface ServiceSpec {
|
|
1584
|
+
readonly callers: ReadonlyArray<string>;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1493
1587
|
/**
|
|
1494
1588
|
* Build a streaming (Server-Sent Events) handler result. `subscribe` gets an
|
|
1495
1589
|
* `emit` for one already-framed SSE chunk and MUST return an unsubscribe, which
|
|
@@ -1558,6 +1652,14 @@ declare const Subject: Schema.Union<[Schema.Struct<{
|
|
|
1558
1652
|
tenantId: typeof Schema.Null;
|
|
1559
1653
|
scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
1560
1654
|
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
1655
|
+
}>, Schema.Struct<{
|
|
1656
|
+
type: Schema.Literal<["service"]>;
|
|
1657
|
+
id: typeof Schema.String;
|
|
1658
|
+
/** The calling app's name, as the callee lists it in `serviceCallers`. */
|
|
1659
|
+
service: typeof Schema.String;
|
|
1660
|
+
tenantId: typeof Schema.Null;
|
|
1661
|
+
scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
1662
|
+
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
1561
1663
|
}>]>;
|
|
1562
1664
|
|
|
1563
1665
|
declare type Subject = typeof Subject.Type;
|