@capxul/sdk 1.0.0-alpha.18 → 1.0.0-alpha.19

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.
package/dist/index.mjs CHANGED
@@ -1258,10 +1258,10 @@ function makeAccountMethods(deps) {
1258
1258
  value: await lane.retryProvisioning()
1259
1259
  };
1260
1260
  };
1261
- const reportAndReturn = (error) => {
1261
+ const reportAndReturn = (error, operation = "getLifecycle") => {
1262
1262
  if (deps.telemetry) captureExceptionSync(deps.telemetry, error, {
1263
1263
  layer: "account",
1264
- operation: "getLifecycle"
1264
+ operation
1265
1265
  });
1266
1266
  return {
1267
1267
  ok: false,
@@ -1292,6 +1292,12 @@ function makeAccountMethods(deps) {
1292
1292
  };
1293
1293
  const getLifecycle = () => resolveLifecycle();
1294
1294
  const retrySetup = async () => {
1295
+ const resetSession = deps.signer?.resetSession;
1296
+ if (typeof resetSession === "function") try {
1297
+ resetSession.call(deps.signer);
1298
+ } catch (cause) {
1299
+ return reportAndReturn(cause instanceof CapxulError ? cause : Errors.providerError("openfort", "resetSession", cause, { failure_mode: "unknown" }), "retrySetup");
1300
+ }
1295
1301
  await lane.retryProvisioning();
1296
1302
  return resolveLifecycle();
1297
1303
  };
@@ -1618,7 +1624,7 @@ async function recoverRawDigestSigner(input) {
1618
1624
  }
1619
1625
  //#endregion
1620
1626
  //#region package.json
1621
- var version = "1.0.0-alpha.18";
1627
+ var version = "1.0.0-alpha.19";
1622
1628
  //#endregion
1623
1629
  //#region src/ports/auth-client.ts
1624
1630
  var AuthClientError = class extends Data.TaggedError("AuthClientError") {};
@@ -3386,24 +3392,6 @@ const envFields = {
3386
3392
  "required": false,
3387
3393
  "example": "development"
3388
3394
  },
3389
- AUTH_DEV_OTP: {
3390
- "runtime": "server",
3391
- "kind": "nonempty",
3392
- "required": false,
3393
- "example": "000000"
3394
- },
3395
- SENTRY_DSN: {
3396
- "runtime": "server",
3397
- "kind": "url",
3398
- "required": false,
3399
- "example": "https://public@example.ingest.sentry.io/1"
3400
- },
3401
- POSTHOG_API_KEY: {
3402
- "runtime": "server",
3403
- "kind": "nonempty",
3404
- "required": false,
3405
- "example": "phx_test"
3406
- },
3407
3395
  POSTHOG_PROJECT_TOKEN: {
3408
3396
  "runtime": "server",
3409
3397
  "kind": "nonempty",
@@ -3434,24 +3422,12 @@ const envFields = {
3434
3422
  "required": false,
3435
3423
  "example": "12345"
3436
3424
  },
3437
- POSTHOG_ENV_ID: {
3438
- "runtime": "server",
3439
- "kind": "nonempty",
3440
- "required": false,
3441
- "example": "12345"
3442
- },
3443
3425
  SENDER_EMAIL: {
3444
3426
  "runtime": "server",
3445
3427
  "kind": "email",
3446
3428
  "required": false,
3447
3429
  "example": "noreply@capxul.test"
3448
3430
  },
3449
- SHIELD_API_URL: {
3450
- "runtime": "server",
3451
- "kind": "url",
3452
- "required": false,
3453
- "example": "https://api.shield.test"
3454
- },
3455
3431
  ALCHEMY_API_KEY: {
3456
3432
  "runtime": "server",
3457
3433
  "kind": "nonempty",
@@ -3470,24 +3446,6 @@ const envFields = {
3470
3446
  "required": true,
3471
3447
  "example": "alchemy_hmac"
3472
3448
  },
3473
- ALCHEMY_AUTH_TOKEN: {
3474
- "runtime": "server",
3475
- "kind": "nonempty",
3476
- "required": false,
3477
- "example": "alchemy_auth"
3478
- },
3479
- ALCHEMY_WEBHOOK_ID: {
3480
- "runtime": "server",
3481
- "kind": "nonempty",
3482
- "required": false,
3483
- "example": "wh_123"
3484
- },
3485
- GOLDSKY_WEBHOOK_SECRET: {
3486
- "runtime": "server",
3487
- "kind": "nonempty",
3488
- "required": false,
3489
- "example": "goldsky_secret"
3490
- },
3491
3449
  DEPLOYER_PRIVATE_KEY: {
3492
3450
  "runtime": "server",
3493
3451
  "kind": "privateKey",
@@ -4207,6 +4165,7 @@ const DEFAULT_FUNCTIONS$1 = {
4207
4165
  prepareFounderAccount: makeFunctionReference("org/actions:prepareFounderAccount"),
4208
4166
  prepareBootstrap: makeFunctionReference("org/actions:prepareBootstrap"),
4209
4167
  submitBootstrap: makeFunctionReference("org/actions:submitBootstrap"),
4168
+ resumeBootstrapSubmission: makeFunctionReference("org/actions:resumeBootstrapSubmission"),
4210
4169
  confirmBootstrap: makeFunctionReference("org/actions:confirmBootstrap"),
4211
4170
  recordFailure: makeFunctionReference("org/lifecycle:recordFailure"),
4212
4171
  load: makeFunctionReference("org/lifecycle:load"),
@@ -4268,14 +4227,22 @@ var ConvexOrganizationSetupAdapter = class {
4268
4227
  if (!submitted.ok) return submitted;
4269
4228
  return parseLifecycle("submitBootstrap", submitted.value);
4270
4229
  }
4230
+ resumeSubmittedBootstrap(input) {
4231
+ return this.#lifecycleAction("resumeBootstrapSubmission", input, () => this.#convex.action(this.#fns.resumeBootstrapSubmission, { orgId: input.orgId }));
4232
+ }
4271
4233
  confirmSubmittedBootstrap(input) {
4272
4234
  return this.#lifecycleAction("confirmBootstrap", input, () => this.#convex.action(this.#fns.confirmBootstrap, { orgId: input.orgId }));
4273
4235
  }
4274
4236
  async recordFailure(input) {
4237
+ const errorProvider = input.error.details?.provider;
4238
+ const errorOperation = input.error.details?.operation;
4275
4239
  const result = await runCall("recordFailure", this.#convex.mutation(this.#fns.recordFailure, {
4276
4240
  orgId: input.orgId,
4277
- at: input.at,
4278
4241
  errorCode: input.error.code,
4242
+ ...typeof errorProvider === "string" && typeof errorOperation === "string" ? {
4243
+ errorProvider,
4244
+ errorOperation
4245
+ } : {},
4279
4246
  retryable: input.retryable
4280
4247
  }));
4281
4248
  return result.ok ? parseLifecycle("recordFailure", result.value) : result;
@@ -4289,8 +4256,15 @@ var ConvexOrganizationSetupAdapter = class {
4289
4256
  async retry(input) {
4290
4257
  const cancelled = cancellation(input.signal);
4291
4258
  if (cancelled !== void 0) return cancelled;
4259
+ const current = await this.loadLifecycle({ orgId: input.orgId });
4260
+ if (!current.ok) return current;
4261
+ if (current.value.status === "failed" && current.value.retryable && (current.value.at === "awaitingFounderAuthorization" || current.value.at === "submittingBootstrap")) {
4262
+ const reset = resetSignerSession(this.#signer);
4263
+ if (!reset.ok) return reset;
4264
+ }
4292
4265
  const result = await runCall("retry", this.#convex.mutation(this.#fns.retry, { orgId: input.orgId }));
4293
- return result.ok ? parseLifecycle("retry", result.value) : result;
4266
+ if (!result.ok) return result;
4267
+ return parseLifecycle("retry", result.value);
4294
4268
  }
4295
4269
  async #lifecycleAction(operation, input, call) {
4296
4270
  const cancelled = cancellation(input.signal);
@@ -4302,6 +4276,22 @@ var ConvexOrganizationSetupAdapter = class {
4302
4276
  return parseLifecycle(operation, result.value);
4303
4277
  }
4304
4278
  };
4279
+ function resetSignerSession(signer) {
4280
+ const resetSession = signer.resetSession;
4281
+ if (typeof resetSession !== "function") return {
4282
+ ok: true,
4283
+ value: void 0
4284
+ };
4285
+ try {
4286
+ resetSession.call(signer);
4287
+ return {
4288
+ ok: true,
4289
+ value: void 0
4290
+ };
4291
+ } catch (cause) {
4292
+ return fail$2(cause instanceof CapxulError ? cause : Errors.providerError("openfort", "resetSession", cause, { failure_mode: "unknown" }));
4293
+ }
4294
+ }
4305
4295
  async function runCall(operation, effect) {
4306
4296
  try {
4307
4297
  const result = await Effect.runPromise(Effect.either(effect));
@@ -4328,7 +4318,7 @@ async function signerResult(operation, run) {
4328
4318
  value: await run()
4329
4319
  };
4330
4320
  } catch (cause) {
4331
- return fail$2(Errors.providerError("organization-signer", operation, cause));
4321
+ return fail$2(cause instanceof CapxulError ? cause : Errors.providerError("organization-signer", operation, cause));
4332
4322
  }
4333
4323
  }
4334
4324
  function validatePreparedAuthorities(prepared, signerAddress) {
@@ -5250,6 +5240,9 @@ function transportErrorFromThrown(operation, request, cause) {
5250
5240
  }
5251
5241
  //#endregion
5252
5242
  //#region src/openfort/create-openfort-browser-signer.ts
5243
+ function openfortProviderError(operation, cause) {
5244
+ return cause instanceof CapxulError ? cause : Errors.providerError("openfort", operation, cause, { failure_mode: "unknown" });
5245
+ }
5253
5246
  /**
5254
5247
  * True when the browser cannot perform Web Crypto — sandboxed iframes, headless
5255
5248
  * agent browsers, or non-HTTPS origins. OpenFort's embedded-wallet `configure`
@@ -5318,21 +5311,39 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
5318
5311
  return `${authBaseUrl}/encryption-session`;
5319
5312
  }
5320
5313
  async function fetchBetterAuthAccessToken() {
5321
- const response = await fetch(betterAuthSessionUrl(), { credentials: "include" });
5322
- if (!response.ok) {
5314
+ try {
5315
+ const response = await fetch(betterAuthSessionUrl(), { credentials: "include" });
5316
+ if (!response.ok) {
5317
+ diagnostic?.trace("openfort.token", {
5318
+ ok: false,
5319
+ tokenPresent: false,
5320
+ httpStatus: response.status,
5321
+ failure_mode: "unknown"
5322
+ });
5323
+ return null;
5324
+ }
5325
+ const token = (await response.json()).session?.token?.trim();
5326
+ if (token === void 0 || token.length === 0) {
5327
+ diagnostic?.trace("openfort.token", {
5328
+ ok: false,
5329
+ tokenPresent: false,
5330
+ failure_mode: "unknown"
5331
+ });
5332
+ return null;
5333
+ }
5323
5334
  diagnostic?.trace("openfort.token", {
5335
+ ok: true,
5336
+ tokenPresent: true
5337
+ });
5338
+ return token;
5339
+ } catch (cause) {
5340
+ diagnostic?.trace("openfort.token", {
5341
+ ok: false,
5324
5342
  tokenPresent: false,
5325
- httpStatus: response.status
5343
+ failure_mode: "unknown"
5326
5344
  });
5327
- return null;
5328
- }
5329
- const token = (await response.json()).session?.token?.trim();
5330
- if (token === void 0 || token.length === 0) {
5331
- diagnostic?.trace("openfort.token", { tokenPresent: false });
5332
- return null;
5345
+ throw openfortProviderError("token", cause);
5333
5346
  }
5334
- diagnostic?.trace("openfort.token", { tokenPresent: true });
5335
- return token;
5336
5347
  }
5337
5348
  const openfort = new Openfort({
5338
5349
  baseConfiguration: { publishableKey: bootstrap.openfortPublishableKey },
@@ -5348,25 +5359,60 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
5348
5359
  if (isInsecureBrowserContext()) failNoSecureContext();
5349
5360
  await openfort.waitForInitialization();
5350
5361
  const accessToken = await fetchBetterAuthAccessToken();
5351
- if (accessToken === null) throw Errors.notAuthenticated();
5352
- const encryptionResponse = await fetch(encryptionSessionUrl(), {
5353
- method: "POST",
5354
- credentials: "include",
5355
- headers: {
5356
- Authorization: `Bearer ${accessToken}`,
5357
- "Content-Type": "application/json"
5358
- },
5359
- body: JSON.stringify({})
5360
- });
5361
- diagnostic?.trace("openfort.encryptionSession", { httpStatus: encryptionResponse.status });
5362
+ if (accessToken === null) throw openfortProviderError("token", /* @__PURE__ */ new Error("Better Auth access token unavailable for Openfort"));
5363
+ let encryptionResponse;
5364
+ try {
5365
+ encryptionResponse = await fetch(encryptionSessionUrl(), {
5366
+ method: "POST",
5367
+ credentials: "include",
5368
+ headers: {
5369
+ Authorization: `Bearer ${accessToken}`,
5370
+ "Content-Type": "application/json"
5371
+ },
5372
+ body: JSON.stringify({})
5373
+ });
5374
+ } catch (cause) {
5375
+ diagnostic?.trace("openfort.encryptionSession", {
5376
+ ok: false,
5377
+ failure_mode: "unknown"
5378
+ });
5379
+ throw openfortProviderError("encryptionSession", cause);
5380
+ }
5362
5381
  if (!encryptionResponse.ok) {
5363
- const detail = await encryptionResponse.text();
5364
- throw Errors.providerError("openfort", "encryptionSession", /* @__PURE__ */ new Error(`Openfort encryption session failed (${encryptionResponse.status}): ${detail.slice(0, 200)}`));
5382
+ diagnostic?.trace("openfort.encryptionSession", {
5383
+ ok: false,
5384
+ httpStatus: encryptionResponse.status,
5385
+ failure_mode: "unknown"
5386
+ });
5387
+ throw Errors.providerError("openfort", "encryptionSession", /* @__PURE__ */ new Error(`Openfort encryption session failed (${encryptionResponse.status})`), { failure_mode: "unknown" });
5388
+ }
5389
+ let encryptionBody;
5390
+ try {
5391
+ encryptionBody = await encryptionResponse.json();
5392
+ if (typeof encryptionBody.sessionId !== "string" || encryptionBody.sessionId.length === 0) throw new Error("Openfort encryption session response missing sessionId");
5393
+ } catch (cause) {
5394
+ diagnostic?.trace("openfort.encryptionSession", {
5395
+ ok: false,
5396
+ httpStatus: encryptionResponse.status,
5397
+ failure_mode: "unknown"
5398
+ });
5399
+ throw openfortProviderError("encryptionSession", cause);
5400
+ }
5401
+ diagnostic?.trace("openfort.encryptionSession", {
5402
+ ok: true,
5403
+ httpStatus: encryptionResponse.status
5404
+ });
5405
+ let embeddedState;
5406
+ try {
5407
+ embeddedState = await openfort.embeddedWallet.getEmbeddedState();
5408
+ diagnostic?.trace("openfort.embeddedState", { state: embeddedState });
5409
+ } catch (cause) {
5410
+ diagnostic?.trace("openfort.embeddedState", {
5411
+ ok: false,
5412
+ failure_mode: "unknown"
5413
+ });
5414
+ throw openfortProviderError("embeddedState", cause);
5365
5415
  }
5366
- const encryptionBody = await encryptionResponse.json();
5367
- if (typeof encryptionBody.sessionId !== "string" || encryptionBody.sessionId.length === 0) throw Errors.providerError("openfort", "encryptionSession", /* @__PURE__ */ new Error("Openfort encryption session response missing sessionId"));
5368
- const embeddedState = await openfort.embeddedWallet.getEmbeddedState();
5369
- diagnostic?.trace("openfort.embeddedState", { state: embeddedState });
5370
5416
  if (embeddedState !== EmbeddedState.READY) {
5371
5417
  clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
5372
5418
  diagnostic?.trace("openfort.storageCleared", { beforeConfigure: true });
@@ -5383,9 +5429,9 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
5383
5429
  } catch (cause) {
5384
5430
  diagnostic?.trace("openfort.configure", {
5385
5431
  ok: false,
5386
- message: cause instanceof Error ? cause.message : String(cause)
5432
+ failure_mode: "unknown"
5387
5433
  });
5388
- throw cause;
5434
+ throw openfortProviderError("configure", cause);
5389
5435
  }
5390
5436
  }
5391
5437
  try {
@@ -5394,9 +5440,9 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
5394
5440
  } catch (cause) {
5395
5441
  diagnostic?.trace("openfort.get", {
5396
5442
  ok: false,
5397
- message: cause instanceof Error ? cause.message : String(cause)
5443
+ failure_mode: "unknown"
5398
5444
  });
5399
- throw cause;
5445
+ throw openfortProviderError("get", cause);
5400
5446
  }
5401
5447
  })();
5402
5448
  }
@@ -5415,6 +5461,19 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
5415
5461
  });
5416
5462
  return {
5417
5463
  ...signer,
5464
+ getAddress: async () => {
5465
+ try {
5466
+ const address = await signer.getAddress();
5467
+ diagnostic?.trace("openfort.address", { ok: true });
5468
+ return address;
5469
+ } catch (cause) {
5470
+ diagnostic?.trace("openfort.address", {
5471
+ ok: false,
5472
+ failure_mode: "unknown"
5473
+ });
5474
+ throw openfortProviderError("getAddress", cause);
5475
+ }
5476
+ },
5418
5477
  resetSession: () => {
5419
5478
  walletReadyPromise = null;
5420
5479
  clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
@@ -7921,16 +7980,17 @@ async function advanceOrganizationSetup(setup, orgId, initial, signal) {
7921
7980
  value: lifecycle
7922
7981
  };
7923
7982
  const currentStep = lifecycle.step;
7924
- if (signal?.aborted) {
7925
- const cancelled = Errors.cancelled({ operation: "organization.setup" });
7926
- return recordRetryableCancellation(setup, orgId, lifecycle.step, cancelled);
7927
- }
7983
+ if (signal?.aborted) return recordSetupFailure(setup, orgId, Errors.cancelled({ operation: "organization.setup" }), true);
7928
7984
  const stepInput = {
7929
7985
  orgId,
7930
7986
  ...signal === void 0 ? {} : { signal }
7931
7987
  };
7932
- const next = lifecycle.step === "preparingFounderAccount" ? await setup.prepareFounderAccount(stepInput) : lifecycle.step === "awaitingFounderAuthorization" || lifecycle.step === "submittingBootstrap" ? await setup.authorizeAndSubmitBootstrap(stepInput) : await setup.confirmSubmittedBootstrap(stepInput);
7933
- if (!next.ok) return next.error.code === "CANCELLED" ? recordRetryableCancellation(setup, orgId, lifecycle.step, next.error) : next;
7988
+ const next = lifecycle.step === "preparingFounderAccount" ? await setup.prepareFounderAccount(stepInput) : lifecycle.step === "awaitingFounderAuthorization" ? await setup.authorizeAndSubmitBootstrap(stepInput) : lifecycle.step === "submittingBootstrap" ? await setup.resumeSubmittedBootstrap(stepInput) : await setup.confirmSubmittedBootstrap(stepInput);
7989
+ if (!next.ok) {
7990
+ if (next.error.code === "CANCELLED") return recordSetupFailure(setup, orgId, next.error, true);
7991
+ if (next.error.code === "PROVIDER_ERROR") return recordSetupFailure(setup, orgId, next.error, isRetryableProviderFailure(next.error));
7992
+ return next;
7993
+ }
7934
7994
  const scopedNext = validateOrganizationLifecycleScope(orgId, next.value);
7935
7995
  if (!scopedNext.ok) return scopedNext;
7936
7996
  lifecycle = scopedNext.value;
@@ -7953,17 +8013,20 @@ async function advanceOrganizationSetup(setup, orgId, initial, signal) {
7953
8013
  ]
7954
8014
  }));
7955
8015
  }
