@aletheia-dev/api-client 0.1.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 +15 -0
- package/LICENSE +202 -0
- package/README.md +22 -0
- package/dist/index.cjs +948 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2215 -0
- package/dist/index.d.ts +2215 -0
- package/dist/index.js +1065 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1065 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var ApiError = class _ApiError extends Error {
|
|
3
|
+
constructor(message, status, code, details) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.details = details;
|
|
8
|
+
this.name = "ApiError";
|
|
9
|
+
}
|
|
10
|
+
status;
|
|
11
|
+
code;
|
|
12
|
+
details;
|
|
13
|
+
/** Builds an ApiError from a response status and its (possibly non-JSON) body. */
|
|
14
|
+
static fromResponse(status, statusText, body) {
|
|
15
|
+
if (isApiErrorBody(body)) {
|
|
16
|
+
return new _ApiError(body.error.message, status, body.error.code, body.error.details);
|
|
17
|
+
}
|
|
18
|
+
const message = statusText ? `HTTP ${status} ${statusText}` : `HTTP ${status}`;
|
|
19
|
+
return new _ApiError(message, status, "http_error", body);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function isApiErrorBody(value) {
|
|
23
|
+
if (typeof value !== "object" || value === null) return false;
|
|
24
|
+
const error = value.error;
|
|
25
|
+
if (typeof error !== "object" || error === null) return false;
|
|
26
|
+
const { code, message } = error;
|
|
27
|
+
return typeof code === "string" && typeof message === "string";
|
|
28
|
+
}
|
|
29
|
+
function isApiError(value) {
|
|
30
|
+
return value instanceof ApiError;
|
|
31
|
+
}
|
|
32
|
+
function isUnauthenticated(value) {
|
|
33
|
+
return isApiError(value) && value.code === "unauthenticated";
|
|
34
|
+
}
|
|
35
|
+
var FORBIDDEN_CODES = ["forbidden", "tenant_not_provisioned"];
|
|
36
|
+
function isForbidden(value) {
|
|
37
|
+
return isApiError(value) && FORBIDDEN_CODES.includes(value.code);
|
|
38
|
+
}
|
|
39
|
+
function isTenantNotProvisioned(value) {
|
|
40
|
+
return isApiError(value) && value.code === "tenant_not_provisioned";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/audit.ts
|
|
44
|
+
import { AuditEvent } from "@aletheia-dev/core";
|
|
45
|
+
import { z } from "zod";
|
|
46
|
+
|
|
47
|
+
// src/http.ts
|
|
48
|
+
function buildUrl(baseUrl, path, query) {
|
|
49
|
+
const url = `${baseUrl}${path}`;
|
|
50
|
+
if (!query) return url;
|
|
51
|
+
const params = new URLSearchParams();
|
|
52
|
+
for (const [key, value] of Object.entries(query)) {
|
|
53
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
54
|
+
if (typeof value === "object") {
|
|
55
|
+
for (const item of value) params.append(key, item);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
params.set(key, String(value));
|
|
59
|
+
}
|
|
60
|
+
const qs = params.toString();
|
|
61
|
+
return qs ? `${url}?${qs}` : url;
|
|
62
|
+
}
|
|
63
|
+
async function readBody(response) {
|
|
64
|
+
const text = await response.text();
|
|
65
|
+
if (!text) return void 0;
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(text);
|
|
68
|
+
} catch {
|
|
69
|
+
return text;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
var Http = class {
|
|
73
|
+
constructor(options) {
|
|
74
|
+
this.options = options;
|
|
75
|
+
}
|
|
76
|
+
options;
|
|
77
|
+
async request(path, options = {}) {
|
|
78
|
+
const { baseUrl, getAccessToken, fetch: fetch2 } = this.options;
|
|
79
|
+
const method = options.method ?? "GET";
|
|
80
|
+
const headers = { accept: "application/json" };
|
|
81
|
+
const token = await getAccessToken();
|
|
82
|
+
if (token) headers.authorization = `Bearer ${token}`;
|
|
83
|
+
const init = { method, headers };
|
|
84
|
+
if (options.raw !== void 0) {
|
|
85
|
+
headers["content-type"] = options.raw.contentType;
|
|
86
|
+
init.body = options.raw.content;
|
|
87
|
+
} else if (options.body !== void 0) {
|
|
88
|
+
headers["content-type"] = "application/json";
|
|
89
|
+
init.body = JSON.stringify(options.body);
|
|
90
|
+
}
|
|
91
|
+
const response = await fetch2(buildUrl(baseUrl, path, options.query), init);
|
|
92
|
+
const body = await readBody(response);
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw ApiError.fromResponse(response.status, response.statusText, body);
|
|
95
|
+
}
|
|
96
|
+
return body;
|
|
97
|
+
}
|
|
98
|
+
/** Requests and parses the body with `schema` (dates are coerced by the core schemas). */
|
|
99
|
+
async parsed(schema, path, options) {
|
|
100
|
+
const body = await this.request(path, options);
|
|
101
|
+
return schema.parse(body);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
function encodeSegment(value) {
|
|
105
|
+
return encodeURIComponent(value);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/audit.ts
|
|
109
|
+
var AuditEventList = z.object({ items: z.array(AuditEvent) });
|
|
110
|
+
function isoOf(value) {
|
|
111
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
112
|
+
}
|
|
113
|
+
function auditFilterParams(filter = {}) {
|
|
114
|
+
return {
|
|
115
|
+
action: filter.action,
|
|
116
|
+
actorId: filter.actorId,
|
|
117
|
+
actorType: filter.actorType,
|
|
118
|
+
resourceType: filter.resourceType,
|
|
119
|
+
resourceId: filter.resourceId,
|
|
120
|
+
from: isoOf(filter.from),
|
|
121
|
+
to: isoOf(filter.to)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function auditDomain(http, transport) {
|
|
125
|
+
const exportUrl = (format, filter) => buildUrl(transport.baseUrl, "/audit-events/export", {
|
|
126
|
+
format,
|
|
127
|
+
...auditFilterParams(filter)
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
list: async (query = {}) => {
|
|
131
|
+
const { limit, offset, ...filter } = query;
|
|
132
|
+
const body = await http.parsed(AuditEventList, "/audit-events", {
|
|
133
|
+
query: { ...auditFilterParams(filter), limit, offset }
|
|
134
|
+
});
|
|
135
|
+
return body.items;
|
|
136
|
+
},
|
|
137
|
+
exportUrl,
|
|
138
|
+
export: async (format, filter) => {
|
|
139
|
+
const headers = { accept: "*/*" };
|
|
140
|
+
const token = await transport.getAccessToken();
|
|
141
|
+
if (token) headers.authorization = `Bearer ${token}`;
|
|
142
|
+
const response = await transport.fetch(exportUrl(format, filter), {
|
|
143
|
+
method: "GET",
|
|
144
|
+
headers
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
const text = await response.text();
|
|
148
|
+
let body = text || void 0;
|
|
149
|
+
try {
|
|
150
|
+
body = text ? JSON.parse(text) : void 0;
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
153
|
+
throw ApiError.fromResponse(response.status, response.statusText, body);
|
|
154
|
+
}
|
|
155
|
+
return response.blob();
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
var CSV_MANIFEST = /^#\s*sha256=([0-9a-fA-F]+)\s+rows=(\d+)\s*$/;
|
|
160
|
+
var JsonlManifest = z.object({
|
|
161
|
+
_manifest: z.object({ sha256: z.string(), rows: z.coerce.number().int().nonnegative() })
|
|
162
|
+
});
|
|
163
|
+
function parseManifest(line, format) {
|
|
164
|
+
const trimmed = line.replace(/\r$/, "");
|
|
165
|
+
if (format === "csv") {
|
|
166
|
+
const match = CSV_MANIFEST.exec(trimmed);
|
|
167
|
+
return match ? { sha256: match[1].toLowerCase(), rows: Number(match[2]) } : void 0;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const parsed = JsonlManifest.safeParse(JSON.parse(trimmed));
|
|
171
|
+
if (!parsed.success) return void 0;
|
|
172
|
+
return { sha256: parsed.data._manifest.sha256.toLowerCase(), rows: parsed.data._manifest.rows };
|
|
173
|
+
} catch {
|
|
174
|
+
return void 0;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function sha256Hex(text) {
|
|
178
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
|
|
179
|
+
return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
180
|
+
}
|
|
181
|
+
async function verifyExport(text, format) {
|
|
182
|
+
const content = text.endsWith("\n") ? text.slice(0, -1) : text;
|
|
183
|
+
const cut = content.lastIndexOf("\n");
|
|
184
|
+
const body = cut === -1 ? "" : content.slice(0, cut + 1);
|
|
185
|
+
const manifest = parseManifest(content.slice(cut + 1), format);
|
|
186
|
+
if (!manifest) {
|
|
187
|
+
return { ok: false, rows: 0, actual: await sha256Hex(text) };
|
|
188
|
+
}
|
|
189
|
+
const actual = await sha256Hex(body);
|
|
190
|
+
return { ok: actual === manifest.sha256, rows: manifest.rows, expected: manifest.sha256, actual };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/governance.ts
|
|
194
|
+
import {
|
|
195
|
+
DEFINITION_PATHS,
|
|
196
|
+
DefinitionApproval,
|
|
197
|
+
DefinitionDiff,
|
|
198
|
+
DefinitionVersionSummary
|
|
199
|
+
} from "@aletheia-dev/core";
|
|
200
|
+
import { z as z2 } from "zod";
|
|
201
|
+
var itemsOf = (item) => z2.union([z2.object({ items: z2.array(item) }).transform((body) => body.items), z2.array(item)]);
|
|
202
|
+
var ApprovalOutcome = z2.object({
|
|
203
|
+
approval: DefinitionApproval,
|
|
204
|
+
definition: z2.unknown()
|
|
205
|
+
});
|
|
206
|
+
function definitionPath(kind, key) {
|
|
207
|
+
return `${DEFINITION_PATHS[kind]}/${encodeSegment(key)}`;
|
|
208
|
+
}
|
|
209
|
+
function versionPath(kind, key, version) {
|
|
210
|
+
return `${definitionPath(kind, key)}/versions/${encodeSegment(String(version))}`;
|
|
211
|
+
}
|
|
212
|
+
function governanceDomain(http) {
|
|
213
|
+
return {
|
|
214
|
+
versions: (kind, key) => http.parsed(itemsOf(DefinitionVersionSummary), `${definitionPath(kind, key)}/versions`),
|
|
215
|
+
getVersion: (kind, key, version) => http.request(versionPath(kind, key, version)),
|
|
216
|
+
diff: (kind, key, query = {}) => http.parsed(DefinitionDiff, `${definitionPath(kind, key)}/diff`, {
|
|
217
|
+
query: { from: query.from, to: query.to }
|
|
218
|
+
}),
|
|
219
|
+
requestApproval: (kind, key, version, body = {}) => http.parsed(DefinitionApproval, `${versionPath(kind, key, version)}/request-approval`, {
|
|
220
|
+
method: "POST",
|
|
221
|
+
body
|
|
222
|
+
}),
|
|
223
|
+
approve: (kind, key, version, body = {}) => http.parsed(ApprovalOutcome, `${versionPath(kind, key, version)}/approve`, {
|
|
224
|
+
method: "POST",
|
|
225
|
+
body
|
|
226
|
+
}),
|
|
227
|
+
reject: (kind, key, version, body = {}) => http.parsed(ApprovalOutcome, `${versionPath(kind, key, version)}/reject`, {
|
|
228
|
+
method: "POST",
|
|
229
|
+
body
|
|
230
|
+
}),
|
|
231
|
+
approvals: {
|
|
232
|
+
listOpen: (query = {}) => http.parsed(itemsOf(DefinitionApproval), "/approvals", { query: { kind: query.kind } })
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/index.ts
|
|
238
|
+
import {
|
|
239
|
+
ActorType,
|
|
240
|
+
ApprovalCommentInput,
|
|
241
|
+
ApprovalDecision,
|
|
242
|
+
AuditEvent as AuditEvent2,
|
|
243
|
+
AuditExportFormat,
|
|
244
|
+
AuditFilter,
|
|
245
|
+
DEFINITION_PATHS as DEFINITION_PATHS2,
|
|
246
|
+
DefinitionApproval as DefinitionApproval2,
|
|
247
|
+
DefinitionDiff as DefinitionDiff2,
|
|
248
|
+
DefinitionKind,
|
|
249
|
+
DefinitionVersionSummary as DefinitionVersionSummary2,
|
|
250
|
+
DiffChange,
|
|
251
|
+
DiffOp
|
|
252
|
+
} from "@aletheia-dev/core";
|
|
253
|
+
import {
|
|
254
|
+
Condition,
|
|
255
|
+
ConditionOp,
|
|
256
|
+
DefinitionIssue,
|
|
257
|
+
FieldTypeDescription as FieldTypeDescription2,
|
|
258
|
+
FormUiHints,
|
|
259
|
+
IssueSeverity,
|
|
260
|
+
StepTypeDescription as StepTypeDescription2,
|
|
261
|
+
ValidationReport as ValidationReport2
|
|
262
|
+
} from "@aletheia-dev/core";
|
|
263
|
+
|
|
264
|
+
// src/cases.ts
|
|
265
|
+
import {
|
|
266
|
+
Case,
|
|
267
|
+
CaseNote,
|
|
268
|
+
CaseQueue,
|
|
269
|
+
CaseView,
|
|
270
|
+
Decision,
|
|
271
|
+
DirectoryUser,
|
|
272
|
+
Subject,
|
|
273
|
+
WorkflowRun
|
|
274
|
+
} from "@aletheia-dev/core";
|
|
275
|
+
import { z as z3 } from "zod";
|
|
276
|
+
var CaseDetail = z3.object({
|
|
277
|
+
case: CaseView,
|
|
278
|
+
subject: Subject,
|
|
279
|
+
run: WorkflowRun.nullable(),
|
|
280
|
+
decisions: z3.array(Decision)
|
|
281
|
+
});
|
|
282
|
+
var DecideCaseResult = z3.object({ case: Case, decision: Decision });
|
|
283
|
+
var CaseQueueCount = z3.object({
|
|
284
|
+
queueId: z3.string(),
|
|
285
|
+
count: z3.number().int().nonnegative()
|
|
286
|
+
});
|
|
287
|
+
var listOf = (item) => z3.object({ items: z3.array(item) });
|
|
288
|
+
function arrayOf(value) {
|
|
289
|
+
if (value === void 0) return void 0;
|
|
290
|
+
return Array.isArray(value) ? value : [value];
|
|
291
|
+
}
|
|
292
|
+
function isoOf2(value) {
|
|
293
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
294
|
+
}
|
|
295
|
+
function caseListParams(query) {
|
|
296
|
+
return {
|
|
297
|
+
status: arrayOf(query.status),
|
|
298
|
+
priority: arrayOf(query.priority),
|
|
299
|
+
assignee: query.assignee,
|
|
300
|
+
type: query.type,
|
|
301
|
+
subjectId: query.subjectId,
|
|
302
|
+
slaState: arrayOf(query.slaState),
|
|
303
|
+
dueBefore: isoOf2(query.dueBefore),
|
|
304
|
+
search: query.search,
|
|
305
|
+
sort: query.sort,
|
|
306
|
+
order: query.order,
|
|
307
|
+
limit: query.limit,
|
|
308
|
+
offset: query.offset
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function casesDomain(http) {
|
|
312
|
+
const casePath = (id, suffix = "") => `/cases/${encodeSegment(id)}${suffix}`;
|
|
313
|
+
const notePath = (id, noteId) => casePath(id, `/notes/${encodeSegment(noteId)}`);
|
|
314
|
+
const post = (path, body) => http.parsed(CaseView, path, body === void 0 ? { method: "POST" } : { method: "POST", body });
|
|
315
|
+
return {
|
|
316
|
+
/** Cases matching `query`, in the requested order (`createdAt` desc by default). */
|
|
317
|
+
list: async (query = {}) => (await http.parsed(listOf(CaseView), "/cases", { query: caseListParams(query) })).items,
|
|
318
|
+
get: (id) => http.parsed(CaseDetail, casePath(id)),
|
|
319
|
+
decide: (id, body) => http.parsed(DecideCaseResult, casePath(id, "/decide"), { method: "POST", body }),
|
|
320
|
+
/** Sets or clears (`assigneeId: null`) the assignee (`cases:assign`). */
|
|
321
|
+
assign: (id, body) => post(casePath(id, "/assign"), body),
|
|
322
|
+
/** Assigns the case to the caller (`cases:claim`). */
|
|
323
|
+
claim: (id) => post(casePath(id, "/claim")),
|
|
324
|
+
/**
|
|
325
|
+
* Closes the case (`cases:decide`). An undecided case gets a `reject` decision with reason
|
|
326
|
+
* code `case_closed` so its workflow run does not wait forever.
|
|
327
|
+
*/
|
|
328
|
+
close: (id, body = {}) => post(casePath(id, "/close"), body),
|
|
329
|
+
/** Analyst notes on a case (`cases:notes`); edits and deletes are limited to the author or an admin. */
|
|
330
|
+
notes: {
|
|
331
|
+
list: async (id) => (await http.parsed(listOf(CaseNote), casePath(id, "/notes"))).items,
|
|
332
|
+
create: (id, body) => http.parsed(CaseNote, casePath(id, "/notes"), { method: "POST", body }),
|
|
333
|
+
update: (id, noteId, body) => http.parsed(CaseNote, notePath(id, noteId), { method: "PATCH", body }),
|
|
334
|
+
remove: async (id, noteId) => {
|
|
335
|
+
await http.request(notePath(id, noteId), { method: "DELETE" });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function caseQueuesDomain(http) {
|
|
341
|
+
const queuePath = (id) => `/case-queues/${encodeSegment(id)}`;
|
|
342
|
+
return {
|
|
343
|
+
list: async () => (await http.parsed(listOf(CaseQueue), "/case-queues")).items,
|
|
344
|
+
counts: async () => (await http.parsed(listOf(CaseQueueCount), "/case-queues/counts")).items,
|
|
345
|
+
create: (body) => http.parsed(CaseQueue, "/case-queues", { method: "POST", body }),
|
|
346
|
+
update: (id, body) => http.parsed(CaseQueue, queuePath(id), { method: "PUT", body }),
|
|
347
|
+
remove: async (id) => {
|
|
348
|
+
await http.request(queuePath(id), { method: "DELETE" });
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function usersDomain(http) {
|
|
353
|
+
return {
|
|
354
|
+
list: async (query = {}) => (await http.parsed(listOf(DirectoryUser), "/users", { query: { role: query.role } })).items
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/index.ts
|
|
359
|
+
import {
|
|
360
|
+
AssignCaseInput,
|
|
361
|
+
Case as Case2,
|
|
362
|
+
CaseFilter,
|
|
363
|
+
CaseNote as CaseNote2,
|
|
364
|
+
CaseNoteInput,
|
|
365
|
+
CasePriority,
|
|
366
|
+
CaseQueue as CaseQueue2,
|
|
367
|
+
CaseSlaState,
|
|
368
|
+
CaseSort,
|
|
369
|
+
CaseStatus,
|
|
370
|
+
CaseView as CaseView2,
|
|
371
|
+
CloseCaseInput,
|
|
372
|
+
DirectoryUser as DirectoryUser2,
|
|
373
|
+
SortOrder,
|
|
374
|
+
UpsertCaseQueueInput,
|
|
375
|
+
caseSlaState
|
|
376
|
+
} from "@aletheia-dev/core";
|
|
377
|
+
|
|
378
|
+
// src/documents.ts
|
|
379
|
+
import {
|
|
380
|
+
DOCUMENT_CONTENT_TYPES,
|
|
381
|
+
DOCUMENT_MAX_BYTES,
|
|
382
|
+
DOCUMENT_OPEN_STATUSES,
|
|
383
|
+
Document,
|
|
384
|
+
DocumentDownload,
|
|
385
|
+
DocumentUploadTicket
|
|
386
|
+
} from "@aletheia-dev/core";
|
|
387
|
+
import { z as z4 } from "zod";
|
|
388
|
+
var DocumentList = z4.object({ items: z4.array(Document) });
|
|
389
|
+
function documentsDomain(http) {
|
|
390
|
+
const idPath = (id, suffix = "") => `/documents/${encodeSegment(id)}${suffix}`;
|
|
391
|
+
return {
|
|
392
|
+
/**
|
|
393
|
+
* Registers a pending document and returns the presigned POST to send the bytes with
|
|
394
|
+
* (`uploadToStorage`). Applicants pass their `submissionId` and the `fieldKey`.
|
|
395
|
+
*/
|
|
396
|
+
create: (input) => http.parsed(DocumentUploadTicket, "/documents", { method: "POST", body: input }),
|
|
397
|
+
/**
|
|
398
|
+
* Verifies the uploaded object and starts the scan. Resolves with status `uploaded` or
|
|
399
|
+
* `rejected` (see `rejectionReason`); throws a 409 `ApiError` when nothing was uploaded.
|
|
400
|
+
*/
|
|
401
|
+
finalize: (id, opts = {}) => http.parsed(Document, idPath(id, "/finalize"), { method: "POST", query: { ...opts } }),
|
|
402
|
+
get: (id, opts = {}) => http.parsed(Document, idPath(id), { query: { ...opts } }),
|
|
403
|
+
list: (filters = {}) => http.parsed(DocumentList, "/documents", { query: { ...filters } }),
|
|
404
|
+
/** A 60-second presigned GET on the storage origin; open it directly, never via the API. */
|
|
405
|
+
download: (id, opts = {}) => http.parsed(DocumentDownload, idPath(id, "/download"), { query: { ...opts } })
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
var UploadError = class extends Error {
|
|
409
|
+
constructor(message, status) {
|
|
410
|
+
super(message);
|
|
411
|
+
this.status = status;
|
|
412
|
+
this.name = "UploadError";
|
|
413
|
+
}
|
|
414
|
+
status;
|
|
415
|
+
};
|
|
416
|
+
function defaultXhr() {
|
|
417
|
+
const ctor = globalThis.XMLHttpRequest;
|
|
418
|
+
if (!ctor) throw new UploadError("XMLHttpRequest is not available in this environment", 0);
|
|
419
|
+
return new ctor();
|
|
420
|
+
}
|
|
421
|
+
function uploadToStorage(ticket, file, opts = {}) {
|
|
422
|
+
return new Promise((resolve, reject) => {
|
|
423
|
+
if (opts.signal?.aborted) {
|
|
424
|
+
reject(new UploadError("Upload aborted", 0));
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const xhr = (opts.xhr ?? defaultXhr)();
|
|
428
|
+
const form = new FormData();
|
|
429
|
+
for (const [name, value] of Object.entries(ticket.upload.fields)) form.append(name, value);
|
|
430
|
+
form.append("file", file);
|
|
431
|
+
const onAbort = () => xhr.abort();
|
|
432
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
433
|
+
const settle = (fn) => {
|
|
434
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
435
|
+
fn();
|
|
436
|
+
};
|
|
437
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
438
|
+
if (event.lengthComputable && event.total > 0) {
|
|
439
|
+
opts.onProgress?.(Math.min(1, event.loaded / event.total));
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
xhr.addEventListener(
|
|
443
|
+
"load",
|
|
444
|
+
() => settle(() => {
|
|
445
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
446
|
+
opts.onProgress?.(1);
|
|
447
|
+
resolve();
|
|
448
|
+
} else {
|
|
449
|
+
reject(new UploadError(`Storage refused the upload (HTTP ${xhr.status})`, xhr.status));
|
|
450
|
+
}
|
|
451
|
+
})
|
|
452
|
+
);
|
|
453
|
+
xhr.addEventListener(
|
|
454
|
+
"error",
|
|
455
|
+
() => settle(() => reject(new UploadError("Network error while uploading the file", 0)))
|
|
456
|
+
);
|
|
457
|
+
xhr.addEventListener("abort", () => settle(() => reject(new UploadError("Upload aborted", 0))));
|
|
458
|
+
xhr.addEventListener(
|
|
459
|
+
"timeout",
|
|
460
|
+
() => settle(() => reject(new UploadError("The upload timed out", 0)))
|
|
461
|
+
);
|
|
462
|
+
xhr.open("POST", ticket.upload.url);
|
|
463
|
+
xhr.send(form);
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
var TYPE_LABELS = "JPEG, PNG, WebP or PDF";
|
|
467
|
+
function checkFileBeforeUpload(file) {
|
|
468
|
+
if (!file.name.trim()) return { ok: false, reason: "The file has no name." };
|
|
469
|
+
if (!DOCUMENT_CONTENT_TYPES.includes(file.type)) {
|
|
470
|
+
const got = file.type || "unknown type";
|
|
471
|
+
return { ok: false, reason: `Only ${TYPE_LABELS} files are accepted (this is ${got}).` };
|
|
472
|
+
}
|
|
473
|
+
if (file.size < 1) return { ok: false, reason: "The file is empty." };
|
|
474
|
+
if (file.size > DOCUMENT_MAX_BYTES) {
|
|
475
|
+
const mib = Math.round(DOCUMENT_MAX_BYTES / (1024 * 1024));
|
|
476
|
+
return { ok: false, reason: `The file is larger than ${mib} MiB.` };
|
|
477
|
+
}
|
|
478
|
+
return { ok: true };
|
|
479
|
+
}
|
|
480
|
+
var DocumentPollTimeoutError = class extends Error {
|
|
481
|
+
constructor(documentId) {
|
|
482
|
+
super("Timed out waiting for the document scan to finish");
|
|
483
|
+
this.documentId = documentId;
|
|
484
|
+
this.name = "DocumentPollTimeoutError";
|
|
485
|
+
}
|
|
486
|
+
documentId;
|
|
487
|
+
};
|
|
488
|
+
function isTerminalDocumentStatus(status) {
|
|
489
|
+
return !DOCUMENT_OPEN_STATUSES.includes(status);
|
|
490
|
+
}
|
|
491
|
+
function sleep(ms, signal) {
|
|
492
|
+
return new Promise((resolve, reject) => {
|
|
493
|
+
const timer = setTimeout(() => {
|
|
494
|
+
signal?.removeEventListener("abort", onAbort);
|
|
495
|
+
resolve();
|
|
496
|
+
}, ms);
|
|
497
|
+
function onAbort() {
|
|
498
|
+
clearTimeout(timer);
|
|
499
|
+
reject(new Error("Polling aborted"));
|
|
500
|
+
}
|
|
501
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
async function pollDocument(client, id, opts = {}) {
|
|
505
|
+
const { intervalMs = 750, timeoutMs = 6e4, signal, submissionId } = opts;
|
|
506
|
+
const deadline = Date.now() + timeoutMs;
|
|
507
|
+
for (; ; ) {
|
|
508
|
+
const document = await client.documents.get(id, { submissionId });
|
|
509
|
+
if (isTerminalDocumentStatus(document.status)) return document;
|
|
510
|
+
const remaining = deadline - Date.now();
|
|
511
|
+
if (remaining <= 0) throw new DocumentPollTimeoutError(id);
|
|
512
|
+
await sleep(Math.min(intervalMs, remaining), signal);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/index.ts
|
|
517
|
+
import {
|
|
518
|
+
CreateDocumentInput,
|
|
519
|
+
DOCUMENT_CONTENT_TYPES as DOCUMENT_CONTENT_TYPES2,
|
|
520
|
+
DOCUMENT_MAX_BYTES as DOCUMENT_MAX_BYTES2,
|
|
521
|
+
DOCUMENT_OPEN_STATUSES as DOCUMENT_OPEN_STATUSES2,
|
|
522
|
+
Document as Document2,
|
|
523
|
+
DocumentContentType,
|
|
524
|
+
DocumentDownload as DocumentDownload2,
|
|
525
|
+
DocumentScan,
|
|
526
|
+
DocumentStatus,
|
|
527
|
+
DocumentUploadTicket as DocumentUploadTicket2,
|
|
528
|
+
FileFieldValue
|
|
529
|
+
} from "@aletheia-dev/core";
|
|
530
|
+
import {
|
|
531
|
+
AggregationPolicy,
|
|
532
|
+
AddListEntriesInput,
|
|
533
|
+
Backtest as Backtest2,
|
|
534
|
+
BacktestProgress,
|
|
535
|
+
BacktestSample,
|
|
536
|
+
BacktestSampleFailure,
|
|
537
|
+
BacktestStatus,
|
|
538
|
+
BacktestSummary,
|
|
539
|
+
Event as Event2,
|
|
540
|
+
EventKey,
|
|
541
|
+
IngestEventsInput,
|
|
542
|
+
IngestEventsResult as IngestEventsResult2,
|
|
543
|
+
NewEvent,
|
|
544
|
+
List as List2,
|
|
545
|
+
ListEntry as ListEntry2,
|
|
546
|
+
ListImportResult as ListImportResult2,
|
|
547
|
+
ListKind,
|
|
548
|
+
NewListEntry,
|
|
549
|
+
RemoveListEntriesInput,
|
|
550
|
+
RuleSetBody,
|
|
551
|
+
RuleSetDefinition as RuleSetDefinition2,
|
|
552
|
+
RuleSetRule,
|
|
553
|
+
RuleSetUseCase,
|
|
554
|
+
ScoreBand,
|
|
555
|
+
ScoreClamp,
|
|
556
|
+
StartBacktestInput,
|
|
557
|
+
UpsertListInput,
|
|
558
|
+
UpsertRuleSetInput,
|
|
559
|
+
bandFor,
|
|
560
|
+
normalizeListValue
|
|
561
|
+
} from "@aletheia-dev/core";
|
|
562
|
+
import { TenantSettings as TenantSettings2, WebOrigin } from "@aletheia-dev/core";
|
|
563
|
+
import {
|
|
564
|
+
Subject as Subject3,
|
|
565
|
+
SubjectKind,
|
|
566
|
+
SubjectListQuery,
|
|
567
|
+
SubjectStatus,
|
|
568
|
+
TimelineEntry as TimelineEntry2,
|
|
569
|
+
TimelineKind,
|
|
570
|
+
TimelineQuery
|
|
571
|
+
} from "@aletheia-dev/core";
|
|
572
|
+
|
|
573
|
+
// src/client.ts
|
|
574
|
+
import { z as z7 } from "zod";
|
|
575
|
+
import {
|
|
576
|
+
Backtest,
|
|
577
|
+
CollectionFlowDefinition,
|
|
578
|
+
CollectionSubmission,
|
|
579
|
+
DefinitionStatus,
|
|
580
|
+
Decision as Decision2,
|
|
581
|
+
DecisionOutcome,
|
|
582
|
+
Event,
|
|
583
|
+
Id,
|
|
584
|
+
IngestEventsResult,
|
|
585
|
+
List,
|
|
586
|
+
ListEntry,
|
|
587
|
+
ListImportResult,
|
|
588
|
+
PluginDefinition,
|
|
589
|
+
PluginInvocation,
|
|
590
|
+
RuleDefinition,
|
|
591
|
+
RuleEvaluationResult,
|
|
592
|
+
RuleSetDefinition,
|
|
593
|
+
RuleType,
|
|
594
|
+
SubmissionStatus as CollectionSubmissionStatus,
|
|
595
|
+
Subject as Subject2,
|
|
596
|
+
TimelineEntry,
|
|
597
|
+
WorkflowDefinition,
|
|
598
|
+
WorkflowRunStatus
|
|
599
|
+
} from "@aletheia-dev/core";
|
|
600
|
+
|
|
601
|
+
// src/builder.ts
|
|
602
|
+
import {
|
|
603
|
+
FieldTypeDescription,
|
|
604
|
+
StepTypeDescription,
|
|
605
|
+
ValidationReport
|
|
606
|
+
} from "@aletheia-dev/core";
|
|
607
|
+
import { z as z5 } from "zod";
|
|
608
|
+
var itemsOf2 = (item) => z5.object({ items: z5.array(item) });
|
|
609
|
+
function builderDomain(http) {
|
|
610
|
+
return {
|
|
611
|
+
stepTypes: async () => (await http.parsed(itemsOf2(StepTypeDescription), "/workflow-step-types")).items,
|
|
612
|
+
fieldTypes: async () => (await http.parsed(itemsOf2(FieldTypeDescription), "/collection-field-types")).items
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
function validateCall(http, path) {
|
|
616
|
+
return (body) => http.parsed(ValidationReport, `${path}/validate`, { method: "POST", body });
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// src/tenants.ts
|
|
620
|
+
import { TenantSettings } from "@aletheia-dev/core";
|
|
621
|
+
function tenantsDomain(http) {
|
|
622
|
+
return {
|
|
623
|
+
getSettings: () => http.parsed(TenantSettings, "/tenants/me/settings"),
|
|
624
|
+
updateSettings: (body) => http.parsed(TenantSettings, "/tenants/me/settings", { method: "PUT", body })
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// src/workflow-runs.ts
|
|
629
|
+
import { WorkflowRun as WorkflowRun2 } from "@aletheia-dev/core";
|
|
630
|
+
import { z as z6 } from "zod";
|
|
631
|
+
var RunList = z6.object({ items: z6.array(WorkflowRun2) });
|
|
632
|
+
function workflowRunsDomain(http) {
|
|
633
|
+
return {
|
|
634
|
+
start: (body) => http.parsed(WorkflowRun2, "/workflow-runs", { method: "POST", body }),
|
|
635
|
+
get: (id) => http.parsed(WorkflowRun2, `/workflow-runs/${encodeSegment(id)}`),
|
|
636
|
+
listBySubject: async (subjectId) => (await http.parsed(RunList, "/workflow-runs", { query: { subjectId } })).items,
|
|
637
|
+
history: (id) => http.request(`/workflow-runs/${encodeSegment(id)}/history`)
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/client.ts
|
|
642
|
+
var HealthStatus = z7.object({ status: z7.literal("ok") });
|
|
643
|
+
var MePrincipal = z7.object({
|
|
644
|
+
kind: z7.enum(["human", "machine", "applicant"]),
|
|
645
|
+
subject: z7.string(),
|
|
646
|
+
name: z7.string().optional(),
|
|
647
|
+
email: z7.string().optional()
|
|
648
|
+
});
|
|
649
|
+
var Me = z7.object({
|
|
650
|
+
principal: MePrincipal,
|
|
651
|
+
tenantId: z7.string(),
|
|
652
|
+
orgId: z7.string().optional(),
|
|
653
|
+
permissions: z7.array(z7.string())
|
|
654
|
+
});
|
|
655
|
+
var CheckStatus = z7.enum(["ok", "error"]);
|
|
656
|
+
var ReadyStatus = z7.object({
|
|
657
|
+
status: z7.enum(["ok", "degraded"]),
|
|
658
|
+
checks: z7.object({ db: CheckStatus, temporal: CheckStatus })
|
|
659
|
+
});
|
|
660
|
+
var PluginSummary = z7.object({
|
|
661
|
+
name: z7.string(),
|
|
662
|
+
version: z7.string(),
|
|
663
|
+
description: z7.string().optional(),
|
|
664
|
+
capabilities: z7.array(z7.string()),
|
|
665
|
+
secrets: z7.array(z7.string()),
|
|
666
|
+
actions: z7.array(z7.string()),
|
|
667
|
+
configSchema: z7.unknown()
|
|
668
|
+
});
|
|
669
|
+
var RuleUiHints = z7.object({
|
|
670
|
+
/** Config fields in display order; unlisted fields follow in schema order. */
|
|
671
|
+
fieldOrder: z7.array(z7.string()).optional(),
|
|
672
|
+
labels: z7.record(z7.string(), z7.string()).optional(),
|
|
673
|
+
help: z7.record(z7.string(), z7.string()).optional(),
|
|
674
|
+
/** Config fields that hold a list key; the builder renders a list picker for them. */
|
|
675
|
+
listPicker: z7.array(z7.string()).optional()
|
|
676
|
+
});
|
|
677
|
+
var RuleTypeDescription = z7.object({
|
|
678
|
+
type: RuleType,
|
|
679
|
+
description: z7.string(),
|
|
680
|
+
/** JSON Schema of the rule config, produced by `z.toJSONSchema` on the API. */
|
|
681
|
+
configSchema: z7.unknown(),
|
|
682
|
+
/** Dot-path namespaces a rule config may reference, e.g. `subject.<field>`. */
|
|
683
|
+
dataPaths: z7.array(z7.string()).default([]),
|
|
684
|
+
uiHints: RuleUiHints.optional()
|
|
685
|
+
});
|
|
686
|
+
var DryRunResult = z7.object({
|
|
687
|
+
result: RuleEvaluationResult,
|
|
688
|
+
rule: z7.object({
|
|
689
|
+
key: z7.string(),
|
|
690
|
+
version: z7.number().int(),
|
|
691
|
+
status: DefinitionStatus,
|
|
692
|
+
type: RuleType
|
|
693
|
+
}),
|
|
694
|
+
/** Data namespaces the rule may reference, a hint for composing inline test data. */
|
|
695
|
+
dataPaths: z7.array(z7.string()).default([])
|
|
696
|
+
});
|
|
697
|
+
var RemovedCount = z7.object({ removed: z7.number().int().nonnegative() });
|
|
698
|
+
var SubmissionDetail = z7.object({
|
|
699
|
+
submission: CollectionSubmission,
|
|
700
|
+
flow: CollectionFlowDefinition
|
|
701
|
+
});
|
|
702
|
+
var SubmissionStatus = z7.object({
|
|
703
|
+
submission: z7.object({
|
|
704
|
+
id: Id,
|
|
705
|
+
status: CollectionSubmissionStatus,
|
|
706
|
+
submittedAt: z7.coerce.date().nullable()
|
|
707
|
+
}),
|
|
708
|
+
run: z7.object({
|
|
709
|
+
id: Id,
|
|
710
|
+
status: WorkflowRunStatus,
|
|
711
|
+
currentStepId: z7.string().nullable(),
|
|
712
|
+
decision: z7.object({ outcome: DecisionOutcome }).nullable()
|
|
713
|
+
}).nullable()
|
|
714
|
+
});
|
|
715
|
+
var CreatedSubmission = z7.object({
|
|
716
|
+
submission: CollectionSubmission,
|
|
717
|
+
link: z7.string().nullable()
|
|
718
|
+
});
|
|
719
|
+
var SubmissionEmbed = z7.object({
|
|
720
|
+
embedOrigins: z7.array(z7.string()),
|
|
721
|
+
flowName: z7.string()
|
|
722
|
+
});
|
|
723
|
+
var listOf2 = (item) => z7.object({ items: z7.array(item) });
|
|
724
|
+
function isoOf3(value) {
|
|
725
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
726
|
+
}
|
|
727
|
+
function timelineParams(query) {
|
|
728
|
+
return {
|
|
729
|
+
from: isoOf3(query.from),
|
|
730
|
+
to: isoOf3(query.to),
|
|
731
|
+
kinds: query.kinds,
|
|
732
|
+
limit: query.limit,
|
|
733
|
+
offset: query.offset
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
function createApiClient(options) {
|
|
737
|
+
const fetchImpl = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
738
|
+
const transport = {
|
|
739
|
+
baseUrl: options.baseUrl.replace(/\/+$/, ""),
|
|
740
|
+
getAccessToken: options.getAccessToken,
|
|
741
|
+
fetch: fetchImpl
|
|
742
|
+
};
|
|
743
|
+
const http = new Http(transport);
|
|
744
|
+
const audit = auditDomain(http, transport);
|
|
745
|
+
const list = async (item, path, query) => (await http.parsed(listOf2(item), path, { query })).items;
|
|
746
|
+
const definitions = (entity, path) => ({
|
|
747
|
+
list: () => list(entity, path),
|
|
748
|
+
get: (key) => http.parsed(entity, `${path}/${encodeSegment(key)}`),
|
|
749
|
+
upsert: (key, body) => http.parsed(entity, `${path}/${encodeSegment(key)}`, { method: "PUT", body }),
|
|
750
|
+
publish: (key) => http.parsed(entity, `${path}/${encodeSegment(key)}/publish`, { method: "POST" })
|
|
751
|
+
});
|
|
752
|
+
return {
|
|
753
|
+
health: {
|
|
754
|
+
check: () => http.parsed(HealthStatus, "/health"),
|
|
755
|
+
ready: () => http.parsed(ReadyStatus, "/health/ready")
|
|
756
|
+
},
|
|
757
|
+
/** The caller's principal, tenant and permissions as the API sees the bearer token. */
|
|
758
|
+
me: () => http.parsed(Me, "/me"),
|
|
759
|
+
subjects: {
|
|
760
|
+
create: (body) => http.parsed(Subject2, "/subjects", { method: "POST", body }),
|
|
761
|
+
/**
|
|
762
|
+
* Subjects matching `query`: `search` matches the external id and `data.name` /
|
|
763
|
+
* `data.legalName`; `riskScoreMin`/`riskScoreMax` bound the risk score (`subjects:read`).
|
|
764
|
+
*/
|
|
765
|
+
list: (query = {}) => list(Subject2, "/subjects", query),
|
|
766
|
+
get: (id) => http.parsed(Subject2, `/subjects/${encodeSegment(id)}`),
|
|
767
|
+
/**
|
|
768
|
+
* The subject's history, newest first (`subjects:read`). Entries of kinds the caller may
|
|
769
|
+
* not read are omitted by the API; page with `offset`.
|
|
770
|
+
*/
|
|
771
|
+
timeline: (id, query = {}) => list(TimelineEntry, `/subjects/${encodeSegment(id)}/timeline`, timelineParams(query))
|
|
772
|
+
},
|
|
773
|
+
workflowRuns: workflowRunsDomain(http),
|
|
774
|
+
/** Review cases: filtered lists, detail, decide, assign/claim/close and notes. See `cases.ts`. */
|
|
775
|
+
cases: casesDomain(http),
|
|
776
|
+
/** Saved case filters shown in the workbench rail, with live counts. */
|
|
777
|
+
caseQueues: caseQueuesDomain(http),
|
|
778
|
+
/** The reviewer directory, for assignment and resolving assignee names. */
|
|
779
|
+
users: usersDomain(http),
|
|
780
|
+
workflowDefinitions: {
|
|
781
|
+
...definitions(
|
|
782
|
+
WorkflowDefinition,
|
|
783
|
+
"/workflow-definitions"
|
|
784
|
+
),
|
|
785
|
+
/** Validates a draft body without saving; issues carry step ids. */
|
|
786
|
+
validate: validateCall(http, "/workflow-definitions")
|
|
787
|
+
},
|
|
788
|
+
/** Step and field type schemas behind the builder's forms. */
|
|
789
|
+
builder: builderDomain(http),
|
|
790
|
+
ruleDefinitions: {
|
|
791
|
+
...definitions(
|
|
792
|
+
RuleDefinition,
|
|
793
|
+
"/rule-definitions"
|
|
794
|
+
),
|
|
795
|
+
/** Evaluates one rule version against a subject or inline data; nothing is recorded. */
|
|
796
|
+
dryRun: (key, body) => http.parsed(DryRunResult, `/rule-definitions/${encodeSegment(key)}/dry-run`, {
|
|
797
|
+
method: "POST",
|
|
798
|
+
body
|
|
799
|
+
})
|
|
800
|
+
},
|
|
801
|
+
ruleTypes: {
|
|
802
|
+
list: () => list(RuleTypeDescription, "/rule-types")
|
|
803
|
+
},
|
|
804
|
+
/** Versioned, ordered groups of rules with an aggregation policy (`/rule-sets`). */
|
|
805
|
+
ruleSets: definitions(
|
|
806
|
+
RuleSetDefinition,
|
|
807
|
+
"/rule-sets"
|
|
808
|
+
),
|
|
809
|
+
collectionFlows: {
|
|
810
|
+
...definitions(
|
|
811
|
+
CollectionFlowDefinition,
|
|
812
|
+
"/collection-flows"
|
|
813
|
+
),
|
|
814
|
+
/** Validates a draft flow body without saving; issues carry step / field keys. */
|
|
815
|
+
validate: validateCall(http, "/collection-flows")
|
|
816
|
+
},
|
|
817
|
+
collectionSubmissions: {
|
|
818
|
+
/** Creates the submission and returns the applicant link alongside it. */
|
|
819
|
+
create: (body) => http.parsed(CreatedSubmission, "/collection-submissions", { method: "POST", body }),
|
|
820
|
+
get: (id) => http.parsed(SubmissionDetail, `/collection-submissions/${encodeSegment(id)}`),
|
|
821
|
+
save: (id, body) => http.parsed(CollectionSubmission, `/collection-submissions/${encodeSegment(id)}`, {
|
|
822
|
+
method: "PATCH",
|
|
823
|
+
body
|
|
824
|
+
}),
|
|
825
|
+
submit: (id) => http.parsed(CollectionSubmission, `/collection-submissions/${encodeSegment(id)}/submit`, {
|
|
826
|
+
method: "POST"
|
|
827
|
+
}),
|
|
828
|
+
/** Where the submission's workflow run is; readable with the applicant's token. */
|
|
829
|
+
status: (id) => http.parsed(SubmissionStatus, `/collection-submissions/${encodeSegment(id)}/status`),
|
|
830
|
+
/** Embed allow-list and flow name for the submission; readable with the applicant's token. */
|
|
831
|
+
embed: (id) => http.parsed(SubmissionEmbed, `/collection-submissions/${encodeSegment(id)}/embed`)
|
|
832
|
+
},
|
|
833
|
+
/** Subject events: the facts velocity rules aggregate over. */
|
|
834
|
+
events: {
|
|
835
|
+
/** Inserts events; re-sent idempotency keys are counted as `duplicates`, not errors. */
|
|
836
|
+
ingest: (body) => http.parsed(IngestEventsResult, "/events", { method: "POST", body }),
|
|
837
|
+
/** Newest first. */
|
|
838
|
+
list: (query = {}) => list(Event, "/events", { ...query, from: isoOf3(query.from), to: isoOf3(query.to) })
|
|
839
|
+
},
|
|
840
|
+
/**
|
|
841
|
+
* Replays recorded evaluations against a rule version (`backtests:run`). `start` returns the
|
|
842
|
+
* queued job; poll `get` until its status is `completed` (with `summary`) or `failed`.
|
|
843
|
+
*/
|
|
844
|
+
backtests: {
|
|
845
|
+
start: (body) => http.parsed(Backtest, "/backtests", { method: "POST", body }),
|
|
846
|
+
/** Newest first. */
|
|
847
|
+
list: (query = {}) => list(Backtest, "/backtests", query),
|
|
848
|
+
get: (id) => http.parsed(Backtest, `/backtests/${encodeSegment(id)}`)
|
|
849
|
+
},
|
|
850
|
+
decisions: {
|
|
851
|
+
listBySubject: (subjectId) => list(Decision2, "/decisions", { subjectId })
|
|
852
|
+
},
|
|
853
|
+
/** Filtered audit trail, exports and manifest verification (`audit:read`). See `audit.ts`. */
|
|
854
|
+
audit,
|
|
855
|
+
/** @deprecated Alias of `audit`, kept for existing callers. */
|
|
856
|
+
auditEvents: {
|
|
857
|
+
list: (query = {}) => audit.list(query)
|
|
858
|
+
},
|
|
859
|
+
/** Version history, diffs and the approval workflow of definitions. See `governance.ts`. */
|
|
860
|
+
governance: governanceDomain(http),
|
|
861
|
+
plugins: {
|
|
862
|
+
list: () => list(PluginDefinition, "/plugins"),
|
|
863
|
+
available: () => list(PluginSummary, "/plugins/available"),
|
|
864
|
+
configure: (name, body) => http.parsed(PluginDefinition, `/plugins/${encodeSegment(name)}`, { method: "PUT", body })
|
|
865
|
+
},
|
|
866
|
+
pluginInvocations: {
|
|
867
|
+
list: (query = {}) => list(PluginInvocation, "/plugin-invocations", query)
|
|
868
|
+
},
|
|
869
|
+
/**
|
|
870
|
+
* Uploaded files: `create` -> `uploadToStorage` (helper) -> `finalize` -> `pollDocument`
|
|
871
|
+
* (helper) until the scan settles. See `documents.ts`.
|
|
872
|
+
*/
|
|
873
|
+
documents: documentsDomain(http),
|
|
874
|
+
/** Tenant settings such as the embed origin allow-list (`tenants:settings`). */
|
|
875
|
+
tenants: tenantsDomain(http),
|
|
876
|
+
/** Tenant lists (blocklists / allowlists) and their entries. Lists are keyed, not versioned. */
|
|
877
|
+
lists: {
|
|
878
|
+
list: () => list(List, "/lists"),
|
|
879
|
+
get: (key) => http.parsed(List, `/lists/${encodeSegment(key)}`),
|
|
880
|
+
/** Creates the list or updates its metadata; entries are untouched. */
|
|
881
|
+
upsert: (key, body) => http.parsed(List, `/lists/${encodeSegment(key)}`, { method: "PUT", body }),
|
|
882
|
+
remove: async (key) => {
|
|
883
|
+
await http.request(`/lists/${encodeSegment(key)}`, { method: "DELETE" });
|
|
884
|
+
},
|
|
885
|
+
entries: (key, query = {}) => list(ListEntry, `/lists/${encodeSegment(key)}/entries`, query),
|
|
886
|
+
addEntries: (key, body) => http.parsed(ListImportResult, `/lists/${encodeSegment(key)}/entries`, {
|
|
887
|
+
method: "POST",
|
|
888
|
+
body
|
|
889
|
+
}),
|
|
890
|
+
removeEntries: (key, body) => http.parsed(RemovedCount, `/lists/${encodeSegment(key)}/entries`, {
|
|
891
|
+
method: "DELETE",
|
|
892
|
+
body
|
|
893
|
+
}),
|
|
894
|
+
/** Uploads a CSV (one value per line, optional `value,reason,expires_at` header). */
|
|
895
|
+
importCsv: (key, csv) => http.parsed(ListImportResult, `/lists/${encodeSegment(key)}/import`, {
|
|
896
|
+
method: "POST",
|
|
897
|
+
raw: { content: csv, contentType: "text/csv" }
|
|
898
|
+
})
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// src/runtime-config.ts
|
|
904
|
+
import { z as z8 } from "zod";
|
|
905
|
+
var RuntimeConfig = z8.object({
|
|
906
|
+
apiUrl: z8.string(),
|
|
907
|
+
zitadelIssuer: z8.string(),
|
|
908
|
+
zitadelProjectId: z8.string(),
|
|
909
|
+
zitadelClientId: z8.string(),
|
|
910
|
+
zitadelOrgDomain: z8.string(),
|
|
911
|
+
collectionFlowUrl: z8.string()
|
|
912
|
+
}).partial();
|
|
913
|
+
var RUNTIME_CONFIG_PATH = "/config.json";
|
|
914
|
+
async function loadRuntimeConfig(fetchImpl = fetch) {
|
|
915
|
+
try {
|
|
916
|
+
const init = { cache: "no-store", headers: { accept: "application/json" } };
|
|
917
|
+
const response = await fetchImpl(RUNTIME_CONFIG_PATH, init);
|
|
918
|
+
if (!response.ok) return {};
|
|
919
|
+
const parsed = RuntimeConfig.safeParse(await response.json());
|
|
920
|
+
return parsed.success ? parsed.data : {};
|
|
921
|
+
} catch {
|
|
922
|
+
return {};
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
function present(value) {
|
|
926
|
+
return value !== void 0 && value !== "";
|
|
927
|
+
}
|
|
928
|
+
function resolveConfig(runtime, env, defaults) {
|
|
929
|
+
const resolved = { ...defaults };
|
|
930
|
+
for (const key of Object.keys(defaults)) {
|
|
931
|
+
const fromRuntime = runtime[key];
|
|
932
|
+
const fromEnv = env[key];
|
|
933
|
+
if (present(fromRuntime)) resolved[key] = fromRuntime;
|
|
934
|
+
else if (present(fromEnv)) resolved[key] = fromEnv;
|
|
935
|
+
}
|
|
936
|
+
return resolved;
|
|
937
|
+
}
|
|
938
|
+
export {
|
|
939
|
+
ActorType,
|
|
940
|
+
AddListEntriesInput,
|
|
941
|
+
AggregationPolicy,
|
|
942
|
+
ApiError,
|
|
943
|
+
ApprovalCommentInput,
|
|
944
|
+
ApprovalDecision,
|
|
945
|
+
ApprovalOutcome,
|
|
946
|
+
AssignCaseInput,
|
|
947
|
+
AuditEvent2 as AuditEvent,
|
|
948
|
+
AuditExportFormat,
|
|
949
|
+
AuditFilter,
|
|
950
|
+
Backtest2 as Backtest,
|
|
951
|
+
BacktestProgress,
|
|
952
|
+
BacktestSample,
|
|
953
|
+
BacktestSampleFailure,
|
|
954
|
+
BacktestStatus,
|
|
955
|
+
BacktestSummary,
|
|
956
|
+
Case2 as Case,
|
|
957
|
+
CaseDetail,
|
|
958
|
+
CaseFilter,
|
|
959
|
+
CaseNote2 as CaseNote,
|
|
960
|
+
CaseNoteInput,
|
|
961
|
+
CasePriority,
|
|
962
|
+
CaseQueue2 as CaseQueue,
|
|
963
|
+
CaseQueueCount,
|
|
964
|
+
CaseSlaState,
|
|
965
|
+
CaseSort,
|
|
966
|
+
CaseStatus,
|
|
967
|
+
CaseView2 as CaseView,
|
|
968
|
+
CloseCaseInput,
|
|
969
|
+
Condition,
|
|
970
|
+
ConditionOp,
|
|
971
|
+
CreateDocumentInput,
|
|
972
|
+
CreatedSubmission,
|
|
973
|
+
DEFINITION_PATHS2 as DEFINITION_PATHS,
|
|
974
|
+
DOCUMENT_CONTENT_TYPES2 as DOCUMENT_CONTENT_TYPES,
|
|
975
|
+
DOCUMENT_MAX_BYTES2 as DOCUMENT_MAX_BYTES,
|
|
976
|
+
DOCUMENT_OPEN_STATUSES2 as DOCUMENT_OPEN_STATUSES,
|
|
977
|
+
DecideCaseResult,
|
|
978
|
+
DefinitionApproval2 as DefinitionApproval,
|
|
979
|
+
DefinitionDiff2 as DefinitionDiff,
|
|
980
|
+
DefinitionIssue,
|
|
981
|
+
DefinitionKind,
|
|
982
|
+
DefinitionVersionSummary2 as DefinitionVersionSummary,
|
|
983
|
+
DiffChange,
|
|
984
|
+
DiffOp,
|
|
985
|
+
DirectoryUser2 as DirectoryUser,
|
|
986
|
+
Document2 as Document,
|
|
987
|
+
DocumentContentType,
|
|
988
|
+
DocumentDownload2 as DocumentDownload,
|
|
989
|
+
DocumentPollTimeoutError,
|
|
990
|
+
DocumentScan,
|
|
991
|
+
DocumentStatus,
|
|
992
|
+
DocumentUploadTicket2 as DocumentUploadTicket,
|
|
993
|
+
DryRunResult,
|
|
994
|
+
Event2 as Event,
|
|
995
|
+
EventKey,
|
|
996
|
+
FORBIDDEN_CODES,
|
|
997
|
+
FieldTypeDescription2 as FieldTypeDescription,
|
|
998
|
+
FileFieldValue,
|
|
999
|
+
FormUiHints,
|
|
1000
|
+
HealthStatus,
|
|
1001
|
+
IngestEventsInput,
|
|
1002
|
+
IngestEventsResult2 as IngestEventsResult,
|
|
1003
|
+
IssueSeverity,
|
|
1004
|
+
List2 as List,
|
|
1005
|
+
ListEntry2 as ListEntry,
|
|
1006
|
+
ListImportResult2 as ListImportResult,
|
|
1007
|
+
ListKind,
|
|
1008
|
+
Me,
|
|
1009
|
+
MePrincipal,
|
|
1010
|
+
NewEvent,
|
|
1011
|
+
NewListEntry,
|
|
1012
|
+
PluginSummary,
|
|
1013
|
+
RUNTIME_CONFIG_PATH,
|
|
1014
|
+
ReadyStatus,
|
|
1015
|
+
RemoveListEntriesInput,
|
|
1016
|
+
RemovedCount,
|
|
1017
|
+
RuleSetBody,
|
|
1018
|
+
RuleSetDefinition2 as RuleSetDefinition,
|
|
1019
|
+
RuleSetRule,
|
|
1020
|
+
RuleSetUseCase,
|
|
1021
|
+
RuleTypeDescription,
|
|
1022
|
+
RuleUiHints,
|
|
1023
|
+
RuntimeConfig,
|
|
1024
|
+
ScoreBand,
|
|
1025
|
+
ScoreClamp,
|
|
1026
|
+
SortOrder,
|
|
1027
|
+
StartBacktestInput,
|
|
1028
|
+
StepTypeDescription2 as StepTypeDescription,
|
|
1029
|
+
Subject3 as Subject,
|
|
1030
|
+
SubjectKind,
|
|
1031
|
+
SubjectListQuery,
|
|
1032
|
+
SubjectStatus,
|
|
1033
|
+
SubmissionDetail,
|
|
1034
|
+
SubmissionEmbed,
|
|
1035
|
+
SubmissionStatus,
|
|
1036
|
+
TenantSettings2 as TenantSettings,
|
|
1037
|
+
TimelineEntry2 as TimelineEntry,
|
|
1038
|
+
TimelineKind,
|
|
1039
|
+
TimelineQuery,
|
|
1040
|
+
UploadError,
|
|
1041
|
+
UpsertCaseQueueInput,
|
|
1042
|
+
UpsertListInput,
|
|
1043
|
+
UpsertRuleSetInput,
|
|
1044
|
+
ValidationReport2 as ValidationReport,
|
|
1045
|
+
WebOrigin,
|
|
1046
|
+
auditFilterParams,
|
|
1047
|
+
bandFor,
|
|
1048
|
+
caseListParams,
|
|
1049
|
+
caseSlaState,
|
|
1050
|
+
checkFileBeforeUpload,
|
|
1051
|
+
createApiClient,
|
|
1052
|
+
isApiError,
|
|
1053
|
+
isApiErrorBody,
|
|
1054
|
+
isForbidden,
|
|
1055
|
+
isTenantNotProvisioned,
|
|
1056
|
+
isTerminalDocumentStatus,
|
|
1057
|
+
isUnauthenticated,
|
|
1058
|
+
loadRuntimeConfig,
|
|
1059
|
+
normalizeListValue,
|
|
1060
|
+
pollDocument,
|
|
1061
|
+
resolveConfig,
|
|
1062
|
+
uploadToStorage,
|
|
1063
|
+
verifyExport
|
|
1064
|
+
};
|
|
1065
|
+
//# sourceMappingURL=index.js.map
|