@capxul/sdk 4.20.0-beta.3 → 4.20.0-beta.31

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-M4dWz5g4.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 { Ai as toEpochMs, D as convexCallErrorFromCapxul, Et as CONFIGURED_MONEY_ASSETS, Fi as toSessionToken, Fr as absurd, Ii as Errors, Li as CapxulError, Mi as toAuthUserId, Mr as toAddress, Ni as toEmail, Nr as toChainId, Ot as deriveCapxulSafeAddress, Pi as toJwtToken, Pr as toWeiAmount, Qn as toOrgId, Ti as InMemoryAuthCacheAdapter, Z as assetAmountFromRaw, _ as accountFromAccountPositions, _i as toAllowedOrigin, ai as ClockPortTag, bi as authClientPortFromPromiseAdapter, f as smartAccountErrorFromCapxul, fn as toCountryCode, gn as toHandle, ii as ClockError, j as wireChainId, ji as toEpochSeconds, ki as toDurationMs, nt as toRaw, oi as readClockNow, pn as toKycTier, ur as toAccountId, vi as toAppId, vn as validateHandle, w as accountReadErrorFromCapxul, yi as toPublishableKey, yn as redactTelemetryEvent } from "../access-B-2qmxF_.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-2L76NWS5.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);
@@ -509,6 +646,47 @@ function fail$2(kind, error) {
509
646
  return Effect.fail(bootstrapErrorFromCapxul(kind, error));
510
647
  }
511
648
  //#endregion
649
+ //#region src/testing/convex-call/subscribers.ts
650
+ /**
651
+ * Register `subscriber` under `path` and return its unsubscribe, which removes
652
+ * it once (a second call does nothing) and drops a path left with no one.
653
+ */
654
+ function subscribeAt(subscribers, path, subscriber) {
655
+ let subs = subscribers.get(path);
656
+ if (subs === void 0) {
657
+ subs = /* @__PURE__ */ new Set();
658
+ subscribers.set(path, subs);
659
+ }
660
+ subs.add(subscriber);
661
+ let active = true;
662
+ return () => {
663
+ if (!active) return;
664
+ active = false;
665
+ const live = subscribers.get(path);
666
+ if (live === void 0) return;
667
+ live.delete(subscriber);
668
+ if (live.size === 0) subscribers.delete(path);
669
+ };
670
+ }
671
+ //#endregion
672
+ //#region src/testing/convex-call/wire-reply.ts
673
+ /**
674
+ * The Convex wire, as the in-memory test doubles stand in for it. A scripted or
675
+ * registered value crosses where a real reply would, and the caller's function
676
+ * reference names its type, exactly as `ConvexClient` types a reply it cannot
677
+ * check. These are the only places the doubles make that claim.
678
+ */
679
+ function wireReply(_fn, value) {
680
+ return value;
681
+ }
682
+ /** A scripted snapshot for a subscription on `fn`: only an `ok` value carries the reply type. */
683
+ function wireSnapshot(fn, snapshot) {
684
+ return snapshot.status === "ok" ? {
685
+ status: "ok",
686
+ value: wireReply(fn, snapshot.value)
687
+ } : snapshot;
688
+ }
689
+ //#endregion
512
690
  //#region src/testing/convex-call/ConvexCallStubAdapter.ts