7956
- async function recordRetryableCancellation(setup, orgId, at, cancelled) {
8016
+ async function recordSetupFailure(setup, orgId, error, retryable) {
7957
8017
  const recorded = await setup.recordFailure({
7958
8018
  orgId,
7959
- at,
7960
- error: cancelled,
7961
- retryable: true
8019
+ error,
8020
+ retryable
7962
8021
  });
7963
8022
  if (!recorded.ok) return recorded;
7964
8023
  const scopedRecorded = validateOrganizationLifecycleScope(orgId, recorded.value);
7965
8024
  if (!scopedRecorded.ok) return scopedRecorded;
7966
- return fail$1(cancelled);
8025
+ return fail$1(error);
8026
+ }
8027
+ function isRetryableProviderFailure(error) {
8028
+ const mode = error.details?.failure_mode;
8029
+ return mode !== "auth-origin-mismatch" && mode !== "app-env-allowlist" && mode !== "no-secure-context";
7967
8030
  }
7968
8031
  //#endregion
7969
8032
  //#region src/client/org.ts
@@ -8314,7 +8377,18 @@ async function runCompleteOrganizationOnboarding(ops, input, options) {
8314
8377
  });
8315
8378
  if (!started.ok) return fail(started.error);
8316
8379
  const lifecycle = await advanceOrganizationSetup(setup, started.value.orgId, started.value.lifecycle, options?.signal);
