@primitivedotdev/sdk 1.26.1 → 1.29.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/README.md +56 -0
- package/dist/api/index.d.ts +4 -4
- package/dist/api/index.js +4 -4
- package/dist/{api-BEv_YML6.js → api-27BvnkVA.js} +552 -4
- package/dist/contract/index.d.ts +14 -5
- package/dist/contract/index.js +6 -3
- package/dist/{errors-CCJX3I_T.d.ts → errors-Li0PXZXa.d.ts} +5 -1
- package/dist/{index-B1a5L2zU.d.ts → index-Bjc-cR2b.d.ts} +360 -6
- package/dist/{index-JpXUV16f.d.ts → index-BxPkB7GU.d.ts} +39 -46
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/openapi/index.js +1 -1
- package/dist/{operations.generated-DVHnicf5.js → operations.generated-Dra-xGtu.js} +663 -6
- package/dist/parser/address.js +1 -1
- package/dist/parser/index.d.ts +1 -1
- package/dist/parser/index.js +1 -1
- package/dist/payloads/index.js +1 -1
- package/dist/{webhook-CLVRgb-L.js → trust-m3dNPmWL.js} +835 -2190
- package/dist/{types-DcXtFARL.d.ts → types-Rn4qu5Q7.d.ts} +12 -5
- package/dist/webhook/index.d.ts +3 -3
- package/dist/webhook/index.js +2 -2
- package/dist/webhook-DArt1Ipq.js +2139 -0
- package/package.json +3 -3
- package/dist/errors-BVnAu5jX.js +0 -688
- /package/dist/{address-parser-C2JJzLdA.js → address-parser-BxXv4vx0.js} +0 -0
- /package/dist/{payloads-BfJAtqW9.js → payloads-BMngnJlE.js} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-pbuEa-1d.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { L as WebhookVerificationError, _ as parseWebhookEvent, m as formatAddress } from "./trust-m3dNPmWL.js";
|
|
3
|
+
import { a as pushFile, i as pushBytes, o as loadNodeFsPromises } from "./payloads-BMngnJlE.js";
|
|
4
4
|
//#region ../packages/api-core/src/api/core/bodySerializer.gen.ts
|
|
5
5
|
const jsonBodySerializer = { bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value) };
|
|
6
6
|
//#endregion
|
|
@@ -649,6 +649,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
649
649
|
getAgent: () => getAgent,
|
|
650
650
|
getChallenge: () => getChallenge,
|
|
651
651
|
getConversation: () => getConversation,
|
|
652
|
+
getCreditBalance: () => getCreditBalance,
|
|
652
653
|
getEmail: () => getEmail,
|
|
653
654
|
getFunction: () => getFunction,
|
|
654
655
|
getFunctionRouting: () => getFunctionRouting,
|
|
@@ -692,6 +693,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
692
693
|
pollCliLogin: () => pollCliLogin,
|
|
693
694
|
publishAgent: () => publishAgent,
|
|
694
695
|
pullWebhookEvent: () => pullWebhookEvent,
|
|
696
|
+
redeemCreditCode: () => redeemCreditCode,
|
|
695
697
|
registerPayoutAddress: () => registerPayoutAddress,
|
|
696
698
|
removeAgentConnection: () => removeAgentConnection,
|
|
697
699
|
reorderRoutes: () => reorderRoutes,
|
|
@@ -1067,6 +1069,55 @@ const rotateWebhookSecret = (options) => (options?.client ?? client$1).post({
|
|
|
1067
1069
|
...options
|
|
1068
1070
|
});
|
|
1069
1071
|
/**
|
|
1072
|
+
* Redeem a credit code
|
|
1073
|
+
*
|
|
1074
|
+
* Redeem a credit code for the authenticated organization. The credit is
|
|
1075
|
+
* added to the organization prepaid credit and shows in
|
|
1076
|
+
* `GET /credits/balance` under `prepaid_credit`.
|
|
1077
|
+
*
|
|
1078
|
+
* Redeeming requires an organization owner or admin. An API key redeems
|
|
1079
|
+
* with the authority of the user who created it, based on that user's
|
|
1080
|
+
* current role; a key without that authority gets the same
|
|
1081
|
+
* `credit_code_invalid` refusal as an unknown code.
|
|
1082
|
+
*
|
|
1083
|
+
* The `Idempotency-Key` header is required. Retrying with the same key
|
|
1084
|
+
* and code returns the original grant with `replayed: true` and grants
|
|
1085
|
+
* nothing new; the same key with a different code is refused with
|
|
1086
|
+
* `idempotency_key_reused`. Every refusal carries a human-readable
|
|
1087
|
+
* `error.message` that can be shown to the user as is.
|
|
1088
|
+
*
|
|
1089
|
+
*/
|
|
1090
|
+
const redeemCreditCode = (options) => (options.client ?? client$1).post({
|
|
1091
|
+
security: [{
|
|
1092
|
+
scheme: "bearer",
|
|
1093
|
+
type: "http"
|
|
1094
|
+
}],
|
|
1095
|
+
url: "/credits/redeem",
|
|
1096
|
+
...options,
|
|
1097
|
+
headers: {
|
|
1098
|
+
...options.body !== void 0 && { "Content-Type": "application/json" },
|
|
1099
|
+
...options.headers
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
/**
|
|
1103
|
+
* Get credit balance
|
|
1104
|
+
*
|
|
1105
|
+
* Read the organization credit position. `prepaid_credit` is the prepaid
|
|
1106
|
+
* usage credit (paid top-ups, redeemed credit codes and granted credit)
|
|
1107
|
+
* that can still pay for usage. `budget` is the active agent spending
|
|
1108
|
+
* budget an operator funded for top-ups, or null when there is none.
|
|
1109
|
+
* Amounts are strings of integer micros (1 USD = 1000000 micros).
|
|
1110
|
+
*
|
|
1111
|
+
*/
|
|
1112
|
+
const getCreditBalance = (options) => (options?.client ?? client$1).get({
|
|
1113
|
+
security: [{
|
|
1114
|
+
scheme: "bearer",
|
|
1115
|
+
type: "http"
|
|
1116
|
+
}],
|
|
1117
|
+
url: "/credits/balance",
|
|
1118
|
+
...options
|
|
1119
|
+
});
|
|
1120
|
+
/**
|
|
1070
1121
|
* List all domains
|
|
1071
1122
|
*
|
|
1072
1123
|
* Returns all verified and unverified domains for your organization,
|
|
@@ -1543,7 +1594,7 @@ const listEndpoints = (options) => (options?.client ?? client$1).get({
|
|
|
1543
1594
|
* accepts the signature.
|
|
1544
1595
|
*
|
|
1545
1596
|
*
|
|
1546
|
-
* For local receiving, use kind=pull and a stable name, without url, function_id or domain_id. Named creation resumes the same active destination; omitted filters preserve its selection, while conflicting supplied configuration returns 409. New unfiltered destinations receive all subsequently ready eligible event types. Pull destinations do not occupy HTTP routing slots. Signing-secret setup and a test HTTP delivery are not required for pull receiving.
|
|
1597
|
+
* For local receiving, use kind=pull and a stable name, without url, function_id or domain_id. Named creation resumes the same active destination; omitted filters preserve its selection, while conflicting supplied configuration returns 409. New unfiltered destinations receive all subsequently ready eligible event types within their credential scope. Connected-agent credentials automatically create private subscriptions restricted to inbound email for their assigned address. Names are isolated per credential; revocation or replacement deletes its subscriptions. Address-scoped events contain parsed mail without raw MIME, signed download links, routing metadata, or sibling envelope recipients. Pull destinations do not occupy HTTP routing slots. Signing-secret setup and a test HTTP delivery are not required for pull receiving.
|
|
1547
1598
|
*/
|
|
1548
1599
|
const createEndpoint = (options) => (options.client ?? client$1).post({
|
|
1549
1600
|
security: [{
|
|
@@ -3393,6 +3444,501 @@ function isMemoryJsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
|
3393
3444
|
}
|
|
3394
3445
|
}
|
|
3395
3446
|
//#endregion
|
|
3447
|
+
//#region src/api/event-transport.ts
|
|
3448
|
+
var EventReceiverError = class extends Error {
|
|
3449
|
+
constructor(message, code, status = 0, retryAfterMs = 0) {
|
|
3450
|
+
super(message);
|
|
3451
|
+
this.code = code;
|
|
3452
|
+
this.status = status;
|
|
3453
|
+
this.retryAfterMs = retryAfterMs;
|
|
3454
|
+
this.name = "EventReceiverError";
|
|
3455
|
+
}
|
|
3456
|
+
};
|
|
3457
|
+
function record(value) {
|
|
3458
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3459
|
+
}
|
|
3460
|
+
function validateOffer(value) {
|
|
3461
|
+
if (!record(value) || !Number.isSafeInteger(value.backlog) || Number(value.backlog) < 0 || !Number.isSafeInteger(value.gap_count) || Number(value.gap_count) < 0 || value.last_gap_reason !== null && typeof value.last_gap_reason !== "string" || value.retention_seconds !== 86400 || value.handler_timeout_seconds !== 30) throw new EventReceiverError("Invalid event status response", "invalid_response");
|
|
3462
|
+
const d = value.delivery;
|
|
3463
|
+
if (d !== null && (!record(d) || ![
|
|
3464
|
+
"queue_id",
|
|
3465
|
+
"event_id",
|
|
3466
|
+
"delivery_id",
|
|
3467
|
+
"lease_token",
|
|
3468
|
+
"event_type",
|
|
3469
|
+
"body",
|
|
3470
|
+
"lease_expires_at"
|
|
3471
|
+
].every((key) => typeof d[key] === "string") || !Number.isFinite(Date.parse(String(d.lease_expires_at))) || !record(d.headers) || !Object.values(d.headers).every((header) => typeof header === "string"))) throw new EventReceiverError("Invalid event delivery response", "invalid_response");
|
|
3472
|
+
return value;
|
|
3473
|
+
}
|
|
3474
|
+
function retryAfter(value) {
|
|
3475
|
+
if (!value) return 0;
|
|
3476
|
+
const ms = /^\d+(\.\d+)?$/.test(value) ? Number(value) * 1e3 : Date.parse(value) - Date.now();
|
|
3477
|
+
return Number.isFinite(ms) ? Math.max(0, ms) : 0;
|
|
3478
|
+
}
|
|
3479
|
+
function unwrap(result) {
|
|
3480
|
+
if (result.data !== void 0 && !result.error) return result.data;
|
|
3481
|
+
const code = record(result.error) && record(result.error.error) && typeof result.error.error.code === "string" ? result.error.error.code : "request_failed";
|
|
3482
|
+
throw new EventReceiverError(`Event request failed (${result.response?.status ?? 0}, ${code})`, code, result.response?.status ?? 0, retryAfter(result.response?.headers.get("retry-after") ?? null));
|
|
3483
|
+
}
|
|
3484
|
+
function delay(ms, signal) {
|
|
3485
|
+
signal.throwIfAborted();
|
|
3486
|
+
return new Promise((resolve, reject) => {
|
|
3487
|
+
const abort = () => {
|
|
3488
|
+
clearTimeout(timer);
|
|
3489
|
+
reject(signal.reason);
|
|
3490
|
+
};
|
|
3491
|
+
const timer = setTimeout(() => {
|
|
3492
|
+
signal.removeEventListener("abort", abort);
|
|
3493
|
+
resolve();
|
|
3494
|
+
}, Math.min(ms, 2147483647));
|
|
3495
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
3496
|
+
});
|
|
3497
|
+
}
|
|
3498
|
+
async function eventRetry(operation, signal, onRetry) {
|
|
3499
|
+
for (let attempt = 0;; attempt++) {
|
|
3500
|
+
signal.throwIfAborted();
|
|
3501
|
+
try {
|
|
3502
|
+
return await operation();
|
|
3503
|
+
} catch (error) {
|
|
3504
|
+
signal.throwIfAborted();
|
|
3505
|
+
if (error instanceof EventReceiverError && (!([
|
|
3506
|
+
0,
|
|
3507
|
+
408,
|
|
3508
|
+
429
|
|
3509
|
+
].includes(error.status) || error.status >= 500) || [
|
|
3510
|
+
"invalid_response",
|
|
3511
|
+
"unsupported",
|
|
3512
|
+
"pull_unavailable",
|
|
3513
|
+
"subscription_unavailable"
|
|
3514
|
+
].includes(error.code))) throw error;
|
|
3515
|
+
if (!(error instanceof EventReceiverError)) throw error;
|
|
3516
|
+
onRetry?.();
|
|
3517
|
+
await delay(Math.max(error instanceof EventReceiverError ? error.retryAfterMs : 0, Math.min(1e4, 250 * 2 ** Math.min(attempt, 6)) * (.8 + Math.random() * .4)), signal);
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
/** Shared by the SDK and CLI. Completion retries preserve the original evidence. */
|
|
3522
|
+
var EventConnection = class {
|
|
3523
|
+
socket;
|
|
3524
|
+
pending;
|
|
3525
|
+
origin;
|
|
3526
|
+
touch;
|
|
3527
|
+
constructor(client, endpointId, options = {}) {
|
|
3528
|
+
this.client = client;
|
|
3529
|
+
this.endpointId = endpointId;
|
|
3530
|
+
this.options = options;
|
|
3531
|
+
}
|
|
3532
|
+
close() {
|
|
3533
|
+
const socket = this.socket;
|
|
3534
|
+
this.socket = void 0;
|
|
3535
|
+
this.pending?.reject(new EventReceiverError("Event connection closed", "disconnected"));
|
|
3536
|
+
this.pending = void 0;
|
|
3537
|
+
socket?.close();
|
|
3538
|
+
}
|
|
3539
|
+
async open(signal) {
|
|
3540
|
+
if (this.options.transport === "poll" || this.socket?.readyState === 1) return;
|
|
3541
|
+
const config = this.client.getConfig();
|
|
3542
|
+
if (this.origin !== void 0 && this.origin !== config.baseUrl) throw new EventReceiverError("The API environment changed; restart the receiver", "identity_changed", 403);
|
|
3543
|
+
this.origin = config.baseUrl;
|
|
3544
|
+
const auth = typeof config.auth === "function" ? await config.auth({
|
|
3545
|
+
type: "http",
|
|
3546
|
+
scheme: "bearer"
|
|
3547
|
+
}) : config.auth;
|
|
3548
|
+
const authorization = new Headers(config.headers).get("authorization");
|
|
3549
|
+
const token = typeof auth === "string" ? auth.replace(/^Bearer /i, "") : authorization?.replace(/^Bearer /i, "");
|
|
3550
|
+
if (!token) throw new EventReceiverError("Event receiver requires bearer credentials", "unauthorized", 401);
|
|
3551
|
+
const url = new URL(`${config.baseUrl?.replace(/\/+$/, "")}/endpoints/${encodeURIComponent(this.endpointId)}/stream`);
|
|
3552
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && [
|
|
3553
|
+
"localhost",
|
|
3554
|
+
"127.0.0.1",
|
|
3555
|
+
"[::1]"
|
|
3556
|
+
].includes(url.hostname))) throw new EventReceiverError("Event connections require HTTPS (except loopback development)", "unsupported");
|
|
3557
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
3558
|
+
const socket = (this.options.webSocketFactory ?? ((address, protocol) => new WebSocket(address, protocol)))(url.href, "primitive.events.v1");
|
|
3559
|
+
this.socket = socket;
|
|
3560
|
+
socket.addEventListener("message", (message) => {
|
|
3561
|
+
if (this.socket !== socket) return;
|
|
3562
|
+
let frame;
|
|
3563
|
+
try {
|
|
3564
|
+
frame = JSON.parse(String(message.data));
|
|
3565
|
+
} catch {
|
|
3566
|
+
this.fail(new EventReceiverError("Invalid event frame", "invalid_response"));
|
|
3567
|
+
return;
|
|
3568
|
+
}
|
|
3569
|
+
this.touch?.();
|
|
3570
|
+
if (!record(frame)) {
|
|
3571
|
+
this.fail(new EventReceiverError("Invalid event frame", "invalid_response"));
|
|
3572
|
+
return;
|
|
3573
|
+
}
|
|
3574
|
+
if (frame.type === "ping") {
|
|
3575
|
+
socket.send(JSON.stringify({ type: "pong" }));
|
|
3576
|
+
return;
|
|
3577
|
+
}
|
|
3578
|
+
if (frame.type === "status") {
|
|
3579
|
+
try {
|
|
3580
|
+
this.options.onStatus?.(validateOffer(frame.data));
|
|
3581
|
+
} catch (error) {
|
|
3582
|
+
this.fail(error);
|
|
3583
|
+
}
|
|
3584
|
+
return;
|
|
3585
|
+
}
|
|
3586
|
+
if (frame.type === "error") {
|
|
3587
|
+
this.fail(new EventReceiverError("Event stream request failed", typeof frame.code === "string" ? frame.code : "request_failed", typeof frame.status === "number" ? frame.status : 0, retryAfter(typeof frame.retry_after === "string" ? frame.retry_after : null)));
|
|
3588
|
+
return;
|
|
3589
|
+
}
|
|
3590
|
+
this.pending?.resolve(frame);
|
|
3591
|
+
});
|
|
3592
|
+
socket.addEventListener("close", () => {
|
|
3593
|
+
if (this.socket === socket) this.fail(new EventReceiverError("Event stream disconnected", "disconnected"));
|
|
3594
|
+
});
|
|
3595
|
+
socket.addEventListener("error", () => {
|
|
3596
|
+
if (this.socket === socket) this.fail(new EventReceiverError("Event stream connection failed", "disconnected"));
|
|
3597
|
+
});
|
|
3598
|
+
const ready = await this.exchange(signal, void 0, () => {
|
|
3599
|
+
socket.addEventListener("open", () => socket.send(JSON.stringify({
|
|
3600
|
+
type: "authenticate",
|
|
3601
|
+
token
|
|
3602
|
+
})), { once: true });
|
|
3603
|
+
}, 15e3);
|
|
3604
|
+
if (!record(ready) || ready.type !== "ready" || ready.protocol !== "primitive.events.v1") {
|
|
3605
|
+
this.close();
|
|
3606
|
+
throw new EventReceiverError("Unsupported event stream protocol", "unsupported");
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
fail(error) {
|
|
3610
|
+
this.pending?.reject(error);
|
|
3611
|
+
this.pending = void 0;
|
|
3612
|
+
this.close();
|
|
3613
|
+
}
|
|
3614
|
+
async exchange(signal, frame, start, timeoutMs = 6e4) {
|
|
3615
|
+
signal.throwIfAborted();
|
|
3616
|
+
if (this.pending) throw new EventReceiverError("Concurrent event operation", "busy", 409);
|
|
3617
|
+
let cleanup = () => {};
|
|
3618
|
+
try {
|
|
3619
|
+
return await new Promise((resolve, reject) => {
|
|
3620
|
+
const abort = () => {
|
|
3621
|
+
reject(signal.reason);
|
|
3622
|
+
this.close();
|
|
3623
|
+
};
|
|
3624
|
+
let timer;
|
|
3625
|
+
this.touch = () => {
|
|
3626
|
+
clearTimeout(timer);
|
|
3627
|
+
timer = setTimeout(() => this.fail(new EventReceiverError("Event stream timed out", "disconnected")), timeoutMs);
|
|
3628
|
+
};
|
|
3629
|
+
this.touch();
|
|
3630
|
+
cleanup = () => {
|
|
3631
|
+
this.touch = void 0;
|
|
3632
|
+
clearTimeout(timer);
|
|
3633
|
+
signal.removeEventListener("abort", abort);
|
|
3634
|
+
this.pending = void 0;
|
|
3635
|
+
};
|
|
3636
|
+
this.pending = {
|
|
3637
|
+
resolve,
|
|
3638
|
+
reject
|
|
3639
|
+
};
|
|
3640
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
3641
|
+
if (frame) this.socket?.send(JSON.stringify(frame));
|
|
3642
|
+
start?.();
|
|
3643
|
+
});
|
|
3644
|
+
} finally {
|
|
3645
|
+
cleanup();
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
async receive(signal) {
|
|
3649
|
+
if (this.options.transport === "poll") return validateOffer(unwrap(await pullWebhookEvent({
|
|
3650
|
+
client: this.client,
|
|
3651
|
+
path: { id: this.endpointId },
|
|
3652
|
+
body: { wait_seconds: 25 },
|
|
3653
|
+
responseStyle: "fields",
|
|
3654
|
+
throwOnError: false,
|
|
3655
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(45e3)])
|
|
3656
|
+
})).data);
|
|
3657
|
+
await this.open(signal);
|
|
3658
|
+
const frame = await this.exchange(signal, { type: "receive" }, void 0, 9e4);
|
|
3659
|
+
if (!record(frame) || frame.type !== "event") throw new EventReceiverError("Invalid event frame", "invalid_response");
|
|
3660
|
+
return validateOffer(frame.data);
|
|
3661
|
+
}
|
|
3662
|
+
async complete(body, signal) {
|
|
3663
|
+
let receipt;
|
|
3664
|
+
if (this.options.transport === "poll") receipt = unwrap(await completeWebhookEvent({
|
|
3665
|
+
client: this.client,
|
|
3666
|
+
path: { id: this.endpointId },
|
|
3667
|
+
body,
|
|
3668
|
+
responseStyle: "fields",
|
|
3669
|
+
throwOnError: false,
|
|
3670
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(15e3)])
|
|
3671
|
+
})).data;
|
|
3672
|
+
else {
|
|
3673
|
+
await this.open(signal);
|
|
3674
|
+
const frame = await this.exchange(signal, {
|
|
3675
|
+
type: "complete",
|
|
3676
|
+
body
|
|
3677
|
+
}, void 0, 15e3);
|
|
3678
|
+
if (!record(frame) || frame.type !== "receipt") throw new EventReceiverError("Invalid event receipt", "invalid_response");
|
|
3679
|
+
receipt = frame.data;
|
|
3680
|
+
}
|
|
3681
|
+
if (!record(receipt) || !["completed", "already_completed"].includes(String(receipt.result))) throw new EventReceiverError("Invalid event receipt", "invalid_response");
|
|
3682
|
+
}
|
|
3683
|
+
};
|
|
3684
|
+
//#endregion
|
|
3685
|
+
//#region src/api/events.ts
|
|
3686
|
+
var DeliveryExpired = class extends EventReceiverError {
|
|
3687
|
+
constructor() {
|
|
3688
|
+
super("Event acceptance deadline expired; the event may be delivered again", "delivery_expired", 409);
|
|
3689
|
+
}
|
|
3690
|
+
};
|
|
3691
|
+
function validate(options) {
|
|
3692
|
+
if (options.transport !== void 0 && options.transport !== "websocket" && options.transport !== "poll") throw new TypeError("transport must be websocket or poll");
|
|
3693
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/.test(options.subscription)) throw new TypeError("subscription must be 1-64 letters, digits, underscores or hyphens, starting with a letter or digit");
|
|
3694
|
+
if (options.events !== void 0 && (options.events.length === 0 || options.events.length > 50 || options.events.some((event) => typeof event !== "string" || !event.trim()))) throw new TypeError("events must contain 1-50 nonempty event names");
|
|
3695
|
+
if (options.timeoutMs !== void 0 && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0 || options.timeoutMs > 2147483647)) throw new TypeError("timeoutMs must be positive and at most 2147483647");
|
|
3696
|
+
}
|
|
3697
|
+
var EventsResource = class {
|
|
3698
|
+
active = /* @__PURE__ */ new Set();
|
|
3699
|
+
constructor(client) {
|
|
3700
|
+
this.client = client;
|
|
3701
|
+
}
|
|
3702
|
+
reserve(options) {
|
|
3703
|
+
validate(options);
|
|
3704
|
+
options.signal?.throwIfAborted();
|
|
3705
|
+
if (this.active.has(options.subscription)) throw new EventReceiverError("A receiver or unsettled delivery already uses this subscription on this client", "busy", 409);
|
|
3706
|
+
this.active.add(options.subscription);
|
|
3707
|
+
return () => {
|
|
3708
|
+
this.active.delete(options.subscription);
|
|
3709
|
+
};
|
|
3710
|
+
}
|
|
3711
|
+
async connect(options, signal) {
|
|
3712
|
+
const config = this.client.getConfig();
|
|
3713
|
+
const client = new PrimitiveApiClient({
|
|
3714
|
+
...config,
|
|
3715
|
+
headers: new Headers(config.headers),
|
|
3716
|
+
apiBaseUrl: config.baseUrl
|
|
3717
|
+
}).client;
|
|
3718
|
+
let status = { type: "ready" };
|
|
3719
|
+
const emit = (next) => {
|
|
3720
|
+
status = {
|
|
3721
|
+
...status,
|
|
3722
|
+
...next
|
|
3723
|
+
};
|
|
3724
|
+
options.onStatus?.(status);
|
|
3725
|
+
};
|
|
3726
|
+
const retry = (operation, operationSignal = signal) => eventRetry(operation, operationSignal, () => emit({ type: "reconnecting" }));
|
|
3727
|
+
const endpoint = (await retry(async () => unwrap(await createEndpoint({
|
|
3728
|
+
client,
|
|
3729
|
+
responseStyle: "fields",
|
|
3730
|
+
throwOnError: false,
|
|
3731
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(15e3)]),
|
|
3732
|
+
body: {
|
|
3733
|
+
kind: "pull",
|
|
3734
|
+
name: options.subscription,
|
|
3735
|
+
...options.events === void 0 ? {} : { rules: { event_types: [...new Set(options.events)] } }
|
|
3736
|
+
}
|
|
3737
|
+
})))).data;
|
|
3738
|
+
if (!endpoint || endpoint.kind !== "pull" || endpoint.enabled === false) throw new EventReceiverError("Subscription is unavailable", "subscription_unavailable", 409);
|
|
3739
|
+
if (!endpoint.receiver_capabilities?.completion_modes.includes("sdk") || options.transport !== "poll" && !endpoint.receiver_capabilities.stream_protocols.includes("primitive.events.v1")) throw new EventReceiverError("This API does not support SDK event receiving with the selected transport", "unsupported");
|
|
3740
|
+
let gaps = -1;
|
|
3741
|
+
const update = (data) => {
|
|
3742
|
+
status = {
|
|
3743
|
+
type: "ready",
|
|
3744
|
+
backlog: data.backlog,
|
|
3745
|
+
gapCount: data.gap_count,
|
|
3746
|
+
lastGapReason: data.last_gap_reason
|
|
3747
|
+
};
|
|
3748
|
+
if (data.gap_count > 0 && data.gap_count !== gaps) {
|
|
3749
|
+
gaps = data.gap_count;
|
|
3750
|
+
emit({
|
|
3751
|
+
...status,
|
|
3752
|
+
type: "gap"
|
|
3753
|
+
});
|
|
3754
|
+
if (options.onGap === "error") throw new EventReceiverError("Subscription reports lost events", "event_gap", 409);
|
|
3755
|
+
}
|
|
3756
|
+
};
|
|
3757
|
+
const connection = new EventConnection(client, endpoint.id, {
|
|
3758
|
+
...options,
|
|
3759
|
+
onStatus: update
|
|
3760
|
+
});
|
|
3761
|
+
try {
|
|
3762
|
+
await retry(() => connection.open(signal));
|
|
3763
|
+
emit(status);
|
|
3764
|
+
} catch (error) {
|
|
3765
|
+
connection.close();
|
|
3766
|
+
throw error;
|
|
3767
|
+
}
|
|
3768
|
+
return {
|
|
3769
|
+
connection,
|
|
3770
|
+
emit,
|
|
3771
|
+
retry,
|
|
3772
|
+
update,
|
|
3773
|
+
get status() {
|
|
3774
|
+
return status;
|
|
3775
|
+
}
|
|
3776
|
+
};
|
|
3777
|
+
}
|
|
3778
|
+
async wait(options) {
|
|
3779
|
+
const release = this.reserve(options);
|
|
3780
|
+
const timeout = new AbortController();
|
|
3781
|
+
const timeoutReason = new DOMException("Event wait timed out", "TimeoutError");
|
|
3782
|
+
const timer = options.timeoutMs === void 0 ? void 0 : setTimeout(() => timeout.abort(timeoutReason), options.timeoutMs);
|
|
3783
|
+
const signal = AbortSignal.any([timeout.signal, ...options.signal ? [options.signal] : []]);
|
|
3784
|
+
let session;
|
|
3785
|
+
let handedOff = false;
|
|
3786
|
+
try {
|
|
3787
|
+
session = await this.connect(options, signal);
|
|
3788
|
+
const receiving = session;
|
|
3789
|
+
while (true) {
|
|
3790
|
+
const data = await receiving.retry(() => receiving.connection.receive(signal));
|
|
3791
|
+
signal.throwIfAborted();
|
|
3792
|
+
session.update(data);
|
|
3793
|
+
if (!data.delivery) {
|
|
3794
|
+
await delay(250, signal);
|
|
3795
|
+
continue;
|
|
3796
|
+
}
|
|
3797
|
+
const selected = session;
|
|
3798
|
+
const delivery = this.delivery(data.delivery, selected.connection, options.signal, () => {
|
|
3799
|
+
selected.connection.close();
|
|
3800
|
+
release();
|
|
3801
|
+
});
|
|
3802
|
+
handedOff = true;
|
|
3803
|
+
return delivery;
|
|
3804
|
+
}
|
|
3805
|
+
} catch (error) {
|
|
3806
|
+
if (timeout.signal.aborted && !options.signal?.aborted && error === timeoutReason) return null;
|
|
3807
|
+
throw error;
|
|
3808
|
+
} finally {
|
|
3809
|
+
clearTimeout(timer);
|
|
3810
|
+
if (!handedOff) {
|
|
3811
|
+
session?.connection.close();
|
|
3812
|
+
release();
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
async listen(handler, options) {
|
|
3817
|
+
if (typeof handler !== "function") throw new TypeError("handler must be a function");
|
|
3818
|
+
const release = this.reserve(options);
|
|
3819
|
+
const stopping = new AbortController();
|
|
3820
|
+
const signal = AbortSignal.any([stopping.signal, ...options.signal ? [options.signal] : []]);
|
|
3821
|
+
let session;
|
|
3822
|
+
try {
|
|
3823
|
+
session = await this.connect(options, signal);
|
|
3824
|
+
} catch (error) {
|
|
3825
|
+
release();
|
|
3826
|
+
throw error;
|
|
3827
|
+
}
|
|
3828
|
+
let ended = false;
|
|
3829
|
+
let disposeDelivery = () => {};
|
|
3830
|
+
const closed = (async () => {
|
|
3831
|
+
try {
|
|
3832
|
+
while (!signal.aborted) {
|
|
3833
|
+
const offer = await session.retry(() => session.connection.receive(signal));
|
|
3834
|
+
signal.throwIfAborted();
|
|
3835
|
+
session.update(offer);
|
|
3836
|
+
if (!offer.delivery) {
|
|
3837
|
+
await delay(250, signal);
|
|
3838
|
+
continue;
|
|
3839
|
+
}
|
|
3840
|
+
const delivery = this.delivery(offer.delivery, session.connection, options.signal, () => {});
|
|
3841
|
+
disposeDelivery = delivery.dispose;
|
|
3842
|
+
let removeAbort = () => {};
|
|
3843
|
+
try {
|
|
3844
|
+
await Promise.race([Promise.resolve().then(() => handler(delivery.event, { signal: delivery.signal })), new Promise((_, reject) => {
|
|
3845
|
+
const aborted = () => reject(delivery.signal.reason);
|
|
3846
|
+
delivery.signal.addEventListener("abort", aborted, { once: true });
|
|
3847
|
+
removeAbort = () => delivery.signal.removeEventListener("abort", aborted);
|
|
3848
|
+
if (delivery.signal.aborted) aborted();
|
|
3849
|
+
})]);
|
|
3850
|
+
} catch (error) {
|
|
3851
|
+
if (delivery.signal.aborted) throw error;
|
|
3852
|
+
session.emit({
|
|
3853
|
+
type: "handler_error",
|
|
3854
|
+
error
|
|
3855
|
+
});
|
|
3856
|
+
await delivery.retry();
|
|
3857
|
+
continue;
|
|
3858
|
+
} finally {
|
|
3859
|
+
removeAbort();
|
|
3860
|
+
}
|
|
3861
|
+
await delivery.ack();
|
|
3862
|
+
}
|
|
3863
|
+
} catch (error) {
|
|
3864
|
+
if (!signal.aborted) throw error;
|
|
3865
|
+
} finally {
|
|
3866
|
+
ended = true;
|
|
3867
|
+
disposeDelivery();
|
|
3868
|
+
session.connection.close();
|
|
3869
|
+
release();
|
|
3870
|
+
session.emit({ type: "closed" });
|
|
3871
|
+
}
|
|
3872
|
+
})();
|
|
3873
|
+
closed.catch(() => {});
|
|
3874
|
+
return {
|
|
3875
|
+
closed,
|
|
3876
|
+
get status() {
|
|
3877
|
+
return ended ? { type: "closed" } : session.status;
|
|
3878
|
+
},
|
|
3879
|
+
close: async () => {
|
|
3880
|
+
stopping.abort();
|
|
3881
|
+
await closed;
|
|
3882
|
+
}
|
|
3883
|
+
};
|
|
3884
|
+
}
|
|
3885
|
+
delivery(raw, connection, callerSignal, release) {
|
|
3886
|
+
callerSignal?.throwIfAborted();
|
|
3887
|
+
const event = {
|
|
3888
|
+
id: raw.event_id,
|
|
3889
|
+
type: raw.event_type,
|
|
3890
|
+
body: raw.body,
|
|
3891
|
+
headers: raw.headers,
|
|
3892
|
+
data: parseWebhookEvent(JSON.parse(raw.body), raw.event_type)
|
|
3893
|
+
};
|
|
3894
|
+
const started = performance.now();
|
|
3895
|
+
const lifetime = new AbortController();
|
|
3896
|
+
const signal = AbortSignal.any([lifetime.signal, ...callerSignal ? [callerSignal] : []]);
|
|
3897
|
+
const remaining = Math.min(3e4, Date.parse(raw.lease_expires_at) - Date.now() - 5e3);
|
|
3898
|
+
if (remaining <= 0) throw new DeliveryExpired();
|
|
3899
|
+
let released = false;
|
|
3900
|
+
let outcome;
|
|
3901
|
+
let settled;
|
|
3902
|
+
const cleanup = () => {
|
|
3903
|
+
if (!released) {
|
|
3904
|
+
released = true;
|
|
3905
|
+
clearTimeout(timer);
|
|
3906
|
+
signal.removeEventListener("abort", cleanup);
|
|
3907
|
+
release();
|
|
3908
|
+
}
|
|
3909
|
+
};
|
|
3910
|
+
const timer = setTimeout(() => lifetime.abort(new DeliveryExpired()), remaining);
|
|
3911
|
+
signal.addEventListener("abort", cleanup, { once: true });
|
|
3912
|
+
function complete(accepted) {
|
|
3913
|
+
if (settled !== void 0) return outcome === accepted ? settled : Promise.reject(new EventReceiverError("Delivery outcome has already been chosen", "completion_conflict", 409));
|
|
3914
|
+
if (signal.aborted) return Promise.reject(signal.reason);
|
|
3915
|
+
outcome = accepted;
|
|
3916
|
+
clearTimeout(timer);
|
|
3917
|
+
const body = {
|
|
3918
|
+
mode: "sdk",
|
|
3919
|
+
accepted,
|
|
3920
|
+
duration_ms: Math.min(3e4, Math.round(performance.now() - started)),
|
|
3921
|
+
queue_id: raw.queue_id,
|
|
3922
|
+
delivery_id: raw.delivery_id,
|
|
3923
|
+
lease_token: raw.lease_token
|
|
3924
|
+
};
|
|
3925
|
+
const receiptSignal = AbortSignal.any([signal, AbortSignal.timeout(Math.min(6e4, Math.max(1, Date.parse(raw.lease_expires_at) - Date.now())))]);
|
|
3926
|
+
settled = eventRetry(() => connection.complete(body, receiptSignal), receiptSignal).finally(cleanup);
|
|
3927
|
+
return settled;
|
|
3928
|
+
}
|
|
3929
|
+
return {
|
|
3930
|
+
event,
|
|
3931
|
+
signal,
|
|
3932
|
+
dispose: () => {
|
|
3933
|
+
lifetime.abort();
|
|
3934
|
+
cleanup();
|
|
3935
|
+
},
|
|
3936
|
+
ack: () => complete(true),
|
|
3937
|
+
retry: () => complete(false)
|
|
3938
|
+
};
|
|
3939
|
+
}
|
|
3940
|
+
};
|
|
3941
|
+
//#endregion
|
|
3396
3942
|
//#region src/api/verify-signature.ts
|
|
3397
3943
|
/**
|
|
3398
3944
|
* Workers-safe webhook signature verification.
|
|
@@ -3980,6 +4526,8 @@ var MemoriesResource = class {
|
|
|
3980
4526
|
}
|
|
3981
4527
|
};
|
|
3982
4528
|
var PrimitiveClient = class extends PrimitiveApiClient {
|
|
4529
|
+
/** Receive durable events in this process, without a public URL. */
|
|
4530
|
+
events = new EventsResource(this.client);
|
|
3983
4531
|
/** Agent-account lifecycle operations (create, claim/upgrade). */
|
|
3984
4532
|
agent = new AgentResource(this.client);
|
|
3985
4533
|
/** Inbound mail: long-poll stream + waitForNext over the forward tail. */
|
|
@@ -4303,4 +4851,4 @@ async function createAgent(options) {
|
|
|
4303
4851
|
};
|
|
4304
4852
|
}
|
|
4305
4853
|
//#endregion
|
|
4306
|
-
export {
|
|
4854
|
+
export { deleteWakeSchedule as $, pullWebhookEvent as $t, createEndpoint as A, unpublishAgent as An, listDeclinedPayments as At, defineAgent as B, updateWakeAuthorization as Bn, listPayoutAddresses as Bt, checkDomainDns as C, startAgentClaim as Cn, getStorageStats as Ct, createAgentClaimLink as D, testEndpoint as Dn, getWakeSchedule as Dt, createAgentAccount as E, startCliSignup as En, getThread as Et, createRegistry as F, updateFilter as Fn, listFilters as Ft, deleteFunction as G, verifyDomain as Gn, listSentEmails as Gt, deleteEmail as H, verifyAgentClaim as Hn, listRegistryAgents as Ht, createRoute as I, updateFunction as In, listFunctionLogs as It, deleteOrgSecret as J, listWakeDispatches as Jt, deleteFunctionSecret as K, createClient as Kn, listTemplates as Kt, createWakeAuthorization as L, updateRegistry as Ln, listFunctionSecrets as Lt, createFunction as M, updateAccount as Mn, listDomains as Mt, createFunctionSecret as N, updateDomain as Nn, listEmails as Nt, createChallenge as O, testEndpointRules as On, getWebhookSecret as Ot, createOrgSecret as P, updateEndpoint as Pn, listEndpoints as Pt, deleteWakeAuthorization as Q, publishAgent as Qt, createWakeSchedule as R, updateRoute as Rn, listFunctions as Rt, cancelSentEmail as S, simulateRoute as Sn, getSpendPolicy as St, completeWebhookEvent as T, startCliLogin as Tn, getTemplateInstall as Tt, deleteEndpoint as U, verifyAgentSignup as Un, listRegistryRequests as Ut, deleteDomain as V, updateWakeSchedule as Vn, listRegistries as Vt, deleteFilter as W, verifyCliSignup as Wn, listRoutes as Wt, deleteRoute as X, payChallenge as Xt, deleteRegistry as Y, listWakeSchedules as Yt, deleteSentEmail as Z, pollCliLogin as Zt, PrimitiveApiClient as _, sendEmail as _n, getOrgRoutingTopology as _t, PrimitiveClient as a, replayEmailWebhooks as an, downloadSentAttachmentPart as at, addDomain as b, setMemory as bn, getSendPermissions as bt, createPrimitiveClient as c, resendAgentSignupVerification as cn, getChallenge as ct, DeliveryExpired as d, rotateWebhookSecret as dn, getEmail as dt, redeemCreditCode as en, discardEmailContent as et, EventsResource as f, runWakeSchedule as fn, getFunction as ft, DEFAULT_API_BASE_URL as g, semanticSearch as gn, getMemory as gt, isMemoryJsonValue as h, searchMemories as hn, getInboxStatus as ht, MemoriesResource as i, replayDelivery as in, downloadRawEmail as it, createFilter as j, unsetFunctionRoute as jn, listDeliveries as jt, createEmailChallenge as k, testFunction as kn, installTemplate as kt, PRIMITIVE_SIGNATURE_HEADER as l, resendCliSignupVerification as ln, getConversation as lt, EventReceiverError as m, searchEmails as mn, getFunctionTestRunTrace as mt, AgentResource as n, removeAgentConnection as nn, downloadDomainZoneFile as nt, client as o, replyToEmail as on, getAccount as ot, EventConnection as p, sdk_gen_exports as pn, getFunctionRouting as pt, deleteMemory as q, createConfig as qn, listWakeAuthorizations as qt, InboxResource as r, reorderRoutes as rn, downloadEmailAttachmentPart as rt, createAgent as s, rescheduleSentEmail as sn, getAgent as st, AccountResource as t, registerPayoutAddress as tn, downloadAttachments as tt, verifyWebhookSignature as u, resolveRegistryHandle as un, getCreditBalance as ut, PrimitiveApiError as v, setFunctionRoute as vn, getOutboundStatus as vt, cliLogout as w, startAgentSignup as wn, getTemplate as wt, awaitReply as x, setOrgSecret as xn, getSentEmail as xt, createPrimitiveApiClient as y, setFunctionSecret as yn, getRegistry as yt, decideRegistryRequest as z, updateSpendPolicy as zn, listOrgSecrets as zt };
|
package/dist/contract/index.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { A as RawContentInline, E as ParsedError, I as WebhookAttachment, T as ParsedDataFailed, d as EmailAuth, f as EmailReceivedEvent, k as RawContentDownloadOnly, l as EmailAddress, u as EmailAnalysis, w as ParsedDataComplete } from "../types-
|
|
2
|
-
import { A as SignResult, M as signWebhookPayload, S as StandardWebhooksSignResult, _ as WEBHOOK_VERSION, w as signStandardWebhooksPayload } from "../index-
|
|
1
|
+
import { A as RawContentInline, E as ParsedError, I as WebhookAttachment, T as ParsedDataFailed, d as EmailAuth, f as EmailReceivedEvent, k as RawContentDownloadOnly, l as EmailAddress, u as EmailAnalysis, w as ParsedDataComplete } from "../types-Rn4qu5Q7.js";
|
|
2
|
+
import { A as SignResult, M as signWebhookPayload, S as StandardWebhooksSignResult, _ as WEBHOOK_VERSION, w as signStandardWebhooksPayload } from "../index-BxPkB7GU.js";
|
|
3
3
|
|
|
4
4
|
//#region src/contract/contract.d.ts
|
|
5
|
+
/** Producer-built events include raw content metadata and download information. */
|
|
6
|
+
type ProducedEmailReceivedEvent = EmailReceivedEvent & {
|
|
7
|
+
email: EmailReceivedEvent["email"] & {
|
|
8
|
+
content: {
|
|
9
|
+
raw: NonNullable<EmailReceivedEvent["email"]["content"]["raw"]>;
|
|
10
|
+
download: NonNullable<EmailReceivedEvent["email"]["content"]["download"]>;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
};
|
|
5
14
|
/** Maximum raw email size for inline inclusion (256 KB). */
|
|
6
15
|
declare const RAW_EMAIL_INLINE_THRESHOLD = 262144;
|
|
7
16
|
/** Parsed content input when parsing succeeded. */
|
|
@@ -145,7 +154,7 @@ declare function generateEventId(endpoint_id: string, email_id: string): string;
|
|
|
145
154
|
*/
|
|
146
155
|
declare function buildEmailReceivedEvent(input: EmailReceivedEventInput, options?: {
|
|
147
156
|
/** Override the attempted-at timestamp, typically for tests. */attempted_at?: string;
|
|
148
|
-
}):
|
|
157
|
+
}): ProducedEmailReceivedEvent;
|
|
149
158
|
/**
|
|
150
159
|
* Input for building an `EmailReceivedEvent` directly from parser output.
|
|
151
160
|
*/
|
|
@@ -224,6 +233,6 @@ interface BuildEventFromParsedDataOptions {
|
|
|
224
233
|
* @throws Error if `smtpRcptTo` is empty.
|
|
225
234
|
* @throws WebhookValidationError if the assembled event fails schema validation.
|
|
226
235
|
*/
|
|
227
|
-
declare function buildEventFromParsedData(params: BuildEventFromParsedDataOptions):
|
|
236
|
+
declare function buildEventFromParsedData(params: BuildEventFromParsedDataOptions): ProducedEmailReceivedEvent;
|
|
228
237
|
//#endregion
|
|
229
|
-
export { BuildEventFromParsedDataOptions, type EmailAddress, type EmailAnalysis, type EmailAuth, type EmailReceivedEvent, EmailReceivedEventInput, type ParsedDataComplete, type ParsedDataFailed, type ParsedError, ParsedInput, ParsedInputComplete, ParsedInputFailed, RAW_EMAIL_INLINE_THRESHOLD, type RawContentDownloadOnly, type RawContentInline, type SignResult, type StandardWebhooksSignResult, WEBHOOK_VERSION, type WebhookAttachment, buildEmailReceivedEvent, buildEventFromParsedData, generateEventId, signStandardWebhooksPayload, signWebhookPayload };
|
|
238
|
+
export { BuildEventFromParsedDataOptions, type EmailAddress, type EmailAnalysis, type EmailAuth, type EmailReceivedEvent, EmailReceivedEventInput, type ParsedDataComplete, type ParsedDataFailed, type ParsedError, ParsedInput, ParsedInputComplete, ParsedInputFailed, ProducedEmailReceivedEvent, RAW_EMAIL_INLINE_THRESHOLD, type RawContentDownloadOnly, type RawContentInline, type SignResult, type StandardWebhooksSignResult, WEBHOOK_VERSION, type WebhookAttachment, buildEmailReceivedEvent, buildEventFromParsedData, generateEventId, signStandardWebhooksPayload, signWebhookPayload };
|