@capxul/sdk 1.0.0-alpha.9 → 1.2.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.
Files changed (32) hide show
  1. package/README.md +121 -6
  2. package/dist/{InMemoryAuthCacheAdapter-v5W-XB5M.mjs → InMemoryAuthCacheAdapter-Dr1sEd9y.mjs} +36 -16
  3. package/dist/InMemoryAuthCacheAdapter-Dr1sEd9y.mjs.map +1 -0
  4. package/dist/create-capxul-client-C7H5b68l.d.mts +2764 -0
  5. package/dist/create-capxul-client-C7H5b68l.d.mts.map +1 -0
  6. package/dist/create-capxul-client-DiqVIxsV.mjs +6980 -0
  7. package/dist/create-capxul-client-DiqVIxsV.mjs.map +1 -0
  8. package/dist/index.d.mts +119 -2020
  9. package/dist/index.d.mts.map +1 -1
  10. package/dist/index.mjs +2464 -6308
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/node/index.d.mts +3 -6
  13. package/dist/node/index.d.mts.map +1 -1
  14. package/dist/node/index.mjs +27 -27
  15. package/dist/node/index.mjs.map +1 -1
  16. package/dist/signer-CJT0pPiO.d.mts +301 -0
  17. package/dist/signer-CJT0pPiO.d.mts.map +1 -0
  18. package/dist/testing/index.d.mts +54 -0
  19. package/dist/testing/index.d.mts.map +1 -0
  20. package/dist/testing/index.mjs +1068 -0
  21. package/dist/testing/index.mjs.map +1 -0
  22. package/package.json +17 -18
  23. package/dist/InMemoryAuthCacheAdapter-v5W-XB5M.mjs.map +0 -1
  24. package/dist/index-CTXgQ_xR.d.mts +0 -158
  25. package/dist/index-CTXgQ_xR.d.mts.map +0 -1
  26. package/dist/ports/safe-deployment.d.mts +0 -2
  27. package/dist/ports/safe-deployment.mjs +0 -38
  28. package/dist/ports/safe-deployment.mjs.map +0 -1
  29. package/dist/safe-deployment-D3k9yndM.d.mts +0 -136
  30. package/dist/safe-deployment-D3k9yndM.d.mts.map +0 -1
  31. package/dist/signer-DqDtJU1l.d.mts +0 -145
  32. package/dist/signer-DqDtJU1l.d.mts.map +0 -1
