@capxul/sdk 4.20.0-beta.4 → 4.20.0-beta.40

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,7 +1,6 @@
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-BMfmzwHq.mjs";
2
- import { B as toAuthUserId, F as toAccountId, G as toDurationMs, H as toChainId, I as toAddress, J as toEpochSeconds, K as toEmail, L as toAllowedOrigin, R as toAppId, U as toCountryCode, X as toJwtToken, Y as toHandle, Z as toKycTier, ct as validateHandle, d as authClientPortFromPromiseAdapter, ft as CapxulError, it as toSessionToken, mt as Errors, n as readClockNow, nt as toPublishableKey, q as toEpochMs, r as InMemoryAuthCacheAdapter, y as deriveCapxulSafeAddress } from "../OAuthBearerAuthClient-DWkofxdZ.mjs";
3
- import { keccak256 } from "viem";
4
- import { Effect, Result, Semaphore } from "effect";
1
+ import { $r as toAccountId, Bi as InMemoryAuthCacheAdapter, Ci as toAddress, Ei as absurd, Fi as authClientPortFromPromiseAdapter, Gi as toEpochMs, Gn as ClockError, Ji as toEmail, Jn as readClockNow, Ki as toEpochSeconds, Kn as ClockPortTag, Lt as CONFIGURED_MONEY_ASSETS, M as wireChainId, Mi as toAllowedOrigin, Ni as toAppId, O as convexCallErrorFromCapxul, Ot as deriveCapxulSafeAddress, Pi as toPublishableKey, Q as assetAmountFromRaw, Sn as toHandle, Ti as toWeiAmount, Wi as toDurationMs, Xi as toSessionToken, Yi as toJwtToken, Zi as Errors, _ as accountFromAccountPositions, f as smartAccountErrorFromCapxul, na as CapxulError, nr as redactTelemetryEvent, qi as toAuthUserId, qn as clockFailure, rt as toRaw, vn as toCountryCode, w as accountReadErrorFromCapxul, wi as toChainId, wn as validateHandle, yn as toKycTier, zr as toOrgId } from "../access-6VLRaqRP.mjs";
2
+ import { a as cloneTelemetryEvent, c as identityErrorFromCapxul, l as bootstrapErrorFromCapxul, n as createCapxulClientWithSignerControls, o as cloneTelemetryProps, r as assembleCapxulClient, s as IdentityPortTag } from "../production-Dy8n4ZBY.mjs";
3
+ import { Effect, Layer, Result, Semaphore } from "effect";
5
4
  import { getFunctionName } from "convex/server";
6
5
  //#region src/testing/production-client.ts
7
6
  /** Create the one production client with per-client controls at its signing boundaries. */
@@ -100,7 +99,123 @@ function armSignerFaults(plan = {}) {
100
99
  };
101
100
  }
102
101
  //#endregion
102
+ //#region src/testing/organization-setup.ts
103
+ /** Exact backend setup shape for SDK tests. */
104
+ function organizationSetupFixture(orgId, status = "ready", overrides = {}) {
105
+ return {
106
+ orgId: toOrgId(orgId),
107
+ status,
108
+ completedAt: status === "ready" || status === "rechecking" ? 100 : null,
109
+ lastCheckedAt: null,
110
+ nextCheckAt: null,
111
+ issue: null,
112
+ lastSendIssue: null,
113
+ recovery: status === "awaitingAuthorization" ? "authorize" : "none",
114
+ ...overrides
115
+ };
116
+ }
117
+ /** In-memory setup transport for packed consumer tests. */
118
+ function createTestOrganizationSetup() {
119
+ const rows = /* @__PURE__ */ new Map();
120
+ const handles = /* @__PURE__ */ new Map();
121
+ const listeners = /* @__PURE__ */ new Map();
122
+ const set = (setup) => {
123
+ rows.set(String(setup.orgId), setup);
124
+ for (const listener of listeners.get(String(setup.orgId)) ?? []) listener({
125
+ status: "value",
126
+ value: setup
127
+ });
128
+ };
129
+ const get = async (orgId) => {
130
+ const value = rows.get(String(orgId));
131
+ return value === void 0 ? {
132
+ ok: false,
133
+ error: Errors.invalidInput("orgId", "Organization setup was not found")
134
+ } : {
135
+ ok: true,
136
+ value
137
+ };
138
+ };
139
+ return {
140
+ set,
141
+ ops: {
142
+ listMine: async (input = {}) => {
143
+ const all = [...rows.values()].filter((row) => row.completedAt === null);
144
+ const offset = input.cursor === void 0 ? 0 : Number(input.cursor);
145
+ const limit = input.limit ?? 50;
146
+ if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(limit) || limit < 1 || limit > 50) return {
147
+ ok: false,
148
+ error: Errors.invalidInput("organizationSetupPage", "Invalid cursor or limit")
149
+ };
150
+ const page = all.slice(offset, offset + limit);
151
+ const next = offset + page.length;
152
+ return {
153
+ ok: true,
154
+ value: {
155
+ items: page,
156
+ nextCursor: next < all.length ? String(next) : null
157
+ }
158
+ };
159
+ },
160
+ begin: async (draft) => {
161
+ const orgId = handles.get(draft.handle) ?? toOrgId(`org_test_${draft.handle}`);
162
+ handles.set(draft.handle, orgId);
163
+ const setup = rows.get(String(orgId)) ?? organizationSetupFixture(orgId, "awaitingAuthorization");
164
+ set(setup);
165
+ return {
166
+ ok: true,
167
+ value: {
168
+ orgId,
169
+ setup
170
+ }
171
+ };
172
+ },
173
+ get,
174
+ resume: get,
175
+ authorize: async (orgId) => {
176
+ const current = await get(orgId);
177
+ if (!current.ok || current.value.status !== "awaitingAuthorization") return current;
178
+ const setup = organizationSetupFixture(orgId, "processing", { nextCheckAt: 1 });
179
+ set(setup);
180
+ return {
181
+ ok: true,
182
+ value: setup
183
+ };
184
+ },
185
+ subscribe: async (orgId, listener) => {
186
+ const key = String(orgId);
187
+ const group = listeners.get(key) ?? /* @__PURE__ */ new Set();
188
+ listeners.set(key, group);
189
+ group.add(listener);
190
+ listener({ status: "loading" });
191
+ const current = rows.get(key);
192
+ if (current !== void 0) listener({
193
+ status: "value",
194
+ value: current
195
+ });
196
+ let active = true;
197
+ return {
198
+ ok: true,
199
+ value: () => {
200
+ if (!active) return;
201
+ active = false;
202
+ group.delete(listener);
203
+ }
204
+ };
205
+ },
206
+ invalidateSubscriptions: () => {
207
+ for (const group of listeners.values()) group.clear();
208
+ }
209
+ }
210
+ };
211
+ }
212
+ //#endregion
103
213
  //#region src/testing/account/InMemoryAccountReadAdapter.ts
