@capxul/sdk 4.1.4 → 4.2.0-rc.2

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,5 +1,10 @@
1
- import { $n as TelemetryIdentifyInput, Qn as TelemetryGroupInput, Zn as TelemetryEvent, n as ObservationAdapter, u as CapxulClient, ur as IdentityTransition } from "../observation-Bhdq9-l6.mjs";
1
+ import { h as CapxulResult } from "../OAuthBearerAuthClient-C-ip-z8M.mjs";
2
+ import { er as TelemetryEvent, f as CapxulClient, i as ObservationAdapter, n as ProductionSignerControls, nr as TelemetryIdentifyInput, pr as IdentityTransition, t as CapxulClientInput, tr as TelemetryGroupInput } from "../production-CBKHzqYP.mjs";
2
3
  import { Effect, Layer } from "effect";
4
+ //#region src/testing/production-client.d.ts
5
+ /** Create the one production client with per-client controls at its signing boundaries. */
6
+ declare function createCapxulProductionTestClient(input: CapxulClientInput, controls: ProductionSignerControls): Promise<CapxulResult<CapxulClient>>;
7
+ //#endregion
3
8
  //#region src/testing/telemetry/RecordingTelemetryAdapter.d.ts
4
9
  type RecordingTelemetryOperation = {
5
10
  readonly type: "emit";
@@ -51,4 +56,4 @@ interface CapxulTestObservation {
51
56
  */
52
57
  declare function createCapxulTestClient(options?: CreateCapxulTestClientOptions): CapxulTestClient;
53
58
  //#endregion
54
- export { CapxulTestClient, CapxulTestClock, CapxulTestObservation, CreateCapxulTestClientOptions, SeedTestIdentityInput, createCapxulTestClient };
59
+ export { CapxulTestClient, CapxulTestClock, CapxulTestObservation, CreateCapxulTestClientOptions, type ProductionSignerControls, SeedTestIdentityInput, createCapxulProductionTestClient, createCapxulTestClient };
@@ -1,8 +1,14 @@
1
- import { M as fromWei, Z as redactTelemetryEvent, f as identityErrorFromCapxul, g as bootstrapErrorFromCapxul, i as smartAccountErrorFromCapxul, m as convexCallErrorFromCapxul, o as accountReadErrorFromCapxul, t as assembleCapxulClient, u as wireChainId, v as toWei } from "../create-capxul-client-B3ET402J.mjs";
2
- import { $ as CapxulError, A as toAuthUserId, B as toJwtToken, D as toAddress, E as toAccountId, F as toDurationMs, I as toEmail, J as toSessionToken, K as toPublishableKey, L as toEpochMs, M as toChainId, N as toCountryCode, O as toAllowedOrigin, R as toEpochSeconds, V as toKycTier, _ as deriveCapxulSafeAddress, k as toAppId, n as InMemoryAuthCacheAdapter, t as readClockNow, tt as Errors, u as authClientPortFromPromiseAdapter, v as validateHandle, z as toHandle } from "../clock-C2AUlq1V.mjs";
3
- import { Effect, Result, Semaphore } from "effect";
1
+ import { D as fromWei, P as redactTelemetryEvent, a as smartAccountErrorFromCapxul, c as identityErrorFromCapxul, i as assembleCapxulClient, l as convexCallErrorFromCapxul, n as createCapxulClientWithSignerControls, o as accountReadErrorFromCapxul, s as wireChainId, u as bootstrapErrorFromCapxul, v as toWei } from "../production-WbMhv2Fb.mjs";
2
+ import { A as toAccountId, B as toEmail, G as toKycTier, H as toEpochSeconds, I as toChainId, L as toCountryCode, M as toAllowedOrigin, N as toAppId, P as toAuthUserId, Q as toSessionToken, U as toHandle, V as toEpochMs, W as toJwtToken, X as toPublishableKey, d as authClientPortFromPromiseAdapter, et as validateHandle, it as CapxulError, j as toAddress, n as readClockNow, ot as Errors, r as InMemoryAuthCacheAdapter, v as deriveCapxulSafeAddress, z as toDurationMs } from "../OAuthBearerAuthClient-BrbXrndM.mjs";
4
3
  import { keccak256 } from "viem";
4
+ import { Effect, Result, Semaphore } from "effect";
5
5
  import { getFunctionName } from "convex/server";
6
+ //#region src/testing/production-client.ts
7
+ /** Create the one production client with per-client controls at its signing boundaries. */
8
+ function createCapxulProductionTestClient(input, controls) {
9
+ return createCapxulClientWithSignerControls(input, controls);
10
+ }
11
+ //#endregion
6
12
  //#region src/testing/account/InMemoryAccountReadAdapter.ts
7
13
  var InMemoryAccountReadAdapter = class {
8
14
  #deps;
@@ -909,4 +915,4 @@ function createCapxulTestClient(options = {}) {
909
915
  };
910
916
  }
911
917
  //#endregion
912
- export { createCapxulTestClient };
918
+ export { createCapxulProductionTestClient, createCapxulTestClient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capxul/sdk",
3
- "version": "4.1.4",
3
+ "version": "4.2.0-rc.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Xelmar-tech/infrastructure.git",
@@ -47,10 +47,10 @@
47
47
  "vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
48
48
  "vite-plus": "0.3.0",
49
49
  "vitest": "4.1.11",
50
+ "@capxul/config": "0.3.0",
51
+ "@capxul/observability": "4.2.0-rc.2",
50
52
  "@capxul/errors": "0.3.0",
51
- "@capxul/observability": "4.1.4",
52
53
  "@capxul/types": "0.3.0",
53
- "@capxul/config": "0.3.0",
54
54
  "@capxul/typescript-config": "0.0.0",
55
55
  "@capxul/wire": "0.7.0"
56
56
  },
@@ -1,211 +0,0 @@
1
- import { A as toAuthUserId, B as toJwtToken, I as toEmail, J as toSessionToken, L as toEpochMs, R as toEpochSeconds, c as ClockError, l as ClockPortTag, t as readClockNow, tt as Errors, u as authClientPortFromPromiseAdapter } from "./clock-C2AUlq1V.mjs";
2
- import { Effect, Layer } from "effect";
3
- //#region src/adapters/clock/SystemClockAdapter.ts
4
- var SystemClockAdapter = class {
5
- now = Effect.try({
6
- try: () => toEpochMs(Date.now()),
7
- catch: (cause) => new ClockError({
8
- operation: "now",
9
- cause
10
- })
11
- });
12
- sleep(duration) {
13
- return Effect.callback((resume) => {
14
- const timeout = setTimeout(() => resume(Effect.void), duration);
15
- return Effect.sync(() => clearTimeout(timeout));
16
- });
17
- }
18
- };
19
- function SystemClockLayer() {
20
- return Layer.succeed(ClockPortTag, new SystemClockAdapter());
21
- }
22
- //#endregion
23
- //#region src/adapters/auth-client/OAuthBearerAuthClient.ts
24
- const PROVIDER = "agent-exchange";
25
- /** Re-exchange this far ahead of expiry, matching the JWT cache eviction margin. */
26
- const REFRESH_MARGIN_MS = 3e4;
27
- /**
28
- * `AuthSession.token` is a Better Auth session handle. This flow has none, and
29
- * the access token must never be copied into it, so the slot holds a placeholder.
30
- */
31
- const OAUTH_SESSION_TOKEN = toSessionToken("oauth-bearer");
32
- var OAuthBearerAuthClient = class {
33
- #exchangeUrl;
34
- #exchangeSecret;
35
- #accessToken;
36
- #fetchImpl;
37
- #clock;
38
- #cached = null;
39
- constructor(input) {
40
- this.#exchangeUrl = input.exchangeUrl;
41
- this.#exchangeSecret = input.exchangeSecret;
42
- this.#accessToken = input.accessToken;
43
- this.#fetchImpl = input.fetch ?? fetch;
44
- this.#clock = input.clock ?? new SystemClockAdapter();
45
- }
46
- async canSendOtp() {
47
- return refuse("canSendOtp");
48
- }
49
- async sendOtp() {
50
- return refuse("sendOtp");
51
- }
52
- async verifyOtp() {
53
- return refuse("verifyOtp");
54
- }
55
- /**
56
- * The authorization server owns the token's lifetime, so revoking it there is
57
- * the sign-out. Reporting success here would claim a token was dropped.
58
- */
59
- async signOut() {
60
- return refuse("signOut");
61
- }
62
- /**
63
- * The exchanged identity. Unlike the Better Auth adapters this never answers
64
- * `null`: the consumer holds a usable access token or a refused one, and a
65
- * refused one is a NOT_AUTHENTICATED failure, not the absence of a session.
66
- */
67
- async getSession(options) {
68
- const exchange = await this.#exchange("getSession", options);
69
- if (!exchange.ok) return exchange;
70
- return {
71
- ok: true,
72
- value: {
73
- authUserId: toAuthUserId(exchange.value.authUserId),
74
- email: toEmail(exchange.value.email),
75
- token: OAUTH_SESSION_TOKEN,
76
- expiresAt: toEpochMs(exchange.value.expiresAt)
77
- }
78
- };
79
- }
80
- async getConvexJwt(options) {
81
- const exchange = await this.#exchange("getConvexJwt", options);
82
- if (!exchange.ok) return exchange;
83
- return {
84
- ok: true,
85
- value: {
86
- token: toJwtToken(exchange.value.token),
87
- expEpochSeconds: toEpochSeconds(Math.floor(exchange.value.expiresAt / 1e3))
88
- }
89
- };
90
- }
91
- /**
92
- * The cached exchange while it is fresh, otherwise a new one. A failed
93
- * exchange never replaces the cache, so a stale token is never served.
94
- *
95
- * lazy: no in-flight de-duplication — two concurrent reads past the margin
96
- * each mint a JWT. Minting is idempotent, so the cost is one extra request.
97
- */
98
- async #exchange(operation, options) {
99
- if (options?.signal?.aborted) return {
100
- ok: false,
101
- error: Errors.cancelled({ operation })
102
- };
103
- const cached = this.#cached;
104
- if (cached !== null && options?.forceRefresh !== true) {
105
- const now = await readClockNow(this.#clock);
106
- if (!now.ok) return now;
107
- if (now.value < cached.expiresAt - REFRESH_MARGIN_MS) return {
108
- ok: true,
109
- value: cached
110
- };
111
- }
112
- return this.#postExchange(operation, options?.signal);
113
- }
114
- async #postExchange(operation, signal) {
115
- let res;
116
- try {
117
- res = await this.#fetchImpl(this.#exchangeUrl, {
118
- method: "POST",
119
- headers: {
120
- "content-type": "application/json",
121
- authorization: `Bearer ${this.#exchangeSecret}`
122
- },
123
- body: JSON.stringify({ accessToken: this.#accessToken }),
124
- ...signal === void 0 ? {} : { signal }
125
- });
126
- } catch (cause) {
127
- return {
128
- ok: false,
129
- error: signal?.aborted === true || isAbortError(cause) ? Errors.cancelled({ operation }) : Errors.networkError(operation, cause)
130
- };
131
- }
132
- if (signal?.aborted === true) return {
133
- ok: false,
134
- error: Errors.cancelled({ operation })
135
- };
136
- const body = await res.json().catch(() => null);
137
- if (!res.ok) return {
138
- ok: false,
139
- error: exchangeRefused(operation, res.status, readErrorCode(body))
140
- };
141
- const exchange = readExchange(body);
142
- if (exchange === null) return {
143
- ok: false,
144
- error: Errors.providerError(PROVIDER, operation, /* @__PURE__ */ new Error("unexpected body"), { httpStatus: res.status })
145
- };
146
- this.#cached = exchange;
147
- return {
148
- ok: true,
149
- value: exchange
150
- };
151
- }
152
- };
153
- /**
154
- * `AuthClientPort` over the backend agent-token exchange. Build one per OAuth
155
- * access token and hand it to `createCapxulClient({ authClient })`.
156
- */
157
- function oauthBearerAuthClient(input) {
158
- return authClientPortFromPromiseAdapter(new OAuthBearerAuthClient(input));
159
- }
160
- /** The OTP verbs belong to the email flow; `validStates` names it for the caller. */
161
- function refuse(method) {
162
- return {
163
- ok: false,
164
- error: Errors.wrongState({
165
- method,
166
- currentState: "oauth-bearer",
167
- validStates: ["email-otp"]
168
- })
169
- };
170
- }
171
- /**
172
- * The error catalog has no FORBIDDEN code, so 403 (`insufficient_scope`) and
173
- * 503 (`exchange_unconfigured`) both land on PROVIDER_ERROR. `httpStatus` and
174
- * `details.reason` carry which one the backend reported. Only 401 becomes
175
- * NOT_AUTHENTICATED, because only 401 is answered by a new access token.
176
- */
177
- function exchangeRefused(operation, status, reason) {
178
- if (status === 401) return Errors.notAuthenticated("The exchange refused this OAuth access token");
179
- return Errors.providerError(PROVIDER, operation, /* @__PURE__ */ new Error(`HTTP ${status}`), {
180
- httpStatus: status,
181
- ...reason === void 0 ? {} : { details: { reason } }
182
- });
183
- }
184
- /** An abort raised by something other than the caller's own signal. */
185
- function isAbortError(cause) {
186
- return readField(cause, "name") === "AbortError";
187
- }
188
- function readErrorCode(body) {
189
- const error = readField(body, "error");
190
- return typeof error === "string" ? error : void 0;
191
- }
192
- function readField(value, key) {
193
- if (typeof value !== "object" || value === null) return void 0;
194
- return value[key];
195
- }
196
- function readExchange(body) {
197
- if (typeof body !== "object" || body === null) return null;
198
- const { token, expiresAt, authUserId, email } = body;
199
- if (typeof token !== "string" || token.length === 0) return null;
200
- if (typeof expiresAt !== "number" || !Number.isFinite(expiresAt)) return null;
201
- if (typeof authUserId !== "string" || authUserId.length === 0) return null;
202
- if (typeof email !== "string" || email.length === 0) return null;
203
- return {
204
- token,
205
- expiresAt,
206
- authUserId,
207
- email
208
- };
209
- }
210
- //#endregion
211
- export { SystemClockLayer as n, oauthBearerAuthClient as t };
@@ -1,19 +0,0 @@
1
- import { _ as ClockPort, v as AuthClientPort } from "./signer-C0hZ6Kiy.mjs";
2
- //#region src/adapters/auth-client/OAuthBearerAuthClient.d.ts
3
- interface OAuthBearerAuthClientInput {
4
- /** The exchange endpoint: `{authBaseUrl}/api/auth/agent/exchange`. */
5
- readonly exchangeUrl: string;
6
- /** The shared secret the exchange endpoint checks. The caller holds it. */
7
- readonly exchangeSecret: string;
8
- /** The agent's OAuth access token. */
9
- readonly accessToken: string;
10
- readonly fetch?: typeof fetch;
11
- readonly clock?: ClockPort;
12
- }
13
- /**
14
- * `AuthClientPort` over the backend agent-token exchange. Build one per OAuth
15
- * access token and hand it to `createCapxulClient({ authClient })`.
16
- */
17
- declare function oauthBearerAuthClient(input: OAuthBearerAuthClientInput): AuthClientPort;
18
- //#endregion
19
- export { oauthBearerAuthClient as n, OAuthBearerAuthClientInput as t };