513
691
  var ConvexCallStubAdapter = class {
514
692
  #script;
@@ -517,43 +695,30 @@ var ConvexCallStubAdapter = class {
517
695
  this.#script = deps.script;
518
696
  }
519
697
  query(fn, _args) {
520
- return this.#resolve(getFunctionName(fn));
698
+ return this.#resolve(fn);
521
699
  }
522
700
  mutation(fn, _args) {
523
- return this.#resolve(getFunctionName(fn));
701
+ return this.#resolve(fn);
524
702
  }
525
703
  action(fn, _args) {
526
- return this.#resolve(getFunctionName(fn));
704
+ return this.#resolve(fn);
527
705
  }
528
706
  subscribe(fn, _args, callback) {
529
707
  return Effect.sync(() => {
530
708
  const path = getFunctionName(fn);
531
- const cb = callback;
709
+ const cb = (snapshot) => callback(wireSnapshot(fn, snapshot));
532
710
  cb({ status: "loading" });
533
711
  const sequence = this.#script.subscriptions?.get(path);
534
712
  const subscriber = {
535
713
  callback: cb,
536
714
  cursor: 0
537
715
  };
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);
716
+ const unsubscribe = subscribeAt(this.#subscribers, path, subscriber);
544
717
  if (sequence !== void 0 && sequence.length > 0) {
545
718
  cb(sequence[0]);
546
719
  subscriber.cursor = 1;
547
720
  }
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
- };
721
+ return unsubscribe;
557
722
  });
558
723
  }
559
724
  advanceSubscription(path) {
@@ -568,10 +733,11 @@ var ConvexCallStubAdapter = class {
568
733
  sub.callback(next);
569
734
  }
570
735
  }
571
- #resolve(path) {
736
+ #resolve(fn) {
737
+ const path = getFunctionName(fn);
572
738
  const failure = this.#script.failures?.get(path);
573
739
  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));
740
+ if (this.#script.responses?.has(path)) return Effect.succeed(wireReply(fn, this.#script.responses.get(path)));
575
741
  return Effect.fail(convexCallErrorFromCapxul(path, Errors.providerError("convex", path, "unknown function")));
576
742
  }
577
743
  };
