@bunbase-ae/js 3.0.1-next.403.265841b → 3.0.1-next.407.08bf4a7
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/package.json +1 -1
- package/src/admin.ts +138 -0
package/package.json
CHANGED
package/src/admin.ts
CHANGED
|
@@ -1307,6 +1307,40 @@ export interface CreateNamedQueryInput {
|
|
|
1307
1307
|
|
|
1308
1308
|
export type UpdateNamedQueryInput = Partial<Omit<CreateNamedQueryInput, "name">>;
|
|
1309
1309
|
|
|
1310
|
+
// ─── Query Advisor (#481) ──────────────────────────────────────────────────────
|
|
1311
|
+
|
|
1312
|
+
export interface QueryAdvisorCandidate {
|
|
1313
|
+
/** Normalized RPC + filter/sort shape — never raw values. */
|
|
1314
|
+
signature: string;
|
|
1315
|
+
tenant_id: string | null;
|
|
1316
|
+
call_count: number;
|
|
1317
|
+
total_duration_ms: number;
|
|
1318
|
+
avg_duration_ms: number;
|
|
1319
|
+
p50_ms: number;
|
|
1320
|
+
p99_ms: number;
|
|
1321
|
+
last_seen: number;
|
|
1322
|
+
cache_hit_estimate: number;
|
|
1323
|
+
projected_savings_ms: number;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
export interface AdvisorListResult {
|
|
1327
|
+
/** False when the advisor is disabled — render the "Enable" CTA. */
|
|
1328
|
+
enabled: boolean;
|
|
1329
|
+
candidates: QueryAdvisorCandidate[];
|
|
1330
|
+
settings: { enabled: boolean; sampleRate: number; retentionDays: number; topK: number };
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
export interface PromoteAdvisorInput {
|
|
1334
|
+
name: string;
|
|
1335
|
+
description?: string | null;
|
|
1336
|
+
sql: string;
|
|
1337
|
+
params?: Record<string, NamedQueryParamDef>;
|
|
1338
|
+
access?: NamedQueryAccessRule;
|
|
1339
|
+
result_cache_ttl_s?: number;
|
|
1340
|
+
/** The originating advisor signature. Stamped so the suggestion disappears. */
|
|
1341
|
+
signature: string;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1310
1344
|
class AdminNamedQueriesClient {
|
|
1311
1345
|
constructor(private readonly http: HttpClient) {}
|
|
1312
1346
|
|
|
@@ -1375,6 +1409,37 @@ class AdminNamedQueriesClient {
|
|
|
1375
1409
|
"/api/v1/admin/queries/install-starter",
|
|
1376
1410
|
);
|
|
1377
1411
|
}
|
|
1412
|
+
|
|
1413
|
+
// ─── Query Advisor (#481) ────────────────────────────────────────────────────
|
|
1414
|
+
|
|
1415
|
+
/**
|
|
1416
|
+
* List the top hot read-pattern candidates the advisor suggests promoting
|
|
1417
|
+
* into named queries, ranked by projected savings. `enabled` is false (with
|
|
1418
|
+
* an empty list) when the advisor is turned off — surface the "Enable" CTA.
|
|
1419
|
+
*/
|
|
1420
|
+
async advisorList(opts: { tenantId?: string | null } = {}): Promise<AdvisorListResult> {
|
|
1421
|
+
const qs =
|
|
1422
|
+
opts.tenantId !== undefined && opts.tenantId !== null
|
|
1423
|
+
? `?tenant_id=${encodeURIComponent(opts.tenantId)}`
|
|
1424
|
+
: "";
|
|
1425
|
+
return this.http.request<AdvisorListResult>("GET", `/api/v1/admin/queries/advisor${qs}`);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
/** Enable the Query Advisor at runtime (existing-deployment opt-in). */
|
|
1429
|
+
async advisorEnable(): Promise<{ enabled: boolean }> {
|
|
1430
|
+
return this.http.request<{ enabled: boolean }>("POST", "/api/v1/admin/queries/advisor/enable");
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
/**
|
|
1434
|
+
* Promote a captured signature into a named query. SQL/params are best-effort
|
|
1435
|
+
* pre-filled by the caller (Studio derives them from the signature). Once
|
|
1436
|
+
* promoted, the advisor stops suggesting that signature.
|
|
1437
|
+
*/
|
|
1438
|
+
async advisorPromote(input: PromoteAdvisorInput): Promise<NamedQuery> {
|
|
1439
|
+
return this.http.request<NamedQuery>("POST", "/api/v1/admin/queries/advisor/promote", {
|
|
1440
|
+
body: input,
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1378
1443
|
}
|
|
1379
1444
|
|
|
1380
1445
|
// ─── Write hooks ──────────────────────────────────────────────────────────────
|
|
@@ -1387,6 +1452,21 @@ export type HookEvent =
|
|
|
1387
1452
|
| "beforeDelete"
|
|
1388
1453
|
| "afterDelete";
|
|
1389
1454
|
|
|
1455
|
+
/**
|
|
1456
|
+
* Per-hook delivery options (issue #682). `mode: "durable"` persists each
|
|
1457
|
+
* after-hook delivery before its first attempt and retries with backoff until
|
|
1458
|
+
* acked or dead-lettered, surviving restarts. Default is fire-and-forget.
|
|
1459
|
+
*/
|
|
1460
|
+
export interface HookDeliveryConfig {
|
|
1461
|
+
mode?: "fire_and_forget" | "durable";
|
|
1462
|
+
/** Max attempts before dead-lettering (durable mode). Default 10. */
|
|
1463
|
+
max_retries?: number;
|
|
1464
|
+
/** Per-attempt request timeout in ms. Default 10000. */
|
|
1465
|
+
timeout_ms?: number;
|
|
1466
|
+
/** Park exhausted deliveries in the dead-letter store for replay. Default true. */
|
|
1467
|
+
dead_letter?: boolean;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1390
1470
|
export interface HookConfig {
|
|
1391
1471
|
id: string;
|
|
1392
1472
|
collection: string;
|
|
@@ -1397,6 +1477,8 @@ export interface HookConfig {
|
|
|
1397
1477
|
timeout_ms: number;
|
|
1398
1478
|
enabled: boolean;
|
|
1399
1479
|
sort_order: number;
|
|
1480
|
+
/** Durable-delivery options, or null for legacy fire-and-forget (#682). */
|
|
1481
|
+
delivery?: HookDeliveryConfig | null;
|
|
1400
1482
|
created_at: number;
|
|
1401
1483
|
}
|
|
1402
1484
|
|
|
@@ -1412,10 +1494,35 @@ export interface CreateHookInput {
|
|
|
1412
1494
|
enabled?: boolean;
|
|
1413
1495
|
/** Ascending order for hooks sharing the same (collection, event). Default 0. */
|
|
1414
1496
|
sort_order?: number;
|
|
1497
|
+
/** Durable, at-least-once delivery options (after-hooks only). (#682) */
|
|
1498
|
+
delivery?: HookDeliveryConfig | null;
|
|
1415
1499
|
}
|
|
1416
1500
|
|
|
1417
1501
|
export type UpdateHookInput = Partial<Omit<CreateHookInput, "collection" | "event">>;
|
|
1418
1502
|
|
|
1503
|
+
/** Status of a durable delivery as surfaced to the admin replay API (#682). */
|
|
1504
|
+
export type WebhookDeliveryStatus = "pending" | "in_flight" | "delivered" | "dead_letter";
|
|
1505
|
+
|
|
1506
|
+
/** Admin-facing view of a durable delivery. The signing secret is never included. */
|
|
1507
|
+
export interface WebhookDeliverySummary {
|
|
1508
|
+
id: string;
|
|
1509
|
+
idempotency_key: string;
|
|
1510
|
+
hook_id: string | null;
|
|
1511
|
+
collection: string;
|
|
1512
|
+
event: string;
|
|
1513
|
+
url: string;
|
|
1514
|
+
has_secret: boolean;
|
|
1515
|
+
status: WebhookDeliveryStatus;
|
|
1516
|
+
attempts: number;
|
|
1517
|
+
max_attempts: number;
|
|
1518
|
+
timeout_ms: number;
|
|
1519
|
+
next_attempt_at: number;
|
|
1520
|
+
last_error: string | null;
|
|
1521
|
+
last_status: number | null;
|
|
1522
|
+
created_at: number;
|
|
1523
|
+
updated_at: number;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1419
1526
|
class AdminHooksClient {
|
|
1420
1527
|
constructor(private readonly http: HttpClient) {}
|
|
1421
1528
|
|
|
@@ -1453,6 +1560,37 @@ class AdminHooksClient {
|
|
|
1453
1560
|
async delete(id: string): Promise<void> {
|
|
1454
1561
|
await this.http.request("DELETE", `/api/v1/admin/hooks/${encodeURIComponent(id)}`);
|
|
1455
1562
|
}
|
|
1563
|
+
|
|
1564
|
+
/**
|
|
1565
|
+
* List durable deliveries (issue #682). `status: "pending"` returns rows
|
|
1566
|
+
* still in the live queue; `status: "dead_letter"` returns exhausted
|
|
1567
|
+
* deliveries parked for inspection/replay. Secrets are never returned.
|
|
1568
|
+
*/
|
|
1569
|
+
async deliveries(opts?: {
|
|
1570
|
+
status?: "pending" | "dead_letter";
|
|
1571
|
+
limit?: number;
|
|
1572
|
+
}): Promise<WebhookDeliverySummary[]> {
|
|
1573
|
+
const query: Record<string, string> = {};
|
|
1574
|
+
if (opts?.status) query.status = opts.status;
|
|
1575
|
+
if (opts?.limit != null) query.limit = String(opts.limit);
|
|
1576
|
+
const res = await this.http.request<{ items: WebhookDeliverySummary[] }>(
|
|
1577
|
+
"GET",
|
|
1578
|
+
"/api/v1/admin/hooks/deliveries",
|
|
1579
|
+
{ query },
|
|
1580
|
+
);
|
|
1581
|
+
return res.items;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
/**
|
|
1585
|
+
* Replay a durable delivery (issue #682) — re-arm a queue row or re-enqueue
|
|
1586
|
+
* a dead-letter row back to pending. Returns the (possibly new) queue id.
|
|
1587
|
+
*/
|
|
1588
|
+
async replayDelivery(id: string): Promise<{ id: string; source: "queue" | "dead_letter" }> {
|
|
1589
|
+
return this.http.request<{ id: string; source: "queue" | "dead_letter" }>(
|
|
1590
|
+
"POST",
|
|
1591
|
+
`/api/v1/admin/hooks/deliveries/${encodeURIComponent(id)}/replay`,
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1456
1594
|
}
|
|
1457
1595
|
|
|
1458
1596
|
// Tenant membership management — surfaces the /api/v1/admin/tenants/* endpoints
|