@capxul/sdk 0.1.0-alpha.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.
@@ -0,0 +1,202 @@
1
+ import * as types from '@repo/api-contract/gen/types';
2
+ import { PublicId, AccountId, SafeId, OrganizationId, KycProfileId, ExternalAccountId, SubAccountId, BalanceLedgerEntryId, ApiKeyId, TreasuryId, MemberId, KybProfileId, NextAction, PaymentId, TransferId, WithdrawalId, DocumentId, WebhookEndpointId, WebhookEventId, VirtualAccountId, VirtualCardId, OperationId, CorrelationId } from '@repo/platform-kernel';
3
+
4
+ /**
5
+ * SDK surface types.
6
+ *
7
+ * Derivation policy (post-codegen-relitigation, 2026-04-20):
8
+ * - Import wire types directly from `@repo/api-contract/gen/types` — the
9
+ * new generator (@hey-api/openapi-ts) emits faithful discriminated
10
+ * unions from OpenAPI `const:` and narrow string-literal enums, so
11
+ * the old `z.infer<typeof schemas.X>` round-trip through Zod (which
12
+ * stripped discriminants via `passthrough()`) is gone.
13
+ * - Rebrand the top-level `id` field on every resource to the kernel's
14
+ * branded ID type per `.claude/rules/typescript-style.md`.
15
+ * - Keep `WithStatus<>` so per-method narrowings are discriminated unions
16
+ * (one variant per status literal), which `Extract<T, { status: K }>`
17
+ * can narrow inside `matchStatus`.
18
+ * - Use `Override<>` ONLY where the OpenAPI source emits `additionalProperties`
19
+ * open shapes (`Safe.owner`, `Operation.actor`, `Operation.subject`) that
20
+ * need the SDK's canonical tagged-ref shape. Every other former
21
+ * `Override<>` site was redundant and has been deleted.
22
+ */
23
+
24
+ type Rebrand<Wire, Id extends PublicId> = Wire & {
25
+ readonly id: Id;
26
+ };
27
+ /**
28
+ * Override-via-intersection helper. Kept for the few wire fields that
29
+ * OpenAPI emits as open-shape (`additionalProperties` → `{ [key:
30
+ * string]: unknown }`) where the SDK surface needs the canonical
31
+ * tagged-ref shape. `Safe.owner`, `Operation.actor`, `Operation.subject`
32
+ * are the current consumers.
33
+ */
34
+ type Override<Wire, Fields> = Wire & Fields;
35
+ /**
36
+ * Distributes over the literal union `S` so the result is a discrim-
37
+ * inated union (one member per status value). `Extract<T, { status:
38
+ * "x" }>` narrows correctly against the output — without the
39
+ * distribution, a plain object with a union-typed `status` field is
40
+ * not a discriminated union and Extract returns `never`.
41
+ *
42
+ * When `S === "action_required"` the variant additionally requires
43
+ * `nextAction: NextAction` (not optional) per Rule D (CANON.md §4.40 +
44
+ * sdk-surface.md §3a): `action_required` is inline on mutation /
45
+ * snapshot responses and callers can safely route via `matchAction`.
46
+ */
47
+ type WithStatus<T, S extends string> = S extends unknown ? T & {
48
+ readonly status: S;
49
+ } & (S extends "action_required" ? {
50
+ readonly nextAction: NextAction;
51
+ } : unknown) : never;
52
+ type Money = types.Money;
53
+ type Settlement = types.Settlement;
54
+ type TimestampIso = types.TimestampIso;
55
+ type UserIdentifier = types.UserIdentifier;
56
+ /**
57
+ * Tagged reference to the public resource a succeeded operation
58
+ * produced (per sdk-surface.md §1b `operation`).
59
+ */
60
+ type OperationResult = {
61
+ readonly object: "account" | "organization" | "safe" | "treasury" | "kyc_profile" | "kyb_profile" | "external_account" | "payment" | "member" | "withdrawal" | "operation" | "webhook_endpoint" | "webhook_event" | "sub_account" | "virtual_account" | "virtual_card" | "transfer" | "document" | "balance_ledger_entry" | "api_key";
62
+ readonly id: string;
63
+ };
64
+ /**
65
+ * Terminal-state error sidecar on resources that can fail (Operation,
66
+ * Payment, Withdrawal, Transfer).
67
+ */
68
+ type ResourceError = {
69
+ readonly code: string;
70
+ readonly message: string;
71
+ };
72
+
73
+ type OperationStatus = types.OperationStatus;
74
+ type OperationSummary = Rebrand<types.OperationSummary, OperationId> & {
75
+ readonly correlationId: CorrelationId;
76
+ };
77
+ /**
78
+ * Full operation envelope.
79
+ *
80
+ * `actor` and `subject` are hand-overridden because OpenAPI emits
81
+ * them as `additionalProperties` open-shape; SDK surface carries the
82
+ * canonical tagged-ref shape.
83
+ *
84
+ * `nextAction` is re-asserted against the kernel's `NextAction` union
85
+ * (9 kinds including `confirm_fx_quote` per §4.58) so callers get the
86
+ * kernel-branded field types (e.g. `Email`, `PhoneNumber`, `Username`
87
+ * on `fix_destination.recipient`). The wire carries the same 9 kinds
88
+ * structurally; the kernel re-assertion preserves the SDK surface's
89
+ * exhaustiveness contract against drift.
90
+ *
91
+ * `result` and `error` come through the generator faithfully, but
92
+ * we re-type them to narrow the tagged-ref `object` field to the
93
+ * closed `OperationResult["object"]` catalog.
94
+ */
95
+ type Operation = Override<Rebrand<types.Operation, OperationId>, {
96
+ readonly correlationId: CorrelationId;
97
+ readonly actor: {
98
+ readonly type: string;
99
+ readonly id: string;
100
+ };
101
+ readonly subject?: {
102
+ readonly type: string;
103
+ readonly id: string;
104
+ };
105
+ readonly nextAction?: NextAction;
106
+ readonly result?: OperationResult | null;
107
+ readonly error?: ResourceError | null;
108
+ }>;
109
+ type Account = Omit<Rebrand<types.Account, AccountId>, "kycTier" | "primarySafeId"> & {
110
+ readonly kycTier?: types.Account["kycTier"] | null;
111
+ readonly primarySafeId?: SafeId | null;
112
+ };
113
+ type AccountLookupResult = types.AccountLookupResult;
114
+ /**
115
+ * `owner` is hand-overridden: the OpenAPI source emits it as an
116
+ * `additionalProperties` open object; SDK surface carries the canonical
117
+ * tagged-ref shape.
118
+ */
119
+ type Safe = Override<Rebrand<types.Safe, SafeId>, {
120
+ readonly owner: {
121
+ readonly type: "account";
122
+ readonly id: AccountId;
123
+ } | {
124
+ readonly type: "organization";
125
+ readonly id: OrganizationId;
126
+ };
127
+ }>;
128
+ type Organization = Rebrand<types.Organization, OrganizationId>;
129
+ type Treasury = Rebrand<types.Treasury, TreasuryId> & {
130
+ readonly organizationId: OrganizationId;
131
+ };
132
+ type KycProfile = Rebrand<types.KycProfile, KycProfileId>;
133
+ type KybProfile = Rebrand<types.KybProfile, KybProfileId>;
134
+ type ExternalAccount = Rebrand<types.ExternalAccount, ExternalAccountId>;
135
+ type ExternalAccountKind = types.ExternalAccountKind;
136
+ type PageInfo = types.PageInfo;
137
+ type List<T> = {
138
+ readonly object: "list";
139
+ readonly data: readonly T[];
140
+ readonly page: PageInfo;
141
+ };
142
+ type WirePaymentWithBrand = Rebrand<types.Payment, PaymentId>;
143
+ type PaymentStatus = "processing" | "action_required" | "succeeded" | "failed" | "canceled";
144
+ /**
145
+ * Full `Payment` — discriminated union on status so `matchStatus`
146
+ * narrows correctly.
147
+ */
148
+ type Payment = WithStatus<WirePaymentWithBrand, PaymentStatus>;
149
+ /**
150
+ * Per-method narrowing for `payments.create`. Terminal states
151
+ * (`succeeded`, `canceled`) arrive out-of-band per Rule D (CANON.md
152
+ * §4.40 + sdk-surface.md §3a), so the synchronous return carries
153
+ * only `processing | action_required | failed`.
154
+ */
155
+ type CreatePaymentResult = WithStatus<WirePaymentWithBrand, "processing" | "action_required" | "failed">;
156
+ type PaymentParty = types.PaymentParty;
157
+ type WireWithdrawalWithBrand = Rebrand<types.Withdrawal, WithdrawalId>;
158
+ /**
159
+ * Canon-aligned superset (resources.mdx §withdrawal lifecycle note).
160
+ * Wider than `OperationStatus` because `submitted → completed` is a
161
+ * withdrawal-specific transition driven by external reconciliation
162
+ * the operation envelope cannot witness directly. Slice 1 of
163
+ * Withdrawals v1 (#440) widened this from `PaymentStatus` to match
164
+ * canon.
165
+ */
166
+ type WithdrawalStatus = "draft" | "action_required" | "processing" | "submitted" | "completed" | "failed" | "canceled";
167
+ type Withdrawal = WithStatus<WireWithdrawalWithBrand, WithdrawalStatus>;
168
+ type CreateWithdrawalResult = WithStatus<WireWithdrawalWithBrand, "processing" | "action_required" | "failed">;
169
+ type WireTransferWithBrand = Rebrand<types.Transfer, TransferId>;
170
+ type TransferStatus = PaymentStatus;
171
+ type Transfer = WithStatus<WireTransferWithBrand, TransferStatus>;
172
+ type CreateTransferResult = WithStatus<WireTransferWithBrand, "processing" | "action_required" | "failed">;
173
+ type TransferEndpoint = types.TransferEndpoint;
174
+ type TransferCustody = types.TransferCustody;
175
+ type TransferFx = types.TransferFx;
176
+ type Document = types.Document & {
177
+ readonly id: DocumentId;
178
+ };
179
+ type InvoiceDocument = types.InvoiceDocument;
180
+ type PayrollRunDocument = types.PayrollRunDocument;
181
+ type PayrollScheduleDocument = types.PayrollScheduleDocument;
182
+ type ReceiptDocument = types.ReceiptDocument;
183
+ type KycUploadDocument = types.KycUploadDocument;
184
+ type BankStatementDocument = types.BankStatementDocument;
185
+ type TaxFormDocument = types.TaxFormDocument;
186
+ type SubAccount = Rebrand<types.SubAccount, SubAccountId>;
187
+ type SubAccountOwnerKind = types.SubAccountOwnerKind;
188
+ type VirtualAccount = Rebrand<types.VirtualAccount, VirtualAccountId>;
189
+ type VirtualAccountOwnerKind = types.VirtualAccountOwnerKind;
190
+ type VirtualCard = Rebrand<types.VirtualCard, VirtualCardId>;
191
+ type VirtualCardOwnerKind = types.VirtualCardOwnerKind;
192
+ type VirtualCardLimits = types.VirtualCardLimits;
193
+ type WebhookEndpoint = Rebrand<types.WebhookEndpoint, WebhookEndpointId>;
194
+ type WebhookEvent = Rebrand<types.WebhookEvent, WebhookEventId>;
195
+ type ApiKey = Rebrand<types.ApiKey, ApiKeyId>;
196
+ type ApiKeyEnvironment = types.ApiKeyEnvironment;
197
+ type ApiKeyType = types.ApiKeyType;
198
+ type Scope = types.Scope;
199
+ type Member = Rebrand<types.Member, MemberId>;
200
+ type BalanceLedgerEntry = Rebrand<types.BalanceLedgerEntry, BalanceLedgerEntryId>;
201
+
202
+ export type { WithdrawalStatus as $, Account as A, BalanceLedgerEntry as B, CreatePaymentResult as C, Document as D, ExternalAccount as E, TransferEndpoint as F, TransferFx as G, TransferStatus as H, InvoiceDocument as I, Treasury as J, KybProfile as K, List as L, Member as M, VirtualAccountOwnerKind as N, Operation as O, PageInfo as P, VirtualCard as Q, ReceiptDocument as R, Safe as S, TaxFormDocument as T, UserIdentifier as U, VirtualAccount as V, VirtualCardLimits as W, VirtualCardOwnerKind as X, WebhookEndpoint as Y, WebhookEvent as Z, Withdrawal as _, AccountLookupResult as a, ApiKey as b, ApiKeyEnvironment as c, ApiKeyType as d, BankStatementDocument as e, CreateTransferResult as f, CreateWithdrawalResult as g, ExternalAccountKind as h, KycProfile as i, KycUploadDocument as j, Money as k, OperationStatus as l, OperationSummary as m, Organization as n, Payment as o, PaymentParty as p, PaymentStatus as q, PayrollRunDocument as r, PayrollScheduleDocument as s, Scope as t, Settlement as u, SubAccount as v, SubAccountOwnerKind as w, TimestampIso as x, Transfer as y, TransferCustody as z };
@@ -0,0 +1,202 @@
1
+ import * as types from '@repo/api-contract/gen/types';
2
+ import { PublicId, AccountId, SafeId, OrganizationId, KycProfileId, ExternalAccountId, SubAccountId, BalanceLedgerEntryId, ApiKeyId, TreasuryId, MemberId, KybProfileId, NextAction, PaymentId, TransferId, WithdrawalId, DocumentId, WebhookEndpointId, WebhookEventId, VirtualAccountId, VirtualCardId, OperationId, CorrelationId } from '@repo/platform-kernel';
3
+
4
+ /**
5
+ * SDK surface types.
6
+ *
7
+ * Derivation policy (post-codegen-relitigation, 2026-04-20):
8
+ * - Import wire types directly from `@repo/api-contract/gen/types` — the
9
+ * new generator (@hey-api/openapi-ts) emits faithful discriminated
10
+ * unions from OpenAPI `const:` and narrow string-literal enums, so
11
+ * the old `z.infer<typeof schemas.X>` round-trip through Zod (which
12
+ * stripped discriminants via `passthrough()`) is gone.
13
+ * - Rebrand the top-level `id` field on every resource to the kernel's
14
+ * branded ID type per `.claude/rules/typescript-style.md`.
15
+ * - Keep `WithStatus<>` so per-method narrowings are discriminated unions
16
+ * (one variant per status literal), which `Extract<T, { status: K }>`
17
+ * can narrow inside `matchStatus`.
18
+ * - Use `Override<>` ONLY where the OpenAPI source emits `additionalProperties`
19
+ * open shapes (`Safe.owner`, `Operation.actor`, `Operation.subject`) that
20
+ * need the SDK's canonical tagged-ref shape. Every other former
21
+ * `Override<>` site was redundant and has been deleted.
22
+ */
23
+
24
+ type Rebrand<Wire, Id extends PublicId> = Wire & {
25
+ readonly id: Id;
26
+ };
27
+ /**
28
+ * Override-via-intersection helper. Kept for the few wire fields that
29
+ * OpenAPI emits as open-shape (`additionalProperties` → `{ [key:
30
+ * string]: unknown }`) where the SDK surface needs the canonical
31
+ * tagged-ref shape. `Safe.owner`, `Operation.actor`, `Operation.subject`
32
+ * are the current consumers.
33
+ */
34
+ type Override<Wire, Fields> = Wire & Fields;
35
+ /**
36
+ * Distributes over the literal union `S` so the result is a discrim-
37
+ * inated union (one member per status value). `Extract<T, { status:
38
+ * "x" }>` narrows correctly against the output — without the
39
+ * distribution, a plain object with a union-typed `status` field is
40
+ * not a discriminated union and Extract returns `never`.
41
+ *
42
+ * When `S === "action_required"` the variant additionally requires
43
+ * `nextAction: NextAction` (not optional) per Rule D (CANON.md §4.40 +
44
+ * sdk-surface.md §3a): `action_required` is inline on mutation /
45
+ * snapshot responses and callers can safely route via `matchAction`.
46
+ */
47
+ type WithStatus<T, S extends string> = S extends unknown ? T & {
48
+ readonly status: S;
49
+ } & (S extends "action_required" ? {
50
+ readonly nextAction: NextAction;
51
+ } : unknown) : never;
52
+ type Money = types.Money;
53
+ type Settlement = types.Settlement;
54
+ type TimestampIso = types.TimestampIso;
55
+ type UserIdentifier = types.UserIdentifier;
56
+ /**
57
+ * Tagged reference to the public resource a succeeded operation
58
+ * produced (per sdk-surface.md §1b `operation`).
59
+ */
60
+ type OperationResult = {
61
+ readonly object: "account" | "organization" | "safe" | "treasury" | "kyc_profile" | "kyb_profile" | "external_account" | "payment" | "member" | "withdrawal" | "operation" | "webhook_endpoint" | "webhook_event" | "sub_account" | "virtual_account" | "virtual_card" | "transfer" | "document" | "balance_ledger_entry" | "api_key";
62
+ readonly id: string;
63
+ };
64
+ /**
65
+ * Terminal-state error sidecar on resources that can fail (Operation,
66
+ * Payment, Withdrawal, Transfer).
67
+ */
68
+ type ResourceError = {
69
+ readonly code: string;
70
+ readonly message: string;
71
+ };
72
+
73
+ type OperationStatus = types.OperationStatus;
74
+ type OperationSummary = Rebrand<types.OperationSummary, OperationId> & {
75
+ readonly correlationId: CorrelationId;
76
+ };
77
+ /**
78
+ * Full operation envelope.
79
+ *
80
+ * `actor` and `subject` are hand-overridden because OpenAPI emits
81
+ * them as `additionalProperties` open-shape; SDK surface carries the
82
+ * canonical tagged-ref shape.
83
+ *
84
+ * `nextAction` is re-asserted against the kernel's `NextAction` union
85
+ * (9 kinds including `confirm_fx_quote` per §4.58) so callers get the
86
+ * kernel-branded field types (e.g. `Email`, `PhoneNumber`, `Username`
87
+ * on `fix_destination.recipient`). The wire carries the same 9 kinds
88
+ * structurally; the kernel re-assertion preserves the SDK surface's
89
+ * exhaustiveness contract against drift.
90
+ *
91
+ * `result` and `error` come through the generator faithfully, but
92
+ * we re-type them to narrow the tagged-ref `object` field to the
93
+ * closed `OperationResult["object"]` catalog.
94
+ */
95
+ type Operation = Override<Rebrand<types.Operation, OperationId>, {
96
+ readonly correlationId: CorrelationId;
97
+ readonly actor: {
98
+ readonly type: string;
99
+ readonly id: string;
100
+ };
101
+ readonly subject?: {
102
+ readonly type: string;
103
+ readonly id: string;
104
+ };
105
+ readonly nextAction?: NextAction;
106
+ readonly result?: OperationResult | null;
107
+ readonly error?: ResourceError | null;
108
+ }>;
109
+ type Account = Omit<Rebrand<types.Account, AccountId>, "kycTier" | "primarySafeId"> & {
110
+ readonly kycTier?: types.Account["kycTier"] | null;
111
+ readonly primarySafeId?: SafeId | null;
112
+ };
113
+ type AccountLookupResult = types.AccountLookupResult;
114
+ /**
115
+ * `owner` is hand-overridden: the OpenAPI source emits it as an
116
+ * `additionalProperties` open object; SDK surface carries the canonical
117
+ * tagged-ref shape.
118
+ */
119
+ type Safe = Override<Rebrand<types.Safe, SafeId>, {
120
+ readonly owner: {
121
+ readonly type: "account";
122
+ readonly id: AccountId;
123
+ } | {
124
+ readonly type: "organization";
125
+ readonly id: OrganizationId;
126
+ };
127
+ }>;
128
+ type Organization = Rebrand<types.Organization, OrganizationId>;
129
+ type Treasury = Rebrand<types.Treasury, TreasuryId> & {
130
+ readonly organizationId: OrganizationId;
131
+ };
132
+ type KycProfile = Rebrand<types.KycProfile, KycProfileId>;
133
+ type KybProfile = Rebrand<types.KybProfile, KybProfileId>;
134
+ type ExternalAccount = Rebrand<types.ExternalAccount, ExternalAccountId>;
135
+ type ExternalAccountKind = types.ExternalAccountKind;
136
+ type PageInfo = types.PageInfo;
137
+ type List<T> = {
138
+ readonly object: "list";
139
+ readonly data: readonly T[];
140
+ readonly page: PageInfo;
141
+ };
142
+ type WirePaymentWithBrand = Rebrand<types.Payment, PaymentId>;
143
+ type PaymentStatus = "processing" | "action_required" | "succeeded" | "failed" | "canceled";
144
+ /**
145
+ * Full `Payment` — discriminated union on status so `matchStatus`
146
+ * narrows correctly.
147
+ */
148
+ type Payment = WithStatus<WirePaymentWithBrand, PaymentStatus>;
149
+ /**
150
+ * Per-method narrowing for `payments.create`. Terminal states
151
+ * (`succeeded`, `canceled`) arrive out-of-band per Rule D (CANON.md
152
+ * §4.40 + sdk-surface.md §3a), so the synchronous return carries
153
+ * only `processing | action_required | failed`.
154
+ */
155
+ type CreatePaymentResult = WithStatus<WirePaymentWithBrand, "processing" | "action_required" | "failed">;
156
+ type PaymentParty = types.PaymentParty;
157
+ type WireWithdrawalWithBrand = Rebrand<types.Withdrawal, WithdrawalId>;
158
+ /**
159
+ * Canon-aligned superset (resources.mdx §withdrawal lifecycle note).
160
+ * Wider than `OperationStatus` because `submitted → completed` is a
161
+ * withdrawal-specific transition driven by external reconciliation
162
+ * the operation envelope cannot witness directly. Slice 1 of
163
+ * Withdrawals v1 (#440) widened this from `PaymentStatus` to match
164
+ * canon.
165
+ */
166
+ type WithdrawalStatus = "draft" | "action_required" | "processing" | "submitted" | "completed" | "failed" | "canceled";
167
+ type Withdrawal = WithStatus<WireWithdrawalWithBrand, WithdrawalStatus>;
168
+ type CreateWithdrawalResult = WithStatus<WireWithdrawalWithBrand, "processing" | "action_required" | "failed">;
169
+ type WireTransferWithBrand = Rebrand<types.Transfer, TransferId>;
170
+ type TransferStatus = PaymentStatus;
171
+ type Transfer = WithStatus<WireTransferWithBrand, TransferStatus>;
172
+ type CreateTransferResult = WithStatus<WireTransferWithBrand, "processing" | "action_required" | "failed">;
173
+ type TransferEndpoint = types.TransferEndpoint;
174
+ type TransferCustody = types.TransferCustody;
175
+ type TransferFx = types.TransferFx;
176
+ type Document = types.Document & {
177
+ readonly id: DocumentId;
178
+ };
179
+ type InvoiceDocument = types.InvoiceDocument;
180
+ type PayrollRunDocument = types.PayrollRunDocument;
181
+ type PayrollScheduleDocument = types.PayrollScheduleDocument;
182
+ type ReceiptDocument = types.ReceiptDocument;
183
+ type KycUploadDocument = types.KycUploadDocument;
184
+ type BankStatementDocument = types.BankStatementDocument;
185
+ type TaxFormDocument = types.TaxFormDocument;
186
+ type SubAccount = Rebrand<types.SubAccount, SubAccountId>;
187
+ type SubAccountOwnerKind = types.SubAccountOwnerKind;
188
+ type VirtualAccount = Rebrand<types.VirtualAccount, VirtualAccountId>;
189
+ type VirtualAccountOwnerKind = types.VirtualAccountOwnerKind;
190
+ type VirtualCard = Rebrand<types.VirtualCard, VirtualCardId>;
191
+ type VirtualCardOwnerKind = types.VirtualCardOwnerKind;
192
+ type VirtualCardLimits = types.VirtualCardLimits;
193
+ type WebhookEndpoint = Rebrand<types.WebhookEndpoint, WebhookEndpointId>;
194
+ type WebhookEvent = Rebrand<types.WebhookEvent, WebhookEventId>;
195
+ type ApiKey = Rebrand<types.ApiKey, ApiKeyId>;
196
+ type ApiKeyEnvironment = types.ApiKeyEnvironment;
197
+ type ApiKeyType = types.ApiKeyType;
198
+ type Scope = types.Scope;
199
+ type Member = Rebrand<types.Member, MemberId>;
200
+ type BalanceLedgerEntry = Rebrand<types.BalanceLedgerEntry, BalanceLedgerEntryId>;
201
+
202
+ export type { WithdrawalStatus as $, Account as A, BalanceLedgerEntry as B, CreatePaymentResult as C, Document as D, ExternalAccount as E, TransferEndpoint as F, TransferFx as G, TransferStatus as H, InvoiceDocument as I, Treasury as J, KybProfile as K, List as L, Member as M, VirtualAccountOwnerKind as N, Operation as O, PageInfo as P, VirtualCard as Q, ReceiptDocument as R, Safe as S, TaxFormDocument as T, UserIdentifier as U, VirtualAccount as V, VirtualCardLimits as W, VirtualCardOwnerKind as X, WebhookEndpoint as Y, WebhookEvent as Z, Withdrawal as _, AccountLookupResult as a, ApiKey as b, ApiKeyEnvironment as c, ApiKeyType as d, BankStatementDocument as e, CreateTransferResult as f, CreateWithdrawalResult as g, ExternalAccountKind as h, KycProfile as i, KycUploadDocument as j, Money as k, OperationStatus as l, OperationSummary as m, Organization as n, Payment as o, PaymentParty as p, PaymentStatus as q, PayrollRunDocument as r, PayrollScheduleDocument as s, Scope as t, Settlement as u, SubAccount as v, SubAccountOwnerKind as w, TimestampIso as x, Transfer as y, TransferCustody as z };
@@ -0,0 +1,118 @@
1
+ 'use strict';
2
+
3
+ // src/errors.ts
4
+ var CapxulError = class extends Error {
5
+ code;
6
+ details;
7
+ operationId;
8
+ correlationId;
9
+ retryable;
10
+ constructor(init) {
11
+ super(
12
+ init.message,
13
+ init.cause !== void 0 ? { cause: init.cause } : void 0
14
+ );
15
+ this.name = "CapxulError";
16
+ this.code = init.code;
17
+ this.details = init.details;
18
+ this.operationId = init.operationId;
19
+ this.correlationId = init.correlationId;
20
+ this.retryable = init.retryable;
21
+ }
22
+ };
23
+
24
+ // ../config/src/timing.ts
25
+ var WEBHOOK_FRESHNESS_WINDOW_MS = 5 * 60 * 1e3;
26
+
27
+ // ../config/src/org-roles.ts
28
+ function roleKeyFromLabel(label) {
29
+ const bytes = new TextEncoder().encode(label);
30
+ const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
31
+ return "0x" + hex.padEnd(64, "0");
32
+ }
33
+ roleKeyFromLabel("OWNER");
34
+ roleKeyFromLabel("FINANCE_MANAGER");
35
+ roleKeyFromLabel("TEAM_LEAD");
36
+
37
+ // src/webhooks.ts
38
+ async function verifyWebhook(request, secret, options) {
39
+ if (!secret) {
40
+ throw new CapxulError({
41
+ code: "INVALID_INPUT",
42
+ message: "A webhook signing secret is required."
43
+ });
44
+ }
45
+ const timestamp = request.headers.get("x-capxul-timestamp");
46
+ const signature = request.headers.get("x-capxul-signature");
47
+ if (!timestamp || !signature) {
48
+ return { valid: false, reason: "invalid_signature" };
49
+ }
50
+ const timestampMs = Number(timestamp);
51
+ if (!Number.isFinite(timestampMs)) {
52
+ return { valid: false, reason: "malformed" };
53
+ }
54
+ const freshnessWindowMs = options?.freshnessWindowMs ?? WEBHOOK_FRESHNESS_WINDOW_MS;
55
+ if (Math.abs(Date.now() - timestampMs) > freshnessWindowMs) {
56
+ return { valid: false, reason: "replay" };
57
+ }
58
+ const body = await request.text();
59
+ const signatureHex = signature.startsWith("sha256=") ? signature.slice("sha256=".length) : signature;
60
+ if (!/^[a-f0-9]{64}$/i.test(signatureHex)) {
61
+ return { valid: false, reason: "invalid_signature" };
62
+ }
63
+ const validSignature = await verifyHmacSha256(
64
+ secret,
65
+ `${timestamp}.${body}`,
66
+ signatureHex
67
+ );
68
+ if (!validSignature) {
69
+ return { valid: false, reason: "invalid_signature" };
70
+ }
71
+ const parsed = parseWebhookEvent(body);
72
+ if (!parsed) {
73
+ return { valid: false, reason: "malformed" };
74
+ }
75
+ return { valid: true, event: parsed };
76
+ }
77
+ async function verifyHmacSha256(secret, message, signatureHex) {
78
+ const encoder = new TextEncoder();
79
+ const key = await crypto.subtle.importKey(
80
+ "raw",
81
+ encoder.encode(secret),
82
+ { name: "HMAC", hash: "SHA-256" },
83
+ false,
84
+ ["verify"]
85
+ );
86
+ return await crypto.subtle.verify(
87
+ "HMAC",
88
+ key,
89
+ hexToArrayBuffer(signatureHex),
90
+ encoder.encode(message)
91
+ );
92
+ }
93
+ function hexToArrayBuffer(hex) {
94
+ const buffer = new ArrayBuffer(hex.length / 2);
95
+ const bytes = new Uint8Array(buffer);
96
+ for (let i = 0; i < bytes.length; i++) {
97
+ bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
98
+ }
99
+ return buffer;
100
+ }
101
+ function parseWebhookEvent(body) {
102
+ try {
103
+ const parsed = JSON.parse(body);
104
+ if (!isWebhookEvent(parsed)) return null;
105
+ return parsed;
106
+ } catch {
107
+ return null;
108
+ }
109
+ }
110
+ function isWebhookEvent(value) {
111
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
112
+ return false;
113
+ }
114
+ const candidate = value;
115
+ return typeof candidate.id === "string" && typeof candidate.type === "string" && typeof candidate.createdAt === "string" && (candidate.operationId === void 0 || typeof candidate.operationId === "string") && (candidate.correlationId === void 0 || typeof candidate.correlationId === "string") && !!candidate.data && typeof candidate.data === "object" && !Array.isArray(candidate.data);
116
+ }
117
+
118
+ exports.verifyWebhook = verifyWebhook;
@@ -0,0 +1,34 @@
1
+ import { Z as WebhookEvent } from './types-Cokyqgwm.cjs';
2
+ import '@repo/api-contract/gen/types';
3
+ import '@repo/platform-kernel';
4
+
5
+ /**
6
+ * Webhook verification primitive per CANON.md §4.41 +
7
+ * `.claude/rules/webhook-security.md`.
8
+ *
9
+ * Verifies HMAC-SHA256 over the raw request body using
10
+ * `crypto.subtle.verify` (timing-safe by default), checks freshness
11
+ * against a configurable window, and returns a tagged-union result.
12
+ *
13
+ * Framework adapters (`@capxul/sdk-next`, future `@capxul/sdk-hono`)
14
+ * wrap this primitive with HTTP handler glue. This file is the one
15
+ * canonical crypto entry point.
16
+ */
17
+ type WebhookVerificationResult = {
18
+ readonly valid: true;
19
+ readonly event: WebhookEvent;
20
+ } | {
21
+ readonly valid: false;
22
+ readonly reason: "invalid_signature" | "replay" | "malformed";
23
+ };
24
+ type WebhookVerificationOptions = {
25
+ /**
26
+ * Maximum age of a payload (based on `createdAt`) before it is
27
+ * rejected as a replay. Defaults to 5 minutes
28
+ * (`WEBHOOK_FRESHNESS_WINDOW_MS` in `@repo/config`).
29
+ */
30
+ readonly freshnessWindowMs?: number;
31
+ };
32
+ declare function verifyWebhook(request: Request, secret: string, options?: WebhookVerificationOptions): Promise<WebhookVerificationResult>;
33
+
34
+ export { type WebhookVerificationOptions, type WebhookVerificationResult, verifyWebhook };
@@ -0,0 +1,34 @@
1
+ import { Z as WebhookEvent } from './types-Cokyqgwm.js';
2
+ import '@repo/api-contract/gen/types';
3
+ import '@repo/platform-kernel';
4
+
5
+ /**
6
+ * Webhook verification primitive per CANON.md §4.41 +
7
+ * `.claude/rules/webhook-security.md`.
8
+ *
9
+ * Verifies HMAC-SHA256 over the raw request body using
10
+ * `crypto.subtle.verify` (timing-safe by default), checks freshness
11
+ * against a configurable window, and returns a tagged-union result.
12
+ *
13
+ * Framework adapters (`@capxul/sdk-next`, future `@capxul/sdk-hono`)
14
+ * wrap this primitive with HTTP handler glue. This file is the one
15
+ * canonical crypto entry point.
16
+ */
17
+ type WebhookVerificationResult = {
18
+ readonly valid: true;
19
+ readonly event: WebhookEvent;
20
+ } | {
21
+ readonly valid: false;
22
+ readonly reason: "invalid_signature" | "replay" | "malformed";
23
+ };
24
+ type WebhookVerificationOptions = {
25
+ /**
26
+ * Maximum age of a payload (based on `createdAt`) before it is
27
+ * rejected as a replay. Defaults to 5 minutes
28
+ * (`WEBHOOK_FRESHNESS_WINDOW_MS` in `@repo/config`).
29
+ */
30
+ readonly freshnessWindowMs?: number;
31
+ };
32
+ declare function verifyWebhook(request: Request, secret: string, options?: WebhookVerificationOptions): Promise<WebhookVerificationResult>;
33
+
34
+ export { type WebhookVerificationOptions, type WebhookVerificationResult, verifyWebhook };