@@ -587,15 +753,15 @@ var InMemoryIdentityAdapter = class {
587
753
  #clock;
588
754
  constructor(deps) {
589
755
  this.#clock = deps.clock;
756
+ for (const [authUserId, profile] of deps.profiles ?? []) this.#identities.set(authUserId, cloneProfile(profile));
590
757
  }
591
758
  loadByAuthUserId(authUserId) {
592
759
  return Effect.sync(() => cloneProfileOrNull(this.#identities.get(authUserId) ?? null));
593
760
  }
594
761
  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");
762
+ return Effect.gen({ self: this }, function* () {
763
+ if (this.#identities.has(input.authUserId)) return yield* fail$1("create", Errors.invalidInput("authUserId", "already exists"));
764
+ const now = yield* this.#now("create.clock");
599
765
  const profile = {
600
766
  authUserId: input.authUserId,
601
767
  email: input.email,
@@ -609,16 +775,15 @@ var InMemoryIdentityAdapter = class {
609
775
  createdAt: now,
610
776
  updatedAt: now
611
777
  };
612
- self.#identities.set(input.authUserId, cloneProfile(profile));
778
+ this.#identities.set(input.authUserId, cloneProfile(profile));
613
779
  return cloneProfile(profile);
614
780
  });
615
781
  }
616
782
  update(input) {
617
- const self = this;
618
- return Effect.gen(function* () {
619
- const existing = self.#identities.get(input.authUserId);
783
+ return Effect.gen({ self: this }, function* () {
784
+ const existing = this.#identities.get(input.authUserId);
620
785
  if (existing === void 0) return yield* fail$1("update", Errors.profileNotFound(input.authUserId));
621
- const now = yield* self.#now("update.clock");
786
+ const now = yield* this.#now("update.clock");
622
787
  const updated = {
623
788
  authUserId: existing.authUserId,
624
789
  email: existing.email,
@@ -632,15 +797,14 @@ var InMemoryIdentityAdapter = class {
632
797
  imageUrl: existing.imageUrl,
633
798
  updatedAt: now
634
799
  };
635
- self.#identities.set(input.authUserId, cloneProfile(updated));
800
+ this.#identities.set(input.authUserId, cloneProfile(updated));
636
801
  return cloneProfile(updated);
637
802
  });
638
803
  }
639
804
  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);
805
+ return Effect.gen({ self: this }, function* () {
806
+ const now = yield* this.#now("completeOnboarding.clock");
807
+ const existing = this.#identities.get(input.authUserId);
644
808
  const profile = {
645
809
  authUserId: input.authUserId,
646
810
  email: existing?.email ?? input.email,
@@ -654,14 +818,16 @@ var InMemoryIdentityAdapter = class {
654
818
  createdAt: existing?.createdAt ?? now,
655
819
  updatedAt: now
656
820
  };
657
- self.#identities.set(input.authUserId, cloneProfile(profile));
821
+ this.#identities.set(input.authUserId, cloneProfile(profile));
658
822
  return cloneProfile(profile);
659
823
  });
660
824
  }
661
825
  #now(operation) {
826
+ if (this.#clock === void 0) return Effect.succeed(toEpochMs(0));
662
827
  return this.#clock.now.pipe(Effect.mapError((cause) => identityErrorFromCapxul(operation, Errors.providerError("clock", "now", cause), cause)));
663
828
  }
664
829
  };
830
+ Layer.effect(IdentityPortTag, Effect.map(ClockPortTag, (clock) => new InMemoryIdentityAdapter({ clock })));
665
831
  function fail$1(operation, error) {
666
832
  return Effect.fail(identityErrorFromCapxul(operation, error));
667
833
  }
@@ -686,8 +852,6 @@ function cloneProfileOrNull(profile) {
686
852
  //#endregion
687
853
  //#region src/testing/smart-account/smart-account-derivation.ts
688
854
  const SUPPORTED_CHAIN_IDS = /* @__PURE__ */ new Set([84532, 8453]);
689
- toAddress("0xa6b71e26c5e0845f74c812102ca7114b6a896ab2");
690
- keccak256("0x");
691
855
  function isSupportedSmartAccountChain(chainId) {
692
856
  return SUPPORTED_CHAIN_IDS.has(wireChainId(chainId));
693
857
  }
@@ -708,6 +872,7 @@ var InMemorySmartAccountAdapter = class {
708
872
  constructor(deps) {
709
873
  this.#clock = deps.clock;
710
874
  this.#failures = deps.failures;
875
+ for (const [authUserId, account] of deps.accounts ?? []) this.#accounts.set(authUserId, cloneSmartAccount(account));
711
876
  }
712
877
  loadByAuthUserId(authUserId) {
713
878
  const scriptedFailure = this.#failures?.loadByAuthUserId;
@@ -726,12 +891,11 @@ var InMemorySmartAccountAdapter = class {
726
891
  provision(input) {
727
892
  const scriptedFailure = this.#failures?.provision;
728
893
  if (scriptedFailure !== void 0) return fail("provision", scriptedFailure);
729
- const self = this;
730
- return Effect.gen(function* () {
894
+ return Effect.gen({ self: this }, function* () {
731
895
  if (!isSupportedSmartAccountChain(input.chainId)) return yield* fail("provision", Errors.invalidInput("chainId", `unsupported chainId ${String(input.chainId)}`));
732
- const existing = self.#accounts.get(input.authUserId);
896
+ const existing = this.#accounts.get(input.authUserId);
733
897
  if (existing !== void 0) return cloneSmartAccount(existing);
734
- const now = yield* self.#now("provision.clock");
898
+ const now = yield* this.#now("provision.clock");
735
899
  const account = {
736
900
  authUserId: input.authUserId,
737
901
  signerAddress: null,
@@ -741,9 +905,9 @@ var InMemorySmartAccountAdapter = class {
741
905
  claimedAt: null,
742
906
  createdAt: now
743
907
  };
744
- self.#accounts.set(input.authUserId, cloneSmartAccount(account));
908
+ this.#accounts.set(input.authUserId, cloneSmartAccount(account));
745
909
  return cloneSmartAccount(account);
746
- }).pipe(self.#provisionMutex.withPermits(1));
910
+ }).pipe(this.#provisionMutex.withPermits(1));
747
911
  }
748
912
  promoteDeployedAt(authUserId, deployedAt) {
749
913
  const existing = this.#accounts.get(authUserId);
@@ -755,27 +919,27 @@ var InMemorySmartAccountAdapter = class {
755
919
  });
756
920
  }
757
921
  confirmDeployment(input) {
758
- const self = this;
759
- return Effect.gen(function* () {
760
- const existing = self.#accounts.get(input.authUserId);
922
+ const scriptedFailure = this.#failures?.confirmDeployment;
923
+ if (scriptedFailure !== void 0) return fail("confirmDeployment", scriptedFailure);
924
+ return Effect.gen({ self: this }, function* () {
925
+ const existing = this.#accounts.get(input.authUserId);
761
926
  if (existing === void 0) return yield* fail("confirmDeployment", Errors.profileNotFound(String(input.authUserId)));
762
927
  if (existing.smartAccountAddress !== toAddress(input.safeAddress)) return yield* fail("confirmDeployment", Errors.invalidInput("safeAddress", "does not match backend smart-account row"));
763
928
  if (existing.deployedAt !== null) return cloneSmartAccount(existing);
764
- const now = yield* self.#now("confirmDeployment.clock");
929
+ const now = yield* this.#now("confirmDeployment.clock");
765
930
  const updated = {
766
931
  ...existing,
767
932
  deployedAt: now
768
933
  };
769
- self.#accounts.set(input.authUserId, cloneSmartAccount(updated));
934
+ this.#accounts.set(input.authUserId, cloneSmartAccount(updated));
770
935
  return cloneSmartAccount(updated);
771
- }).pipe(self.#provisionMutex.withPermits(1));
936
+ }).pipe(this.#provisionMutex.withPermits(1));
772
937
  }
773
938
  claim(input) {
774
939
  const scriptedFailure = this.#failures?.claim;
775
940
  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);
941
+ return Effect.gen({ self: this }, function* () {
942
+ const existing = this.#accounts.get(input.authUserId);
779
943
  if (existing === void 0) return yield* fail("claim", Errors.invalidInput("account", "no provisioned smart account for this user"));
780
944
  if (existing.chainId !== input.chainId) return yield* fail("claim", Errors.invalidInput("chainId", "does not match backend smart-account row"));
781
945
  const newOwner = toAddress(input.signerAddress);
@@ -783,18 +947,19 @@ var InMemorySmartAccountAdapter = class {
783
947
  if (existing.signerAddress !== newOwner) return yield* fail("claim", Errors.invalidInput("signerAddress", "account already claimed by a different signer"));
784
948
  return cloneSmartAccount(existing);
785
949
  }
786
- const now = yield* self.#now("claim.clock");
950
+ const now = yield* this.#now("claim.clock");
787
951
  const updated = {
788
952
  ...existing,
789
953
  signerAddress: newOwner,
790
954
  claimedAt: now,
791
955
  deployedAt: existing.deployedAt ?? now
792
956
  };
793
- self.#accounts.set(input.authUserId, cloneSmartAccount(updated));
957
+ this.#accounts.set(input.authUserId, cloneSmartAccount(updated));
794
958
  return cloneSmartAccount(updated);
795
- }).pipe(self.#provisionMutex.withPermits(1));
959
+ }).pipe(this.#provisionMutex.withPermits(1));
796
960
  }
797
961
  #now(operation) {
962
+ if (this.#clock === void 0) return Effect.succeed(toEpochMs(0));
798
963
  return this.#clock.now.pipe(Effect.mapError((cause) => smartAccountErrorFromCapxul(operation, Errors.providerError("clock", "now", cause), cause)));
799
964
  }
800
965
  };
@@ -825,7 +990,7 @@ var RecordingTelemetryAdapter = class {
825
990
  this.#rawMode = options.rawMode === true;
826
991
  }
827
992
  get events() {
828
- return this.#events.map(cloneEvent);
993
+ return this.#events.map(cloneTelemetryEvent);
829
994
  }
830
995
  get operations() {
831
996
  return this.#operations.map(cloneOperation);
@@ -836,7 +1001,7 @@ var RecordingTelemetryAdapter = class {
836
1001
  this.#events.push(recorded);
837
1002
  this.#operations.push({
838
1003
  type: "emit",
839
- event: cloneEvent(recorded)
1004
+ event: cloneTelemetryEvent(recorded)
840
1005
  });
841
1006
  });
842
1007
  }
@@ -866,17 +1031,11 @@ var RecordingTelemetryAdapter = class {
866
1031
  this.#operations.length = 0;
867
1032
  }
868
1033
  };
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
1034
  function cloneOperation(operation) {
876
1035
  switch (operation.type) {
877
1036
  case "emit": return {
878
1037
  type: "emit",
879
- event: cloneEvent(operation.event)
1038
+ event: cloneTelemetryEvent(operation.event)
880
1039
  };
881
1040
  case "identify": return {
882
1041
  type: "identify",
@@ -887,14 +1046,15 @@ function cloneOperation(operation) {
887
1046
  input: cloneGroupInput(operation.input)
888
1047
  };
889
1048
  case "reset": return { type: "reset" };
1049
+ default: return absurd(operation);
890
1050
  }
891
1051
  }
892
1052
  function cloneIdentifyInput(input) {
893
1053
  return {
894
1054
  distinctId: input.distinctId,
895
1055
  ...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) }
1056
+ ...input.traits === void 0 ? {} : { traits: cloneTelemetryProps(input.traits) },
1057
+ ...input.properties === void 0 ? {} : { properties: cloneTelemetryProps(input.properties) }
898
1058
  };
899
1059
  }
900
1060
  function cloneGroupInput(input) {
@@ -904,22 +1064,9 @@ function cloneGroupInput(input) {
904
1064
  } : {
905
1065
  groupType: input.groupType,
906
1066
  groupKey: input.groupKey,
907
- properties: cloneProps(input.properties)
1067
+ properties: cloneTelemetryProps(input.properties)
908
1068
  };
909
1069
  }
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
1070
  //#endregion
924
1071
  //#region src/testing/index.ts
925
1072
  const TEST_BOOTSTRAP = {
@@ -947,6 +1094,7 @@ function createCapxulTestClient(options = {}) {
947
1094
  });
948
1095
  const identity = new InMemoryIdentityAdapter({ clock });
949
1096
  const telemetry = new RecordingTelemetryAdapter();
1097
+ const organizationSetup = createTestOrganizationSetup();
950
1098
  const ports = {
951
1099
  authClient: authClientPortFromPromiseAdapter(authClient),
952
1100
  authCache,
@@ -963,6 +1111,7 @@ function createCapxulTestClient(options = {}) {
963
1111
  bootstrap: TEST_BOOTSTRAP,
964
1112
  authCache,
965
1113
  requirement: options.requirement ?? "none",
1114
+ organizationSetup: organizationSetup.ops,
966
1115
  ...options.failureObservation === void 0 ? {} : { failureObservation: options.failureObservation }
967
1116
  });
968
1117
  const transitions = [];
@@ -986,6 +1135,7 @@ function createCapxulTestClient(options = {}) {
986
1135
  return telemetry.operations;
987
1136
  },
988
1137
  peekOtp: (email) => authClient.peekOtpForTesting(toEmail(email)),
1138
+ organizationSetup: { set: organizationSetup.set },
989
1139
  seedIdentity: async (input) => {
990
1140
  const authUserId = toAuthUserId(input.authUserId);
991
1141
  const email = toEmail(input.email);
@@ -1006,4 +1156,4 @@ function createCapxulTestClient(options = {}) {
1006
1156
  };
1007
1157
  }
1008
1158
  //#endregion
1009
- export { ControlledSignerFault, armSignerFaults, createCapxulProductionTestClient, createCapxulTestClient, signerFaultCause };
1159
+ export { ControlledSignerFault, armSignerFaults, createCapxulProductionTestClient, createCapxulTestClient, organizationSetupFixture, signerFaultCause };