@intelmesh/sdk 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/.github/scripts/compute-disttag.sh +47 -0
- package/.github/workflows/release.yml +206 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +2 -0
- package/.prettierrc +8 -0
- package/CLAUDE.md +37 -0
- package/LICENSE +21 -0
- package/commitlint.config.cjs +3 -0
- package/dist/index.d.ts +1293 -0
- package/dist/index.js +1651 -0
- package/docs/superpowers/plans/2026-04-10-release-pipeline.md +798 -0
- package/docs/superpowers/specs/2026-04-10-release-pipeline-design.md +309 -0
- package/eslint.config.mjs +38 -0
- package/package.json +72 -0
- package/src/builders/event.ts +72 -0
- package/src/builders/rule.ts +143 -0
- package/src/client/errors.ts +171 -0
- package/src/client/http.ts +209 -0
- package/src/client/intelmesh.ts +57 -0
- package/src/client/pagination.ts +50 -0
- package/src/generated/types.ts +11 -0
- package/src/index.ts +106 -0
- package/src/provision/index.ts +6 -0
- package/src/provision/provisioner.ts +326 -0
- package/src/provision/rule-builder.ts +193 -0
- package/src/resources/apikeys.ts +63 -0
- package/src/resources/audit.ts +29 -0
- package/src/resources/evaluations.ts +38 -0
- package/src/resources/events.ts +61 -0
- package/src/resources/lists.ts +91 -0
- package/src/resources/phases.ts +71 -0
- package/src/resources/rules.ts +98 -0
- package/src/resources/scopes.ts +71 -0
- package/src/resources/scores.ts +63 -0
- package/src/testkit/assertion.ts +76 -0
- package/src/testkit/harness.ts +252 -0
- package/src/testkit/index.ts +7 -0
- package/src/types.ts +330 -0
- package/tests/client/errors.test.ts +159 -0
- package/tests/provision/provisioner.test.ts +311 -0
- package/tests/scripts/compute-disttag.test.ts +178 -0
- package/tests/testkit/harness.test.ts +291 -0
- package/tsconfig.eslint.json +8 -0
- package/tsconfig.json +29 -0
- package/vitest.config.ts +14 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1651 @@
|
|
|
1
|
+
// src/client/errors.ts
|
|
2
|
+
var IntelMeshError = class extends Error {
|
|
3
|
+
/** HTTP status code, if applicable. */
|
|
4
|
+
status;
|
|
5
|
+
/** Machine-readable error code from the API. */
|
|
6
|
+
code;
|
|
7
|
+
constructor(message, status, code) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "IntelMeshError";
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.code = code;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var ValidationError = class extends IntelMeshError {
|
|
15
|
+
constructor(message, code = "VALIDATION_ERROR") {
|
|
16
|
+
super(message, 400, code);
|
|
17
|
+
this.name = "ValidationError";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var NotFoundError = class extends IntelMeshError {
|
|
21
|
+
constructor(message, code = "NOT_FOUND") {
|
|
22
|
+
super(message, 404, code);
|
|
23
|
+
this.name = "NotFoundError";
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var UnauthorizedError = class extends IntelMeshError {
|
|
27
|
+
constructor(message, code = "UNAUTHORIZED") {
|
|
28
|
+
super(message, 401, code);
|
|
29
|
+
this.name = "UnauthorizedError";
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var ForbiddenError = class extends IntelMeshError {
|
|
33
|
+
constructor(message, code = "FORBIDDEN") {
|
|
34
|
+
super(message, 403, code);
|
|
35
|
+
this.name = "ForbiddenError";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var InternalError = class extends IntelMeshError {
|
|
39
|
+
constructor(message, code = "INTERNAL_ERROR") {
|
|
40
|
+
super(message, 500, code);
|
|
41
|
+
this.name = "InternalError";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var UnavailableError = class extends IntelMeshError {
|
|
45
|
+
constructor(message, code = "UNAVAILABLE") {
|
|
46
|
+
super(message, 503, code);
|
|
47
|
+
this.name = "UnavailableError";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var NetworkError = class extends IntelMeshError {
|
|
51
|
+
constructor(message) {
|
|
52
|
+
super(message, 0, "NETWORK_ERROR");
|
|
53
|
+
this.name = "NetworkError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var ParseError = class extends IntelMeshError {
|
|
57
|
+
constructor(message) {
|
|
58
|
+
super(message, 0, "PARSE_ERROR");
|
|
59
|
+
this.name = "ParseError";
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
function isNotFound(error) {
|
|
63
|
+
return error instanceof NotFoundError;
|
|
64
|
+
}
|
|
65
|
+
function isValidation(error) {
|
|
66
|
+
return error instanceof ValidationError;
|
|
67
|
+
}
|
|
68
|
+
function isUnauthorized(error) {
|
|
69
|
+
return error instanceof UnauthorizedError;
|
|
70
|
+
}
|
|
71
|
+
function isForbidden(error) {
|
|
72
|
+
return error instanceof ForbiddenError;
|
|
73
|
+
}
|
|
74
|
+
function isNetwork(error) {
|
|
75
|
+
return error instanceof NetworkError;
|
|
76
|
+
}
|
|
77
|
+
function isIntelMeshError(error) {
|
|
78
|
+
return error instanceof IntelMeshError;
|
|
79
|
+
}
|
|
80
|
+
function mapStatusToError(status, message, code) {
|
|
81
|
+
switch (status) {
|
|
82
|
+
case 400:
|
|
83
|
+
return new ValidationError(message, code);
|
|
84
|
+
case 401:
|
|
85
|
+
return new UnauthorizedError(message, code);
|
|
86
|
+
case 403:
|
|
87
|
+
return new ForbiddenError(message, code);
|
|
88
|
+
case 404:
|
|
89
|
+
return new NotFoundError(message, code);
|
|
90
|
+
case 503:
|
|
91
|
+
return new UnavailableError(message, code);
|
|
92
|
+
default:
|
|
93
|
+
if (status >= 500) {
|
|
94
|
+
return new InternalError(message, code);
|
|
95
|
+
}
|
|
96
|
+
return new IntelMeshError(message, status, code);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/client/http.ts
|
|
101
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
102
|
+
var HttpClient = class {
|
|
103
|
+
baseUrl;
|
|
104
|
+
apiKey;
|
|
105
|
+
timeout;
|
|
106
|
+
fetchFn;
|
|
107
|
+
constructor(config) {
|
|
108
|
+
this.baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
109
|
+
this.apiKey = config.apiKey;
|
|
110
|
+
this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
111
|
+
this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Executes a GET request and returns the parsed data.
|
|
115
|
+
* @param path - The API path.
|
|
116
|
+
* @param query - Optional query parameters.
|
|
117
|
+
* @returns The response data of type T.
|
|
118
|
+
*/
|
|
119
|
+
async get(path, query) {
|
|
120
|
+
return this.request({ method: "GET", path, query });
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Executes a POST request and returns the parsed data.
|
|
124
|
+
* @param path - The API path.
|
|
125
|
+
* @param body - The request body.
|
|
126
|
+
* @returns The response data of type T.
|
|
127
|
+
*/
|
|
128
|
+
async post(path, body) {
|
|
129
|
+
return this.request({ method: "POST", path, body });
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Executes a PUT request and returns the parsed data.
|
|
133
|
+
* @param path - The API path.
|
|
134
|
+
* @param body - The request body.
|
|
135
|
+
* @returns The response data of type T.
|
|
136
|
+
*/
|
|
137
|
+
async put(path, body) {
|
|
138
|
+
return this.request({ method: "PUT", path, body });
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Executes a DELETE request and returns the parsed data.
|
|
142
|
+
* @param path - The API path.
|
|
143
|
+
* @returns The response data of type T.
|
|
144
|
+
*/
|
|
145
|
+
async delete(path) {
|
|
146
|
+
return this.request({ method: "DELETE", path });
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Builds the full URL with query parameters.
|
|
150
|
+
* @param path - The API path.
|
|
151
|
+
* @param query - Optional query parameters.
|
|
152
|
+
* @returns The full URL string.
|
|
153
|
+
*/
|
|
154
|
+
buildUrl(path, query) {
|
|
155
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
156
|
+
if (query) {
|
|
157
|
+
for (const [key, value] of Object.entries(query)) {
|
|
158
|
+
if (value !== void 0) {
|
|
159
|
+
url.searchParams.set(key, String(value));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return url.toString();
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Executes an HTTP request with timeout, auth, and error handling.
|
|
167
|
+
* @param options - The request options.
|
|
168
|
+
* @returns The parsed response data.
|
|
169
|
+
*/
|
|
170
|
+
async request(options) {
|
|
171
|
+
const url = this.buildUrl(options.path, options.query);
|
|
172
|
+
const controller = new AbortController();
|
|
173
|
+
const timer = setTimeout(() => {
|
|
174
|
+
controller.abort();
|
|
175
|
+
}, this.timeout);
|
|
176
|
+
try {
|
|
177
|
+
const response = await this.executeFetch(url, options, controller.signal);
|
|
178
|
+
return await this.handleResponse(response);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
throw this.wrapError(error);
|
|
181
|
+
} finally {
|
|
182
|
+
clearTimeout(timer);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Calls fetch with the configured headers and body.
|
|
187
|
+
* @param url - The full URL.
|
|
188
|
+
* @param options - The request options.
|
|
189
|
+
* @param signal - The abort signal.
|
|
190
|
+
* @returns The fetch Response.
|
|
191
|
+
*/
|
|
192
|
+
async executeFetch(url, options, signal) {
|
|
193
|
+
const headers = {
|
|
194
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
195
|
+
Accept: "application/json"
|
|
196
|
+
};
|
|
197
|
+
if (options.body !== void 0) {
|
|
198
|
+
headers["Content-Type"] = "application/json";
|
|
199
|
+
}
|
|
200
|
+
return this.fetchFn(url, {
|
|
201
|
+
method: options.method,
|
|
202
|
+
headers,
|
|
203
|
+
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
204
|
+
signal
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Parses the response and throws typed errors for non-2xx status.
|
|
209
|
+
* @param response - The fetch Response.
|
|
210
|
+
* @returns The parsed data.
|
|
211
|
+
*/
|
|
212
|
+
async handleResponse(response) {
|
|
213
|
+
const text = await response.text();
|
|
214
|
+
if (!response.ok) {
|
|
215
|
+
this.throwApiError(response.status, text);
|
|
216
|
+
}
|
|
217
|
+
const parsed = this.parseJson(text);
|
|
218
|
+
return parsed.data;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Parses and throws an API error from the response body.
|
|
222
|
+
* @param status - The HTTP status code.
|
|
223
|
+
* @param text - The raw response body.
|
|
224
|
+
*/
|
|
225
|
+
throwApiError(status, text) {
|
|
226
|
+
try {
|
|
227
|
+
const body = JSON.parse(text);
|
|
228
|
+
throw mapStatusToError(status, body.error.message, body.error.code);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
if (error instanceof Error && "status" in error) throw error;
|
|
231
|
+
throw mapStatusToError(status, text || `HTTP ${String(status)}`, "UNKNOWN");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Safely parses JSON with a typed parse error.
|
|
236
|
+
* @param text - The raw JSON text.
|
|
237
|
+
* @returns The parsed JSON object.
|
|
238
|
+
*/
|
|
239
|
+
parseJson(text) {
|
|
240
|
+
try {
|
|
241
|
+
return JSON.parse(text);
|
|
242
|
+
} catch {
|
|
243
|
+
throw new ParseError(`Failed to parse response: ${text.slice(0, 200)}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Wraps unexpected errors into NetworkError.
|
|
248
|
+
* @param error - The caught error.
|
|
249
|
+
* @returns A NetworkError or the original error.
|
|
250
|
+
*/
|
|
251
|
+
wrapError(error) {
|
|
252
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
253
|
+
return new NetworkError("Request timed out");
|
|
254
|
+
}
|
|
255
|
+
if (error instanceof Error && "status" in error) {
|
|
256
|
+
return error;
|
|
257
|
+
}
|
|
258
|
+
if (error instanceof Error) {
|
|
259
|
+
return new NetworkError(error.message);
|
|
260
|
+
}
|
|
261
|
+
return new NetworkError("Unknown network error");
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// src/resources/apikeys.ts
|
|
266
|
+
var APIKeys = class {
|
|
267
|
+
http;
|
|
268
|
+
constructor(http) {
|
|
269
|
+
this.http = http;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Lists all API keys with cursor-based pagination.
|
|
273
|
+
* @param params - Optional pagination parameters.
|
|
274
|
+
* @returns A paginated list of API keys.
|
|
275
|
+
*/
|
|
276
|
+
async list(params = {}) {
|
|
277
|
+
return this.http.get("/api/v1/api-keys", {
|
|
278
|
+
cursor: params.cursor,
|
|
279
|
+
limit: params.limit
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Creates a new API key. The plain key is returned only at creation time.
|
|
284
|
+
* @param request - The API key creation payload.
|
|
285
|
+
* @returns The created API key with the plain key value.
|
|
286
|
+
*/
|
|
287
|
+
async create(request) {
|
|
288
|
+
return this.http.post("/api/v1/api-keys", request);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Updates an existing API key by ID.
|
|
292
|
+
* @param id - The API key ID.
|
|
293
|
+
* @param request - The update payload.
|
|
294
|
+
* @returns The updated API key.
|
|
295
|
+
*/
|
|
296
|
+
async update(id, request) {
|
|
297
|
+
return this.http.put(`/api/v1/api-keys/${id}`, request);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Deletes an API key by ID.
|
|
301
|
+
* @param id - The API key ID.
|
|
302
|
+
*/
|
|
303
|
+
async delete(id) {
|
|
304
|
+
await this.http.delete(`/api/v1/api-keys/${id}`);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// src/resources/audit.ts
|
|
309
|
+
var Audit = class {
|
|
310
|
+
http;
|
|
311
|
+
constructor(http) {
|
|
312
|
+
this.http = http;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Lists audit logs with cursor-based pagination.
|
|
316
|
+
* @param params - Optional pagination parameters.
|
|
317
|
+
* @returns A paginated list of audit entries.
|
|
318
|
+
*/
|
|
319
|
+
async list(params = {}) {
|
|
320
|
+
return this.http.get("/api/v1/audit", {
|
|
321
|
+
cursor: params.cursor,
|
|
322
|
+
limit: params.limit
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// src/resources/evaluations.ts
|
|
328
|
+
var Evaluations = class {
|
|
329
|
+
http;
|
|
330
|
+
constructor(http) {
|
|
331
|
+
this.http = http;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Lists evaluation logs with cursor-based pagination.
|
|
335
|
+
* @param params - Optional pagination parameters.
|
|
336
|
+
* @returns A paginated list of evaluation logs.
|
|
337
|
+
*/
|
|
338
|
+
async list(params = {}) {
|
|
339
|
+
return this.http.get("/api/v1/evaluations", {
|
|
340
|
+
cursor: params.cursor,
|
|
341
|
+
limit: params.limit
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Retrieves a single evaluation log by ID.
|
|
346
|
+
* @param id - The evaluation log ID.
|
|
347
|
+
* @returns The evaluation log with full pipeline trace.
|
|
348
|
+
*/
|
|
349
|
+
async get(id) {
|
|
350
|
+
return this.http.get(`/api/v1/evaluations/${id}`);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// src/resources/events.ts
|
|
355
|
+
var Events = class {
|
|
356
|
+
http;
|
|
357
|
+
constructor(http) {
|
|
358
|
+
this.http = http;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Ingests an event synchronously and returns the decision.
|
|
362
|
+
* @param request - The ingest request payload.
|
|
363
|
+
* @returns The ingestion result with decision and score.
|
|
364
|
+
*/
|
|
365
|
+
async ingest(request) {
|
|
366
|
+
return this.http.post("/api/v1/events/ingest", request);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Ingests an event asynchronously (fire-and-forget with acknowledgement).
|
|
370
|
+
* @param request - The ingest request payload.
|
|
371
|
+
* @returns The event ID assigned by the server.
|
|
372
|
+
*/
|
|
373
|
+
async ingestAsync(request) {
|
|
374
|
+
const result = await this.http.post(
|
|
375
|
+
"/api/v1/events/ingest-async",
|
|
376
|
+
request
|
|
377
|
+
);
|
|
378
|
+
return result.data.event_id;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Ingests an event for storage only, without evaluation.
|
|
382
|
+
* @param request - The ingest request payload.
|
|
383
|
+
* @returns The event ID assigned by the server.
|
|
384
|
+
*/
|
|
385
|
+
async ingestOnly(request) {
|
|
386
|
+
const result = await this.http.post(
|
|
387
|
+
"/api/v1/events/ingest-only",
|
|
388
|
+
request
|
|
389
|
+
);
|
|
390
|
+
return result.data.event_id;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Simulates event evaluation without persisting (dry-run).
|
|
394
|
+
* @param request - The ingest request payload.
|
|
395
|
+
* @returns The simulated result with decision and trace.
|
|
396
|
+
*/
|
|
397
|
+
async simulate(request) {
|
|
398
|
+
return this.http.post("/api/v1/events/simulate", request);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// src/resources/lists.ts
|
|
403
|
+
var Lists = class {
|
|
404
|
+
http;
|
|
405
|
+
constructor(http) {
|
|
406
|
+
this.http = http;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Lists all named lists with cursor-based pagination.
|
|
410
|
+
* @param params - Optional pagination parameters.
|
|
411
|
+
* @returns A paginated list of named lists.
|
|
412
|
+
*/
|
|
413
|
+
async list(params = {}) {
|
|
414
|
+
return this.http.get("/api/v1/lists", {
|
|
415
|
+
cursor: params.cursor,
|
|
416
|
+
limit: params.limit
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Retrieves a single list by ID.
|
|
421
|
+
* @param id - The list ID.
|
|
422
|
+
* @returns The list.
|
|
423
|
+
*/
|
|
424
|
+
async get(id) {
|
|
425
|
+
return this.http.get(`/api/v1/lists/${id}`);
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Creates a new named list.
|
|
429
|
+
* @param request - The list creation payload.
|
|
430
|
+
* @returns The created list.
|
|
431
|
+
*/
|
|
432
|
+
async create(request) {
|
|
433
|
+
return this.http.post("/api/v1/lists", request);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Updates an existing list by ID.
|
|
437
|
+
* @param id - The list ID.
|
|
438
|
+
* @param request - The update payload.
|
|
439
|
+
* @returns The updated list.
|
|
440
|
+
*/
|
|
441
|
+
async update(id, request) {
|
|
442
|
+
return this.http.put(`/api/v1/lists/${id}`, request);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Deletes a list by ID.
|
|
446
|
+
* @param id - The list ID.
|
|
447
|
+
*/
|
|
448
|
+
async delete(id) {
|
|
449
|
+
await this.http.delete(`/api/v1/lists/${id}`);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Adds items to a list.
|
|
453
|
+
* @param listId - The list ID.
|
|
454
|
+
* @param request - The items to add.
|
|
455
|
+
*/
|
|
456
|
+
async addItems(listId, request) {
|
|
457
|
+
await this.http.post(`/api/v1/lists/${listId}/items`, request);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Bulk-imports items into a list, replacing existing items.
|
|
461
|
+
* @param listId - The list ID.
|
|
462
|
+
* @param request - The items to import.
|
|
463
|
+
*/
|
|
464
|
+
async bulkImport(listId, request) {
|
|
465
|
+
await this.http.post(`/api/v1/lists/${listId}/import`, request);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// src/resources/phases.ts
|
|
470
|
+
var Phases = class {
|
|
471
|
+
http;
|
|
472
|
+
constructor(http) {
|
|
473
|
+
this.http = http;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Lists all phases with cursor-based pagination.
|
|
477
|
+
* @param params - Optional pagination parameters.
|
|
478
|
+
* @returns A paginated list of phases.
|
|
479
|
+
*/
|
|
480
|
+
async list(params = {}) {
|
|
481
|
+
return this.http.get("/api/v1/phases", {
|
|
482
|
+
cursor: params.cursor,
|
|
483
|
+
limit: params.limit
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Retrieves a single phase by ID.
|
|
488
|
+
* @param id - The phase ID.
|
|
489
|
+
* @returns The phase.
|
|
490
|
+
*/
|
|
491
|
+
async get(id) {
|
|
492
|
+
return this.http.get(`/api/v1/phases/${id}`);
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Creates a new phase.
|
|
496
|
+
* @param request - The phase creation payload.
|
|
497
|
+
* @returns The created phase.
|
|
498
|
+
*/
|
|
499
|
+
async create(request) {
|
|
500
|
+
return this.http.post("/api/v1/phases", request);
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Updates an existing phase by ID.
|
|
504
|
+
* @param id - The phase ID.
|
|
505
|
+
* @param request - The update payload.
|
|
506
|
+
* @returns The updated phase.
|
|
507
|
+
*/
|
|
508
|
+
async update(id, request) {
|
|
509
|
+
return this.http.put(`/api/v1/phases/${id}`, request);
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Deletes a phase by ID.
|
|
513
|
+
* @param id - The phase ID.
|
|
514
|
+
*/
|
|
515
|
+
async delete(id) {
|
|
516
|
+
await this.http.delete(`/api/v1/phases/${id}`);
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// src/resources/rules.ts
|
|
521
|
+
var Rules = class {
|
|
522
|
+
http;
|
|
523
|
+
constructor(http) {
|
|
524
|
+
this.http = http;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Lists all rules with cursor-based pagination.
|
|
528
|
+
* @param params - Optional pagination parameters.
|
|
529
|
+
* @returns A paginated list of rules.
|
|
530
|
+
*/
|
|
531
|
+
async list(params = {}) {
|
|
532
|
+
return this.http.get("/api/v1/rules", {
|
|
533
|
+
cursor: params.cursor,
|
|
534
|
+
limit: params.limit
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Retrieves a single rule by ID.
|
|
539
|
+
* @param id - The rule ID.
|
|
540
|
+
* @returns The rule.
|
|
541
|
+
*/
|
|
542
|
+
async get(id) {
|
|
543
|
+
return this.http.get(`/api/v1/rules/${id}`);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Creates a new rule.
|
|
547
|
+
* @param request - The rule creation payload.
|
|
548
|
+
* @returns The created rule.
|
|
549
|
+
*/
|
|
550
|
+
async create(request) {
|
|
551
|
+
return this.http.post("/api/v1/rules", request);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Updates an existing rule by ID.
|
|
555
|
+
* @param id - The rule ID.
|
|
556
|
+
* @param request - The update payload.
|
|
557
|
+
* @returns The updated rule.
|
|
558
|
+
*/
|
|
559
|
+
async update(id, request) {
|
|
560
|
+
return this.http.put(`/api/v1/rules/${id}`, request);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Deletes a rule by ID.
|
|
564
|
+
* @param id - The rule ID.
|
|
565
|
+
*/
|
|
566
|
+
async delete(id) {
|
|
567
|
+
await this.http.delete(`/api/v1/rules/${id}`);
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Lists all versions of a rule.
|
|
571
|
+
* @param ruleId - The rule ID.
|
|
572
|
+
* @param params - Optional pagination parameters.
|
|
573
|
+
* @returns A paginated list of rule versions.
|
|
574
|
+
*/
|
|
575
|
+
async listVersions(ruleId, params = {}) {
|
|
576
|
+
return this.http.get(
|
|
577
|
+
`/api/v1/rules/${ruleId}/versions`,
|
|
578
|
+
{ cursor: params.cursor, limit: params.limit }
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Retrieves a specific version of a rule.
|
|
583
|
+
* @param ruleId - The rule ID.
|
|
584
|
+
* @param versionId - The version ID.
|
|
585
|
+
* @returns The rule version.
|
|
586
|
+
*/
|
|
587
|
+
async getVersion(ruleId, versionId) {
|
|
588
|
+
return this.http.get(`/api/v1/rules/${ruleId}/versions/${versionId}`);
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// src/resources/scores.ts
|
|
593
|
+
var Scores = class {
|
|
594
|
+
http;
|
|
595
|
+
constructor(http) {
|
|
596
|
+
this.http = http;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Retrieves a score for a specific scope name and value.
|
|
600
|
+
* @param scopeName - The scope name (e.g. "customer_id").
|
|
601
|
+
* @param scopeValue - The scope value (e.g. "12345678900").
|
|
602
|
+
* @returns The score entry.
|
|
603
|
+
*/
|
|
604
|
+
async get(scopeName, scopeValue) {
|
|
605
|
+
return this.http.get(`/api/v1/scores/${scopeName}/${scopeValue}`);
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Lists scores for a scope name with cursor-based pagination.
|
|
609
|
+
* @param scopeName - The scope name.
|
|
610
|
+
* @param params - Optional pagination parameters.
|
|
611
|
+
* @returns A paginated list of scores.
|
|
612
|
+
*/
|
|
613
|
+
async list(scopeName, params = {}) {
|
|
614
|
+
return this.http.get(`/api/v1/scores/${scopeName}`, {
|
|
615
|
+
cursor: params.cursor,
|
|
616
|
+
limit: params.limit
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Sets a score for a specific scope name and value.
|
|
621
|
+
* @param scopeName - The scope name.
|
|
622
|
+
* @param scopeValue - The scope value.
|
|
623
|
+
* @param request - The score value to set.
|
|
624
|
+
* @returns The updated score.
|
|
625
|
+
*/
|
|
626
|
+
async set(scopeName, scopeValue, request) {
|
|
627
|
+
return this.http.put(`/api/v1/scores/${scopeName}/${scopeValue}`, request);
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Resets a score to zero for a specific scope name and value.
|
|
631
|
+
* @param scopeName - The scope name.
|
|
632
|
+
* @param scopeValue - The scope value.
|
|
633
|
+
*/
|
|
634
|
+
async reset(scopeName, scopeValue) {
|
|
635
|
+
await this.http.delete(`/api/v1/scores/${scopeName}/${scopeValue}`);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
// src/resources/scopes.ts
|
|
640
|
+
var Scopes = class {
|
|
641
|
+
http;
|
|
642
|
+
constructor(http) {
|
|
643
|
+
this.http = http;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Lists all scopes with cursor-based pagination.
|
|
647
|
+
* @param params - Optional pagination parameters.
|
|
648
|
+
* @returns A paginated list of scopes.
|
|
649
|
+
*/
|
|
650
|
+
async list(params = {}) {
|
|
651
|
+
return this.http.get("/api/v1/scopes", {
|
|
652
|
+
cursor: params.cursor,
|
|
653
|
+
limit: params.limit
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Retrieves a single scope by ID.
|
|
658
|
+
* @param id - The scope ID.
|
|
659
|
+
* @returns The scope.
|
|
660
|
+
*/
|
|
661
|
+
async get(id) {
|
|
662
|
+
return this.http.get(`/api/v1/scopes/${id}`);
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Creates a new scope.
|
|
666
|
+
* @param request - The scope creation payload.
|
|
667
|
+
* @returns The created scope.
|
|
668
|
+
*/
|
|
669
|
+
async create(request) {
|
|
670
|
+
return this.http.post("/api/v1/scopes", request);
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Updates an existing scope by ID.
|
|
674
|
+
* @param id - The scope ID.
|
|
675
|
+
* @param request - The update payload.
|
|
676
|
+
* @returns The updated scope.
|
|
677
|
+
*/
|
|
678
|
+
async update(id, request) {
|
|
679
|
+
return this.http.put(`/api/v1/scopes/${id}`, request);
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Deletes a scope by ID.
|
|
683
|
+
* @param id - The scope ID.
|
|
684
|
+
*/
|
|
685
|
+
async delete(id) {
|
|
686
|
+
await this.http.delete(`/api/v1/scopes/${id}`);
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
// src/client/intelmesh.ts
|
|
691
|
+
var IntelMesh = class {
|
|
692
|
+
/** Event ingestion and simulation. */
|
|
693
|
+
events;
|
|
694
|
+
/** Rule management and versioning. */
|
|
695
|
+
rules;
|
|
696
|
+
/** Pipeline phase management. */
|
|
697
|
+
phases;
|
|
698
|
+
/** Scope management. */
|
|
699
|
+
scopes;
|
|
700
|
+
/** Named list management. */
|
|
701
|
+
lists;
|
|
702
|
+
/** Score management. */
|
|
703
|
+
scores;
|
|
704
|
+
/** API key management. */
|
|
705
|
+
apiKeys;
|
|
706
|
+
/** Evaluation log inspection. */
|
|
707
|
+
evaluations;
|
|
708
|
+
/** Audit log access. */
|
|
709
|
+
audit;
|
|
710
|
+
/**
|
|
711
|
+
* Creates a new IntelMesh client instance.
|
|
712
|
+
* @param config - Client configuration with baseUrl and apiKey.
|
|
713
|
+
*/
|
|
714
|
+
constructor(config) {
|
|
715
|
+
const http = new HttpClient(config);
|
|
716
|
+
this.events = new Events(http);
|
|
717
|
+
this.rules = new Rules(http);
|
|
718
|
+
this.phases = new Phases(http);
|
|
719
|
+
this.scopes = new Scopes(http);
|
|
720
|
+
this.lists = new Lists(http);
|
|
721
|
+
this.scores = new Scores(http);
|
|
722
|
+
this.apiKeys = new APIKeys(http);
|
|
723
|
+
this.evaluations = new Evaluations(http);
|
|
724
|
+
this.audit = new Audit(http);
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
// src/client/pagination.ts
|
|
729
|
+
async function* paginate(fetcher, params = {}) {
|
|
730
|
+
let cursor = params.cursor;
|
|
731
|
+
let hasMore = true;
|
|
732
|
+
while (hasMore) {
|
|
733
|
+
const page = await fetcher({ cursor, limit: params.limit });
|
|
734
|
+
for (const item of page.items) {
|
|
735
|
+
yield item;
|
|
736
|
+
}
|
|
737
|
+
if (page.next_cursor) {
|
|
738
|
+
cursor = page.next_cursor;
|
|
739
|
+
} else {
|
|
740
|
+
hasMore = false;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
async function collectAll(iter) {
|
|
745
|
+
const results = [];
|
|
746
|
+
for await (const item of iter) {
|
|
747
|
+
results.push(item);
|
|
748
|
+
}
|
|
749
|
+
return results;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// src/builders/event.ts
|
|
753
|
+
var EventBuilder = class {
|
|
754
|
+
eventType = "";
|
|
755
|
+
idempotencyKey;
|
|
756
|
+
payload = {};
|
|
757
|
+
/**
|
|
758
|
+
* Sets the event type.
|
|
759
|
+
* @param type - The event type string (e.g. "transaction.pix").
|
|
760
|
+
* @returns This builder for chaining.
|
|
761
|
+
*/
|
|
762
|
+
type(type) {
|
|
763
|
+
this.eventType = type;
|
|
764
|
+
return this;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Sets the idempotency key for deduplication.
|
|
768
|
+
* @param key - The idempotency key.
|
|
769
|
+
* @returns This builder for chaining.
|
|
770
|
+
*/
|
|
771
|
+
idempotency(key) {
|
|
772
|
+
this.idempotencyKey = key;
|
|
773
|
+
return this;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Sets a single payload field.
|
|
777
|
+
* @param key - The field name.
|
|
778
|
+
* @param value - The field value.
|
|
779
|
+
* @returns This builder for chaining.
|
|
780
|
+
*/
|
|
781
|
+
set(key, value) {
|
|
782
|
+
Object.assign(this.payload, { [key]: value });
|
|
783
|
+
return this;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Merges multiple fields into the payload.
|
|
787
|
+
* @param data - Record of fields to merge.
|
|
788
|
+
* @returns This builder for chaining.
|
|
789
|
+
*/
|
|
790
|
+
data(data) {
|
|
791
|
+
Object.assign(this.payload, data);
|
|
792
|
+
return this;
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Builds and returns the IngestRequest.
|
|
796
|
+
* @returns The constructed IngestRequest.
|
|
797
|
+
* @throws {Error} If event type is not set.
|
|
798
|
+
*/
|
|
799
|
+
build() {
|
|
800
|
+
if (!this.eventType) {
|
|
801
|
+
throw new Error("EventBuilder: event type is required");
|
|
802
|
+
}
|
|
803
|
+
return {
|
|
804
|
+
event_type: this.eventType,
|
|
805
|
+
payload: { ...this.payload },
|
|
806
|
+
...this.idempotencyKey !== void 0 && { idempotency_key: this.idempotencyKey }
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
// src/builders/rule.ts
|
|
812
|
+
var RuleBuilder = class {
|
|
813
|
+
ruleName = "";
|
|
814
|
+
ruleExpression = "";
|
|
815
|
+
ruleApplicableWhen = "";
|
|
816
|
+
rulePhaseId = "";
|
|
817
|
+
rulePriority = 0;
|
|
818
|
+
ruleEnabled = true;
|
|
819
|
+
ruleDryRun = false;
|
|
820
|
+
ruleActions = {};
|
|
821
|
+
/**
|
|
822
|
+
* Sets the rule name.
|
|
823
|
+
* @param n - The rule name.
|
|
824
|
+
* @returns This builder for chaining.
|
|
825
|
+
*/
|
|
826
|
+
name(n) {
|
|
827
|
+
this.ruleName = n;
|
|
828
|
+
return this;
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Sets the CEL expression for the rule.
|
|
832
|
+
* @param expr - The CEL expression.
|
|
833
|
+
* @returns This builder for chaining.
|
|
834
|
+
*/
|
|
835
|
+
expression(expr) {
|
|
836
|
+
this.ruleExpression = expr;
|
|
837
|
+
return this;
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Sets the condition under which this rule applies.
|
|
841
|
+
* @param condition - The applicable-when CEL expression.
|
|
842
|
+
* @returns This builder for chaining.
|
|
843
|
+
*/
|
|
844
|
+
applicableWhen(condition) {
|
|
845
|
+
this.ruleApplicableWhen = condition;
|
|
846
|
+
return this;
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Sets the phase this rule belongs to.
|
|
850
|
+
* @param id - The phase ID.
|
|
851
|
+
* @returns This builder for chaining.
|
|
852
|
+
*/
|
|
853
|
+
phase(id) {
|
|
854
|
+
this.rulePhaseId = id;
|
|
855
|
+
return this;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Sets the rule priority (lower number = higher priority).
|
|
859
|
+
* @param p - The priority value.
|
|
860
|
+
* @returns This builder for chaining.
|
|
861
|
+
*/
|
|
862
|
+
priority(p) {
|
|
863
|
+
this.rulePriority = p;
|
|
864
|
+
return this;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Sets whether the rule is enabled.
|
|
868
|
+
* @param value - True to enable, false to disable.
|
|
869
|
+
* @returns This builder for chaining.
|
|
870
|
+
*/
|
|
871
|
+
enabled(value) {
|
|
872
|
+
this.ruleEnabled = value;
|
|
873
|
+
return this;
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Sets whether the rule runs in dry-run mode.
|
|
877
|
+
* @param value - True for dry-run.
|
|
878
|
+
* @returns This builder for chaining.
|
|
879
|
+
*/
|
|
880
|
+
dryRun(value) {
|
|
881
|
+
this.ruleDryRun = value;
|
|
882
|
+
return this;
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Sets the decision action when the rule matches.
|
|
886
|
+
* @param action - The action string.
|
|
887
|
+
* @param severity - The severity level.
|
|
888
|
+
* @returns This builder for chaining.
|
|
889
|
+
*/
|
|
890
|
+
decide(action, severity) {
|
|
891
|
+
const decision = { action, severity };
|
|
892
|
+
this.ruleActions = { ...this.ruleActions, decision };
|
|
893
|
+
return this;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Sets the flow control when the rule matches.
|
|
897
|
+
* @param flow - The flow control value.
|
|
898
|
+
* @returns This builder for chaining.
|
|
899
|
+
*/
|
|
900
|
+
flow(flow) {
|
|
901
|
+
this.ruleActions = { ...this.ruleActions, flow };
|
|
902
|
+
return this;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Sets the score delta when the rule matches.
|
|
906
|
+
* @param delta - The score points to add.
|
|
907
|
+
* @returns This builder for chaining.
|
|
908
|
+
*/
|
|
909
|
+
scoreDelta(delta) {
|
|
910
|
+
this.ruleActions = { ...this.ruleActions, score: { add: delta } };
|
|
911
|
+
return this;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Builds and returns the CreateRuleRequest.
|
|
915
|
+
* @returns The constructed CreateRuleRequest.
|
|
916
|
+
* @throws {Error} If required fields are missing.
|
|
917
|
+
*/
|
|
918
|
+
build() {
|
|
919
|
+
if (!this.ruleName) throw new Error("RuleBuilder: name is required");
|
|
920
|
+
if (!this.ruleExpression) throw new Error("RuleBuilder: expression is required");
|
|
921
|
+
if (!this.rulePhaseId) throw new Error("RuleBuilder: phase is required");
|
|
922
|
+
return {
|
|
923
|
+
name: this.ruleName,
|
|
924
|
+
expression: this.ruleExpression,
|
|
925
|
+
applicable_when: this.ruleApplicableWhen,
|
|
926
|
+
phase_id: this.rulePhaseId,
|
|
927
|
+
priority: this.rulePriority,
|
|
928
|
+
enabled: this.ruleEnabled,
|
|
929
|
+
dry_run: this.ruleDryRun,
|
|
930
|
+
actions: this.ruleActions
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
// src/provision/rule-builder.ts
|
|
936
|
+
var ProvisionRuleBuilder = class {
|
|
937
|
+
parent;
|
|
938
|
+
/** @internal */
|
|
939
|
+
ruleName;
|
|
940
|
+
/** @internal */
|
|
941
|
+
phaseName = "";
|
|
942
|
+
/** @internal */
|
|
943
|
+
rulePriority = 0;
|
|
944
|
+
/** @internal */
|
|
945
|
+
applicable = "";
|
|
946
|
+
/** @internal */
|
|
947
|
+
expression = "";
|
|
948
|
+
/** @internal */
|
|
949
|
+
ruleDecision;
|
|
950
|
+
/** @internal */
|
|
951
|
+
ruleScore;
|
|
952
|
+
/** @internal */
|
|
953
|
+
ruleFlow;
|
|
954
|
+
/** @internal */
|
|
955
|
+
ruleMutations = [];
|
|
956
|
+
/** @internal */
|
|
957
|
+
isDryRun = false;
|
|
958
|
+
/**
|
|
959
|
+
* Constructs a new ProvisionRuleBuilder.
|
|
960
|
+
* @param parent
|
|
961
|
+
* @param name
|
|
962
|
+
* @internal
|
|
963
|
+
*/
|
|
964
|
+
constructor(parent, name) {
|
|
965
|
+
this.parent = parent;
|
|
966
|
+
this.ruleName = name;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Sets the phase for the rule (by provisioner name, not ID).
|
|
970
|
+
* @param name - The phase name as registered with the provisioner.
|
|
971
|
+
* @returns This builder for chaining.
|
|
972
|
+
*/
|
|
973
|
+
inPhase(name) {
|
|
974
|
+
this.phaseName = name;
|
|
975
|
+
return this;
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Sets the rule priority (lower number = higher priority).
|
|
979
|
+
* @param p - The priority value.
|
|
980
|
+
* @returns This builder for chaining.
|
|
981
|
+
*/
|
|
982
|
+
priority(p) {
|
|
983
|
+
this.rulePriority = p;
|
|
984
|
+
return this;
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Sets the applicability expression.
|
|
988
|
+
* @param expr - The CEL expression for applicability.
|
|
989
|
+
* @returns This builder for chaining.
|
|
990
|
+
*/
|
|
991
|
+
applicableWhen(expr) {
|
|
992
|
+
this.applicable = expr;
|
|
993
|
+
return this;
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Sets the matching expression.
|
|
997
|
+
* @param expr - The CEL expression for matching.
|
|
998
|
+
* @returns This builder for chaining.
|
|
999
|
+
*/
|
|
1000
|
+
when(expr) {
|
|
1001
|
+
this.expression = expr;
|
|
1002
|
+
return this;
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Sets the decision action and severity.
|
|
1006
|
+
* @param action - The action string (e.g. "block").
|
|
1007
|
+
* @param severity - The severity level.
|
|
1008
|
+
* @returns This builder for chaining.
|
|
1009
|
+
*/
|
|
1010
|
+
decide(action, severity) {
|
|
1011
|
+
this.ruleDecision = { action, severity };
|
|
1012
|
+
return this;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Adds a score delta for the rule.
|
|
1016
|
+
* @param delta - The score points to add.
|
|
1017
|
+
* @returns This builder for chaining.
|
|
1018
|
+
*/
|
|
1019
|
+
addScore(delta) {
|
|
1020
|
+
this.ruleScore = { add: delta };
|
|
1021
|
+
return this;
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Sets flow to halt.
|
|
1025
|
+
* @returns This builder for chaining.
|
|
1026
|
+
*/
|
|
1027
|
+
halt() {
|
|
1028
|
+
this.ruleFlow = "halt";
|
|
1029
|
+
return this;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Sets flow to continue.
|
|
1033
|
+
* @returns This builder for chaining.
|
|
1034
|
+
*/
|
|
1035
|
+
continue() {
|
|
1036
|
+
this.ruleFlow = "continue";
|
|
1037
|
+
return this;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Sets flow to skip_phase.
|
|
1041
|
+
* @returns This builder for chaining.
|
|
1042
|
+
*/
|
|
1043
|
+
skipPhase() {
|
|
1044
|
+
this.ruleFlow = "skip_phase";
|
|
1045
|
+
return this;
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Adds a list mutation to the rule.
|
|
1049
|
+
* @param mutType - The mutation type (e.g. "list.add").
|
|
1050
|
+
* @param listName - The list name.
|
|
1051
|
+
* @param valuePath - The value path expression.
|
|
1052
|
+
* @returns This builder for chaining.
|
|
1053
|
+
*/
|
|
1054
|
+
mutateList(mutType, listName, valuePath) {
|
|
1055
|
+
this.ruleMutations.push({ mutationType: mutType, listName, valuePath });
|
|
1056
|
+
return this;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Enables dry-run mode for the rule.
|
|
1060
|
+
* @returns This builder for chaining.
|
|
1061
|
+
*/
|
|
1062
|
+
dryRun() {
|
|
1063
|
+
this.isDryRun = true;
|
|
1064
|
+
return this;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Finishes building the rule and returns to the parent provisioner.
|
|
1068
|
+
* @returns The parent Provisioner for chaining.
|
|
1069
|
+
*/
|
|
1070
|
+
done() {
|
|
1071
|
+
return this.parent._addRuleStep(this);
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Constructs the SDK Actions from the builder state.
|
|
1075
|
+
* @returns The Actions object for the API request.
|
|
1076
|
+
* @internal
|
|
1077
|
+
*/
|
|
1078
|
+
buildActions() {
|
|
1079
|
+
const actions = {};
|
|
1080
|
+
if (this.ruleDecision) {
|
|
1081
|
+
actions.decision = this.ruleDecision;
|
|
1082
|
+
}
|
|
1083
|
+
if (this.ruleScore) {
|
|
1084
|
+
actions.score = this.ruleScore;
|
|
1085
|
+
}
|
|
1086
|
+
if (this.ruleFlow) {
|
|
1087
|
+
actions.flow = this.ruleFlow;
|
|
1088
|
+
}
|
|
1089
|
+
if (this.ruleMutations.length > 0) {
|
|
1090
|
+
actions.mutations = this.ruleMutations.map((m) => ({
|
|
1091
|
+
type: m.mutationType,
|
|
1092
|
+
target: m.listName,
|
|
1093
|
+
value_path: m.valuePath
|
|
1094
|
+
}));
|
|
1095
|
+
}
|
|
1096
|
+
return actions;
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
|
|
1100
|
+
// src/provision/provisioner.ts
|
|
1101
|
+
var STEP_PHASE = 0;
|
|
1102
|
+
var STEP_SCOPE = 1;
|
|
1103
|
+
var STEP_LIST = 2;
|
|
1104
|
+
var STEP_RULE = 3;
|
|
1105
|
+
var Provisioner = class {
|
|
1106
|
+
client;
|
|
1107
|
+
steps = [];
|
|
1108
|
+
phases = /* @__PURE__ */ new Map();
|
|
1109
|
+
scopes = /* @__PURE__ */ new Map();
|
|
1110
|
+
lists = /* @__PURE__ */ new Map();
|
|
1111
|
+
rules = /* @__PURE__ */ new Map();
|
|
1112
|
+
/**
|
|
1113
|
+
* Creates a new Provisioner backed by the given SDK client.
|
|
1114
|
+
* @param client - The IntelMesh client instance.
|
|
1115
|
+
*/
|
|
1116
|
+
constructor(client) {
|
|
1117
|
+
this.client = client;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Registers a pipeline phase to be created.
|
|
1121
|
+
* @param name - The phase name.
|
|
1122
|
+
* @param position - The phase position (execution order).
|
|
1123
|
+
* @returns This provisioner for chaining.
|
|
1124
|
+
*/
|
|
1125
|
+
phase(name, position) {
|
|
1126
|
+
this.steps.push({
|
|
1127
|
+
kind: STEP_PHASE,
|
|
1128
|
+
name,
|
|
1129
|
+
position,
|
|
1130
|
+
applicableWhen: "",
|
|
1131
|
+
jsonPath: "",
|
|
1132
|
+
ruleBuilder: void 0
|
|
1133
|
+
});
|
|
1134
|
+
return this;
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Registers a pipeline phase with an applicable_when CEL expression.
|
|
1138
|
+
* @param name - The phase name.
|
|
1139
|
+
* @param position - The phase position (execution order).
|
|
1140
|
+
* @param applicableWhen - CEL expression controlling when this phase runs.
|
|
1141
|
+
* @returns This provisioner for chaining.
|
|
1142
|
+
*/
|
|
1143
|
+
phaseWithFilter(name, position, applicableWhen) {
|
|
1144
|
+
this.steps.push({
|
|
1145
|
+
kind: STEP_PHASE,
|
|
1146
|
+
name,
|
|
1147
|
+
position,
|
|
1148
|
+
applicableWhen,
|
|
1149
|
+
jsonPath: "",
|
|
1150
|
+
ruleBuilder: void 0
|
|
1151
|
+
});
|
|
1152
|
+
return this;
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Registers a scope to be created.
|
|
1156
|
+
* @param name - The scope name.
|
|
1157
|
+
* @param jsonPath - The JSON path expression.
|
|
1158
|
+
* @returns This provisioner for chaining.
|
|
1159
|
+
*/
|
|
1160
|
+
scope(name, jsonPath) {
|
|
1161
|
+
this.steps.push({
|
|
1162
|
+
kind: STEP_SCOPE,
|
|
1163
|
+
name,
|
|
1164
|
+
position: 0,
|
|
1165
|
+
applicableWhen: "",
|
|
1166
|
+
jsonPath,
|
|
1167
|
+
ruleBuilder: void 0
|
|
1168
|
+
});
|
|
1169
|
+
return this;
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Registers a named list to be created.
|
|
1173
|
+
* @param name - The list name.
|
|
1174
|
+
* @returns This provisioner for chaining.
|
|
1175
|
+
*/
|
|
1176
|
+
list(name) {
|
|
1177
|
+
this.steps.push({
|
|
1178
|
+
kind: STEP_LIST,
|
|
1179
|
+
name,
|
|
1180
|
+
position: 0,
|
|
1181
|
+
applicableWhen: "",
|
|
1182
|
+
jsonPath: "",
|
|
1183
|
+
ruleBuilder: void 0
|
|
1184
|
+
});
|
|
1185
|
+
return this;
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Starts building a rule and returns a ProvisionRuleBuilder.
|
|
1189
|
+
* @param name - The rule name.
|
|
1190
|
+
* @returns A rule builder for chaining.
|
|
1191
|
+
*/
|
|
1192
|
+
rule(name) {
|
|
1193
|
+
return new ProvisionRuleBuilder(this, name);
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Adds a completed rule step from the rule builder.
|
|
1197
|
+
* @param rb - The completed rule builder.
|
|
1198
|
+
* @returns This provisioner for chaining.
|
|
1199
|
+
* @internal
|
|
1200
|
+
*/
|
|
1201
|
+
_addRuleStep(rb) {
|
|
1202
|
+
this.steps.push({
|
|
1203
|
+
kind: STEP_RULE,
|
|
1204
|
+
name: rb.ruleName,
|
|
1205
|
+
position: 0,
|
|
1206
|
+
applicableWhen: "",
|
|
1207
|
+
jsonPath: "",
|
|
1208
|
+
ruleBuilder: rb
|
|
1209
|
+
});
|
|
1210
|
+
return this;
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Creates all registered resources via the API in registration order.
|
|
1214
|
+
* Throws on the first failure.
|
|
1215
|
+
*/
|
|
1216
|
+
async apply() {
|
|
1217
|
+
for (const step of this.steps) {
|
|
1218
|
+
await this.applyStep(step);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* Deletes all created resources in reverse dependency order:
|
|
1223
|
+
* rules, lists, scopes, phases.
|
|
1224
|
+
* Collects errors but continues deleting; throws the first error at the end.
|
|
1225
|
+
*/
|
|
1226
|
+
async teardown() {
|
|
1227
|
+
let firstError;
|
|
1228
|
+
const record = (err) => {
|
|
1229
|
+
if (!firstError && err instanceof Error) {
|
|
1230
|
+
firstError = err;
|
|
1231
|
+
}
|
|
1232
|
+
};
|
|
1233
|
+
await this.deleteAll(this.rules, (id) => this.client.rules.delete(id), record);
|
|
1234
|
+
await this.deleteAll(this.lists, (id) => this.client.lists.delete(id), record);
|
|
1235
|
+
await this.deleteAll(this.scopes, (id) => this.client.scopes.delete(id), record);
|
|
1236
|
+
await this.deleteAll(this.phases, (id) => this.client.phases.delete(id), record);
|
|
1237
|
+
if (firstError) {
|
|
1238
|
+
throw firstError;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Returns the provisioned ID for the named phase.
|
|
1243
|
+
* @param name - The phase name.
|
|
1244
|
+
* @returns The phase ID, or empty string if not found.
|
|
1245
|
+
*/
|
|
1246
|
+
phaseId(name) {
|
|
1247
|
+
return this.phases.get(name) ?? "";
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Returns the provisioned ID for the named list.
|
|
1251
|
+
* @param name - The list name.
|
|
1252
|
+
* @returns The list ID, or empty string if not found.
|
|
1253
|
+
*/
|
|
1254
|
+
listId(name) {
|
|
1255
|
+
return this.lists.get(name) ?? "";
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Returns the provisioned ID for the named scope.
|
|
1259
|
+
* @param name - The scope name.
|
|
1260
|
+
* @returns The scope ID, or empty string if not found.
|
|
1261
|
+
*/
|
|
1262
|
+
scopeId(name) {
|
|
1263
|
+
return this.scopes.get(name) ?? "";
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Returns the provisioned ID for the named rule.
|
|
1267
|
+
* @param name - The rule name.
|
|
1268
|
+
* @returns The rule ID, or empty string if not found.
|
|
1269
|
+
*/
|
|
1270
|
+
ruleId(name) {
|
|
1271
|
+
return this.rules.get(name) ?? "";
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Dispatches a single step to the appropriate creator.
|
|
1275
|
+
* @param step
|
|
1276
|
+
*/
|
|
1277
|
+
async applyStep(step) {
|
|
1278
|
+
switch (step.kind) {
|
|
1279
|
+
case STEP_PHASE:
|
|
1280
|
+
return this.createPhase(step);
|
|
1281
|
+
case STEP_SCOPE:
|
|
1282
|
+
return this.createScope(step);
|
|
1283
|
+
case STEP_LIST:
|
|
1284
|
+
return this.createList(step);
|
|
1285
|
+
case STEP_RULE:
|
|
1286
|
+
return this.createRule(step);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* Creates a phase and stores its ID.
|
|
1291
|
+
* @param step
|
|
1292
|
+
*/
|
|
1293
|
+
async createPhase(step) {
|
|
1294
|
+
const req = {
|
|
1295
|
+
name: step.name,
|
|
1296
|
+
position: step.position
|
|
1297
|
+
};
|
|
1298
|
+
if (step.applicableWhen) {
|
|
1299
|
+
req.applicable_when = step.applicableWhen;
|
|
1300
|
+
}
|
|
1301
|
+
const phase = await this.client.phases.create(req);
|
|
1302
|
+
this.phases.set(step.name, phase.id);
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* Creates a scope and stores its ID.
|
|
1306
|
+
* @param step
|
|
1307
|
+
*/
|
|
1308
|
+
async createScope(step) {
|
|
1309
|
+
const scope = await this.client.scopes.create({
|
|
1310
|
+
name: step.name,
|
|
1311
|
+
json_path: step.jsonPath
|
|
1312
|
+
});
|
|
1313
|
+
this.scopes.set(step.name, scope.id);
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Creates a list and stores its ID.
|
|
1317
|
+
* @param step
|
|
1318
|
+
*/
|
|
1319
|
+
async createList(step) {
|
|
1320
|
+
const list = await this.client.lists.create({
|
|
1321
|
+
name: step.name,
|
|
1322
|
+
description: ""
|
|
1323
|
+
});
|
|
1324
|
+
this.lists.set(step.name, list.id);
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Creates a rule after resolving its phase name to an ID.
|
|
1328
|
+
* @param step
|
|
1329
|
+
*/
|
|
1330
|
+
async createRule(step) {
|
|
1331
|
+
const rb = step.ruleBuilder;
|
|
1332
|
+
if (!rb) {
|
|
1333
|
+
throw new Error(`provision: rule step "${step.name}" has no builder`);
|
|
1334
|
+
}
|
|
1335
|
+
const phaseID = this.phases.get(rb.phaseName);
|
|
1336
|
+
if (!phaseID) {
|
|
1337
|
+
throw new Error(`provision rule "${rb.ruleName}": phase not found: ${rb.phaseName}`);
|
|
1338
|
+
}
|
|
1339
|
+
const rule = await this.client.rules.create({
|
|
1340
|
+
name: rb.ruleName,
|
|
1341
|
+
phase_id: phaseID,
|
|
1342
|
+
priority: rb.rulePriority,
|
|
1343
|
+
applicable_when: rb.applicable,
|
|
1344
|
+
expression: rb.expression,
|
|
1345
|
+
actions: rb.buildActions(),
|
|
1346
|
+
enabled: true,
|
|
1347
|
+
dry_run: rb.isDryRun
|
|
1348
|
+
});
|
|
1349
|
+
this.rules.set(rb.ruleName, rule.id);
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Deletes all resources in a map, recording errors.
|
|
1353
|
+
* @param ids
|
|
1354
|
+
* @param deleteFn
|
|
1355
|
+
* @param record
|
|
1356
|
+
*/
|
|
1357
|
+
async deleteAll(ids, deleteFn, record) {
|
|
1358
|
+
for (const [name, id] of ids) {
|
|
1359
|
+
try {
|
|
1360
|
+
await deleteFn(id);
|
|
1361
|
+
} catch (err) {
|
|
1362
|
+
record(
|
|
1363
|
+
err instanceof Error ? new Error(`deleting ${name} (${id}): ${err.message}`) : new Error(`deleting ${name} (${id}): unknown error`)
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
// src/testkit/assertion.ts
|
|
1371
|
+
var EventAssertion = class {
|
|
1372
|
+
result;
|
|
1373
|
+
constructor(result) {
|
|
1374
|
+
this.result = result;
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Asserts the result has a specific decision action and severity.
|
|
1378
|
+
* @param action - The expected action string (e.g. "block").
|
|
1379
|
+
* @param severity - The expected severity level.
|
|
1380
|
+
* @returns This assertion for chaining.
|
|
1381
|
+
* @throws {Error} If the decision does not match.
|
|
1382
|
+
*/
|
|
1383
|
+
expectDecision(action, severity) {
|
|
1384
|
+
const decision = this.result.decision;
|
|
1385
|
+
if (!decision) {
|
|
1386
|
+
throw new Error(`testkit: expected decision ${action}/${severity}, got no decision`);
|
|
1387
|
+
}
|
|
1388
|
+
if (decision.action !== action) {
|
|
1389
|
+
throw new Error(`testkit: decision action: got "${decision.action}", want "${action}"`);
|
|
1390
|
+
}
|
|
1391
|
+
if (decision.severity !== severity) {
|
|
1392
|
+
throw new Error(`testkit: decision severity: got "${decision.severity}", want "${severity}"`);
|
|
1393
|
+
}
|
|
1394
|
+
return this;
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Asserts the result has no decision (action is empty or undefined).
|
|
1398
|
+
* @returns This assertion for chaining.
|
|
1399
|
+
* @throws {Error} If a decision is present.
|
|
1400
|
+
*/
|
|
1401
|
+
expectNoDecision() {
|
|
1402
|
+
const decision = this.result.decision;
|
|
1403
|
+
if (decision.action !== "") {
|
|
1404
|
+
throw new Error(`testkit: expected no decision, got ${decision.action}/${decision.severity}`);
|
|
1405
|
+
}
|
|
1406
|
+
return this;
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Asserts the transient score equals the expected value.
|
|
1410
|
+
* @param score - The expected score value.
|
|
1411
|
+
* @returns This assertion for chaining.
|
|
1412
|
+
* @throws {Error} If the score does not match.
|
|
1413
|
+
*/
|
|
1414
|
+
expectScore(score) {
|
|
1415
|
+
if (this.result.transient_score !== score) {
|
|
1416
|
+
throw new Error(
|
|
1417
|
+
`testkit: score: got ${String(this.result.transient_score)}, want ${String(score)}`
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
return this;
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Returns the underlying IngestResult for custom assertions.
|
|
1424
|
+
* @returns The raw IngestResult.
|
|
1425
|
+
*/
|
|
1426
|
+
raw() {
|
|
1427
|
+
return this.result;
|
|
1428
|
+
}
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1431
|
+
// src/testkit/harness.ts
|
|
1432
|
+
var ALL_PERMISSIONS = [
|
|
1433
|
+
"events:write",
|
|
1434
|
+
"events:simulate",
|
|
1435
|
+
"rules:read",
|
|
1436
|
+
"rules:write",
|
|
1437
|
+
"scopes:read",
|
|
1438
|
+
"scopes:write",
|
|
1439
|
+
"lists:read",
|
|
1440
|
+
"lists:write",
|
|
1441
|
+
"scores:read",
|
|
1442
|
+
"scores:write",
|
|
1443
|
+
"api_keys:manage",
|
|
1444
|
+
"evaluations:read",
|
|
1445
|
+
"audit:read"
|
|
1446
|
+
];
|
|
1447
|
+
var PROJECTOR_WAIT_MS = 500;
|
|
1448
|
+
var Harness = class {
|
|
1449
|
+
config;
|
|
1450
|
+
adminClient;
|
|
1451
|
+
testClient;
|
|
1452
|
+
testKeyId = "";
|
|
1453
|
+
provisioner;
|
|
1454
|
+
/**
|
|
1455
|
+
* Creates a new test harness.
|
|
1456
|
+
* @param config - The harness configuration.
|
|
1457
|
+
*/
|
|
1458
|
+
constructor(config) {
|
|
1459
|
+
this.config = config;
|
|
1460
|
+
this.adminClient = new IntelMesh({
|
|
1461
|
+
baseUrl: config.baseURL,
|
|
1462
|
+
apiKey: config.adminKey,
|
|
1463
|
+
timeout: config.timeout
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Creates an ephemeral API key with all permissions.
|
|
1468
|
+
* Must be called before sending events or provisioning.
|
|
1469
|
+
*/
|
|
1470
|
+
async setup() {
|
|
1471
|
+
const result = await this.adminClient.apiKeys.create({
|
|
1472
|
+
name: `testkit-${String(Date.now())}`,
|
|
1473
|
+
permissions: [...ALL_PERMISSIONS]
|
|
1474
|
+
});
|
|
1475
|
+
this.testKeyId = result.id;
|
|
1476
|
+
this.testClient = new IntelMesh({
|
|
1477
|
+
baseUrl: this.config.baseURL,
|
|
1478
|
+
apiKey: result.key,
|
|
1479
|
+
timeout: this.config.timeout
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Returns the test-scoped SDK client.
|
|
1484
|
+
* @returns The IntelMesh client using the ephemeral key.
|
|
1485
|
+
* @throws {Error} If setup() has not been called.
|
|
1486
|
+
*/
|
|
1487
|
+
client() {
|
|
1488
|
+
if (!this.testClient) {
|
|
1489
|
+
throw new Error("testkit: harness not set up; call setup() first");
|
|
1490
|
+
}
|
|
1491
|
+
return this.testClient;
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Applies a provisioner and registers it for teardown on cleanup.
|
|
1495
|
+
* @param p - The provisioner to apply.
|
|
1496
|
+
*/
|
|
1497
|
+
async provision(p) {
|
|
1498
|
+
this.provisioner = p;
|
|
1499
|
+
await p.apply();
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Sends an event for synchronous evaluation.
|
|
1503
|
+
* @param eventType - The event type (e.g. "transaction.pix").
|
|
1504
|
+
* @param payload - The event payload.
|
|
1505
|
+
* @returns An EventAssertion for chaining assertions.
|
|
1506
|
+
*/
|
|
1507
|
+
async send(eventType, payload) {
|
|
1508
|
+
const c = this.client();
|
|
1509
|
+
const result = await c.events.ingest({
|
|
1510
|
+
event_type: eventType,
|
|
1511
|
+
payload
|
|
1512
|
+
});
|
|
1513
|
+
return new EventAssertion(result);
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Sends an event to the simulation endpoint.
|
|
1517
|
+
* @param eventType - The event type.
|
|
1518
|
+
* @param payload - The event payload.
|
|
1519
|
+
* @returns An EventAssertion for chaining assertions.
|
|
1520
|
+
*/
|
|
1521
|
+
async sendSimulate(eventType, payload) {
|
|
1522
|
+
const c = this.client();
|
|
1523
|
+
const result = await c.events.simulate({
|
|
1524
|
+
event_type: eventType,
|
|
1525
|
+
payload
|
|
1526
|
+
});
|
|
1527
|
+
return new EventAssertion(result);
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Pauses for async projectors to complete.
|
|
1531
|
+
* @param ms - Optional wait time in milliseconds (default 500).
|
|
1532
|
+
*/
|
|
1533
|
+
async waitForProjectors(ms) {
|
|
1534
|
+
const wait = ms ?? PROJECTOR_WAIT_MS;
|
|
1535
|
+
await new Promise((resolve) => {
|
|
1536
|
+
setTimeout(resolve, wait);
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Verifies that a list contains a specific value.
|
|
1541
|
+
* Requires a provisioner to have been applied to resolve list names.
|
|
1542
|
+
* @param listName - The list name (as registered with the provisioner).
|
|
1543
|
+
* @param value - The value to search for.
|
|
1544
|
+
* @throws {Error} If the list does not contain the value.
|
|
1545
|
+
*/
|
|
1546
|
+
async verifyListContains(listName, value) {
|
|
1547
|
+
await this.verifyList(listName, value, true);
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* Verifies that a list does NOT contain a specific value.
|
|
1551
|
+
* Requires a provisioner to have been applied to resolve list names.
|
|
1552
|
+
* @param listName - The list name (as registered with the provisioner).
|
|
1553
|
+
* @param value - The value to search for.
|
|
1554
|
+
* @throws {Error} If the list contains the value.
|
|
1555
|
+
*/
|
|
1556
|
+
async verifyListNotContains(listName, value) {
|
|
1557
|
+
await this.verifyList(listName, value, false);
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Deletes the ephemeral API key and tears down the provisioner.
|
|
1561
|
+
* Should be called in afterAll or finally blocks.
|
|
1562
|
+
*/
|
|
1563
|
+
async cleanup() {
|
|
1564
|
+
if (this.provisioner) {
|
|
1565
|
+
try {
|
|
1566
|
+
await this.provisioner.teardown();
|
|
1567
|
+
} catch (err) {
|
|
1568
|
+
const msg = err instanceof Error ? err.message : "unknown error";
|
|
1569
|
+
console.warn(`testkit: teardown warning: ${msg}`);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
if (this.testKeyId) {
|
|
1573
|
+
try {
|
|
1574
|
+
await this.adminClient.apiKeys.delete(this.testKeyId);
|
|
1575
|
+
} catch (err) {
|
|
1576
|
+
const msg = err instanceof Error ? err.message : "unknown error";
|
|
1577
|
+
console.warn(`testkit: warning: failed to delete ephemeral key ${this.testKeyId}: ${msg}`);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Internal list verification.
|
|
1583
|
+
* @param listName
|
|
1584
|
+
* @param _value
|
|
1585
|
+
* @param shouldContain
|
|
1586
|
+
*/
|
|
1587
|
+
async verifyList(listName, _value, shouldContain) {
|
|
1588
|
+
if (!this.provisioner) {
|
|
1589
|
+
throw new Error(`testkit: no provisioner set, cannot resolve list "${listName}"`);
|
|
1590
|
+
}
|
|
1591
|
+
const listID = this.provisioner.listId(listName);
|
|
1592
|
+
if (!listID) {
|
|
1593
|
+
throw new Error(`testkit: list "${listName}" not found in provisioner`);
|
|
1594
|
+
}
|
|
1595
|
+
const c = this.client();
|
|
1596
|
+
const page = await c.lists.get(listID);
|
|
1597
|
+
if (!page) {
|
|
1598
|
+
throw new Error(`testkit: could not retrieve list "${listName}" (${listID})`);
|
|
1599
|
+
}
|
|
1600
|
+
if (shouldContain) {
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
async function withHarness(config, fn) {
|
|
1606
|
+
const harness = new Harness(config);
|
|
1607
|
+
await harness.setup();
|
|
1608
|
+
try {
|
|
1609
|
+
await fn(harness);
|
|
1610
|
+
} finally {
|
|
1611
|
+
await harness.cleanup();
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
export {
|
|
1615
|
+
APIKeys,
|
|
1616
|
+
Audit,
|
|
1617
|
+
Evaluations,
|
|
1618
|
+
EventAssertion,
|
|
1619
|
+
EventBuilder,
|
|
1620
|
+
Events,
|
|
1621
|
+
ForbiddenError,
|
|
1622
|
+
Harness,
|
|
1623
|
+
HttpClient,
|
|
1624
|
+
IntelMesh,
|
|
1625
|
+
IntelMeshError,
|
|
1626
|
+
InternalError,
|
|
1627
|
+
Lists,
|
|
1628
|
+
NetworkError,
|
|
1629
|
+
NotFoundError,
|
|
1630
|
+
ParseError,
|
|
1631
|
+
Phases,
|
|
1632
|
+
ProvisionRuleBuilder,
|
|
1633
|
+
Provisioner,
|
|
1634
|
+
RuleBuilder,
|
|
1635
|
+
Rules,
|
|
1636
|
+
Scopes,
|
|
1637
|
+
Scores,
|
|
1638
|
+
UnauthorizedError,
|
|
1639
|
+
UnavailableError,
|
|
1640
|
+
ValidationError,
|
|
1641
|
+
collectAll,
|
|
1642
|
+
isForbidden,
|
|
1643
|
+
isIntelMeshError,
|
|
1644
|
+
isNetwork,
|
|
1645
|
+
isNotFound,
|
|
1646
|
+
isUnauthorized,
|
|
1647
|
+
isValidation,
|
|
1648
|
+
mapStatusToError,
|
|
1649
|
+
paginate,
|
|
1650
|
+
withHarness
|
|
1651
|
+
};
|