214
+ function fixtureAsset(symbol) {
215
+ const asset = CONFIGURED_MONEY_ASSETS.find((row) => row.symbol === symbol);
216
+ if (asset === void 0) throw new Error(`no configured settlement asset ${symbol}`);
217
+ return asset;
218
+ }
104
219
  var InMemoryAccountReadAdapter = class {
105
220
  #deps;
106
221
  #rawBalance;
@@ -108,26 +223,29 @@ var InMemoryAccountReadAdapter = class {
108
223
  this.#deps = deps;
109
224
  this.#rawBalance = deps.rawBalance ?? "0";
110
225
  }
226
+ #asset() {
227
+ return fixtureAsset(this.#deps.assetSymbol ?? "USDC");
228
+ }
111
229
  readBalance(_input) {
112
230
  const scriptedFailure = this.#deps.failures?.readBalance;
113
231
  if (scriptedFailure !== void 0) return Effect.fail(accountReadErrorFromCapxul("readBalance", scriptedFailure));
114
232
  if (this.#deps.account !== void 0) return Effect.succeed(cloneAccount(this.#deps.account));
115
- const decimals = this.#deps.decimals ?? 6;
116
- const currency = this.#deps.currency ?? "USD";
117
- const money = fromWei(this.#rawBalance, decimals, currency);
118
- return Effect.succeed({
119
- id: toAccountId(this.#deps.accountId ?? "account_01TEST000000000000000000"),
120
- balance: money,
121
- available: money
122
- });
233
+ const asset = this.#asset();
234
+ const balance = assetAmountFromRaw(toWeiAmount(this.#rawBalance), asset).value;
235
+ return Effect.succeed(accountFromAccountPositions(toAccountId(this.#deps.accountId ?? "account_01TEST000000000000000000"), [{
236
+ asset: asset.assetId,
237
+ symbol: asset.symbol,
238
+ decimals: asset.decimals,
239
+ balance,
240
+ available: balance
241
+ }]));
123
242
  }
124
243
  fundFromFaucet(input) {
125
244
  const scriptedFailure = this.#deps.failures?.fundFromFaucet;
126
245
  if (scriptedFailure !== void 0) return Effect.fail(accountReadErrorFromCapxul("fundFromFaucet", scriptedFailure));
127
- const expectedDecimals = this.#deps.decimals ?? 6;
128
- const expectedCurrency = this.#deps.currency ?? "USD";
129
- if (input.amount.decimals !== expectedDecimals || String(input.amount.currency) !== expectedCurrency) return Effect.fail(accountReadErrorFromCapxul("fundFromFaucet", Errors.invalidInput("amount", "decimals/currency must match adapter config")));
130
- const minted = BigInt(toWei(input.amount));
246
+ const asset = this.#asset();
247
+ if (input.amount.asset !== asset.assetId) return Effect.fail(accountReadErrorFromCapxul("fundFromFaucet", Errors.invalidInput("amount.asset", "must match adapter config")));
248
+ const minted = toRaw(input.amount, asset);
131
249
  this.#rawBalance = (BigInt(this.#rawBalance) + minted).toString();
132
250
  return Effect.succeed({ txHash: "0x" + "f".repeat(64) });
133
251
  }
@@ -135,36 +253,68 @@ var InMemoryAccountReadAdapter = class {
135
253
  function cloneAccount(account) {
136
254
  return {
137
255
  id: account.id,
138
- balance: { ...account.balance },
139
- available: { ...account.available }
256
+ balances: account.balances.map((position) => ({ ...position })),
257
+ balance: account.balance === null ? null : { ...account.balance },
258
+ available: account.available === null ? null : { ...account.available }
140
259
  };
141
260
  }
142
261
  //#endregion
143
- //#region src/testing/clock/ManualClockAdapter.ts
144
- /**
145
- * Event-discipline test clock. Time moves only when a release primitive is
146
- * invoked. `tickOne()` releases EXACTLY ONE pending sleep (the earliest
147
- * deadline, FIFO at ties) and snaps `now()` to that deadline. `advance(ms)`
148
- * composes repeated `tickOne()` calls over the resulting window — used to
149
- * keep the cross-adapter conformance harness uniform with the distance model
150
- * of `VirtualClockAdapter`. See `packages/sdk/docs/architecture.md` and the
151
- * slice spec `docs/slices/clock-adapters.md`.
152
- */
153
- var ManualClockAdapter = class ManualClockAdapter {
262
+ //#region src/testing/clock/sleep-queue.ts
263
+ var SleepQueueClock = class {
154
264
  currentTime = 0;
155
- queue = [];
265
+ pending = [];
156
266
  seqCounter = 0;
157
- constructor(deps) {}
158
267
  now = Effect.sync(() => toEpochMs(this.currentTime));
159
268
  sleep(duration) {
160
269
  return Effect.promise(() => new Promise((resolve) => {
161
- this.queue.push({
270
+ this.enqueue({
162
271
  deadline: this.currentTime + duration,
163
272
  resolve,
164
273
  seq: this.seqCounter++
165
274
  });
166
275
  }));
167
276
  }
277
+ /** Move time forward by `ms`, releasing every sleep that falls due. */
278
+ advance(ms) {
279
+ return Effect.runPromise(this.advanceEffect(toDurationMs(ms)));
280
+ }
281
+ /** This clock as the cross-adapter conformance harness drives it. */
282
+ underTest() {
283
+ return {
284
+ clock: this,
285
+ advance: (ms) => this.advance(ms)
286
+ };
287
+ }
288
+ enqueue(entry) {
289
+ this.pending.push(entry);
290
+ }
291
+ };
292
+ /** The clock in context when it is a `kind`; otherwise a ClockError naming the layer `helper` needs. */
293
+ function requireClock(kind, helper, layer) {
294
+ return Effect.gen(function* () {
295
+ const clock = yield* ClockPortTag;
296
+ if (clock instanceof kind) return clock;
297
+ return yield* new ClockError({
298
+ operation: "sleep",
299
+ cause: /* @__PURE__ */ new Error(`${helper} requires ${layer}`)
300
+ });
301
+ });
302
+ }
303
+ //#endregion
304
+ //#region src/testing/clock/ManualClockAdapter.ts
305
+ /**
306
+ * Event-discipline test clock. Time moves only when a release primitive is
307
+ * invoked. `tickOne()` releases EXACTLY ONE pending sleep (the earliest
308
+ * deadline, FIFO at ties) and snaps `now()` to that deadline. `advance(ms)`
309
+ * composes repeated `tickOne()` calls over the resulting window — used to
310
+ * keep the cross-adapter conformance harness uniform with the distance model
311
+ * of `VirtualClockAdapter`. See `packages/sdk/docs/architecture.md` and the
312
+ * slice spec `docs/slices/clock-adapters.md`.
313
+ */
314
+ var ManualClockAdapter = class ManualClockAdapter extends SleepQueueClock {
315
+ constructor(deps) {
316
+ super();
317
+ }
168
318
  /**
169
319
  * Release exactly one pending sleep — the earliest deadline, FIFO at ties.
170
320
  * Snaps `now()` forward to that sleep's deadline. No-op if the queue is
@@ -174,39 +324,33 @@ var ManualClockAdapter = class ManualClockAdapter {
174
324
  return Effect.runPromise(this.tickOneEffect());
175
325
  }
176
326
  tickOneEffect() {
177
- const self = this;
178
- return Effect.gen(function* () {
179
- if (self.queue.length === 0) return;
180
- const nextIndex = self.findEarliestIndex();
181
- const next = self.queue.splice(nextIndex, 1)[0];
182
- if (next.deadline > self.currentTime) self.currentTime = next.deadline;
327
+ return Effect.gen({ self: this }, function* () {
328
+ if (this.pending.length === 0) return;
329
+ const next = this.pending.splice(this.findEarliestIndex(), 1)[0];
330
+ if (next.deadline > this.currentTime) this.currentTime = next.deadline;
183
331
  next.resolve();
184
332
  yield* Effect.yieldNow;
185
333
  });
186
334
  }
187
335
  /**
188
- * Cross-adapter harness affordance. Releases every sleep whose deadline
189
- * falls inside `[currentTime, currentTime + ms]` via repeated `tickOne()`,
190
- * then snaps `now()` to `currentTime + ms`. The composition is the seam
191
- * that lets the harness drive Manual and Virtual identically.
336
+ * Releases every sleep whose deadline falls inside
337
+ * `[currentTime, currentTime + ms]` via repeated `tickOne()`, then snaps
338
+ * `now()` to `currentTime + ms`. The composition is the seam that lets the
339
+ * harness drive Manual and Virtual identically.
192
340
  */
193
- advance(ms) {
194
- return Effect.runPromise(this.advanceEffect(toDurationMs(ms)));
195
- }
196
341
  advanceEffect(duration) {
197
- const self = this;
198
- return Effect.gen(function* () {
342
+ return Effect.gen({ self: this }, function* () {
199
343
  yield* Effect.yieldNow;
200
- const target = self.currentTime + duration;
201
- while (self.queue.length > 0 && self.earliestDeadline() <= target) yield* self.tickOneEffect();
202
- self.currentTime = target;
344
+ const target = this.currentTime + duration;
345
+ while (this.pending.length > 0 && this.earliestDeadline() <= target) yield* this.tickOneEffect();
346
+ this.currentTime = target;
203
347
  });
204
348
  }
205
349
  findEarliestIndex() {
206
350
  let bestIndex = 0;
207
- let best = this.queue[0];
208
- for (let i = 1; i < this.queue.length; i++) {
209
- const candidate = this.queue[i];
351
+ let best = this.pending[0];
352
+ for (let i = 1; i < this.pending.length; i++) {
353
+ const candidate = this.pending[i];
210
354
  if (candidate.deadline < best.deadline || candidate.deadline === best.deadline && candidate.seq < best.seq) {
211
355
  best = candidate;
212
356
  bestIndex = i;
@@ -215,21 +359,14 @@ var ManualClockAdapter = class ManualClockAdapter {
215
359
  return bestIndex;
216
360
  }
217
361
  earliestDeadline() {
218
- return this.queue[this.findEarliestIndex()].deadline;
362
+ return this.pending[this.findEarliestIndex()].deadline;
219
363
  }
220
364
  static forConformance() {
221
- return new ManualClockUnderTest(new ManualClockAdapter({}));
222
- }
223
- };
224
- var ManualClockUnderTest = class {
225
- clock;
226
- constructor(clock) {
227
- this.clock = clock;
228
- }
229
- advance(ms) {
230
- return this.clock.advance(ms);
365
+ return new ManualClockAdapter({}).underTest();
231
366
  }
232
367
  };
368
+ Layer.sync(ClockPortTag, () => new ManualClockAdapter({}));
369
+ requireClock(ManualClockAdapter, "tickManualClockOnce", "ManualClockLayer").pipe(Effect.flatMap((clock) => clock.tickOneEffect()));
233
370
  //#endregion
234
371
  //#region src/testing/auth-client/InMemoryAuthClientAdapter.ts
235
372
  const DEFAULT_OTP_TTL_MS = toDurationMs(3e5);
@@ -471,7 +608,12 @@ async function runAuthCacheEffect(effect, operation) {
471
608
  const result = await Effect.runPromise(Effect.result(effect));
472
609
  if (Result.isFailure(result)) return {
473
610
  ok: false,
474
- error: Errors.providerError("auth-cache", operation, result.failure)
611
+ error: Errors.providerUnavailable({
612
+ provider: "auth-cache",
613
+ operation,
614
+ mode: "unknown",
615
+ cause: result.failure
616
+ })
475
617
  };
476
618
  return {
477
619
  ok: true,
@@ -509,6 +651,47 @@ function fail$2(kind, error) {
509
651
  return Effect.fail(bootstrapErrorFromCapxul(kind, error));
510
652
  }
511
653
  //#endregion
654
+ //#region src/testing/convex-call/subscribers.ts
655
+ /**
656
+ * Register `subscriber` under `path` and return its unsubscribe, which removes
657
+ * it once (a second call does nothing) and drops a path left with no one.
658
+ */
659
+ function subscribeAt(subscribers, path, subscriber) {
660
+ let subs = subscribers.get(path);
661
+ if (subs === void 0) {
662
+ subs = /* @__PURE__ */ new Set();
663
+ subscribers.set(path, subs);
664
+ }
665
+ subs.add(subscriber);
666
+ let active = true;
667
+ return () => {
668
+ if (!active) return;
669
+ active = false;
670
+ const live = subscribers.get(path);
671
+ if (live === void 0) return;
672
+ live.delete(subscriber);
673
+ if (live.size === 0) subscribers.delete(path);
674
+ };
675
+ }
676
+ //#endregion
677
+ //#region src/testing/convex-call/wire-reply.ts
678
+ /**
679
+ * The Convex wire, as the in-memory test doubles stand in for it. A scripted or
680
+ * registered value crosses where a real reply would, and the caller's function
681
+ * reference names its type, exactly as `ConvexClient` types a reply it cannot
682
+ * check. These are the only places the doubles make that claim.
683
+ */
684
+ function wireReply(_fn, value) {
685
+ return value;
686
+ }
687
+ /** A scripted snapshot for a subscription on `fn`: only an `ok` value carries the reply type. */
688
+ function wireSnapshot(fn, snapshot) {
689
+ return snapshot.status === "ok" ? {
690
+ status: "ok",
691
+ value: wireReply(fn, snapshot.value)
692
+ } : snapshot;
693
+ }
694
+ //#endregion
512
695
  //#region src/testing/convex-call/ConvexCallStubAdapter.ts
513
696
  var ConvexCallStubAdapter = class {
514
697
  #script;
@@ -517,43 +700,30 @@ var ConvexCallStubAdapter = class {
517
700
  this.#script = deps.script;
518
701
  }
519
702
  query(fn, _args) {
520
- return this.#resolve(getFunctionName(fn));
703
+ return this.#resolve(fn);
521
704
  }
522
705
  mutation(fn, _args) {
523
- return this.#resolve(getFunctionName(fn));
706
+ return this.#resolve(fn);
524
707
  }
525
708
  action(fn, _args) {
526
- return this.#resolve(getFunctionName(fn));
709
+ return this.#resolve(fn);
527
710
  }
528
711
  subscribe(fn, _args, callback) {
529
712
  return Effect.sync(() => {
530
713
  const path = getFunctionName(fn);
531
- const cb = callback;
714
+ const cb = (snapshot) => callback(wireSnapshot(fn, snapshot));
532
715
  cb({ status: "loading" });
533
716
  const sequence = this.#script.subscriptions?.get(path);
534
717
  const subscriber = {
535
718
  callback: cb,
536
719
  cursor: 0
537
720
  };
538
- let subs = this.#subscribers.get(path);
539
- if (subs === void 0) {
540
- subs = /* @__PURE__ */ new Set();
541
- this.#subscribers.set(path, subs);
542
- }
543
- subs.add(subscriber);
721
+ const unsubscribe = subscribeAt(this.#subscribers, path, subscriber);
544
722
  if (sequence !== void 0 && sequence.length > 0) {
545
723
  cb(sequence[0]);
546
724
  subscriber.cursor = 1;
547
725
  }
548
- let active = true;
549
- return () => {
550
- if (!active) return;
551
- active = false;
552
- const liveSubs = this.#subscribers.get(path);
553
- if (liveSubs === void 0) return;
554
- liveSubs.delete(subscriber);
555
- if (liveSubs.size === 0) this.#subscribers.delete(path);
556
- };
726
+ return unsubscribe;
557
727
  });
558
728
  }
559
729
  advanceSubscription(path) {
@@ -568,17 +738,26 @@ var ConvexCallStubAdapter = class {
568
738
  sub.callback(next);
569
739
  }
570
740
  }
571
- #resolve(path) {
741
+ #resolve(fn) {
742
+ const path = getFunctionName(fn);
572
743
  const failure = this.#script.failures?.get(path);
573
744
  if (failure !== void 0) return Effect.fail(mapFailure(path, failure));
574
- if (this.#script.responses?.has(path)) return Effect.succeed(this.#script.responses.get(path));
575
- return Effect.fail(convexCallErrorFromCapxul(path, Errors.providerError("convex", path, "unknown function")));
745
+ if (this.#script.responses?.has(path)) return Effect.succeed(wireReply(fn, this.#script.responses.get(path)));
746
+ return Effect.fail(convexCallErrorFromCapxul(path, Errors.providerUnavailable({
747
+ provider: "convex",
748
+ operation: path,
749
+ mode: "unknown"
750
+ })));
576
751
  }
577
752
  };
578
753
  function mapFailure(operation, failure) {
579
754
  if (failure instanceof CapxulError) return convexCallErrorFromCapxul(operation, failure);
580
- if (failure instanceof Error) return convexCallErrorFromCapxul(operation, Errors.providerError("convex", operation, failure));
581
- return convexCallErrorFromCapxul(operation, Errors.providerError("convex", operation, new Error(String(failure))));
755
+ return convexCallErrorFromCapxul(operation, Errors.providerUnavailable({
756
+ provider: "convex",
757
+ operation,
758
+ mode: "unknown",
759
+ cause: failure
760
+ }));
582
761
  }
583
762
  //#endregion
584
763
  //#region src/testing/identity/InMemoryIdentityAdapter.ts
@@ -587,15 +766,15 @@ var InMemoryIdentityAdapter = class {
587
766
  #clock;
588
767
  constructor(deps) {
589
768
  this.#clock = deps.clock;
769
+ for (const [authUserId, profile] of deps.profiles ?? []) this.#identities.set(authUserId, cloneProfile(profile));
590
770
  }
591
771
  loadByAuthUserId(authUserId) {
592
772
  return Effect.sync(() => cloneProfileOrNull(this.#identities.get(authUserId) ?? null));
593
773
  }
594
774
  create(input) {
595
- const self = this;
596
- return Effect.gen(function* () {
597
- if (self.#identities.has(input.authUserId)) return yield* fail$1("create", Errors.invalidInput("authUserId", "already exists"));
598
- const now = yield* self.#now("create.clock");
775
+ return Effect.gen({ self: this }, function* () {
776
+ if (this.#identities.has(input.authUserId)) return yield* fail$1("create", Errors.invalidInput("authUserId", "already exists"));
777
+ const now = yield* this.#now("create.clock");
599
778
  const profile = {
600
779
  authUserId: input.authUserId,
601
780
  email: input.email,
@@ -609,16 +788,15 @@ var InMemoryIdentityAdapter = class {
609
788
  createdAt: now,
610
789
  updatedAt: now
611
790
  };
612
- self.#identities.set(input.authUserId, cloneProfile(profile));
791
+ this.#identities.set(input.authUserId, cloneProfile(profile));
613
792
  return cloneProfile(profile);
614
793
  });
615
794
  }
616
795
  update(input) {
617
- const self = this;
618
- return Effect.gen(function* () {
619
- const existing = self.#identities.get(input.authUserId);
796
+ return Effect.gen({ self: this }, function* () {
797
+ const existing = this.#identities.get(input.authUserId);
620
798
  if (existing === void 0) return yield* fail$1("update", Errors.profileNotFound(input.authUserId));
621
- const now = yield* self.#now("update.clock");
799
+ const now = yield* this.#now("update.clock");
622
800
  const updated = {
623
801
  authUserId: existing.authUserId,
624
802
  email: existing.email,
@@ -632,15 +810,14 @@ var InMemoryIdentityAdapter = class {
632
810
  imageUrl: existing.imageUrl,
633
811
  updatedAt: now
634
812
  };
635
- self.#identities.set(input.authUserId, cloneProfile(updated));
813
+ this.#identities.set(input.authUserId, cloneProfile(updated));
636
814
  return cloneProfile(updated);
637
815
  });
638
816
  }
639
817
  completeOnboarding(input) {
640
- const self = this;
641
- return Effect.gen(function* () {
642
- const now = yield* self.#now("completeOnboarding.clock");
643
- const existing = self.#identities.get(input.authUserId);
818
+ return Effect.gen({ self: this }, function* () {
819
+ const now = yield* this.#now("completeOnboarding.clock");
820
+ const existing = this.#identities.get(input.authUserId);
644
821
  const profile = {
645
822
  authUserId: input.authUserId,
646
823
  email: existing?.email ?? input.email,
@@ -654,14 +831,16 @@ var InMemoryIdentityAdapter = class {
654
831
  createdAt: existing?.createdAt ?? now,
655
832
  updatedAt: now
656
833
  };
657
- self.#identities.set(input.authUserId, cloneProfile(profile));
834
+ this.#identities.set(input.authUserId, cloneProfile(profile));
658
835
  return cloneProfile(profile);
659
836
  });
660
837
  }
661
838
  #now(operation) {
662
- return this.#clock.now.pipe(Effect.mapError((cause) => identityErrorFromCapxul(operation, Errors.providerError("clock", "now", cause), cause)));
839
+ if (this.#clock === void 0) return Effect.succeed(toEpochMs(0));
840
+ return this.#clock.now.pipe(Effect.mapError((cause) => identityErrorFromCapxul(operation, clockFailure(cause), cause)));
663
841
  }
664
842
  };
843
+ Layer.effect(IdentityPortTag, Effect.map(ClockPortTag, (clock) => new InMemoryIdentityAdapter({ clock })));
665
844
  function fail$1(operation, error) {
666
845
  return Effect.fail(identityErrorFromCapxul(operation, error));
667
846
  }
@@ -686,8 +865,6 @@ function cloneProfileOrNull(profile) {
686
865
  //#endregion
687
866
  //#region src/testing/smart-account/smart-account-derivation.ts
688
867
  const SUPPORTED_CHAIN_IDS = /* @__PURE__ */ new Set([84532, 8453]);
689
- toAddress("0xa6b71e26c5e0845f74c812102ca7114b6a896ab2");
690
- keccak256("0x");
691
868
  function isSupportedSmartAccountChain(chainId) {
692
869
  return SUPPORTED_CHAIN_IDS.has(wireChainId(chainId));
693
870
  }
@@ -708,6 +885,7 @@ var InMemorySmartAccountAdapter = class {
708
885
  constructor(deps) {
709
886
  this.#clock = deps.clock;
710
887
  this.#failures = deps.failures;
888
+ for (const [authUserId, account] of deps.accounts ?? []) this.#accounts.set(authUserId, cloneSmartAccount(account));
711
889
  }
712
890
  loadByAuthUserId(authUserId) {
713
891
  const scriptedFailure = this.#failures?.loadByAuthUserId;
@@ -726,12 +904,11 @@ var InMemorySmartAccountAdapter = class {
726
904
  provision(input) {
727
905
  const scriptedFailure = this.#failures?.provision;
728
906
  if (scriptedFailure !== void 0) return fail("provision", scriptedFailure);
729
- const self = this;
730
- return Effect.gen(function* () {
907
+ return Effect.gen({ self: this }, function* () {
731
908
  if (!isSupportedSmartAccountChain(input.chainId)) return yield* fail("provision", Errors.invalidInput("chainId", `unsupported chainId ${String(input.chainId)}`));
732
- const existing = self.#accounts.get(input.authUserId);
909
+ const existing = this.#accounts.get(input.authUserId);
733
910
  if (existing !== void 0) return cloneSmartAccount(existing);
734
- const now = yield* self.#now("provision.clock");
911
+ const now = yield* this.#now("provision.clock");
735
912
  const account = {
736
913
  authUserId: input.authUserId,
737
914
  signerAddress: null,
@@ -741,9 +918,9 @@ var InMemorySmartAccountAdapter = class {
741
918
  claimedAt: null,
742
919
  createdAt: now
743
920
  };
744
- self.#accounts.set(input.authUserId, cloneSmartAccount(account));
921
+ this.#accounts.set(input.authUserId, cloneSmartAccount(account));
745
922
  return cloneSmartAccount(account);
746
- }).pipe(self.#provisionMutex.withPermits(1));
923
+ }).pipe(this.#provisionMutex.withPermits(1));
747
924
  }
748
925
  promoteDeployedAt(authUserId, deployedAt) {
749
926
  const existing = this.#accounts.get(authUserId);
@@ -755,27 +932,27 @@ var InMemorySmartAccountAdapter = class {
755
932
  });
756
933
  }
757
934
  confirmDeployment(input) {
758
- const self = this;
759
- return Effect.gen(function* () {
760
- const existing = self.#accounts.get(input.authUserId);
935
+ const scriptedFailure = this.#failures?.confirmDeployment;
936
+ if (scriptedFailure !== void 0) return fail("confirmDeployment", scriptedFailure);
937
+ return Effect.gen({ self: this }, function* () {
938
+ const existing = this.#accounts.get(input.authUserId);
761
939
  if (existing === void 0) return yield* fail("confirmDeployment", Errors.profileNotFound(String(input.authUserId)));
762
940
  if (existing.smartAccountAddress !== toAddress(input.safeAddress)) return yield* fail("confirmDeployment", Errors.invalidInput("safeAddress", "does not match backend smart-account row"));
763
941
  if (existing.deployedAt !== null) return cloneSmartAccount(existing);
764
- const now = yield* self.#now("confirmDeployment.clock");
942
+ const now = yield* this.#now("confirmDeployment.clock");
765
943
  const updated = {
766
944
  ...existing,
767
945
  deployedAt: now
768
946
  };
769
- self.#accounts.set(input.authUserId, cloneSmartAccount(updated));
947
+ this.#accounts.set(input.authUserId, cloneSmartAccount(updated));
770
948
  return cloneSmartAccount(updated);
771
- }).pipe(self.#provisionMutex.withPermits(1));
949
+ }).pipe(this.#provisionMutex.withPermits(1));
772
950
  }
773
951
  claim(input) {
774
952
  const scriptedFailure = this.#failures?.claim;
775
953
  if (scriptedFailure !== void 0) return fail("claim", scriptedFailure);
776
- const self = this;
777
- return Effect.gen(function* () {
778
- const existing = self.#accounts.get(input.authUserId);
954
+ return Effect.gen({ self: this }, function* () {
955
+ const existing = this.#accounts.get(input.authUserId);
779
956
  if (existing === void 0) return yield* fail("claim", Errors.invalidInput("account", "no provisioned smart account for this user"));
780
957
  if (existing.chainId !== input.chainId) return yield* fail("claim", Errors.invalidInput("chainId", "does not match backend smart-account row"));
781
958
  const newOwner = toAddress(input.signerAddress);
@@ -783,19 +960,20 @@ var InMemorySmartAccountAdapter = class {
783
960
  if (existing.signerAddress !== newOwner) return yield* fail("claim", Errors.invalidInput("signerAddress", "account already claimed by a different signer"));
784
961
  return cloneSmartAccount(existing);
785
962
  }
786
- const now = yield* self.#now("claim.clock");
963
+ const now = yield* this.#now("claim.clock");
787
964
  const updated = {
788
965
  ...existing,
789
966
  signerAddress: newOwner,
790
967
  claimedAt: now,
791
968
  deployedAt: existing.deployedAt ?? now
792
969
  };
793
- self.#accounts.set(input.authUserId, cloneSmartAccount(updated));
970
+ this.#accounts.set(input.authUserId, cloneSmartAccount(updated));
794
971
  return cloneSmartAccount(updated);
795
- }).pipe(self.#provisionMutex.withPermits(1));
972
+ }).pipe(this.#provisionMutex.withPermits(1));
796
973
  }
797
974
  #now(operation) {
798
- return this.#clock.now.pipe(Effect.mapError((cause) => smartAccountErrorFromCapxul(operation, Errors.providerError("clock", "now", cause), cause)));
975
+ if (this.#clock === void 0) return Effect.succeed(toEpochMs(0));
976
+ return this.#clock.now.pipe(Effect.mapError((cause) => smartAccountErrorFromCapxul(operation, clockFailure(cause), cause)));
799
977
  }
800
978
  };
801
979
  function fail(operation, error) {
@@ -825,7 +1003,7 @@ var RecordingTelemetryAdapter = class {
825
1003
  this.#rawMode = options.rawMode === true;
826
1004
  }
827
1005
  get events() {
828
- return this.#events.map(cloneEvent);
1006
+ return this.#events.map(cloneTelemetryEvent);
829
1007
  }
830
1008
  get operations() {
831
1009
  return this.#operations.map(cloneOperation);
@@ -836,7 +1014,7 @@ var RecordingTelemetryAdapter = class {
836
1014
  this.#events.push(recorded);
837
1015
  this.#operations.push({
838
1016
  type: "emit",
839
- event: cloneEvent(recorded)
1017
+ event: cloneTelemetryEvent(recorded)
840
1018
  });
841
1019
  });
842
1020
  }
@@ -866,17 +1044,11 @@ var RecordingTelemetryAdapter = class {
866
1044
  this.#operations.length = 0;
867
1045
  }
868
1046
  };
869
- function cloneEvent(event) {
870
- return event.props === void 0 ? { name: event.name } : {
871
- name: event.name,
872
- props: cloneProps(event.props)
873
- };
874
- }
875
1047
  function cloneOperation(operation) {
876
1048
  switch (operation.type) {
877
1049
  case "emit": return {
878
1050
  type: "emit",
879
- event: cloneEvent(operation.event)
1051
+ event: cloneTelemetryEvent(operation.event)
880
1052
  };
881
1053
  case "identify": return {
882
1054
  type: "identify",
@@ -887,14 +1059,15 @@ function cloneOperation(operation) {
887
1059
  input: cloneGroupInput(operation.input)
888
1060
  };
889
1061
  case "reset": return { type: "reset" };
1062
+ default: return absurd(operation);
890
1063
  }
891
1064
  }
892
1065
  function cloneIdentifyInput(input) {
893
1066
  return {
894
1067
  distinctId: input.distinctId,
895
1068
  ...input.anonDistinctId === void 0 ? {} : { anonDistinctId: input.anonDistinctId },
896
- ...input.traits === void 0 ? {} : { traits: cloneProps(input.traits) },
897
- ...input.properties === void 0 ? {} : { properties: cloneProps(input.properties) }
1069
+ ...input.traits === void 0 ? {} : { traits: cloneTelemetryProps(input.traits) },
1070
+ ...input.properties === void 0 ? {} : { properties: cloneTelemetryProps(input.properties) }
898
1071
  };
899
1072
  }
900
1073
  function cloneGroupInput(input) {
@@ -904,22 +1077,9 @@ function cloneGroupInput(input) {
904
1077
  } : {
905
1078
  groupType: input.groupType,
906
1079
  groupKey: input.groupKey,
907
- properties: cloneProps(input.properties)
1080
+ properties: cloneTelemetryProps(input.properties)
908
1081
  };
909
1082
  }
910
- function cloneProps(props) {
911
- const cloned = {};
912
- for (const [key, value] of Object.entries(props)) cloned[key] = cloneTelemetryValue(value);
913
- return cloned;
914
- }
915
- function cloneTelemetryValue(value) {
916
- if (Array.isArray(value)) return value.map(cloneTelemetryValue);
917
- if (value === null || typeof value !== "object") return value;
918
- if (Object.getPrototypeOf(value) !== Object.prototype) return value;
919
- const cloned = {};
920
- for (const [key, nested] of Object.entries(value)) cloned[key] = cloneTelemetryValue(nested);
921
- return cloned;
922
- }
923
1083
  //#endregion
924
1084
  //#region src/testing/index.ts
925
1085
  const TEST_BOOTSTRAP = {
@@ -947,6 +1107,7 @@ function createCapxulTestClient(options = {}) {
947
1107
  });
948
1108
  const identity = new InMemoryIdentityAdapter({ clock });
949
1109
  const telemetry = new RecordingTelemetryAdapter();
1110
+ const organizationSetup = createTestOrganizationSetup();
950
1111
  const ports = {
951
1112
  authClient: authClientPortFromPromiseAdapter(authClient),
952
1113
  authCache,
@@ -963,6 +1124,7 @@ function createCapxulTestClient(options = {}) {
963
1124
  bootstrap: TEST_BOOTSTRAP,
964
1125
  authCache,
965
1126
  requirement: options.requirement ?? "none",
1127
+ organizationSetup: organizationSetup.ops,
966
1128
  ...options.failureObservation === void 0 ? {} : { failureObservation: options.failureObservation }
967
1129
  });
968
1130
  const transitions = [];
@@ -986,6 +1148,7 @@ function createCapxulTestClient(options = {}) {
986
1148
  return telemetry.operations;
987
1149
  },
988
1150
  peekOtp: (email) => authClient.peekOtpForTesting(toEmail(email)),
1151
+ organizationSetup: { set: organizationSetup.set },
989
1152
  seedIdentity: async (input) => {
990
1153
  const authUserId = toAuthUserId(input.authUserId);
991
1154
  const email = toEmail(input.email);
@@ -1006,4 +1169,4 @@ function createCapxulTestClient(options = {}) {
1006
1169
  };
1007
1170
  }
1008
1171
  //#endregion
1009
- export { ControlledSignerFault, armSignerFaults, createCapxulProductionTestClient, createCapxulTestClient, signerFaultCause };
1172
+ export { ControlledSignerFault, armSignerFaults, createCapxulProductionTestClient, createCapxulTestClient, organizationSetupFixture, signerFaultCause };