@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.
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk-pbuEa-1d.js";
2
- import { a as pushFile, i as pushBytes, o as loadNodeFsPromises } from "./payloads-BfJAtqW9.js";
3
- import { S as formatAddress, c as WebhookVerificationError } from "./errors-BVnAu5jX.js";
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 { downloadEmailAttachmentPart as $, replayEmailWebhooks as $t, createOrgSecret as A, updateFunction as An, listFilters as At, deleteFilter as B, createClient as Bn, listSentEmails as Bt, createAgentClaimLink as C, testFunction as Cn, getWebhookSecret as Ct, createFilter as D, updateDomain as Dn, listDomains as Dt, createEndpoint as E, updateAccount as En, listDeliveries as Et, decideRegistryRequest as F, updateWakeSchedule as Fn, listPayoutAddresses as Ft, deleteRegistry as G, payChallenge as Gt, deleteFunctionSecret as H, listWakeAuthorizations as Ht, defineAgent as I, verifyAgentClaim as In, listRegistries as It, deleteWakeAuthorization as J, pullWebhookEvent as Jt, deleteRoute as K, pollCliLogin as Kt, deleteDomain as L, verifyAgentSignup as Ln, listRegistryAgents as Lt, createRoute as M, updateRoute as Mn, listFunctionSecrets as Mt, createWakeAuthorization as N, updateSpendPolicy as Nn, listFunctions as Nt, createFunction as O, updateEndpoint as On, listEmails as Ot, createWakeSchedule as P, updateWakeAuthorization as Pn, listOrgSecrets as Pt, downloadDomainZoneFile as Q, replayDelivery as Qt, deleteEmail as R, verifyCliSignup as Rn, listRegistryRequests as Rt, createAgentAccount as S, testEndpointRules as Sn, getWakeSchedule as St, createEmailChallenge as T, unsetFunctionRoute as Tn, listDeclinedPayments as Tt, deleteMemory as U, listWakeDispatches as Ut, deleteFunction as V, createConfig as Vn, listTemplates as Vt, deleteOrgSecret as W, listWakeSchedules as Wt, discardEmailContent as X, removeAgentConnection as Xt, deleteWakeSchedule as Y, registerPayoutAddress as Yt, downloadAttachments as Z, reorderRoutes as Zt, awaitReply as _, startAgentClaim as _n, getSpendPolicy as _t, PrimitiveClient as a, rotateWebhookSecret as an, getConversation as at, cliLogout as b, startCliSignup as bn, getTemplateInstall as bt, createPrimitiveClient as c, searchEmails as cn, getFunctionRouting as ct, isMemoryJsonValue as d, sendEmail as dn, getMemory as dt, replyToEmail as en, downloadRawEmail as et, DEFAULT_API_BASE_URL as f, setFunctionRoute as fn, getOrgRoutingTopology as ft, addDomain as g, simulateRoute as gn, getSentEmail as gt, createPrimitiveApiClient as h, setOrgSecret as hn, getSendPermissions as ht, MemoriesResource as i, resolveRegistryHandle as in, getChallenge as it, createRegistry as j, updateRegistry as jn, listFunctionLogs as jt, createFunctionSecret as k, updateFilter as kn, listEndpoints as kt, PRIMITIVE_SIGNATURE_HEADER as l, searchMemories as ln, getFunctionTestRunTrace as lt, PrimitiveApiError as m, setMemory as mn, getRegistry as mt, AgentResource as n, resendAgentSignupVerification as nn, getAccount as nt, client as o, runWakeSchedule as on, getEmail as ot, PrimitiveApiClient as p, setFunctionSecret as pn, getOutboundStatus as pt, deleteSentEmail as q, publishAgent as qt, InboxResource as r, resendCliSignupVerification as rn, getAgent as rt, createAgent as s, sdk_gen_exports as sn, getFunction as st, AccountResource as t, rescheduleSentEmail as tn, downloadSentAttachmentPart as tt, verifyWebhookSignature as u, semanticSearch as un, getInboxStatus as ut, cancelSentEmail as v, startAgentSignup as vn, getStorageStats as vt, createChallenge as w, unpublishAgent as wn, installTemplate as wt, completeWebhookEvent as x, testEndpoint as xn, getThread as xt, checkDomainDns as y, startCliLogin as yn, getTemplate as yt, deleteEndpoint as z, verifyDomain as zn, listRoutes as zt };
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 };
@@ -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-DcXtFARL.js";
2
- import { A as SignResult, M as signWebhookPayload, S as StandardWebhooksSignResult, _ as WEBHOOK_VERSION, w as signStandardWebhooksPayload } from "../index-JpXUV16f.js";
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
- }): EmailReceivedEvent;
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): EmailReceivedEvent;
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 };