8317
- if (!lifecycle.ok) return lifecycle;
8380
+ if (!lifecycle.ok) {
8381
+ const error = lifecycle.error;
8382
+ return fail(new CapxulError(error.code, error.message, {
8383
+ cause: error.cause,
8384
+ details: {
8385
+ ...error.details,
8386
+ orgId: started.value.orgId
8387
+ },
8388
+ ...error.correlationId === void 0 ? {} : { correlationId: error.correlationId },
8389
+ ...error.layer === void 0 ? {} : { layer: error.layer }
8390
+ }));
8391
+ }
8318
8392
  return {
8319
8393
  ok: true,
8320
8394
  value: {
@@ -8438,6 +8512,7 @@ function detectAuthCacheAdapter() {
8438
8512
  }
8439
8513
  //#endregion
8440
8514
  //#region src/client/create-capxul-client.ts
8515
+ const GET_ORGANIZATION_SETUP_PROOF_RECEIPT = makeFunctionReference("org/lifecycle:getProofReceipt");
8441
8516
  function assembleCapxulClient(input) {
8442
8517
  const authCache = input.authCache ?? detectAuthCacheAdapter();
8443
8518
  const layer = Layer.mergeAll(Layer.succeed(AuthClientPortTag, input.ports.authClient), Layer.succeed(ClockPortTag, input.ports.clock), Layer.succeed(TelemetryPortTag, input.ports.telemetry));
@@ -8561,6 +8636,7 @@ function assembleCapxulClient(input) {
8561
8636
  },
8562
8637
  bootstrap: input.bootstrap,
8563
8638
  accounts: { fund: accounts.fund },
8639
+ organizationSetup: { getProofReceipt: (orgId) => runPortEffect(input.ports.convexCall.query(GET_ORGANIZATION_SETUP_PROOF_RECEIPT, { orgId })) },
8564
8640
  telemetry: input.ports.telemetry
8565
8641
  }
8566
8642
  };