@@ -0,0 +1,1068 @@
1
+ import { C as convexCallErrorFromCapxul, D as bootstrapErrorFromCapxul, H as deriveCapxulSafeAddress, O as authClientPortFromPromiseAdapter, _ as identityErrorFromCapxul, c as smartAccountErrorFromCapxul, d as fromWei, h as wireChainId, i as redactTelemetryEvent, m as toWei, p as accountReadErrorFromCapxul, t as assembleCapxulClient, u as subAccountErrorFromCapxul } from "../create-capxul-client-DiqVIxsV.mjs";
2
+ import { C as toJwtToken, E as toPublishableKey, N as Errors, O as toSessionToken, S as toEpochSeconds, _ as toCountryCode, b as toEmail, d as toAccountId, f as toAddress, g as toChainId, h as toAuthUserId, j as CapxulError, k as toSubAccountId, m as toAppId, p as toAllowedOrigin, t as InMemoryAuthCacheAdapter, v as toCurrencyCode, w as toKycTier, x as toEpochMs, y as toDurationMs } from "../InMemoryAuthCacheAdapter-Dr1sEd9y.mjs";
3
+ import { keccak256 } from "viem";
4
+ import { Effect, Result, Semaphore } from "effect";
5
+ import { getFunctionName } from "convex/server";
6
+ //#region src/testing/account/InMemoryAccountReadAdapter.ts
7
+ var InMemoryAccountReadAdapter = class {
8
+ #deps;
9
+ #rawBalance;
10
+ constructor(deps = {}) {
11
+ this.#deps = deps;
12
+ this.#rawBalance = deps.rawBalance ?? "0";
13
+ }
14
+ readBalance(_input) {
15
+ const scriptedFailure = this.#deps.failures?.readBalance;
16
+ if (scriptedFailure !== void 0) return Effect.fail(accountReadErrorFromCapxul("readBalance", scriptedFailure));
17
+ if (this.#deps.account !== void 0) return Effect.succeed(cloneAccount(this.#deps.account));
18
+ const decimals = this.#deps.decimals ?? 6;
19
+ const currency = this.#deps.currency ?? "USD";
20
+ const money = fromWei(this.#rawBalance, decimals, currency);
21
+ return Effect.succeed({
22
+ id: toAccountId(this.#deps.accountId ?? "account_01TEST000000000000000000"),
23
+ balance: money,
24
+ available: money
25
+ });
26
+ }
27
+ fundFromFaucet(input) {
28
+ const scriptedFailure = this.#deps.failures?.fundFromFaucet;
29
+ if (scriptedFailure !== void 0) return Effect.fail(accountReadErrorFromCapxul("fundFromFaucet", scriptedFailure));
30
+ const expectedDecimals = this.#deps.decimals ?? 6;
31
+ const expectedCurrency = this.#deps.currency ?? "USD";
32
+ if (input.amount.decimals !== expectedDecimals || String(input.amount.currency) !== expectedCurrency) return Effect.fail(accountReadErrorFromCapxul("fundFromFaucet", Errors.invalidInput("amount", "decimals/currency must match adapter config")));
33
+ const minted = BigInt(toWei(input.amount));
34
+ this.#rawBalance = (BigInt(this.#rawBalance) + minted).toString();
35
+ return Effect.succeed({ txHash: "0x" + "f".repeat(64) });
36
+ }
37
+ };
38
+ function cloneAccount(account) {
39
+ return {
40
+ id: account.id,
41
+ balance: { ...account.balance },
42
+ available: { ...account.available }
43
+ };
44
+ }
45
+ //#endregion
46
+ //#region src/adapters/_shared/clock.ts
47
+ async function readClockNow(clock) {
48
+ try {
49
+ const result = await Effect.runPromise(Effect.result(clock.now));
50
+ if (Result.isFailure(result)) return {
51
+ ok: false,
52
+ error: Errors.providerError("clock", "now", result.failure)
53
+ };
54
+ return {
55
+ ok: true,
56
+ value: result.success
57
+ };
58
+ } catch (cause) {
59
+ return {
60
+ ok: false,
61
+ error: Errors.providerError("clock", "now", cause)
62
+ };
63
+ }
64
+ }
65
+ //#endregion
66
+ //#region src/testing/clock/ManualClockAdapter.ts
67
+ /**
68
+ * Event-discipline test clock. Time moves only when a release primitive is
69
+ * invoked. `tickOne()` releases EXACTLY ONE pending sleep (the earliest
70
+ * deadline, FIFO at ties) and snaps `now()` to that deadline. `advance(ms)`
71
+ * composes repeated `tickOne()` calls over the resulting window — used to
72
+ * keep the cross-adapter conformance harness uniform with the distance model
73
+ * of `VirtualClockAdapter`. See `packages/sdk/docs/architecture.md` and the
74
+ * slice spec `docs/slices/clock-adapters.md`.
75
+ */
76
+ var ManualClockAdapter = class ManualClockAdapter {
77
+ currentTime = 0;
78
+ queue = [];
79
+ seqCounter = 0;
80
+ constructor(deps) {}
81
+ now = Effect.sync(() => toEpochMs(this.currentTime));
82
+ sleep(duration) {
83
+ return Effect.promise(() => new Promise((resolve) => {
84
+ this.queue.push({
85
+ deadline: this.currentTime + duration,
86
+ resolve,
87
+ seq: this.seqCounter++
88
+ });
89
+ }));
90
+ }
91
+ /**
92
+ * Release exactly one pending sleep — the earliest deadline, FIFO at ties.
93
+ * Snaps `now()` forward to that sleep's deadline. No-op if the queue is
94
+ * empty (preserves the empty-tick safety property).
95
+ */
96
+ tickOne() {
97
+ return Effect.runPromise(this.tickOneEffect());
98
+ }
99
+ tickOneEffect() {
100
+ const self = this;
101
+ return Effect.gen(function* () {
102
+ if (self.queue.length === 0) return;
103
+ const nextIndex = self.findEarliestIndex();
104
+ const next = self.queue.splice(nextIndex, 1)[0];
105
+ if (next.deadline > self.currentTime) self.currentTime = next.deadline;
106
+ next.resolve();
107
+ yield* Effect.yieldNow;
108
+ });
109
+ }
110
+ /**
111
+ * Cross-adapter harness affordance. Releases every sleep whose deadline
112
+ * falls inside `[currentTime, currentTime + ms]` via repeated `tickOne()`,
113
+ * then snaps `now()` to `currentTime + ms`. The composition is the seam
114
+ * that lets the harness drive Manual and Virtual identically.
115
+ */
116
+ advance(ms) {
117
+ return Effect.runPromise(this.advanceEffect(toDurationMs(ms)));
118
+ }
119
+ advanceEffect(duration) {
120
+ const self = this;
121
+ return Effect.gen(function* () {
122
+ yield* Effect.yieldNow;
123
+ const target = self.currentTime + duration;
124
+ while (self.queue.length > 0 && self.earliestDeadline() <= target) yield* self.tickOneEffect();
125
+ self.currentTime = target;
126
+ });
127
+ }
128
+ findEarliestIndex() {
129
+ let bestIndex = 0;
130
+ let best = this.queue[0];
131
+ for (let i = 1; i < this.queue.length; i++) {
132
+ const candidate = this.queue[i];
133
+ if (candidate.deadline < best.deadline || candidate.deadline === best.deadline && candidate.seq < best.seq) {
134
+ best = candidate;
135
+ bestIndex = i;
136
+ }
137
+ }
138
+ return bestIndex;
139
+ }
140
+ earliestDeadline() {
141
+ return this.queue[this.findEarliestIndex()].deadline;
142
+ }
143
+ static forConformance() {
144
+ return new ManualClockUnderTest(new ManualClockAdapter({}));
145
+ }
146
+ };
147
+ var ManualClockUnderTest = class {
148
+ clock;
149
+ constructor(clock) {
150
+ this.clock = clock;
151
+ }
152
+ advance(ms) {
153
+ return this.clock.advance(ms);
154
+ }
155
+ };
156
+ //#endregion
157
+ //#region src/testing/auth-client/InMemoryAuthClientAdapter.ts
158
+ const DEFAULT_OTP_TTL_MS = toDurationMs(300 * 1e3);
159
+ const DEFAULT_SESSION_TTL_MS = toDurationMs(1440 * 60 * 1e3);
160
+ const DEFAULT_JWT_TTL_MS = toDurationMs(900 * 1e3);
161
+ /**
162
+ * Hermetic OTP + session state machine implementing `AuthClientPort`. Wires
163
+ * through `AuthCachePort` for persistence (clause A11) and `ClockPort`
164
+ * for TTL + expiry math. OTPs are deterministic per-email counters (`"000000"`,
165
+ * `"000001"`, …) so test assertions remain stable. Session tokens are
166
+ * deterministic per-adapter counters (`"tok_0"`, `"tok_1"`, …). Returning users
167
+ * (same email post-signOut) get the same `AuthUserId`.
168
+ *
169
+ * Never throws — every failure path returns `{ ok: false, error: CapxulError }`.
170
+ * Honors `options.signal` via a pre-check (clause A10): an aborted controller
171
+ * before method entry yields `{ ok: false, error: Errors.cancelled({ operation }) }`
172
+ * without any state change.
173
+ */
174
+ var InMemoryAuthClientAdapter = class {
175
+ #pendingOtps = /* @__PURE__ */ new Map();
176
+ #otpCountersByEmail = /* @__PURE__ */ new Map();
177
+ #authUserIdByEmail = /* @__PURE__ */ new Map();
178
+ #allocatedAuthUserIds = /* @__PURE__ */ new Set();
179
+ #authCache;
180
+ #clock;
181
+ #otpTtlMs;
182
+ #failures;
183
+ #authUserIdCounter = 0;
184
+ #tokenCounter = 0;
185
+ #jwtCounter = 0;
186
+ constructor(deps) {
187
+ this.#authCache = deps.authCache;
188
+ this.#clock = deps.clock;
189
+ this.#otpTtlMs = deps.otpTtlMs ?? DEFAULT_OTP_TTL_MS;
190
+ this.#failures = deps.failures ?? {};
191
+ }
192
+ /** Test affordance: peek the OTP for an email, simulating "user reads email." */
193
+ peekOtpForTesting(email) {
194
+ return this.#pendingOtps.get(email)?.otp;
195
+ }
196
+ /** Test affordance: make a seeded identity own the session minted for its email. */
197
+ bindAuthUserIdForTesting(email, authUserId) {
198
+ this.#authUserIdByEmail.set(email, authUserId);
199
+ this.#allocatedAuthUserIds.add(authUserId);
200
+ }
201
+ async canSendOtp(input, options) {
202
+ if (options?.signal?.aborted) return {
203
+ ok: false,
204
+ error: Errors.cancelled({ operation: "canSendOtp" })
205
+ };
206
+ if (this.#failures.canSendOtp !== void 0) return {
207
+ ok: false,
208
+ error: this.#failures.canSendOtp
209
+ };
210
+ const pending = this.#pendingOtps.get(input.email);
211
+ if (pending === void 0) return {
212
+ ok: true,
213
+ value: {
214
+ allowed: true,
215
+ cooldownMs: toDurationMs(0)
216
+ }
217
+ };
218
+ const nowResult = await readClockNow(this.#clock);
219
+ if (!nowResult.ok) return nowResult;
220
+ const elapsedMs = nowResult.value - pending.issuedAt;
221
+ const cooldownMs = Math.max(0, this.#otpTtlMs - elapsedMs);
222
+ if (cooldownMs === 0) {
223
+ this.#pendingOtps.delete(input.email);
224
+ return {
225
+ ok: true,
226
+ value: {
227
+ allowed: true,
228
+ cooldownMs: toDurationMs(0)
229
+ }
230
+ };
231
+ }
232
+ return {
233
+ ok: true,
234
+ value: {
235
+ allowed: false,
236
+ cooldownMs: toDurationMs(cooldownMs)
237
+ }
238
+ };
239
+ }
240
+ async sendOtp(input, options) {
241
+ if (options?.signal?.aborted) return {
242
+ ok: false,
243
+ error: Errors.cancelled({ operation: "sendOtp" })
244
+ };
245
+ if (this.#failures.sendOtp !== void 0) return {
246
+ ok: false,
247
+ error: this.#failures.sendOtp
248
+ };
249
+ const issuedAt = await readClockNow(this.#clock);
250
+ if (!issuedAt.ok) return issuedAt;
251
+ const counter = this.#otpCountersByEmail.get(input.email) ?? 0;
252
+ this.#otpCountersByEmail.set(input.email, counter + 1);
253
+ const otp = counter.toString().padStart(6, "0");
254
+ this.#pendingOtps.set(input.email, {
255
+ otp,
256
+ issuedAt: issuedAt.value
257
+ });
258
+ return {
259
+ ok: true,
260
+ value: void 0
261
+ };
262
+ }
263
+ async verifyOtp(input, options) {
264
+ if (options?.signal?.aborted) return {
265
+ ok: false,
266
+ error: Errors.cancelled({ operation: "verifyOtp" })
267
+ };
268
+ if (this.#failures.verifyOtp !== void 0) return {
269
+ ok: false,
270
+ error: this.#failures.verifyOtp
271
+ };
272
+ const pending = this.#pendingOtps.get(input.email);
273
+ if (pending === void 0) return {
274
+ ok: false,
275
+ error: Errors.invalidInput("email", "no OTP issued for this email")
276
+ };
277
+ const nowResult = await readClockNow(this.#clock);
278
+ if (!nowResult.ok) return nowResult;
279
+ const now = nowResult.value;
280
+ if (now - pending.issuedAt > this.#otpTtlMs) {
281
+ this.#pendingOtps.delete(input.email);
282
+ return {
283
+ ok: false,
284
+ error: Errors.otpExpired()
285
+ };
286
+ }
287
+ if (input.otp !== pending.otp) return {
288
+ ok: false,
289
+ error: Errors.invalidInput("otp", "incorrect OTP")
290
+ };
291
+ const authUserId = this.#getOrIssueAuthUserId(input.email);
292
+ const token = toSessionToken(`tok_${this.#tokenCounter++}`);
293
+ const expiresAt = toEpochMs(now + DEFAULT_SESSION_TTL_MS);
294
+ const session = {
295
+ authUserId,
296
+ email: input.email,
297
+ token,
298
+ expiresAt
299
+ };
300
+ const cacheWrite = await runAuthCacheEffect(this.#authCache.setSession(session), "setSession");
301
+ if (!cacheWrite.ok) return cacheWrite;
302
+ this.#pendingOtps.delete(input.email);
303
+ return {
304
+ ok: true,
305
+ value: session
306
+ };
307
+ }
308
+ async getSession(options) {
309
+ if (options?.signal?.aborted) return {
310
+ ok: false,
311
+ error: Errors.cancelled({ operation: "getSession" })
312
+ };
313
+ if (this.#failures.getSession !== void 0) return {
314
+ ok: false,
315
+ error: this.#failures.getSession
316
+ };
317
+ const cacheRead = await runAuthCacheEffect(this.#authCache.getSession, "getSession");
318
+ if (!cacheRead.ok) return cacheRead;
319
+ const persisted = cacheRead.value;
320
+ if (persisted === null) return {
321
+ ok: true,
322
+ value: null
323
+ };
324
+ const nowResult = await readClockNow(this.#clock);
325
+ if (!nowResult.ok) return nowResult;
326
+ const now = nowResult.value;
327
+ if (persisted.expiresAt <= now) {
328
+ const cacheClear = await runAuthCacheEffect(this.#authCache.clearSession, "clearSession");
329
+ if (!cacheClear.ok) return cacheClear;
330
+ return {
331
+ ok: true,
332
+ value: null
333
+ };
334
+ }
335
+ return {
336
+ ok: true,
337
+ value: persisted
338
+ };
339
+ }
340
+ async signOut(options) {
341
+ if (options?.signal?.aborted) return {
342
+ ok: false,
343
+ error: Errors.cancelled({ operation: "signOut" })
344
+ };
345
+ if (this.#failures.signOut !== void 0) return {
346
+ ok: false,
347
+ error: this.#failures.signOut
348
+ };
349
+ return runAuthCacheEffect(this.#authCache.clearSession, "clearSession");
350
+ }
351
+ async getConvexJwt(options) {
352
+ if (options?.signal?.aborted) return {
353
+ ok: false,
354
+ error: Errors.cancelled({ operation: "getConvexJwt" })
355
+ };
356
+ if (this.#failures.getConvexJwt !== void 0) return {
357
+ ok: false,
358
+ error: this.#failures.getConvexJwt
359
+ };
360
+ if (options?.forceRefresh !== true) {
361
+ const cacheRead = await runAuthCacheEffect(this.#authCache.getJwt, "getJwt");
362
+ if (!cacheRead.ok) return cacheRead;
363
+ if (cacheRead.value !== null) return {
364
+ ok: true,
365
+ value: cacheRead.value
366
+ };
367
+ }
368
+ const now = await readClockNow(this.#clock);
369
+ if (!now.ok) return now;
370
+ const jwt = {
371
+ token: toJwtToken(`in-memory-jwt-${this.#jwtCounter++}`),
372
+ expEpochSeconds: toEpochSeconds(Math.floor(now.value / 1e3 + DEFAULT_JWT_TTL_MS / 1e3))
373
+ };
374
+ const cacheWrite = await runAuthCacheEffect(this.#authCache.setJwt(jwt), "setJwt");
375
+ if (!cacheWrite.ok) return cacheWrite;
376
+ return {
377
+ ok: true,
378
+ value: jwt
379
+ };
380
+ }
381
+ #getOrIssueAuthUserId(email) {
382
+ const existing = this.#authUserIdByEmail.get(email);
383
+ if (existing !== void 0) return existing;
384
+ let fresh;
385
+ do
386
+ fresh = toAuthUserId(`user_${this.#authUserIdCounter++}`);
387
+ while (this.#allocatedAuthUserIds.has(fresh));
388
+ this.#authUserIdByEmail.set(email, fresh);
389
+ this.#allocatedAuthUserIds.add(fresh);
390
+ return fresh;
391
+ }
392
+ };
393
+ async function runAuthCacheEffect(effect, operation) {
394
+ const result = await Effect.runPromise(Effect.result(effect));
395
+ if (Result.isFailure(result)) return {
396
+ ok: false,
397
+ error: Errors.providerError("auth-cache", operation, result.failure)
398
+ };
399
+ return {
400
+ ok: true,
401
+ value: result.success
402
+ };
403
+ }
404
+ //#endregion
405
+ //#region src/testing/bootstrap/BootstrapStubAdapter.ts
406
+ var BootstrapStubAdapter = class {
407
+ #script;
408
+ constructor(deps) {
409
+ this.#script = deps.script;
410
+ }
411
+ resolve(input) {
412
+ const key = bootstrapStubScriptKey(input);
413
+ const failure = this.#script.failures?.get(key);
414
+ if (failure !== void 0) return fail$2(kindFromCapxulError(failure), failure);
415
+ const resolution = this.#script.resolutions?.get(key);
416
+ if (resolution !== void 0) return Effect.succeed(resolution);
417
+ const defaultFailure = this.#script.defaultFailure ?? Errors.notAuthenticated();
418
+ return fail$2(kindFromCapxulError(defaultFailure), defaultFailure);
419
+ }
420
+ };
421
+ function bootstrapStubScriptKey(input) {
422
+ return `${String(input.publishableKey)}::${input.origin ?? ""}`;
423
+ }
424
+ function kindFromCapxulError(error) {
425
+ if (error.code === "NOT_AUTHENTICATED") return "notAuthenticated";
426
+ if (error.code === "NETWORK_ERROR") return "network";
427
+ if (error.code === "RATE_LIMITED") return "rateLimited";
428
+ if (error.code === "INVALID_INPUT") return "invalidInput";
429
+ return "provider";
430
+ }
431
+ function fail$2(kind, error) {
432
+ return Effect.fail(bootstrapErrorFromCapxul(kind, error));
433
+ }
434
+ //#endregion
435
+ //#region src/testing/convex-call/ConvexCallStubAdapter.ts
436
+ var ConvexCallStubAdapter = class {
437
+ #script;
438
+ #subscribers = /* @__PURE__ */ new Map();
439
+ constructor(deps) {
440
+ this.#script = deps.script;
441
+ }
442
+ query(fn, _args) {
443
+ return this.#resolve(getFunctionName(fn));
444
+ }
445
+ mutation(fn, _args) {
446
+ return this.#resolve(getFunctionName(fn));
447
+ }
448
+ action(fn, _args) {
449
+ return this.#resolve(getFunctionName(fn));
450
+ }
451
+ subscribe(fn, _args, callback) {
452
+ return Effect.sync(() => {
453
+ const path = getFunctionName(fn);
454
+ const cb = callback;
455
+ cb({ status: "loading" });
456
+ const sequence = this.#script.subscriptions?.get(path);
457
+ const subscriber = {
458
+ callback: cb,
459
+ cursor: 0
460
+ };
461
+ let subs = this.#subscribers.get(path);
462
+ if (subs === void 0) {
463
+ subs = /* @__PURE__ */ new Set();
464
+ this.#subscribers.set(path, subs);
465
+ }
466
+ subs.add(subscriber);
467
+ if (sequence !== void 0 && sequence.length > 0) {
468
+ cb(sequence[0]);
469
+ subscriber.cursor = 1;
470
+ }
471
+ let active = true;
472
+ return () => {
473
+ if (!active) return;
474
+ active = false;
475
+ const liveSubs = this.#subscribers.get(path);
476
+ if (liveSubs === void 0) return;
477
+ liveSubs.delete(subscriber);
478
+ if (liveSubs.size === 0) this.#subscribers.delete(path);
479
+ };
480
+ });
481
+ }
482
+ advanceSubscription(path) {
483
+ const sequence = this.#script.subscriptions?.get(path);
484
+ if (sequence === void 0) return;
485
+ const subs = this.#subscribers.get(path);
486
+ if (subs === void 0) return;
487
+ for (const sub of subs) {
488
+ if (sub.cursor >= sequence.length) continue;
489
+ const next = sequence[sub.cursor];
490
+ sub.cursor += 1;
491
+ sub.callback(next);
492
+ }
493
+ }
494
+ #resolve(path) {
495
+ const failure = this.#script.failures?.get(path);
496
+ if (failure !== void 0) return Effect.fail(mapFailure(path, failure));
497
+ if (this.#script.responses?.has(path)) return Effect.succeed(this.#script.responses.get(path));
498
+ return Effect.fail(convexCallErrorFromCapxul(path, Errors.providerError("convex", path, "unknown function")));
499
+ }
500
+ };
501
+ function mapFailure(operation, failure) {
502
+ if (failure instanceof CapxulError) return convexCallErrorFromCapxul(operation, failure);
503
+ if (failure instanceof Error) return convexCallErrorFromCapxul(operation, Errors.providerError("convex", operation, failure));
504
+ return convexCallErrorFromCapxul(operation, Errors.providerError("convex", operation, new Error(String(failure))));
505
+ }
506
+ //#endregion
507
+ //#region src/testing/identity/InMemoryIdentityAdapter.ts
508
+ var InMemoryIdentityAdapter = class {
509
+ #identities = /* @__PURE__ */ new Map();
510
+ #clock;
511
+ constructor(deps) {
512
+ this.#clock = deps.clock;
513
+ }
514
+ loadByAuthUserId(authUserId) {
515
+ return Effect.sync(() => cloneProfileOrNull(this.#identities.get(authUserId) ?? null));
516
+ }
517
+ create(input) {
518
+ const self = this;
519
+ return Effect.gen(function* () {
520
+ if (self.#identities.has(input.authUserId)) return yield* fail$1("create", Errors.invalidInput("authUserId", "already exists"));
521
+ const now = yield* self.#now("create.clock");
522
+ const profile = {
523
+ authUserId: input.authUserId,
524
+ email: input.email,
525
+ displayName: input.displayName ?? null,
526
+ country: input.country ?? null,
527
+ onboarded: false,
528
+ withdrawalAddress: null,
529
+ username: null,
530
+ imageUrl: null,
531
+ kycTier: toKycTier(0),
532
+ createdAt: now,
533
+ updatedAt: now
534
+ };
535
+ self.#identities.set(input.authUserId, cloneProfile(profile));
536
+ return cloneProfile(profile);
537
+ });
538
+ }
539
+ update(input) {
540
+ const self = this;
541
+ return Effect.gen(function* () {
542
+ const existing = self.#identities.get(input.authUserId);
543
+ if (existing === void 0) return yield* fail$1("update", Errors.profileNotFound(input.authUserId));
544
+ const now = yield* self.#now("update.clock");
545
+ const updated = {
546
+ authUserId: existing.authUserId,
547
+ email: existing.email,
548
+ kycTier: existing.kycTier,
549
+ createdAt: existing.createdAt,
550
+ displayName: input.displayName !== void 0 ? input.displayName : existing.displayName,
551
+ country: input.country !== void 0 ? input.country : existing.country,
552
+ onboarded: existing.onboarded,
553
+ withdrawalAddress: existing.withdrawalAddress,
554
+ username: existing.username,
555
+ imageUrl: existing.imageUrl,
556
+ updatedAt: now
557
+ };
558
+ self.#identities.set(input.authUserId, cloneProfile(updated));
559
+ return cloneProfile(updated);
560
+ });
561
+ }
562
+ completeOnboarding(input) {
563
+ const self = this;
564
+ return Effect.gen(function* () {
565
+ const now = yield* self.#now("completeOnboarding.clock");
566
+ const existing = self.#identities.get(input.authUserId);
567
+ const profile = {
568
+ authUserId: input.authUserId,
569
+ email: existing?.email ?? input.email,
570
+ displayName: input.displayName,
571
+ country: input.country,
572
+ onboarded: true,
573
+ withdrawalAddress: input.withdrawalAddress ?? existing?.withdrawalAddress ?? null,
574
+ username: existing?.username ?? null,
575
+ imageUrl: existing?.imageUrl ?? null,
576
+ kycTier: existing?.kycTier ?? toKycTier(0),
577
+ createdAt: existing?.createdAt ?? now,
578
+ updatedAt: now
579
+ };
580
+ self.#identities.set(input.authUserId, cloneProfile(profile));
581
+ return cloneProfile(profile);
582
+ });
583
+ }
584
+ #now(operation) {
585
+ return this.#clock.now.pipe(Effect.mapError((cause) => identityErrorFromCapxul(operation, Errors.providerError("clock", "now", cause), cause)));
586
+ }
587
+ };
588
+ function fail$1(operation, error) {
589
+ return Effect.fail(identityErrorFromCapxul(operation, error));
590
+ }
591
+ function cloneProfile(profile) {
592
+ return {
593
+ authUserId: profile.authUserId,
594
+ email: profile.email,
595
+ displayName: profile.displayName,
596
+ country: profile.country,
597
+ onboarded: profile.onboarded,
598
+ withdrawalAddress: profile.withdrawalAddress,
599
+ username: profile.username,
600
+ imageUrl: profile.imageUrl,
601
+ kycTier: profile.kycTier,
602
+ createdAt: profile.createdAt,
603
+ updatedAt: profile.updatedAt
604
+ };
605
+ }
606
+ function cloneProfileOrNull(profile) {
607
+ return profile === null ? null : cloneProfile(profile);
608
+ }
609
+ //#endregion
610
+ //#region src/testing/smart-account/smart-account-derivation.ts
611
+ const SUPPORTED_CHAIN_IDS = new Set([84532, 8453]);
612
+ toAddress("0xa6b71e26c5e0845f74c812102ca7114b6a896ab2");
613
+ keccak256("0x");
614
+ function isSupportedSmartAccountChain(chainId) {
615
+ return SUPPORTED_CHAIN_IDS.has(wireChainId(chainId));
616
+ }
617
+ //#endregion
618
+ //#region src/testing/smart-account/InMemorySmartAccountAdapter.ts
619
+ /**
620
+ * Hermetic backend stand-in (PRD #462 / derivation v2): `provision` derives
621
+ * the Safe from the identity email alone via the REAL canonical derivation
622
+ * (`deriveCapxulSafeAddress({ email })` — owner is the pinned bootstrap
623
+ * constant). No signer exists at provision; `claim` installs the signer and
624
+ * marks the row deployed, mirroring the backend's one-userOp claim lane.
625
+ */
626
+ var InMemorySmartAccountAdapter = class {
627
+ #accounts = /* @__PURE__ */ new Map();
628
+ #clock;
629
+ #identity;
630
+ #failures;
631
+ #provisionMutex = Semaphore.makeUnsafe(1);
632
+ constructor(deps) {
633
+ this.#clock = deps.clock;
634
+ this.#identity = deps.identity;
635
+ this.#failures = deps.failures;
636
+ }
637
+ loadByAuthUserId(authUserId) {
638
+ const scriptedFailure = this.#failures?.loadByAuthUserId;
639
+ if (scriptedFailure !== void 0) return fail("loadByAuthUserId", scriptedFailure);
640
+ return Effect.sync(() => cloneSmartAccountOrNull(this.#accounts.get(authUserId) ?? null));
641
+ }
642
+ loadBySmartAccountAddress(address) {
643
+ const scriptedFailure = this.#failures?.loadBySmartAccountAddress;
644
+ if (scriptedFailure !== void 0) return fail("loadBySmartAccountAddress", scriptedFailure);
645
+ return Effect.sync(() => {
646
+ const needle = toAddress(address);
647
+ for (const account of this.#accounts.values()) if (account.smartAccountAddress === needle) return cloneSmartAccount(account);
648
+ return null;
649
+ });
650
+ }
651
+ provision(input) {
652
+ const scriptedFailure = this.#failures?.provision;
653
+ if (scriptedFailure !== void 0) return fail("provision", scriptedFailure);
654
+ const self = this;
655
+ return Effect.gen(function* () {
656
+ if (!isSupportedSmartAccountChain(input.chainId)) return yield* fail("provision", Errors.invalidInput("chainId", `unsupported chainId ${String(input.chainId)}`));
657
+ const identity = yield* self.#identity.loadByAuthUserId(input.authUserId).pipe(Effect.mapError((error) => smartAccountErrorFromCapxul("provision.identity", error.publicError, error)));
658
+ if (identity === null) return yield* fail("provision", Errors.profileNotFound(String(input.authUserId)));
659
+ const existing = self.#accounts.get(input.authUserId);
660
+ if (existing !== void 0) return cloneSmartAccount(existing);
661
+ const now = yield* self.#now("provision.clock");
662
+ const account = {
663
+ authUserId: input.authUserId,
664
+ signerAddress: null,
665
+ smartAccountAddress: toAddress(deriveCapxulSafeAddress({ email: identity.email })),
666
+ chainId: input.chainId,
667
+ deployedAt: null,
668
+ claimedAt: null,
669
+ createdAt: now
670
+ };
671
+ self.#accounts.set(input.authUserId, cloneSmartAccount(account));
672
+ return cloneSmartAccount(account);
673
+ }).pipe(self.#provisionMutex.withPermits(1));
674
+ }
675
+ promoteDeployedAt(authUserId, deployedAt) {
676
+ const existing = this.#accounts.get(authUserId);
677
+ if (existing === void 0) return;
678
+ if (existing.deployedAt !== null) return;
679
+ this.#accounts.set(authUserId, {
680
+ ...existing,
681
+ deployedAt
682
+ });
683
+ }
684
+ confirmDeployment(input) {
685
+ const self = this;
686
+ return Effect.gen(function* () {
687
+ const existing = self.#accounts.get(input.authUserId);
688
+ if (existing === void 0) return yield* fail("confirmDeployment", Errors.profileNotFound(String(input.authUserId)));
689
+ if (existing.smartAccountAddress !== toAddress(input.safeAddress)) return yield* fail("confirmDeployment", Errors.invalidInput("safeAddress", "does not match backend smart-account row"));
690
+ if (existing.deployedAt !== null) return cloneSmartAccount(existing);
691
+ const now = yield* self.#now("confirmDeployment.clock");
692
+ const updated = {
693
+ ...existing,
694
+ deployedAt: now
695
+ };
696
+ self.#accounts.set(input.authUserId, cloneSmartAccount(updated));
697
+ return cloneSmartAccount(updated);
698
+ }).pipe(self.#provisionMutex.withPermits(1));
699
+ }
700
+ claim(input) {
701
+ const scriptedFailure = this.#failures?.claim;
702
+ if (scriptedFailure !== void 0) return fail("claim", scriptedFailure);
703
+ const self = this;
704
+ return Effect.gen(function* () {
705
+ const existing = self.#accounts.get(input.authUserId);
706
+ if (existing === void 0) return yield* fail("claim", Errors.invalidInput("account", "no provisioned smart account for this user"));
707
+ if (existing.chainId !== input.chainId) return yield* fail("claim", Errors.invalidInput("chainId", "does not match backend smart-account row"));
708
+ const newOwner = toAddress(input.signerAddress);
709
+ if (existing.claimedAt !== null) {
710
+ if (existing.signerAddress !== newOwner) return yield* fail("claim", Errors.invalidInput("signerAddress", "account already claimed by a different signer"));
711
+ return cloneSmartAccount(existing);
712
+ }
713
+ const now = yield* self.#now("claim.clock");
714
+ const updated = {
715
+ ...existing,
716
+ signerAddress: newOwner,
717
+ claimedAt: now,
718
+ deployedAt: existing.deployedAt ?? now
719
+ };
720
+ self.#accounts.set(input.authUserId, cloneSmartAccount(updated));
721
+ return cloneSmartAccount(updated);
722
+ }).pipe(self.#provisionMutex.withPermits(1));
723
+ }
724
+ #now(operation) {
725
+ return this.#clock.now.pipe(Effect.mapError((cause) => smartAccountErrorFromCapxul(operation, Errors.providerError("clock", "now", cause), cause)));
726
+ }
727
+ };
728
+ function fail(operation, error) {
729
+ return Effect.fail(smartAccountErrorFromCapxul(operation, error));
730
+ }
731
+ function cloneSmartAccount(account) {
732
+ return {
733
+ authUserId: account.authUserId,
734
+ signerAddress: account.signerAddress,
735
+ smartAccountAddress: account.smartAccountAddress,
736
+ chainId: account.chainId,
737
+ deployedAt: account.deployedAt,
738
+ claimedAt: account.claimedAt,
739
+ createdAt: account.createdAt
740
+ };
741
+ }
742
+ function cloneSmartAccountOrNull(account) {
743
+ return account === null ? null : cloneSmartAccount(account);
744
+ }
745
+ //#endregion
746
+ //#region src/testing/sub-account/InMemorySubAccountAdapter.ts
747
+ var InMemorySubAccountAdapter = class {
748
+ #rows = /* @__PURE__ */ new Map();
749
+ #failures;
750
+ #balanceOfRaw;
751
+ #created;
752
+ constructor(deps = {}) {
753
+ this.#failures = deps.failures ?? {};
754
+ this.#balanceOfRaw = deps.balanceOfRaw ?? "0";
755
+ for (const row of deps.seed ?? []) this.#rows.set(row.id, row);
756
+ this.#created = this.#rows.size;
757
+ }
758
+ create(input) {
759
+ const failure = this.#failures.create;
760
+ if (failure !== void 0) return Effect.fail(subAccountErrorFromCapxul("create", failure));
761
+ this.#created += 1;
762
+ const id = toSubAccountId(`subaccount_test${String(this.#created)}`);
763
+ const row = {
764
+ id,
765
+ accountId: input.accountId,
766
+ name: input.name,
767
+ balance: {
768
+ currency: toCurrencyCode("USD"),
769
+ value: "0",
770
+ decimals: 6
771
+ },
772
+ createdAt: toEpochMs(Date.now())
773
+ };
774
+ this.#rows.set(id, row);
775
+ return Effect.succeed(row);
776
+ }
777
+ get(input) {
778
+ return Effect.succeed(this.#rows.get(input.subAccountId) ?? null);
779
+ }
780
+ list(input) {
781
+ const accountId = input.accountId;
782
+ return Effect.succeed([...this.#rows.values()].filter((row) => row.accountId === accountId));
783
+ }
784
+ rename(input) {
785
+ const existing = this.#rows.get(input.subAccountId);
786
+ if (existing === void 0) return Effect.fail(subAccountErrorFromCapxul("rename", Errors.accountNotFound(input.subAccountId)));
787
+ const next = {
788
+ ...existing,
789
+ name: input.name
790
+ };
791
+ this.#rows.set(input.subAccountId, next);
792
+ return Effect.succeed(next);
793
+ }
794
+ delete(input) {
795
+ const existing = this.#rows.get(input.subAccountId);
796
+ if (existing === void 0) return Effect.fail(subAccountErrorFromCapxul("delete", Errors.accountNotFound(input.subAccountId)));
797
+ if (Number(existing.balance.value) !== 0) return Effect.fail(subAccountErrorFromCapxul("delete", Errors.invalidInput("subAccountId", "delete requires zero balance")));
798
+ this.#rows.delete(input.subAccountId);
799
+ return Effect.succeed(void 0);
800
+ }
801
+ transfer(input) {
802
+ const decimals = input.amount.decimals;
803
+ const invalid = (field, reason) => Effect.fail(subAccountErrorFromCapxul("transfer", Errors.invalidInput(field, reason)));
804
+ let rawAmount;
805
+ try {
806
+ rawAmount = BigInt(toWei(input.amount));
807
+ } catch {
808
+ return invalid("amount", "must be a decimal money string");
809
+ }
810
+ if (rawAmount <= 0n) return invalid("amount", "must be positive");
811
+ if (input.from === input.to) return invalid("transfer", "from and to must differ");
812
+ const fromRow = input.from === "main" ? null : this.#rows.get(input.from) ?? "missing";
813
+ if (fromRow === "missing") return invalid("subAccountId", "unknown sub-account");
814
+ const toRow = input.to === "main" ? null : this.#rows.get(input.to) ?? "missing";
815
+ if (toRow === "missing") return invalid("subAccountId", "unknown sub-account");
816
+ const safeRaw = BigInt(this.#balanceOfRaw);
817
+ const subTotalRaw = this.#sumSubAccountRaws(decimals);
818
+ if (input.from === "main") {
819
+ const availableRaw = safeRaw - subTotalRaw;
820
+ if (availableRaw < rawAmount) return Effect.fail(subAccountErrorFromCapxul("transfer", Errors.insufficientBalance("main", maxRaw(availableRaw), rawAmount.toString(10))));
821
+ } else if (fromRow !== null) {
822
+ const fromRaw = BigInt(toWei({
823
+ value: fromRow.balance.value,
824
+ decimals
825
+ }));
826
+ if (fromRaw < rawAmount) return Effect.fail(subAccountErrorFromCapxul("transfer", Errors.insufficientBalance(fromRow.name, fromRaw.toString(10), rawAmount.toString(10))));
827
+ }
828
+ let nextFrom = null;
829
+ let nextTo = null;
830
+ if (fromRow !== null) {
831
+ const fromRaw = BigInt(toWei({
832
+ value: fromRow.balance.value,
833
+ decimals
834
+ }));
835
+ nextFrom = this.#patchBalance(fromRow, (fromRaw - rawAmount).toString(10), decimals);
836
+ }
837
+ if (toRow !== null) {
838
+ const toRaw = BigInt(toWei({
839
+ value: toRow.balance.value,
840
+ decimals
841
+ }));
842
+ nextTo = this.#patchBalance(toRow, (toRaw + rawAmount).toString(10), decimals);
843
+ }
844
+ const available = fromWei(maxRaw(safeRaw - this.#sumSubAccountRaws(decimals)), decimals, input.amount.currency);
845
+ return Effect.succeed({
846
+ available,
847
+ from: nextFrom,
848
+ to: nextTo
849
+ });
850
+ }
851
+ #sumSubAccountRaws(decimals) {
852
+ let total = 0n;
853
+ for (const row of this.#rows.values()) total += BigInt(toWei({
854
+ value: row.balance.value,
855
+ decimals
856
+ }));
857
+ return total;
858
+ }
859
+ #patchBalance(row, rawBalance, decimals) {
860
+ const balance = fromWei(rawBalance, decimals, row.balance.currency);
861
+ const next = {
862
+ ...row,
863
+ balance
864
+ };
865
+ this.#rows.set(row.id, next);
866
+ return next;
867
+ }
868
+ };
869
+ /** Floor a (possibly negative) base-unit raw to a non-negative base-10 string. */
870
+ function maxRaw(raw) {
871
+ return (raw < 0n ? 0n : raw).toString(10);
872
+ }
873
+ //#endregion
874
+ //#region src/testing/telemetry/RecordingTelemetryAdapter.ts
875
+ var RecordingTelemetryAdapter = class {
876
+ #events = [];
877
+ #operations = [];
878
+ #rawMode;
879
+ constructor(options = {}) {
880
+ this.#rawMode = options.rawMode === true;
881
+ }
882
+ get events() {
883
+ return this.#events.map(cloneEvent);
884
+ }
885
+ get operations() {
886
+ return this.#operations.map(cloneOperation);
887
+ }
888
+ emit(event) {
889
+ return Effect.sync(() => {
890
+ const recorded = redactTelemetryEvent(event, { rawMode: this.#rawMode });
891
+ this.#events.push(recorded);
892
+ this.#operations.push({
893
+ type: "emit",
894
+ event: cloneEvent(recorded)
895
+ });
896
+ });
897
+ }
898
+ identify(input) {
899
+ return Effect.sync(() => {
900
+ this.#operations.push({
901
+ type: "identify",
902
+ input: cloneIdentifyInput(input)
903
+ });
904
+ });
905
+ }
906
+ group(input) {
907
+ return Effect.sync(() => {
908
+ this.#operations.push({
909
+ type: "group",
910
+ input: cloneGroupInput(input)
911
+ });
912
+ });
913
+ }
914
+ reset() {
915
+ return Effect.sync(() => {
916
+ this.#operations.push({ type: "reset" });
917
+ });
918
+ }
919
+ clear() {
920
+ this.#events.length = 0;
921
+ this.#operations.length = 0;
922
+ }
923
+ };
924
+ function cloneEvent(event) {
925
+ return event.props === void 0 ? { name: event.name } : {
926
+ name: event.name,
927
+ props: cloneProps(event.props)
928
+ };
929
+ }
930
+ function cloneOperation(operation) {
931
+ switch (operation.type) {
932
+ case "emit": return {
933
+ type: "emit",
934
+ event: cloneEvent(operation.event)
935
+ };
936
+ case "identify": return {
937
+ type: "identify",
938
+ input: cloneIdentifyInput(operation.input)
939
+ };
940
+ case "group": return {
941
+ type: "group",
942
+ input: cloneGroupInput(operation.input)
943
+ };
944
+ case "reset": return { type: "reset" };
945
+ }
946
+ }
947
+ function cloneIdentifyInput(input) {
948
+ return {
949
+ distinctId: input.distinctId,
950
+ ...input.anonDistinctId === void 0 ? {} : { anonDistinctId: input.anonDistinctId },
951
+ ...input.traits === void 0 ? {} : { traits: cloneProps(input.traits) },
952
+ ...input.properties === void 0 ? {} : { properties: cloneProps(input.properties) }
953
+ };
954
+ }
955
+ function cloneGroupInput(input) {
956
+ return input.properties === void 0 ? {
957
+ groupType: input.groupType,
958
+ groupKey: input.groupKey
959
+ } : {
960
+ groupType: input.groupType,
961
+ groupKey: input.groupKey,
962
+ properties: cloneProps(input.properties)
963
+ };
964
+ }
965
+ function cloneProps(props) {
966
+ const cloned = {};
967
+ for (const [key, value] of Object.entries(props)) cloned[key] = cloneTelemetryValue(value);
968
+ return cloned;
969
+ }
970
+ function cloneTelemetryValue(value) {
971
+ if (Array.isArray(value)) return value.map(cloneTelemetryValue);
972
+ if (value === null || typeof value !== "object") return value;
973
+ if (Object.getPrototypeOf(value) !== Object.prototype) return value;
974
+ const cloned = {};
975
+ for (const [key, nested] of Object.entries(value)) cloned[key] = cloneTelemetryValue(nested);
976
+ return cloned;
977
+ }
978
+ //#endregion
979
+ //#region src/testing/index.ts
980
+ const TEST_BOOTSTRAP = {
981
+ applicationId: toAppId("app_01HX0000000000000000000000"),
982
+ chainId: toChainId(84532),
983
+ sessionToken: toSessionToken("bootstrap-test-token"),
984
+ issuedAt: toEpochMs(0),
985
+ expiresIn: toDurationMs(6e4),
986
+ authBaseUrl: "https://auth.example.test",
987
+ convexUrl: "https://convex.example.test",
988
+ siteBaseUrl: "https://example.test",
989
+ openfortPublishableKey: "pk_test_openfort_fixture",
990
+ shieldPublishableKey: "shield_pk_test_fixture"
991
+ };
992
+ /**
993
+ * Assemble the real public client over deterministic in-memory ports.
994
+ * Unconfigured backend calls fail locally; no adapter owns a network exit.
995
+ */
996
+ function createCapxulTestClient(options = {}) {
997
+ const clock = new ManualClockAdapter({});
998
+ const authCache = new InMemoryAuthCacheAdapter();
999
+ const authClient = new InMemoryAuthClientAdapter({
1000
+ authCache,
1001
+ clock
1002
+ });
1003
+ const identity = new InMemoryIdentityAdapter({ clock });
1004
+ const telemetry = new RecordingTelemetryAdapter();
1005
+ const client = assembleCapxulClient({
1006
+ ports: {
1007
+ authClient: authClientPortFromPromiseAdapter(authClient),
1008
+ authCache,
1009
+ identity,
1010
+ smartAccount: new InMemorySmartAccountAdapter({
1011
+ clock,
1012
+ identity
1013
+ }),
1014
+ accountRead: new InMemoryAccountReadAdapter({ rawBalance: "0" }),
1015
+ subAccount: new InMemorySubAccountAdapter(),
1016
+ bootstrap: new BootstrapStubAdapter({ script: { resolutions: new Map([[`${toPublishableKey("cap_pk_test_0123456789ABCDEFGHJKMNPQRSTVWXYZ")}::${toAllowedOrigin("https://example.test")}`, TEST_BOOTSTRAP]]) } }),
1017
+ clock,
1018
+ telemetry,
1019
+ convexCall: new ConvexCallStubAdapter({ script: {} })
1020
+ },
1021
+ bootstrap: TEST_BOOTSTRAP,
1022
+ authCache,
1023
+ requirement: options.requirement ?? "none"
1024
+ });
1025
+ const transitions = [];
1026
+ const unsubscribe = client._internal.identity.subscribeTransitions((record) => {
1027
+ transitions.push(record);
1028
+ });
1029
+ let closed = false;
1030
+ return {
1031
+ client,
1032
+ clock: Object.freeze({ advance: (ms) => clock.advance(ms) }),
1033
+ observation: {
1034
+ emit: (event) => Effect.runPromise(telemetry.emit(event)),
1035
+ identify: (input) => Effect.runPromise(telemetry.identify(input)),
1036
+ group: (input) => Effect.runPromise(telemetry.group(input)),
1037
+ reset: () => Effect.runPromise(telemetry.reset())
1038
+ },
1039
+ get transitions() {
1040
+ return transitions.slice();
1041
+ },
1042
+ get observations() {
1043
+ return telemetry.operations;
1044
+ },
1045
+ peekOtp: (email) => authClient.peekOtpForTesting(toEmail(email)),
1046
+ seedIdentity: async (input) => {
1047
+ const authUserId = toAuthUserId(input.authUserId);
1048
+ const email = toEmail(input.email);
1049
+ await Effect.runPromise(identity.create({
1050
+ authUserId,
1051
+ email,
1052
+ ...input.displayName === void 0 ? {} : { displayName: input.displayName },
1053
+ ...input.country === void 0 ? {} : { country: toCountryCode(input.country) }
1054
+ }));
1055
+ authClient.bindAuthUserIdForTesting(email, authUserId);
1056
+ },
1057
+ close: async () => {
1058
+ if (closed) return;
1059
+ closed = true;
1060
+ unsubscribe();
1061
+ await client._internal.close?.();
1062
+ }
1063
+ };
1064
+ }
1065
+ //#endregion
1066
+ export { createCapxulTestClient };
1067
+
1068
+ //# sourceMappingURL=index.mjs.map