@capxul/sdk 2.3.2 → 2.4.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.
- package/README.md +22 -0
- package/dist/{create-capxul-client-DgvnboPR.mjs → create-capxul-client-BrE6tLg6.mjs} +1356 -1251
- package/dist/index.d.mts +39 -4
- package/dist/index.mjs +93 -9
- package/dist/node/index.d.mts +1 -1
- package/dist/{observation-CLhrvkF6.d.mts → observation-BfCkJY5z.d.mts} +116 -31
- package/dist/{signer-BejoR3bA.d.mts → signer-BGTaqZqg.d.mts} +1 -2
- package/dist/testing/index.d.mts +1 -1
- package/dist/testing/index.mjs +1 -1
- package/package.json +4 -4
|
@@ -1291,1078 +1291,240 @@ new Map([
|
|
|
1291
1291
|
["setAllowance(bytes32,uint128,uint128,uint128,uint64,uint64)", "SetAllowance(bytes32,uint128,uint128,uint128,uint64,uint64)"]
|
|
1292
1292
|
].map(([functionSignature, eventSignature]) => [toFunctionSelector(functionSignature), toEventSelector(eventSignature)]));
|
|
1293
1293
|
//#endregion
|
|
1294
|
-
//#region src/
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
}
|
|
1313
|
-
//#endregion
|
|
1314
|
-
//#region src/telemetry/get-failure-mode.ts
|
|
1294
|
+
//#region ../wire/src/brands.ts
|
|
1295
|
+
const lowercasedString = Schema.String.pipe(Schema.decodeTo(Schema.String, {
|
|
1296
|
+
decode: SchemaGetter.transform((value) => value.toLowerCase()),
|
|
1297
|
+
encode: SchemaGetter.transform((value) => value)
|
|
1298
|
+
}));
|
|
1299
|
+
const addressSchema = (name) => lowercasedString.pipe(Schema.refine((value) => EVM_ADDRESS_RE$1.test(value), { message: `${name} must be a 0x-prefixed EVM address` }));
|
|
1300
|
+
const bytes32BrandSchema = (name) => lowercasedString.pipe(Schema.refine((value) => BYTES32_RE.test(value), { message: `${name} must be a 0x-prefixed bytes32 value` }));
|
|
1301
|
+
/** Lowercase, 0x-prefixed bytes32 evidence without a public brand. */
|
|
1302
|
+
const Bytes32Schema = bytes32BrandSchema("value");
|
|
1303
|
+
const AddressSchema$1 = addressSchema("address");
|
|
1304
|
+
const SafeAddressSchema = addressSchema("safe address");
|
|
1305
|
+
const ModuleAddressSchema = addressSchema("module address");
|
|
1306
|
+
const TxHashSchema$1 = bytes32BrandSchema("transaction hash");
|
|
1307
|
+
const RoleKeySchema = bytes32BrandSchema("role key");
|
|
1308
|
+
const AllowanceKeySchema = bytes32BrandSchema("allowance key");
|
|
1309
|
+
const BlockNumberSchema$1 = Schema.Number.pipe(Schema.refine((value) => Number.isSafeInteger(value) && value >= 0, { message: "must be a non-negative safe integer" }));
|
|
1310
|
+
const LogIndexSchema = Schema.Number.pipe(Schema.refine((value) => Number.isSafeInteger(value) && value >= 0, { message: "must be a non-negative safe integer" }));
|
|
1311
|
+
const WeiAmountSchema$1 = Schema.String.pipe(Schema.refine((value) => WEI_RE.test(value), { message: "must be a non-negative integer string" }));
|
|
1312
|
+
const SettlementIdSchema = bytes32BrandSchema("settlementId").pipe(Schema.refine((value) => value !== ZERO_BYTES32, { message: "settlementId must not be zero" }));
|
|
1315
1313
|
/**
|
|
1316
|
-
*
|
|
1317
|
-
*
|
|
1318
|
-
* and its guard can never drift apart.
|
|
1314
|
+
* `AppId` schema. Mirrors `toAppId` from `@capxul/types`:
|
|
1315
|
+
* `app_` + Crockford-base32 ULID (26 chars, first char in `[0-7]`).
|
|
1319
1316
|
*/
|
|
1320
|
-
const
|
|
1321
|
-
"auth-origin-mismatch",
|
|
1322
|
-
"stale-openfort-cache",
|
|
1323
|
-
"app-env-allowlist",
|
|
1324
|
-
"no-secure-context",
|
|
1325
|
-
"unknown"
|
|
1326
|
-
]);
|
|
1327
|
-
function isFailureMode(value) {
|
|
1328
|
-
return typeof value === "string" && FAILURE_MODES.has(value);
|
|
1329
|
-
}
|
|
1317
|
+
const AppIdSchema$1 = Schema.String.pipe(Schema.refine((s) => APP_ID_RE.test(s), { message: "must be app_ plus a ULID" }));
|
|
1330
1318
|
/**
|
|
1331
|
-
*
|
|
1332
|
-
*
|
|
1333
|
-
* Reads `details.failure_mode` and returns it only when it is one of the five
|
|
1334
|
-
* canonical members; any other value (a legacy free string, a typo, a
|
|
1335
|
-
* non-string) yields `undefined` so downstream telemetry never reports an
|
|
1336
|
-
* unrecognised cause.
|
|
1319
|
+
* `ChainId` schema. Mirrors `toChainId`: positive safe integer.
|
|
1337
1320
|
*/
|
|
1338
|
-
|
|
1339
|
-
if (!isCapxulError(error)) return void 0;
|
|
1340
|
-
const details = error.details;
|
|
1341
|
-
if (details === void 0) return void 0;
|
|
1342
|
-
return isFailureMode(details.failure_mode) ? details.failure_mode : void 0;
|
|
1343
|
-
}
|
|
1321
|
+
const ChainIdSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n > 0, { message: "must be a positive safe integer" }));
|
|
1344
1322
|
/**
|
|
1345
|
-
*
|
|
1346
|
-
*
|
|
1347
|
-
*
|
|
1348
|
-
* Resolution order:
|
|
1349
|
-
* 1. the structured mode on the error's `details.failure_mode` (already guarded);
|
|
1350
|
-
* 2. a caller-supplied `contextFailureMode`, but ONLY when it passes the same
|
|
1351
|
-
* runtime membership check — the static `FailureMode` type is erased at
|
|
1352
|
-
* runtime, so an operation string (e.g. `"signer-get-address"`) injected via
|
|
1353
|
-
* a JS caller or `as` cast is rejected here rather than leaking to telemetry;
|
|
1354
|
-
* 3. `"unknown"` otherwise, so an unclassifiable error is still tagged with a
|
|
1355
|
-
* canonical value instead of being emitted with no `failure_mode` at all.
|
|
1323
|
+
* `CurrencyCode` schema. Mirrors `toCurrencyCode`: currently supported
|
|
1324
|
+
* consumer-facing ISO-ish currency code set.
|
|
1356
1325
|
*/
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
}
|
|
1360
|
-
//#endregion
|
|
1361
|
-
//#region src/signer.ts
|
|
1362
|
-
const EVM_ADDRESS_HEX = /^0x[0-9a-fA-F]{40}$/;
|
|
1363
|
-
const ECDSA_SIGNATURE_HEX = /^0x[0-9a-fA-F]{130}$/;
|
|
1364
|
-
const SAFE_OP_DIGEST_HEX = /^0x[0-9a-fA-F]{64}$/;
|
|
1326
|
+
const CurrencyCodeSchema$1 = Schema.String.pipe(Schema.refine((s) => SUPPORTED_CURRENCY_CODES.includes(s), { message: "must be a supported currency code" }));
|
|
1327
|
+
const DOCUMENT_HASH_HEX_RE = /^[0-9a-fA-F]{64}$/;
|
|
1365
1328
|
/**
|
|
1366
|
-
*
|
|
1367
|
-
*
|
|
1368
|
-
* so a gate that reads this never opens on a guess.
|
|
1329
|
+
* `DocumentHash` schema. Mirrors `toDocumentHash`: bare or 0x-prefixed bytes32,
|
|
1330
|
+
* normalized to lowercase 0x-prefixed form.
|
|
1369
1331
|
*/
|
|
1370
|
-
const
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
};
|
|
1332
|
+
const DocumentHashSchema = Schema.String.pipe(Schema.decodeTo(Schema.String, {
|
|
1333
|
+
decode: SchemaGetter.transform((s) => {
|
|
1334
|
+
const stripped = s.startsWith("0x") || s.startsWith("0X") ? s.slice(2) : s;
|
|
1335
|
+
return DOCUMENT_HASH_HEX_RE.test(stripped) ? `0x${stripped.toLowerCase()}` : s;
|
|
1336
|
+
}),
|
|
1337
|
+
encode: SchemaGetter.transform((s) => s)
|
|
1338
|
+
}), Schema.refine((s) => BYTES32_RE.test(s), { message: "must be 32 bytes of hex" }));
|
|
1339
|
+
/** L2 orchestrated money evidence requires a nonzero Document hash. */
|
|
1340
|
+
const NonzeroDocumentHashSchema = DocumentHashSchema.pipe(Schema.refine((value) => value !== ZERO_BYTES32, { message: "documentHash must not be zero" }));
|
|
1374
1341
|
/**
|
|
1375
|
-
*
|
|
1376
|
-
*
|
|
1342
|
+
* `SessionToken` schema. Mirrors `toSessionToken`: non-empty string.
|
|
1343
|
+
* Issuance source distinguishes SDK-handshake tokens from auth-session
|
|
1344
|
+
* tokens; the brand itself is opaque.
|
|
1377
1345
|
*/
|
|
1378
|
-
|
|
1379
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1380
|
-
let current = cause;
|
|
1381
|
-
while (typeof current === "object" && current !== null && !seen.has(current)) {
|
|
1382
|
-
seen.add(current);
|
|
1383
|
-
yield current;
|
|
1384
|
-
current = current.cause;
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
/** Fold a signer throw into the public error contract. */
|
|
1388
|
-
function signerFailure(source, operation, cause) {
|
|
1389
|
-
let failureMode;
|
|
1390
|
-
for (const link of causeChain(cause)) {
|
|
1391
|
-
if (link instanceof CapxulError && link.code === "SIGNER_REJECTED") return link;
|
|
1392
|
-
failureMode = getFailureMode(link) ?? failureMode;
|
|
1393
|
-
const error = link;
|
|
1394
|
-
if (error.code === 4001 || error.error === "passkey_user_cancelled") return Errors.signerRejected({
|
|
1395
|
-
source,
|
|
1396
|
-
cause
|
|
1397
|
-
});
|
|
1398
|
-
}
|
|
1399
|
-
return Errors.providerError("signer", operation, cause, failureMode === void 0 ? void 0 : { failure_mode: failureMode });
|
|
1400
|
-
}
|
|
1346
|
+
const SessionTokenSchema = Schema.String.pipe(Schema.refine((s) => s.length > 0, { message: "must be a non-empty string" }));
|
|
1401
1347
|
/**
|
|
1402
|
-
*
|
|
1403
|
-
* Signs the SafeOp digest via `eth_sign`, then verifies the returned signature
|
|
1404
|
-
* recovers the selected account against that raw digest. Wallets that prefix
|
|
1405
|
-
* `eth_sign` payloads are rejected before the backend submits an invalid SafeOp.
|
|
1406
|
-
* The node key signer lives in `@capxul/sdk/node` (`localPrivateKeySigner`).
|
|
1348
|
+
* `EpochMs` schema. Mirrors `toEpochMs`: non-negative safe integer.
|
|
1407
1349
|
*/
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
source: "injected-eip1193",
|
|
1418
|
-
getAddress: resolveAddress,
|
|
1419
|
-
async signUserOpHash(hash) {
|
|
1420
|
-
if (!SAFE_OP_DIGEST_HEX.test(hash)) throw new Error("injectedWalletSigner: SafeOp digest must be a 0x-prefixed 32-byte hex");
|
|
1421
|
-
const address = await resolveAddress();
|
|
1422
|
-
let signature;
|
|
1423
|
-
try {
|
|
1424
|
-
signature = await provider.request({
|
|
1425
|
-
method: "eth_sign",
|
|
1426
|
-
params: [address, hash]
|
|
1427
|
-
});
|
|
1428
|
-
} catch (cause) {
|
|
1429
|
-
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
1430
|
-
throw new Error(`injectedWalletSigner: eth_sign failed; enable raw-hash signing for deployment (${detail})`, { cause });
|
|
1431
|
-
}
|
|
1432
|
-
if (typeof signature !== "string") throw new Error("injectedWalletSigner: wallet returned a non-string signature");
|
|
1433
|
-
if (!ECDSA_SIGNATURE_HEX.test(signature)) throw new Error("injectedWalletSigner: wallet returned invalid signature format");
|
|
1434
|
-
if ((await recoverRawDigestSigner({
|
|
1435
|
-
hash,
|
|
1436
|
-
signature
|
|
1437
|
-
})).toLowerCase() !== address.toLowerCase()) throw new Error("injectedWalletSigner: wallet signature did not recover the selected account for the raw SafeOp digest; use a raw-hash-capable wallet or @capxul/sdk/node localPrivateKeySigner for deployed flows");
|
|
1438
|
-
return signature;
|
|
1439
|
-
}
|
|
1440
|
-
};
|
|
1441
|
-
}
|
|
1442
|
-
async function recoverRawDigestSigner(input) {
|
|
1350
|
+
const EpochMsSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n >= 0, { message: "must be a non-negative safe integer" }));
|
|
1351
|
+
/**
|
|
1352
|
+
* `DurationMs` schema. Mirrors `toDurationMs`: non-negative safe integer.
|
|
1353
|
+
*/
|
|
1354
|
+
const DurationMsSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n >= 0, { message: "must be a non-negative safe integer" }));
|
|
1355
|
+
//#endregion
|
|
1356
|
+
//#region ../wire/src/bootstrap.ts
|
|
1357
|
+
const PUBLIC_POSTHOG_PROJECT_TOKEN = /^phc_[A-Za-z0-9_-]{1,191}$/u;
|
|
1358
|
+
const PostHogIngestOrigin = Schema.String.pipe(Schema.refine((value) => {
|
|
1443
1359
|
try {
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1360
|
+
const url = new URL(value);
|
|
1361
|
+
return url.protocol === "https:" && url.username.length === 0 && url.password.length === 0 && url.pathname === "/" && url.search.length === 0 && url.hash.length === 0 && (url.hostname === "posthog.com" || url.hostname.endsWith(".posthog.com"));
|
|
1362
|
+
} catch {
|
|
1363
|
+
return false;
|
|
1448
1364
|
}
|
|
1449
|
-
}
|
|
1365
|
+
}, { message: "must be a credential-free PostHog HTTPS ingest origin" }));
|
|
1366
|
+
const EngineeringTelemetryBootstrapPolicy = Schema.Struct({
|
|
1367
|
+
host: PostHogIngestOrigin,
|
|
1368
|
+
projectToken: Schema.String.pipe(Schema.refine((value) => PUBLIC_POSTHOG_PROJECT_TOKEN.test(value), { message: "must be a public PostHog project token" })),
|
|
1369
|
+
capxulEnv: Schema.Union([
|
|
1370
|
+
Schema.Literal("development"),
|
|
1371
|
+
Schema.Literal("staging"),
|
|
1372
|
+
Schema.Literal("production"),
|
|
1373
|
+
Schema.Literal("local")
|
|
1374
|
+
])
|
|
1375
|
+
});
|
|
1376
|
+
/**
|
|
1377
|
+
* `BootstrapEnvelope` v1.
|
|
1378
|
+
*
|
|
1379
|
+
* - `protocol`: discriminator that lets future protocols coexist on the
|
|
1380
|
+
* same endpoint without a wire-shape conflict.
|
|
1381
|
+
* - `version`: numeric version inside the protocol. Unknown versions MUST
|
|
1382
|
+
* fail decode with an INVALID_INPUT-class `CapxulError` at the SDK seam.
|
|
1383
|
+
* - `state`: the resolved bootstrap payload. Field shape matches the
|
|
1384
|
+
* `BootstrapResolution` port; the SDK consumer can pass `state`
|
|
1385
|
+
* directly (after `normalizeRuntimeUrl` on the two URL fields) into
|
|
1386
|
+
* the port without re-branding.
|
|
1387
|
+
*/
|
|
1388
|
+
const BootstrapEnvelope = Schema.Struct({
|
|
1389
|
+
protocol: Schema.Literal("capxul.bootstrap"),
|
|
1390
|
+
version: Schema.Literal(1),
|
|
1391
|
+
state: Schema.Struct({
|
|
1392
|
+
applicationId: AppIdSchema$1,
|
|
1393
|
+
chainId: ChainIdSchema$1,
|
|
1394
|
+
sessionToken: SessionTokenSchema,
|
|
1395
|
+
issuedAt: EpochMsSchema$1,
|
|
1396
|
+
expiresIn: DurationMsSchema$1,
|
|
1397
|
+
authBaseUrl: Schema.String,
|
|
1398
|
+
convexUrl: Schema.String,
|
|
1399
|
+
siteBaseUrl: Schema.String,
|
|
1400
|
+
openfortPublishableKey: Schema.String,
|
|
1401
|
+
shieldPublishableKey: Schema.String,
|
|
1402
|
+
engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
|
|
1403
|
+
})
|
|
1404
|
+
});
|
|
1450
1405
|
//#endregion
|
|
1451
|
-
//#region src/
|
|
1452
|
-
const INVOCATION_OBSERVATION = Symbol("capxul.invocation-observation");
|
|
1453
|
-
const FAILURE_INVOCATION_SNAPSHOT = Symbol("capxul.failure-invocation-snapshot");
|
|
1454
|
-
/** @internal Attach one immutable, non-wire invocation snapshot to an SDK-owned object. */
|
|
1455
|
-
function attachInvocationObservation(target, source) {
|
|
1456
|
-
const snapshot = Object.freeze(source.context === void 0 ? { active: source.active } : {
|
|
1457
|
-
active: source.active,
|
|
1458
|
-
context: Object.freeze({ ...source.context })
|
|
1459
|
-
});
|
|
1460
|
-
Object.defineProperty(target, INVOCATION_OBSERVATION, {
|
|
1461
|
-
configurable: false,
|
|
1462
|
-
enumerable: false,
|
|
1463
|
-
value: snapshot,
|
|
1464
|
-
writable: false
|
|
1465
|
-
});
|
|
1466
|
-
return target;
|
|
1467
|
-
}
|
|
1468
|
-
/** @internal Read the snapshot without exposing its symbol or adding a wire field. */
|
|
1469
|
-
function readInvocationObservation(source) {
|
|
1470
|
-
if (typeof source !== "object" || source === null) return void 0;
|
|
1471
|
-
return source[INVOCATION_OBSERVATION];
|
|
1472
|
-
}
|
|
1473
|
-
/** @internal Carry a snapshot across an SDK adapter projection without resolving again. */
|
|
1474
|
-
function copyInvocationObservation(source, target) {
|
|
1475
|
-
const snapshot = readInvocationObservation(source);
|
|
1476
|
-
return snapshot === void 0 ? target : attachInvocationObservation(target, snapshot);
|
|
1477
|
-
}
|
|
1478
|
-
/** @internal Carry the public call-start delivery decision with its failure envelope. */
|
|
1479
|
-
function markFailureInvocationSnapshot(failure, snapshot) {
|
|
1480
|
-
Object.defineProperty(failure, FAILURE_INVOCATION_SNAPSHOT, {
|
|
1481
|
-
configurable: false,
|
|
1482
|
-
enumerable: false,
|
|
1483
|
-
value: Object.freeze(snapshot),
|
|
1484
|
-
writable: false
|
|
1485
|
-
});
|
|
1486
|
-
return failure;
|
|
1487
|
-
}
|
|
1488
|
-
/** @internal Read the call-start delivery decision; undefined means a direct adapter call. */
|
|
1489
|
-
function readFailureInvocationSnapshot(failure) {
|
|
1490
|
-
if (typeof failure !== "object" || failure === null) return void 0;
|
|
1491
|
-
const snapshot = failure[FAILURE_INVOCATION_SNAPSHOT];
|
|
1492
|
-
return typeof snapshot === "object" && snapshot !== null && "active" in snapshot ? snapshot : void 0;
|
|
1493
|
-
}
|
|
1494
|
-
//#endregion
|
|
1495
|
-
//#region src/domain/machine/telemetry.ts
|
|
1496
|
-
const definedEntries = (values) => Object.fromEntries(Object.entries(values).filter((entry) => entry[1] !== void 0));
|
|
1497
|
-
const SAFE_ENGINEERING_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
1498
|
-
const safeEngineeringIdentifier = (value) => value !== void 0 && SAFE_ENGINEERING_ID.test(value) ? value : void 0;
|
|
1499
|
-
const portFailureOutcome = (code) => code === "CANCELLED" ? "cancelled" : "failed";
|
|
1500
|
-
function formatTraceparent(span) {
|
|
1501
|
-
const traceId = span.traceId.toLowerCase();
|
|
1502
|
-
const spanId = span.spanId.toLowerCase();
|
|
1503
|
-
if (!/^[0-9a-f]{32}$/u.test(traceId) || !/^[0-9a-f]{16}$/u.test(spanId)) return void 0;
|
|
1504
|
-
if (/^0+$/u.test(traceId) || /^0+$/u.test(spanId)) return void 0;
|
|
1505
|
-
return `00-${traceId}-${spanId}-${span.sampled ? "01" : "00"}`;
|
|
1506
|
-
}
|
|
1507
|
-
/** Canonical, bounded fields for the one wide engineering log owned by P3. */
|
|
1508
|
-
const transitionLogFields = (record) => {
|
|
1509
|
-
const { correlation_id, journey_id, ...canonical } = record;
|
|
1510
|
-
const safeCorrelation = safeEngineeringIdentifier(correlation_id);
|
|
1511
|
-
const safeJourney = safeEngineeringIdentifier(journey_id);
|
|
1512
|
-
return definedEntries({
|
|
1513
|
-
...canonical,
|
|
1514
|
-
...safeCorrelation === void 0 ? {} : { correlation_id: safeCorrelation },
|
|
1515
|
-
...safeJourney === void 0 ? {} : { journey_id: safeJourney }
|
|
1516
|
-
});
|
|
1517
|
-
};
|
|
1518
|
-
/** The shell span owns transition/refusal classification and no other seam's fields. */
|
|
1519
|
-
const transitionSpanFields = (record) => {
|
|
1520
|
-
if (record.outcome === "applied") return {
|
|
1521
|
-
machine: record.machine,
|
|
1522
|
-
from: record.from,
|
|
1523
|
-
event: record.event,
|
|
1524
|
-
to: record.to
|
|
1525
|
-
};
|
|
1526
|
-
return {
|
|
1527
|
-
machine: record.machine,
|
|
1528
|
-
state: record.state,
|
|
1529
|
-
event: record.event,
|
|
1530
|
-
...record.outcome === "refused" ? { refused: record.refusal_code } : {}
|
|
1531
|
-
};
|
|
1532
|
-
};
|
|
1406
|
+
//#region ../wire/src/functions.ts
|
|
1533
1407
|
/**
|
|
1534
|
-
*
|
|
1535
|
-
*
|
|
1408
|
+
* Backend function paths, keyed by Convex module path then export name.
|
|
1409
|
+
*
|
|
1410
|
+
* Invariant (gate-enforced): `CAPXUL_FUNCTIONS[m][e] === \`${m}:${e}\``.
|
|
1536
1411
|
*/
|
|
1537
|
-
const
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
to: spec.label(to),
|
|
1654
|
-
slot,
|
|
1655
|
-
epoch,
|
|
1656
|
-
outcome: "applied",
|
|
1657
|
-
duration_ms: duration(env.startedAt),
|
|
1658
|
-
...withCarriage(env.invocation)
|
|
1659
|
-
});
|
|
1660
|
-
const finish = (cursor, state) => {
|
|
1661
|
-
const held = slots.get(cursor.slot);
|
|
1662
|
-
if (held === void 0 || held.cursor !== cursor) return Effect.void;
|
|
1663
|
-
slots.delete(cursor.slot);
|
|
1664
|
-
return Effect.asVoid(Deferred.succeed(held.reply, state));
|
|
1665
|
-
};
|
|
1666
|
-
const recoverFailure = (state, cursor, failure) => {
|
|
1667
|
-
const event = spec.recoverFailure?.(state, cursor.event, failure);
|
|
1668
|
-
if (event === void 0) return Effect.succeed(state);
|
|
1669
|
-
const outcome = spec.transition(state, event);
|
|
1670
|
-
if ("refused" in outcome) return Effect.succeed(state);
|
|
1671
|
-
return Ref.set(cell, outcome.next).pipe(Effect.tap(() => {
|
|
1672
|
-
return emit(applied(state, outcome.next, event, cursor.slot, cursor.epoch, {
|
|
1673
|
-
invocation: cursor.invocation,
|
|
1674
|
-
startedAt: cursor.startedAt
|
|
1675
|
-
})).pipe(Effect.andThen(Effect.sync(() => {
|
|
1676
|
-
notify(outcome.next);
|
|
1677
|
-
})));
|
|
1678
|
-
}), Effect.as(outcome.next));
|
|
1679
|
-
};
|
|
1680
|
-
const step = (env) => Effect.gen(function* () {
|
|
1681
|
-
const state = yield* Ref.get(cell);
|
|
1682
|
-
if (env.origin !== null && (slotEpochs.get(env.origin.slot) ?? 0) !== env.origin.epoch) {
|
|
1683
|
-
const held = slots.get(env.origin.slot);
|
|
1684
|
-
if (held === void 0 || held.cursor !== env.origin) return;
|
|
1685
|
-
slots.delete(env.origin.slot);
|
|
1686
|
-
const event = { _tag: env.origin.event };
|
|
1687
|
-
yield* emit(nonApplied(state, event, "STALE_EPOCH", "refused", env));
|
|
1688
|
-
yield* failReply(held.reply, makeFailure(state, event, "STALE_EPOCH"));
|
|
1689
|
-
return;
|
|
1690
|
-
}
|
|
1691
|
-
if (env.origin !== null && env.event._tag === "~lane/exit") {
|
|
1692
|
-
const exit = env.event;
|
|
1693
|
-
const held = slots.get(env.origin.slot);
|
|
1694
|
-
if (held === void 0 || held.cursor !== env.origin) return;
|
|
1695
|
-
if (exit.defect !== void 0) {
|
|
1696
|
-
slots.delete(env.origin.slot);
|
|
1697
|
-
defect(exit.defect);
|
|
1698
|
-
const event = { _tag: env.origin.event };
|
|
1699
|
-
const failedState = yield* recoverFailure(state, env.origin, {
|
|
1700
|
-
code: "WORK_DIED",
|
|
1701
|
-
message: "Identity work died"
|
|
1702
|
-
});
|
|
1703
|
-
yield* emit(nonApplied(failedState, event, "WORK_DIED", "failed", env));
|
|
1704
|
-
yield* failReply(held.reply, makeFailure(failedState, event, "WORK_DIED"));
|
|
1705
|
-
return;
|
|
1706
|
-
}
|
|
1707
|
-
if (exit.failure !== void 0) {
|
|
1708
|
-
slots.delete(env.origin.slot);
|
|
1709
|
-
const outcome = exit.failure.code === "CANCELLED" ? "cancelled" : "failed";
|
|
1710
|
-
const event = { _tag: env.origin.event };
|
|
1711
|
-
const failedState = exit.failure === timeoutFailure || exit.failure === cancelledFailure ? yield* recoverFailure(state, env.origin, exit.failure) : state;
|
|
1712
|
-
yield* emit(nonApplied(failedState, event, exit.failure.code, outcome, env));
|
|
1713
|
-
const details = exit.failure === timeoutFailure ? { reason: "timeout" } : void 0;
|
|
1714
|
-
yield* failReply(held.reply, makeFailure(failedState, event, exit.failure.code, details));
|
|
1715
|
-
return;
|
|
1716
|
-
}
|
|
1717
|
-
yield* finish(env.origin, state);
|
|
1718
|
-
return;
|
|
1719
|
-
}
|
|
1720
|
-
const outcome = spec.transition(state, env.event);
|
|
1721
|
-
if ("refused" in outcome) {
|
|
1722
|
-
yield* emit(nonApplied(state, env.event, outcome.refused, "refused", env));
|
|
1723
|
-
yield* failReply(env.reply, makeFailure(state, env.event, outcome.refused));
|
|
1724
|
-
return;
|
|
1725
|
-
}
|
|
1726
|
-
const next = outcome.next;
|
|
1727
|
-
yield* Ref.set(cell, next);
|
|
1728
|
-
const emitApplied = (slot, epoch) => emit(applied(state, next, env.event, slot, epoch, env));
|
|
1729
|
-
if (env.origin !== null) {
|
|
1730
|
-
yield* emitApplied(env.origin.slot, env.origin.epoch);
|
|
1731
|
-
notify(next);
|
|
1732
|
-
return;
|
|
1733
|
-
}
|
|
1734
|
-
for (const slot of spec.invalidates?.(env.event) ?? []) {
|
|
1735
|
-
if (!slots.has(slot)) continue;
|
|
1736
|
-
slotEpochs.set(slot, (slotEpochs.get(slot) ?? 0) + 1);
|
|
1737
|
-
}
|
|
1738
|
-
const work = env.reply === null ? void 0 : spec.work?.({
|
|
1739
|
-
state: next,
|
|
1740
|
-
event: env.event
|
|
1741
|
-
});
|
|
1742
|
-
if (work === void 0 || env.reply === null) {
|
|
1743
|
-
const slot = spec.slot(env.event);
|
|
1744
|
-
yield* emitApplied(slot, slotEpochs.get(slot) ?? 0);
|
|
1745
|
-
notify(next);
|
|
1746
|
-
if (env.reply !== null) yield* Deferred.succeed(env.reply, next).pipe(Effect.asVoid);
|
|
1747
|
-
return;
|
|
1748
|
-
}
|
|
1749
|
-
const previous = slots.get(work.slot);
|
|
1750
|
-
if (previous !== void 0) {
|
|
1751
|
-
slots.delete(work.slot);
|
|
1752
|
-
yield* Fiber.interrupt(previous.fiber);
|
|
1753
|
-
const previousEvent = { _tag: previous.cursor.event };
|
|
1754
|
-
const reason = previous.cursor.epoch === (slotEpochs.get(work.slot) ?? 0) ? "SUPERSEDED" : "STALE_EPOCH";
|
|
1755
|
-
yield* emit(nonApplied(next, previousEvent, reason, "refused", {
|
|
1756
|
-
origin: previous.cursor,
|
|
1757
|
-
invocation: previous.cursor.invocation,
|
|
1758
|
-
startedAt: previous.cursor.startedAt
|
|
1759
|
-
}));
|
|
1760
|
-
yield* failReply(previous.reply, makeFailure(next, previousEvent, reason));
|
|
1761
|
-
}
|
|
1762
|
-
const epoch = (slotEpochs.get(work.slot) ?? 0) + 1;
|
|
1763
|
-
slotEpochs.set(work.slot, epoch);
|
|
1764
|
-
const cursor = {
|
|
1765
|
-
slot: work.slot,
|
|
1766
|
-
epoch,
|
|
1767
|
-
event: env.event._tag,
|
|
1768
|
-
startedAt: env.startedAt,
|
|
1769
|
-
invocation: env.invocation
|
|
1770
|
-
};
|
|
1771
|
-
yield* emitApplied(work.slot, epoch);
|
|
1772
|
-
notify(next);
|
|
1773
|
-
const send = (event) => Queue.offer(mailbox, {
|
|
1774
|
-
event,
|
|
1775
|
-
reply: null,
|
|
1776
|
-
origin: cursor,
|
|
1777
|
-
invocation: env.invocation,
|
|
1778
|
-
startedAt: env.startedAt
|
|
1779
|
-
}).pipe(Effect.asVoid);
|
|
1780
|
-
const complete = (event) => Queue.offer(mailbox, {
|
|
1781
|
-
event,
|
|
1782
|
-
reply: null,
|
|
1783
|
-
origin: cursor,
|
|
1784
|
-
invocation: env.invocation,
|
|
1785
|
-
startedAt: env.startedAt
|
|
1786
|
-
}).pipe(Effect.asVoid);
|
|
1787
|
-
const guarded = Effect.gen(function* () {
|
|
1788
|
-
yield* Effect.annotateCurrentSpan({
|
|
1789
|
-
slot: work.slot,
|
|
1790
|
-
epoch,
|
|
1791
|
-
port: work.port
|
|
1792
|
-
});
|
|
1793
|
-
return yield* control(work.run(send, env.invocation), env.invocation);
|
|
1794
|
-
}).pipe(Effect.withSpan(`identity.work.${work.slot}`)).pipe(Effect.matchEffect({
|
|
1795
|
-
onFailure: (failure) => complete({
|
|
1796
|
-
_tag: "~lane/exit",
|
|
1797
|
-
failure
|
|
1798
|
-
}),
|
|
1799
|
-
onSuccess: () => complete({ _tag: "~lane/exit" })
|
|
1800
|
-
}), Effect.catchDefect((cause) => complete({
|
|
1801
|
-
_tag: "~lane/exit",
|
|
1802
|
-
defect: cause
|
|
1803
|
-
})));
|
|
1804
|
-
const fiber = yield* Effect.forkChild(guarded, { startImmediately: true });
|
|
1805
|
-
slots.set(work.slot, {
|
|
1806
|
-
fiber,
|
|
1807
|
-
reply: env.reply,
|
|
1808
|
-
cursor
|
|
1809
|
-
});
|
|
1810
|
-
});
|
|
1811
|
-
const loop = yield* Effect.forkScoped(Effect.forever(Queue.take(mailbox).pipe(Effect.flatMap((env) => withInvocationParentSpan(step(env), env.invocation)))));
|
|
1812
|
-
yield* Effect.addFinalizer(() => Effect.gen(function* () {
|
|
1813
|
-
stopped = true;
|
|
1814
|
-
yield* Fiber.interrupt(loop);
|
|
1815
|
-
const state = yield* Ref.get(cell);
|
|
1816
|
-
const outstanding = [...slots.values()];
|
|
1817
|
-
slots.clear();
|
|
1818
|
-
for (const held of outstanding) {
|
|
1819
|
-
yield* Fiber.interrupt(held.fiber);
|
|
1820
|
-
const env = {
|
|
1821
|
-
origin: held.cursor,
|
|
1822
|
-
invocation: held.cursor.invocation,
|
|
1823
|
-
startedAt: held.cursor.startedAt
|
|
1824
|
-
};
|
|
1825
|
-
const event = { _tag: held.cursor.event };
|
|
1826
|
-
yield* emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", env));
|
|
1827
|
-
yield* failReply(held.reply, makeFailure(state, event, "ACTOR_STOPPED"));
|
|
1828
|
-
}
|
|
1829
|
-
stateObservers.clear();
|
|
1830
|
-
transitionObservers.clear();
|
|
1831
|
-
yield* Queue.shutdown(mailbox);
|
|
1832
|
-
}));
|
|
1833
|
-
const offer = (event, reply, invocation) => {
|
|
1834
|
-
const state = Ref.getUnsafe(cell);
|
|
1835
|
-
if (stopped) return withInvocationParentSpan(emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", {
|
|
1836
|
-
origin: null,
|
|
1837
|
-
invocation,
|
|
1838
|
-
startedAt: Date.now()
|
|
1839
|
-
})).pipe(Effect.andThen(Effect.fail(makeFailure(state, event, "ACTOR_STOPPED")))), invocation);
|
|
1840
|
-
const startedAt = Date.now();
|
|
1841
|
-
return Queue.offer(mailbox, {
|
|
1842
|
-
event,
|
|
1843
|
-
reply,
|
|
1844
|
-
origin: null,
|
|
1845
|
-
invocation,
|
|
1846
|
-
startedAt
|
|
1847
|
-
}).pipe(Effect.asVoid);
|
|
1848
|
-
};
|
|
1849
|
-
return {
|
|
1850
|
-
ask: (event, invocation = {}) => Effect.gen(function* () {
|
|
1851
|
-
const reply = yield* Deferred.make();
|
|
1852
|
-
yield* offer(event, reply, invocation);
|
|
1853
|
-
return yield* Deferred.await(reply);
|
|
1854
|
-
}),
|
|
1855
|
-
tell: (event, invocation = {}) => offer(event, null, invocation),
|
|
1856
|
-
snapshot: () => Ref.getUnsafe(cell),
|
|
1857
|
-
subscribe: (observer) => {
|
|
1858
|
-
stateObservers.add(observer);
|
|
1859
|
-
return () => {
|
|
1860
|
-
stateObservers.delete(observer);
|
|
1861
|
-
};
|
|
1862
|
-
},
|
|
1863
|
-
subscribeTransitions: (observer) => {
|
|
1864
|
-
transitionObservers.add(observer);
|
|
1865
|
-
return () => {
|
|
1866
|
-
transitionObservers.delete(observer);
|
|
1867
|
-
};
|
|
1868
|
-
}
|
|
1869
|
-
};
|
|
1870
|
-
});
|
|
1871
|
-
//#endregion
|
|
1872
|
-
//#region src/surface/to-capxul-result.ts
|
|
1873
|
-
/** The one place an Effect becomes a Promise on the public surface. */
|
|
1874
|
-
async function runProgram(program, runPromise = Effect.runPromise) {
|
|
1875
|
-
const result = await runPromise(program.pipe(Effect.catchDefect((defect) => Effect.fail(Errors.unknown(defect))), Effect.result));
|
|
1876
|
-
if (Result.isFailure(result)) return {
|
|
1877
|
-
ok: false,
|
|
1878
|
-
error: result.failure
|
|
1879
|
-
};
|
|
1880
|
-
return {
|
|
1881
|
-
ok: true,
|
|
1882
|
-
value: result.success
|
|
1883
|
-
};
|
|
1884
|
-
}
|
|
1885
|
-
async function toCapxulResult(program, layer) {
|
|
1886
|
-
return runProgram(program.pipe(Effect.provide(layer)));
|
|
1887
|
-
}
|
|
1888
|
-
/**
|
|
1889
|
-
* Signal-aware bridge for the Convex-backed method bundles. The adapters fail
|
|
1890
|
-
* with `{ publicError }` rather than a bare `CapxulError`, so the wrapper is
|
|
1891
|
-
* unwrapped here — once — instead of at every call site.
|
|
1892
|
-
*/
|
|
1893
|
-
async function runIfActive(signal, operation, effect, runPromise = Effect.runPromise) {
|
|
1894
|
-
if (signal?.aborted === true) return {
|
|
1895
|
-
ok: false,
|
|
1896
|
-
error: Errors.cancelled({ operation })
|
|
1897
|
-
};
|
|
1898
|
-
const program = effect().pipe(Effect.mapError((error) => error.publicError));
|
|
1899
|
-
return runProgram(signal === void 0 ? program : Effect.raceFirst(program, Effect.callback((resume) => {
|
|
1900
|
-
if (signal.aborted) {
|
|
1901
|
-
resume(Effect.fail(Errors.cancelled({ operation })));
|
|
1902
|
-
return Effect.void;
|
|
1903
|
-
}
|
|
1904
|
-
const onAbort = () => resume(Effect.fail(Errors.cancelled({ operation })));
|
|
1905
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
1906
|
-
return Effect.sync(() => signal.removeEventListener("abort", onAbort));
|
|
1907
|
-
})), runPromise);
|
|
1908
|
-
}
|
|
1909
|
-
//#endregion
|
|
1910
|
-
//#region src/surface/_shared/effect-actor-bridge.ts
|
|
1911
|
-
const facadeOutcome = (value) => {
|
|
1912
|
-
if (typeof value !== "object" || value === null || !("ok" in value) || value.ok !== false) return "succeeded";
|
|
1913
|
-
const reason = value.reason;
|
|
1914
|
-
if (reason === "CANCELLED") return "cancelled";
|
|
1915
|
-
return typeof reason === "string" && EXPECTED_OPERATION_OUTCOMES.has(reason) ? "refused" : "failed";
|
|
1916
|
-
};
|
|
1917
|
-
const rejectedFacadeOutcome = (cause) => {
|
|
1918
|
-
if (cause instanceof CapxulError) return cause.code === "CANCELLED" ? "cancelled" : "failed";
|
|
1919
|
-
if (typeof cause !== "object" || cause === null) return "failed";
|
|
1920
|
-
const candidate = cause;
|
|
1921
|
-
return candidate.code === "CANCELLED" || candidate.reason === "CANCELLED" || candidate.error?.code === "CANCELLED" || candidate.publicError?.code === "CANCELLED" ? "cancelled" : "failed";
|
|
1922
|
-
};
|
|
1923
|
-
const RESOLVED_FACADE_SPAN_FAILURE = Symbol("resolved facade span failure");
|
|
1924
|
-
/** The single facade-span bridge consumed by the renderer-neutral React facade. */
|
|
1925
|
-
async function runIdentityFacade(verb, controls, run, runPromise = Effect.runPromise) {
|
|
1926
|
-
const correlationId = safeEngineeringIdentifier(controls?.correlation_id);
|
|
1927
|
-
let resolvedFailure;
|
|
1928
|
-
return runPromise(Effect.gen(function* () {
|
|
1929
|
-
const parent = yield* Effect.currentSpan;
|
|
1930
|
-
return yield* Effect.tryPromise({
|
|
1931
|
-
try: () => run(carryInvocationParentSpan(controls, parent)),
|
|
1932
|
-
catch: (cause) => cause
|
|
1933
|
-
});
|
|
1934
|
-
}).pipe(Effect.tapError((cause) => Effect.annotateCurrentSpan({ outcome: rejectedFacadeOutcome(cause) })), Effect.flatMap((value) => {
|
|
1935
|
-
const outcome = facadeOutcome(value);
|
|
1936
|
-
return Effect.annotateCurrentSpan({ outcome }).pipe(Effect.andThen(outcome === "failed" || outcome === "cancelled" ? Effect.sync(() => {
|
|
1937
|
-
resolvedFailure = value;
|
|
1938
|
-
}).pipe(Effect.andThen(Effect.fail(RESOLVED_FACADE_SPAN_FAILURE))) : Effect.succeed(value)));
|
|
1939
|
-
}), Effect.withSpan(`identity.${verb}`), Effect.annotateSpans({
|
|
1940
|
-
verb,
|
|
1941
|
-
...correlationId === void 0 ? {} : { correlation_id: correlationId }
|
|
1942
|
-
}), Effect.catch((cause) => cause === RESOLVED_FACADE_SPAN_FAILURE ? Effect.succeed(resolvedFailure) : Effect.fail(cause))));
|
|
1943
|
-
}
|
|
1944
|
-
/**
|
|
1945
|
-
* Bridge an Effect whose typed failure carries `{ publicError: CapxulError }`
|
|
1946
|
-
* into the `Promise<CapxulResult<T>>` shape the consumer-facing method bundles
|
|
1947
|
-
* return. Applies to every port that surfaces a `publicError` (smart-account,
|
|
1948
|
-
* identity, auth-cache, account provision/deploy).
|
|
1949
|
-
*/
|
|
1950
|
-
async function runPortEffect(effect, controls, operation = "port", runPromise = Effect.runPromise) {
|
|
1951
|
-
const deadlineDelay = controls?.deadlineMs === void 0 ? void 0 : Math.max(0, controls.deadlineMs - Date.now());
|
|
1952
|
-
const timeoutMs = controls?.timeoutMs === void 0 ? deadlineDelay : deadlineDelay === void 0 ? controls.timeoutMs : Math.min(controls.timeoutMs, deadlineDelay);
|
|
1953
|
-
const observed = (timeoutMs === void 0 ? effect : effect.pipe(Effect.timeoutOrElse({
|
|
1954
|
-
duration: `${timeoutMs} millis`,
|
|
1955
|
-
orElse: () => Effect.fail({ publicError: Errors.providerTimeout("sdk", operation, timeoutMs) })
|
|
1956
|
-
}))).pipe(Effect.tap(() => Effect.annotateCurrentSpan({ outcome: "succeeded" })), Effect.tapError((failure) => {
|
|
1957
|
-
const mode = failure.publicError.details?.failure_mode;
|
|
1958
|
-
return Effect.annotateCurrentSpan({
|
|
1959
|
-
outcome: portFailureOutcome(failure.publicError.code),
|
|
1960
|
-
failure_code: failure.publicError.code,
|
|
1961
|
-
...typeof mode === "string" ? { mode } : {}
|
|
1962
|
-
});
|
|
1963
|
-
}), Effect.tapDefect(() => Effect.annotateCurrentSpan({
|
|
1964
|
-
outcome: "failed",
|
|
1965
|
-
failure_code: "UNKNOWN"
|
|
1966
|
-
})), Effect.withSpan(`identity.port.${operation}`));
|
|
1967
|
-
return runIfActive(controls?.signal, operation, () => withInvocationParentSpan(observed, controls), runPromise);
|
|
1968
|
-
}
|
|
1969
|
-
/** Map machine-internal failure state onto the public SDK error vocabulary. */
|
|
1970
|
-
function publicIdentityFailure(failure) {
|
|
1971
|
-
if (failure.error !== void 0) return failure.error;
|
|
1972
|
-
return new CapxulError(failure.code === "WORK_DIED" ? "UNKNOWN" : failure.code, failure.message, {
|
|
1973
|
-
...failure.mode === void 0 ? {} : { details: { failure_mode: failure.mode } },
|
|
1974
|
-
layer: "identity"
|
|
1975
|
-
});
|
|
1976
|
-
}
|
|
1977
|
-
/** The session carried by the actor's current snapshot, or `null`. */
|
|
1978
|
-
function sessionFromActor(actor) {
|
|
1979
|
-
return actor.authSession();
|
|
1980
|
-
}
|
|
1981
|
-
//#endregion
|
|
1982
|
-
//#region src/surface/account-deps.ts
|
|
1983
|
-
/** The single Context tag the account atom + `account.getStatus` resolve. */
|
|
1984
|
-
var AccountDepsTag = class extends Context.Service()("@capxul/sdk/AccountDeps") {};
|
|
1985
|
-
/** Wrap a bundle as the `Layer<AccountDepsTag>` the React provider consumes. */
|
|
1986
|
-
function accountDepsLayer(deps) {
|
|
1987
|
-
return Layer.succeed(AccountDepsTag, deps);
|
|
1988
|
-
}
|
|
1989
|
-
/**
|
|
1990
|
-
* `account.getStatus` as a single Effect requiring ONLY `AccountDepsTag`.
|
|
1991
|
-
* Faithful transcription of the current public method body (`account.ts`):
|
|
1992
|
-
* resolve the session (actor first, then the resume-path `authCache`), then
|
|
1993
|
-
* walk the readiness ladder. The `SmartAccountPort` failure narrows to its
|
|
1994
|
-
* `publicError`; a consumer `AccountProvider.getAddress` rejection becomes a
|
|
1995
|
-
* `providerError` (defending the public `CapxulResult` contract).
|
|
1996
|
-
*/
|
|
1997
|
-
const accountStatusProgram = Effect.gen(function* () {
|
|
1998
|
-
const deps = yield* AccountDepsTag;
|
|
1999
|
-
const session = yield* Effect.promise(() => currentSession(deps.actor, deps.authCache));
|
|
2000
|
-
if (session === null) return { status: "notAuthenticated" };
|
|
2001
|
-
const current = yield* deps.smartAccountPort.loadByAuthUserId(session.authUserId).pipe(Effect.mapError((failure) => failure.publicError));
|
|
2002
|
-
if (current !== null) return statusFromAccount(current, deps.requirement);
|
|
2003
|
-
if (deps.requirement === "none") return {
|
|
2004
|
-
status: "accountReady",
|
|
2005
|
-
requirement: deps.requirement,
|
|
2006
|
-
account: null,
|
|
2007
|
-
deployment: { status: "counterfactual" }
|
|
2008
|
-
};
|
|
2009
|
-
const signer = deps.signer;
|
|
2010
|
-
if (signer === void 0) return {
|
|
2011
|
-
status: "accountRequired",
|
|
2012
|
-
requirement: deps.requirement,
|
|
2013
|
-
chainId: deps.chainId
|
|
2014
|
-
};
|
|
2015
|
-
const signerAddress = yield* Effect.tryPromise({
|
|
2016
|
-
try: () => signer.getAddress(),
|
|
2017
|
-
catch: (cause) => signerFailure(signer.source, "getAddress", cause)
|
|
2018
|
-
});
|
|
2019
|
-
return {
|
|
2020
|
-
status: "accountProviderReady",
|
|
2021
|
-
requirement: deps.requirement,
|
|
2022
|
-
chainId: deps.chainId,
|
|
2023
|
-
source: signer.source,
|
|
2024
|
-
signerAddress
|
|
2025
|
-
};
|
|
2026
|
-
});
|
|
2027
|
-
/**
|
|
2028
|
-
* Resume-path session resolution. Consults the actor first; if the actor has
|
|
2029
|
-
* no session (e.g. page refresh before any sign-in event), reads
|
|
2030
|
-
* `authCache.getSession`. `AuthCacheError` is non-fatal at this read point —
|
|
2031
|
-
* treat as "no session" and let the consumer's `auth.signIn` path resolve.
|
|
2032
|
-
*/
|
|
2033
|
-
async function currentSession(actor, authCache) {
|
|
2034
|
-
const fromActor = sessionFromActor(actor);
|
|
2035
|
-
if (fromActor !== null) return fromActor;
|
|
2036
|
-
if (authCache === void 0) return null;
|
|
2037
|
-
const cached = await Effect.runPromise(Effect.result(authCache.getSession));
|
|
2038
|
-
if (Result.isSuccess(cached)) {
|
|
2039
|
-
if (cached.success !== null && typeof actor.restoreAuthSession === "function") {
|
|
2040
|
-
const restored = await Effect.runPromise(Effect.result(actor.restoreAuthSession(cached.success)));
|
|
2041
|
-
if (Result.isFailure(restored)) return null;
|
|
2042
|
-
}
|
|
2043
|
-
return cached.success;
|
|
2044
|
-
}
|
|
2045
|
-
return null;
|
|
2046
|
-
}
|
|
2047
|
-
/** Map a backend `SmartAccount` row onto the readiness `AccountStatus`. */
|
|
2048
|
-
function statusFromAccount(account, requirement) {
|
|
2049
|
-
if (requirement === "deployed") {
|
|
2050
|
-
if (account.deployedAt === null) return {
|
|
2051
|
-
status: "accountPrepared",
|
|
2052
|
-
requirement,
|
|
2053
|
-
account,
|
|
2054
|
-
deployment: { status: "counterfactual" }
|
|
2055
|
-
};
|
|
2056
|
-
return {
|
|
2057
|
-
status: "accountReady",
|
|
2058
|
-
requirement,
|
|
2059
|
-
account,
|
|
2060
|
-
deployment: {
|
|
2061
|
-
status: "deployed",
|
|
2062
|
-
deployedAt: account.deployedAt
|
|
2063
|
-
}
|
|
2064
|
-
};
|
|
2065
|
-
}
|
|
2066
|
-
return {
|
|
2067
|
-
status: "accountReady",
|
|
2068
|
-
requirement,
|
|
2069
|
-
account,
|
|
2070
|
-
deployment: account.deployedAt === null ? { status: "counterfactual" } : {
|
|
2071
|
-
status: "deployed",
|
|
2072
|
-
deployedAt: account.deployedAt
|
|
2073
|
-
}
|
|
2074
|
-
};
|
|
2075
|
-
}
|
|
2076
|
-
//#endregion
|
|
2077
|
-
//#region src/surface/account-lifecycle.ts
|
|
2078
|
-
function isActiveProvisioningPhase(phase) {
|
|
2079
|
-
return phase.status === "wallet" || phase.status === "identity" || phase.status === "provision" || phase.status === "deploy";
|
|
2080
|
-
}
|
|
2081
|
-
function isRequirementMet(status, requirement) {
|
|
2082
|
-
if (requirement === "none") return status.status !== "notAuthenticated";
|
|
2083
|
-
if (status.status !== "accountReady") return false;
|
|
2084
|
-
return requirement !== "deployed" || status.deployment.status === "deployed";
|
|
2085
|
-
}
|
|
2086
|
-
function mapProvisioningFailureStep(step) {
|
|
2087
|
-
switch (step) {
|
|
2088
|
-
case "wallet": return "connecting";
|
|
2089
|
-
case "identity": return "registering";
|
|
2090
|
-
case "provision":
|
|
2091
|
-
case "deploy": return "activating";
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
function mapProvisioningPhaseToSetupStep(phase) {
|
|
2095
|
-
switch (phase.status) {
|
|
2096
|
-
case "idle":
|
|
2097
|
-
case "wallet": return "connecting";
|
|
2098
|
-
case "identity": return "registering";
|
|
2099
|
-
case "provision":
|
|
2100
|
-
case "deploy": return "activating";
|
|
2101
|
-
case "ready":
|
|
2102
|
-
case "failed": return "connecting";
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2105
|
-
function isSettingUpLifecycle(lifecycle) {
|
|
2106
|
-
return lifecycle.status === "settingUp";
|
|
2107
|
-
}
|
|
2108
|
-
function readyLifecycle(status, accountId) {
|
|
2109
|
-
return {
|
|
2110
|
-
status: "ready",
|
|
2111
|
-
accountId,
|
|
2112
|
-
canTransact: status.status === "accountReady" && status.deployment.status === "deployed"
|
|
2113
|
-
};
|
|
2114
|
-
}
|
|
2115
|
-
function mapAccountLifecycle(input) {
|
|
2116
|
-
const { status, phase, requirement, accountId } = input;
|
|
2117
|
-
if (status.status === "notAuthenticated") return { status: "loading" };
|
|
2118
|
-
if (phase.status === "failed") return {
|
|
2119
|
-
status: "failed",
|
|
2120
|
-
at: mapProvisioningFailureStep(phase.at),
|
|
2121
|
-
error: phase.error
|
|
2122
|
-
};
|
|
2123
|
-
if (isRequirementMet(status, requirement) || phase.status === "ready") {
|
|
2124
|
-
if (accountId === void 0) return { status: "loading" };
|
|
2125
|
-
return readyLifecycle(status, accountId);
|
|
2126
|
-
}
|
|
2127
|
-
if (isActiveProvisioningPhase(phase) || phase.status === "idle") return {
|
|
2128
|
-
status: "settingUp",
|
|
2129
|
-
step: mapProvisioningPhaseToSetupStep(phase)
|
|
2130
|
-
};
|
|
2131
|
-
return { status: "loading" };
|
|
2132
|
-
}
|
|
2133
|
-
//#endregion
|
|
2134
|
-
//#region ../wire/src/brands.ts
|
|
2135
|
-
const lowercasedString = Schema.String.pipe(Schema.decodeTo(Schema.String, {
|
|
2136
|
-
decode: SchemaGetter.transform((value) => value.toLowerCase()),
|
|
2137
|
-
encode: SchemaGetter.transform((value) => value)
|
|
2138
|
-
}));
|
|
2139
|
-
const addressSchema = (name) => lowercasedString.pipe(Schema.refine((value) => EVM_ADDRESS_RE$1.test(value), { message: `${name} must be a 0x-prefixed EVM address` }));
|
|
2140
|
-
const bytes32BrandSchema = (name) => lowercasedString.pipe(Schema.refine((value) => BYTES32_RE.test(value), { message: `${name} must be a 0x-prefixed bytes32 value` }));
|
|
2141
|
-
/** Lowercase, 0x-prefixed bytes32 evidence without a public brand. */
|
|
2142
|
-
const Bytes32Schema = bytes32BrandSchema("value");
|
|
2143
|
-
const AddressSchema$1 = addressSchema("address");
|
|
2144
|
-
const SafeAddressSchema = addressSchema("safe address");
|
|
2145
|
-
const ModuleAddressSchema = addressSchema("module address");
|
|
2146
|
-
const TxHashSchema$1 = bytes32BrandSchema("transaction hash");
|
|
2147
|
-
const RoleKeySchema = bytes32BrandSchema("role key");
|
|
2148
|
-
const AllowanceKeySchema = bytes32BrandSchema("allowance key");
|
|
2149
|
-
const BlockNumberSchema$1 = Schema.Number.pipe(Schema.refine((value) => Number.isSafeInteger(value) && value >= 0, { message: "must be a non-negative safe integer" }));
|
|
2150
|
-
const LogIndexSchema = Schema.Number.pipe(Schema.refine((value) => Number.isSafeInteger(value) && value >= 0, { message: "must be a non-negative safe integer" }));
|
|
2151
|
-
const WeiAmountSchema$1 = Schema.String.pipe(Schema.refine((value) => WEI_RE.test(value), { message: "must be a non-negative integer string" }));
|
|
2152
|
-
const SettlementIdSchema = bytes32BrandSchema("settlementId").pipe(Schema.refine((value) => value !== ZERO_BYTES32, { message: "settlementId must not be zero" }));
|
|
2153
|
-
/**
|
|
2154
|
-
* `AppId` schema. Mirrors `toAppId` from `@capxul/types`:
|
|
2155
|
-
* `app_` + Crockford-base32 ULID (26 chars, first char in `[0-7]`).
|
|
2156
|
-
*/
|
|
2157
|
-
const AppIdSchema$1 = Schema.String.pipe(Schema.refine((s) => APP_ID_RE.test(s), { message: "must be app_ plus a ULID" }));
|
|
2158
|
-
/**
|
|
2159
|
-
* `ChainId` schema. Mirrors `toChainId`: positive safe integer.
|
|
2160
|
-
*/
|
|
2161
|
-
const ChainIdSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n > 0, { message: "must be a positive safe integer" }));
|
|
2162
|
-
/**
|
|
2163
|
-
* `CurrencyCode` schema. Mirrors `toCurrencyCode`: currently supported
|
|
2164
|
-
* consumer-facing ISO-ish currency code set.
|
|
2165
|
-
*/
|
|
2166
|
-
const CurrencyCodeSchema$1 = Schema.String.pipe(Schema.refine((s) => SUPPORTED_CURRENCY_CODES.includes(s), { message: "must be a supported currency code" }));
|
|
2167
|
-
const DOCUMENT_HASH_HEX_RE = /^[0-9a-fA-F]{64}$/;
|
|
2168
|
-
/**
|
|
2169
|
-
* `DocumentHash` schema. Mirrors `toDocumentHash`: bare or 0x-prefixed bytes32,
|
|
2170
|
-
* normalized to lowercase 0x-prefixed form.
|
|
2171
|
-
*/
|
|
2172
|
-
const DocumentHashSchema = Schema.String.pipe(Schema.decodeTo(Schema.String, {
|
|
2173
|
-
decode: SchemaGetter.transform((s) => {
|
|
2174
|
-
const stripped = s.startsWith("0x") || s.startsWith("0X") ? s.slice(2) : s;
|
|
2175
|
-
return DOCUMENT_HASH_HEX_RE.test(stripped) ? `0x${stripped.toLowerCase()}` : s;
|
|
2176
|
-
}),
|
|
2177
|
-
encode: SchemaGetter.transform((s) => s)
|
|
2178
|
-
}), Schema.refine((s) => BYTES32_RE.test(s), { message: "must be 32 bytes of hex" }));
|
|
2179
|
-
/** L2 orchestrated money evidence requires a nonzero Document hash. */
|
|
2180
|
-
const NonzeroDocumentHashSchema = DocumentHashSchema.pipe(Schema.refine((value) => value !== ZERO_BYTES32, { message: "documentHash must not be zero" }));
|
|
2181
|
-
/**
|
|
2182
|
-
* `SessionToken` schema. Mirrors `toSessionToken`: non-empty string.
|
|
2183
|
-
* Issuance source distinguishes SDK-handshake tokens from auth-session
|
|
2184
|
-
* tokens; the brand itself is opaque.
|
|
2185
|
-
*/
|
|
2186
|
-
const SessionTokenSchema = Schema.String.pipe(Schema.refine((s) => s.length > 0, { message: "must be a non-empty string" }));
|
|
2187
|
-
/**
|
|
2188
|
-
* `EpochMs` schema. Mirrors `toEpochMs`: non-negative safe integer.
|
|
2189
|
-
*/
|
|
2190
|
-
const EpochMsSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n >= 0, { message: "must be a non-negative safe integer" }));
|
|
2191
|
-
/**
|
|
2192
|
-
* `DurationMs` schema. Mirrors `toDurationMs`: non-negative safe integer.
|
|
2193
|
-
*/
|
|
2194
|
-
const DurationMsSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n >= 0, { message: "must be a non-negative safe integer" }));
|
|
2195
|
-
//#endregion
|
|
2196
|
-
//#region ../wire/src/bootstrap.ts
|
|
2197
|
-
const PUBLIC_POSTHOG_PROJECT_TOKEN = /^phc_[A-Za-z0-9_-]{1,191}$/u;
|
|
2198
|
-
const PostHogIngestOrigin = Schema.String.pipe(Schema.refine((value) => {
|
|
2199
|
-
try {
|
|
2200
|
-
const url = new URL(value);
|
|
2201
|
-
return url.protocol === "https:" && url.username.length === 0 && url.password.length === 0 && url.pathname === "/" && url.search.length === 0 && url.hash.length === 0 && (url.hostname === "posthog.com" || url.hostname.endsWith(".posthog.com"));
|
|
2202
|
-
} catch {
|
|
2203
|
-
return false;
|
|
2204
|
-
}
|
|
2205
|
-
}, { message: "must be a credential-free PostHog HTTPS ingest origin" }));
|
|
2206
|
-
const EngineeringTelemetryBootstrapPolicy = Schema.Struct({
|
|
2207
|
-
host: PostHogIngestOrigin,
|
|
2208
|
-
projectToken: Schema.String.pipe(Schema.refine((value) => PUBLIC_POSTHOG_PROJECT_TOKEN.test(value), { message: "must be a public PostHog project token" })),
|
|
2209
|
-
capxulEnv: Schema.Union([
|
|
2210
|
-
Schema.Literal("development"),
|
|
2211
|
-
Schema.Literal("staging"),
|
|
2212
|
-
Schema.Literal("production")
|
|
2213
|
-
])
|
|
2214
|
-
});
|
|
2215
|
-
/**
|
|
2216
|
-
* `BootstrapEnvelope` v1.
|
|
2217
|
-
*
|
|
2218
|
-
* - `protocol`: discriminator that lets future protocols coexist on the
|
|
2219
|
-
* same endpoint without a wire-shape conflict.
|
|
2220
|
-
* - `version`: numeric version inside the protocol. Unknown versions MUST
|
|
2221
|
-
* fail decode with an INVALID_INPUT-class `CapxulError` at the SDK seam.
|
|
2222
|
-
* - `state`: the resolved bootstrap payload. Field shape matches the
|
|
2223
|
-
* `BootstrapResolution` port; the SDK consumer can pass `state`
|
|
2224
|
-
* directly (after `normalizeRuntimeUrl` on the two URL fields) into
|
|
2225
|
-
* the port without re-branding.
|
|
2226
|
-
*/
|
|
2227
|
-
const BootstrapEnvelope = Schema.Struct({
|
|
2228
|
-
protocol: Schema.Literal("capxul.bootstrap"),
|
|
2229
|
-
version: Schema.Literal(1),
|
|
2230
|
-
state: Schema.Struct({
|
|
2231
|
-
applicationId: AppIdSchema$1,
|
|
2232
|
-
chainId: ChainIdSchema$1,
|
|
2233
|
-
sessionToken: SessionTokenSchema,
|
|
2234
|
-
issuedAt: EpochMsSchema$1,
|
|
2235
|
-
expiresIn: DurationMsSchema$1,
|
|
2236
|
-
authBaseUrl: Schema.String,
|
|
2237
|
-
convexUrl: Schema.String,
|
|
2238
|
-
siteBaseUrl: Schema.String,
|
|
2239
|
-
openfortPublishableKey: Schema.String,
|
|
2240
|
-
shieldPublishableKey: Schema.String,
|
|
2241
|
-
engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
|
|
2242
|
-
})
|
|
2243
|
-
});
|
|
2244
|
-
//#endregion
|
|
2245
|
-
//#region ../wire/src/functions.ts
|
|
2246
|
-
/**
|
|
2247
|
-
* Backend function paths, keyed by Convex module path then export name.
|
|
2248
|
-
*
|
|
2249
|
-
* Invariant (gate-enforced): `CAPXUL_FUNCTIONS[m][e] === \`${m}:${e}\``.
|
|
2250
|
-
*/
|
|
2251
|
-
const CAPXUL_FUNCTIONS = {
|
|
2252
|
-
"account/actions": {
|
|
2253
|
-
faucetMint: "account/actions:faucetMint",
|
|
2254
|
-
readBalance: "account/actions:readBalance"
|
|
2255
|
-
},
|
|
2256
|
-
"financialOps/addressBook": {
|
|
2257
|
-
add: "financialOps/addressBook:add",
|
|
2258
|
-
get: "financialOps/addressBook:get",
|
|
2259
|
-
hide: "financialOps/addressBook:hide",
|
|
2260
|
-
label: "financialOps/addressBook:label",
|
|
2261
|
-
list: "financialOps/addressBook:list",
|
|
2262
|
-
unhide: "financialOps/addressBook:unhide"
|
|
2263
|
-
},
|
|
2264
|
-
"financialOps/destinations": {
|
|
2265
|
-
add: "financialOps/destinations:add",
|
|
2266
|
-
list: "financialOps/destinations:list",
|
|
2267
|
-
remove: "financialOps/destinations:remove"
|
|
2268
|
-
},
|
|
2269
|
-
"financialOps/mutations": { createPayee: "financialOps/mutations:createPayee" },
|
|
2270
|
-
"financialOps/queries": {
|
|
2271
|
-
depositInstructions: "financialOps/queries:depositInstructions",
|
|
2272
|
-
getPayee: "financialOps/queries:getPayee",
|
|
2273
|
-
getPayment: "financialOps/queries:getPayment",
|
|
2274
|
-
listPayments: "financialOps/queries:listPayments",
|
|
2275
|
-
me: "financialOps/queries:me",
|
|
2276
|
-
renderStoredDocument: "financialOps/queries:renderStoredDocument",
|
|
2277
|
-
resolveHandle: "financialOps/queries:resolveHandle",
|
|
2278
|
-
resolvePayee: "financialOps/queries:resolvePayee",
|
|
2279
|
-
verifyPaymentDocument: "financialOps/queries:verifyPaymentDocument"
|
|
2280
|
-
},
|
|
2281
|
-
"financialOps/requestsInbox": {
|
|
2282
|
-
approve: "financialOps/requestsInbox:approve",
|
|
2283
|
-
cancel: "financialOps/requestsInbox:cancel",
|
|
2284
|
-
decline: "financialOps/requestsInbox:decline",
|
|
2285
|
-
get: "financialOps/requestsInbox:get",
|
|
2286
|
-
inboxList: "financialOps/requestsInbox:inboxList",
|
|
2287
|
-
issue: "financialOps/requestsInbox:issue",
|
|
2288
|
-
list: "financialOps/requestsInbox:list"
|
|
2289
|
-
},
|
|
2290
|
-
"identity/mutations": {
|
|
2291
|
-
completeOnboarding: "identity/mutations:completeOnboarding",
|
|
2292
|
-
create: "identity/mutations:create",
|
|
2293
|
-
update: "identity/mutations:update"
|
|
2294
|
-
},
|
|
2295
|
-
"identity/queries": {
|
|
2296
|
-
loadByAuthUserId: "identity/queries:loadByAuthUserId",
|
|
2297
|
-
usernameAvailable: "identity/queries:usernameAvailable"
|
|
2298
|
-
},
|
|
2299
|
-
"holdings/actions": { current: "holdings/actions:current" },
|
|
2300
|
-
"movement/activity": {
|
|
2301
|
-
annotate: "movement/activity:annotate",
|
|
2302
|
-
get: "movement/activity:get",
|
|
2303
|
-
list: "movement/activity:list"
|
|
2304
|
-
},
|
|
2305
|
-
"moneyExecution/actions": {
|
|
2306
|
-
preparePermissionExecution: "moneyExecution/actions:preparePermissionExecution",
|
|
2307
|
-
preparePaymentExecution: "moneyExecution/actions:preparePaymentExecution",
|
|
2308
|
-
submitPermissionExecution: "moneyExecution/actions:submitPermissionExecution",
|
|
2309
|
-
submitPaymentExecution: "moneyExecution/actions:submitPaymentExecution"
|
|
2310
|
-
},
|
|
2311
|
-
"moneyExecution/paymentCommandActions": {
|
|
2312
|
-
preparePaymentLifecycleExecution: "moneyExecution/paymentCommandActions:preparePaymentLifecycleExecution",
|
|
2313
|
-
prepareOrganizationPaymentExecution: "moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
|
|
2314
|
-
submitPaymentCommandExecution: "moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
|
|
2315
|
-
},
|
|
2316
|
-
media: {
|
|
2317
|
-
generateUploadUrl: "media:generateUploadUrl",
|
|
2318
|
-
setOrgLogo: "media:setOrgLogo",
|
|
2319
|
-
setProfileImage: "media:setProfileImage"
|
|
2320
|
-
},
|
|
2321
|
-
"org/actions": {
|
|
2322
|
-
confirmBootstrap: "org/actions:confirmBootstrap",
|
|
2323
|
-
detectAndAcceptPendingInvitations: "org/actions:detectAndAcceptPendingInvitations",
|
|
2324
|
-
inviteMember: "org/actions:inviteMember",
|
|
2325
|
-
prepareBootstrap: "org/actions:prepareBootstrap",
|
|
2326
|
-
prepareFounderAccount: "org/actions:prepareFounderAccount",
|
|
2327
|
-
readTreasury: "org/actions:readTreasury",
|
|
2328
|
-
resumeBootstrapSubmission: "org/actions:resumeBootstrapSubmission",
|
|
2329
|
-
submitBootstrap: "org/actions:submitBootstrap"
|
|
2330
|
-
},
|
|
2331
|
-
"permission/actions": { verify: "permission/actions:verify" },
|
|
2332
|
-
"permission/mutations": { command: "permission/mutations:command" },
|
|
2333
|
-
"permission/queries": {
|
|
2334
|
-
authorize: "permission/queries:authorize",
|
|
2335
|
-
read: "permission/queries:read"
|
|
2336
|
-
},
|
|
2337
|
-
"org/lifecycle": {
|
|
2338
|
-
getProofReceipt: "org/lifecycle:getProofReceipt",
|
|
2339
|
-
load: "org/lifecycle:load",
|
|
2340
|
-
recordFailure: "org/lifecycle:recordFailure",
|
|
2341
|
-
retry: "org/lifecycle:retry",
|
|
2342
|
-
startOrResume: "org/lifecycle:startOrResume"
|
|
2343
|
-
},
|
|
2344
|
-
"org/mutations": {
|
|
2345
|
-
createOrg: "org/mutations:createOrg",
|
|
2346
|
-
resendInviteToken: "org/mutations:resendInviteToken"
|
|
2347
|
-
},
|
|
2348
|
-
"org/queries": {
|
|
2349
|
-
listAll: "org/queries:listAll",
|
|
2350
|
-
listMembersByOrgId: "org/queries:listMembersByOrgId",
|
|
2351
|
-
listMine: "org/queries:listMine",
|
|
2352
|
-
listRolesByOrgId: "org/queries:listRolesByOrgId",
|
|
2353
|
-
loadByOrgId: "org/queries:loadByOrgId",
|
|
2354
|
-
me: "org/queries:me"
|
|
2355
|
-
},
|
|
2356
|
-
"smartAccount/actions": {
|
|
2357
|
-
claim: "smartAccount/actions:claim",
|
|
2358
|
-
confirmDeployment: "smartAccount/actions:confirmDeployment"
|
|
2359
|
-
},
|
|
2360
|
-
"smartAccount/mutations": { provision: "smartAccount/mutations:provision" },
|
|
2361
|
-
"smartAccount/queries": {
|
|
2362
|
-
loadByAuthUserId: "smartAccount/queries:loadByAuthUserId",
|
|
2363
|
-
loadBySmartAccountAddress: "smartAccount/queries:loadBySmartAccountAddress"
|
|
2364
|
-
},
|
|
2365
|
-
system: { health: "system:health" }
|
|
1412
|
+
const CAPXUL_FUNCTIONS = {
|
|
1413
|
+
"account/actions": {
|
|
1414
|
+
faucetMint: "account/actions:faucetMint",
|
|
1415
|
+
readBalance: "account/actions:readBalance"
|
|
1416
|
+
},
|
|
1417
|
+
"financialOps/addressBook": {
|
|
1418
|
+
add: "financialOps/addressBook:add",
|
|
1419
|
+
get: "financialOps/addressBook:get",
|
|
1420
|
+
hide: "financialOps/addressBook:hide",
|
|
1421
|
+
label: "financialOps/addressBook:label",
|
|
1422
|
+
list: "financialOps/addressBook:list",
|
|
1423
|
+
unhide: "financialOps/addressBook:unhide"
|
|
1424
|
+
},
|
|
1425
|
+
"financialOps/destinations": {
|
|
1426
|
+
add: "financialOps/destinations:add",
|
|
1427
|
+
list: "financialOps/destinations:list",
|
|
1428
|
+
remove: "financialOps/destinations:remove"
|
|
1429
|
+
},
|
|
1430
|
+
"financialOps/mutations": { createPayee: "financialOps/mutations:createPayee" },
|
|
1431
|
+
"financialOps/queries": {
|
|
1432
|
+
depositInstructions: "financialOps/queries:depositInstructions",
|
|
1433
|
+
getPayee: "financialOps/queries:getPayee",
|
|
1434
|
+
getPayment: "financialOps/queries:getPayment",
|
|
1435
|
+
listPayments: "financialOps/queries:listPayments",
|
|
1436
|
+
me: "financialOps/queries:me",
|
|
1437
|
+
renderStoredDocument: "financialOps/queries:renderStoredDocument",
|
|
1438
|
+
resolveHandle: "financialOps/queries:resolveHandle",
|
|
1439
|
+
resolvePayee: "financialOps/queries:resolvePayee",
|
|
1440
|
+
verifyPaymentDocument: "financialOps/queries:verifyPaymentDocument"
|
|
1441
|
+
},
|
|
1442
|
+
"financialOps/requestsInbox": {
|
|
1443
|
+
approve: "financialOps/requestsInbox:approve",
|
|
1444
|
+
cancel: "financialOps/requestsInbox:cancel",
|
|
1445
|
+
decline: "financialOps/requestsInbox:decline",
|
|
1446
|
+
get: "financialOps/requestsInbox:get",
|
|
1447
|
+
inboxList: "financialOps/requestsInbox:inboxList",
|
|
1448
|
+
issue: "financialOps/requestsInbox:issue",
|
|
1449
|
+
list: "financialOps/requestsInbox:list"
|
|
1450
|
+
},
|
|
1451
|
+
"identity/mutations": {
|
|
1452
|
+
completeOnboarding: "identity/mutations:completeOnboarding",
|
|
1453
|
+
create: "identity/mutations:create",
|
|
1454
|
+
update: "identity/mutations:update"
|
|
1455
|
+
},
|
|
1456
|
+
"identity/queries": {
|
|
1457
|
+
loadByAuthUserId: "identity/queries:loadByAuthUserId",
|
|
1458
|
+
usernameAvailable: "identity/queries:usernameAvailable"
|
|
1459
|
+
},
|
|
1460
|
+
"holdings/actions": { current: "holdings/actions:current" },
|
|
1461
|
+
"movement/activity": {
|
|
1462
|
+
annotate: "movement/activity:annotate",
|
|
1463
|
+
get: "movement/activity:get",
|
|
1464
|
+
list: "movement/activity:list",
|
|
1465
|
+
summary: "movement/activity:summary"
|
|
1466
|
+
},
|
|
1467
|
+
"moneyExecution/actions": {
|
|
1468
|
+
preparePermissionExecution: "moneyExecution/actions:preparePermissionExecution",
|
|
1469
|
+
preparePaymentExecution: "moneyExecution/actions:preparePaymentExecution",
|
|
1470
|
+
submitPermissionExecution: "moneyExecution/actions:submitPermissionExecution",
|
|
1471
|
+
submitPaymentExecution: "moneyExecution/actions:submitPaymentExecution"
|
|
1472
|
+
},
|
|
1473
|
+
"moneyExecution/paymentCommandActions": {
|
|
1474
|
+
preparePaymentLifecycleExecution: "moneyExecution/paymentCommandActions:preparePaymentLifecycleExecution",
|
|
1475
|
+
prepareOrganizationPaymentExecution: "moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
|
|
1476
|
+
submitPaymentCommandExecution: "moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
|
|
1477
|
+
},
|
|
1478
|
+
media: {
|
|
1479
|
+
generateUploadUrl: "media:generateUploadUrl",
|
|
1480
|
+
setOrgLogo: "media:setOrgLogo",
|
|
1481
|
+
setProfileImage: "media:setProfileImage"
|
|
1482
|
+
},
|
|
1483
|
+
"org/actions": {
|
|
1484
|
+
confirmBootstrap: "org/actions:confirmBootstrap",
|
|
1485
|
+
detectAndAcceptPendingInvitations: "org/actions:detectAndAcceptPendingInvitations",
|
|
1486
|
+
inviteMember: "org/actions:inviteMember",
|
|
1487
|
+
prepareBootstrap: "org/actions:prepareBootstrap",
|
|
1488
|
+
prepareFounderAccount: "org/actions:prepareFounderAccount",
|
|
1489
|
+
readTreasury: "org/actions:readTreasury",
|
|
1490
|
+
resumeBootstrapSubmission: "org/actions:resumeBootstrapSubmission",
|
|
1491
|
+
submitBootstrap: "org/actions:submitBootstrap"
|
|
1492
|
+
},
|
|
1493
|
+
"permission/actions": { verify: "permission/actions:verify" },
|
|
1494
|
+
"permission/mutations": { command: "permission/mutations:command" },
|
|
1495
|
+
"permission/queries": {
|
|
1496
|
+
authorize: "permission/queries:authorize",
|
|
1497
|
+
read: "permission/queries:read"
|
|
1498
|
+
},
|
|
1499
|
+
"org/lifecycle": {
|
|
1500
|
+
getProofReceipt: "org/lifecycle:getProofReceipt",
|
|
1501
|
+
load: "org/lifecycle:load",
|
|
1502
|
+
recordFailure: "org/lifecycle:recordFailure",
|
|
1503
|
+
retry: "org/lifecycle:retry",
|
|
1504
|
+
startOrResume: "org/lifecycle:startOrResume"
|
|
1505
|
+
},
|
|
1506
|
+
"org/mutations": {
|
|
1507
|
+
createOrg: "org/mutations:createOrg",
|
|
1508
|
+
resendInviteToken: "org/mutations:resendInviteToken"
|
|
1509
|
+
},
|
|
1510
|
+
"org/queries": {
|
|
1511
|
+
listAll: "org/queries:listAll",
|
|
1512
|
+
listMembersByOrgId: "org/queries:listMembersByOrgId",
|
|
1513
|
+
listMine: "org/queries:listMine",
|
|
1514
|
+
listRolesByOrgId: "org/queries:listRolesByOrgId",
|
|
1515
|
+
loadByOrgId: "org/queries:loadByOrgId",
|
|
1516
|
+
me: "org/queries:me"
|
|
1517
|
+
},
|
|
1518
|
+
"smartAccount/actions": {
|
|
1519
|
+
claim: "smartAccount/actions:claim",
|
|
1520
|
+
confirmDeployment: "smartAccount/actions:confirmDeployment"
|
|
1521
|
+
},
|
|
1522
|
+
"smartAccount/mutations": { provision: "smartAccount/mutations:provision" },
|
|
1523
|
+
"smartAccount/queries": {
|
|
1524
|
+
loadByAuthUserId: "smartAccount/queries:loadByAuthUserId",
|
|
1525
|
+
loadBySmartAccountAddress: "smartAccount/queries:loadBySmartAccountAddress"
|
|
1526
|
+
},
|
|
1527
|
+
system: { health: "system:health" }
|
|
2366
1528
|
};
|
|
2367
1529
|
//#endregion
|
|
2368
1530
|
//#region ../wire/src/status.ts
|
|
@@ -3579,170 +2741,1010 @@ const PermissionResourceSchema = Schema.Union([
|
|
|
3579
2741
|
PermissionAssignmentSchema,
|
|
3580
2742
|
Schema.Array(PermissionSchema)
|
|
3581
2743
|
]);
|
|
3582
|
-
Schema.Struct({
|
|
3583
|
-
status: Schema.Literal("prepared"),
|
|
3584
|
-
commandId: PaymentCommandIdSchema,
|
|
3585
|
-
operation: PermissionOperationSchema,
|
|
3586
|
-
expectedRevision: nonNegativeInteger("expectedRevision"),
|
|
3587
|
-
userOpSenderSafe: SafeAddressSchema,
|
|
3588
|
-
organizationSafe: SafeAddressSchema,
|
|
3589
|
-
calls: Schema.Array(PermissionChainCallSchema),
|
|
3590
|
-
value: PermissionResourceSchema
|
|
3591
|
-
});
|
|
3592
|
-
Schema.Struct({
|
|
3593
|
-
orgId: nonEmpty("orgId"),
|
|
3594
|
-
signerAddress: AddressSchema$1,
|
|
3595
|
-
command: PermissionCommandSchema
|
|
2744
|
+
Schema.Struct({
|
|
2745
|
+
status: Schema.Literal("prepared"),
|
|
2746
|
+
commandId: PaymentCommandIdSchema,
|
|
2747
|
+
operation: PermissionOperationSchema,
|
|
2748
|
+
expectedRevision: nonNegativeInteger("expectedRevision"),
|
|
2749
|
+
userOpSenderSafe: SafeAddressSchema,
|
|
2750
|
+
organizationSafe: SafeAddressSchema,
|
|
2751
|
+
calls: Schema.Array(PermissionChainCallSchema),
|
|
2752
|
+
value: PermissionResourceSchema
|
|
2753
|
+
});
|
|
2754
|
+
Schema.Struct({
|
|
2755
|
+
orgId: nonEmpty("orgId"),
|
|
2756
|
+
signerAddress: AddressSchema$1,
|
|
2757
|
+
command: PermissionCommandSchema
|
|
2758
|
+
});
|
|
2759
|
+
Schema.Struct({
|
|
2760
|
+
executionId: MoneyExecutionIdSchema,
|
|
2761
|
+
commandId: PaymentCommandIdSchema,
|
|
2762
|
+
operation: PermissionOperationSchema,
|
|
2763
|
+
orgId: nonEmpty("orgId"),
|
|
2764
|
+
command: PermissionCommandSchema,
|
|
2765
|
+
expectedRevision: nonNegativeInteger("expectedRevision"),
|
|
2766
|
+
chainId: BaseSepoliaChainIdSchema,
|
|
2767
|
+
signerAddress: AddressSchema$1,
|
|
2768
|
+
userOpSenderSafe: SafeAddressSchema,
|
|
2769
|
+
digest: Bytes32Schema,
|
|
2770
|
+
value: PermissionResourceSchema
|
|
2771
|
+
});
|
|
2772
|
+
Schema.Struct({
|
|
2773
|
+
status: Schema.Literal("submitted"),
|
|
2774
|
+
executionId: MoneyExecutionIdSchema,
|
|
2775
|
+
commandId: PaymentCommandIdSchema,
|
|
2776
|
+
userOpHash: Bytes32Schema,
|
|
2777
|
+
value: PermissionResourceSchema
|
|
2778
|
+
});
|
|
2779
|
+
`
|
|
2780
|
+
.capxul-doc{--ink:#1d1d1f;--muted:#6e6e73;--line:#e7e7ea;--accent:#0a7d4b;--bg:#fff;
|
|
2781
|
+
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;
|
|
2782
|
+
color:var(--ink);background:var(--bg);max-width:44rem;margin:0 auto;padding:2.75rem 3rem;
|
|
2783
|
+
border:1px solid var(--line);border-radius:16px;box-shadow:0 1px 2px rgba(0,0,0,.04),0 12px 32px rgba(0,0,0,.06);
|
|
2784
|
+
line-height:1.5;font-size:15px;overflow-wrap:anywhere;word-break:break-word}
|
|
2785
|
+
.capxul-doc *{box-sizing:border-box;min-width:0}
|
|
2786
|
+
.capxul-doc .doc-header{display:flex;flex-direction:column;gap:1.25rem;padding-bottom:1.5rem;border-bottom:1px solid var(--line);margin-bottom:1.75rem}
|
|
2787
|
+
.capxul-doc .doc-brand{display:flex;align-items:center;gap:.5rem;color:var(--accent);font-weight:600}
|
|
2788
|
+
.capxul-doc .doc-brand-mark{font-size:1.1rem}
|
|
2789
|
+
.capxul-doc .doc-brand-name{letter-spacing:.02em}
|
|
2790
|
+
.capxul-doc .doc-headline{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;flex-wrap:wrap}
|
|
2791
|
+
.capxul-doc .doc-title{font-size:1.9rem;font-weight:700;letter-spacing:-.02em;margin:0}
|
|
2792
|
+
.capxul-doc .doc-badge{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
|
|
2793
|
+
color:var(--accent);background:rgba(10,125,75,.1);padding:.3rem .6rem;border-radius:999px;max-width:100%;text-align:right}
|
|
2794
|
+
.capxul-doc .doc-parties{display:grid;grid-template-columns:1fr 1fr;gap:1.25rem;margin-bottom:1.75rem}
|
|
2795
|
+
.capxul-doc .doc-party{display:flex;flex-direction:column;gap:.15rem}
|
|
2796
|
+
.capxul-doc .doc-party-label{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}
|
|
2797
|
+
.capxul-doc .doc-party-name{font-weight:600}
|
|
2798
|
+
.capxul-doc .doc-meta{display:flex;flex-direction:column;gap:.4rem;margin-bottom:1.75rem}
|
|
2799
|
+
.capxul-doc .doc-meta-row{display:flex;justify-content:space-between;gap:1rem;font-size:.92rem}
|
|
2800
|
+
.capxul-doc .doc-meta-label{color:var(--muted);flex-shrink:0}
|
|
2801
|
+
.capxul-doc .doc-meta-value{font-weight:500;text-align:right}
|
|
2802
|
+
.capxul-doc time{color:var(--ink);font-variant-numeric:tabular-nums}
|
|
2803
|
+
.capxul-doc .doc-line-items{width:100%;border-collapse:collapse;margin:.5rem 0 1.5rem;font-size:.92rem}
|
|
2804
|
+
.capxul-doc .doc-line-items th{text-align:left;font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;
|
|
2805
|
+
color:var(--muted);font-weight:600;padding:.5rem .25rem;border-bottom:1px solid var(--line)}
|
|
2806
|
+
.capxul-doc .doc-line-items td{padding:.7rem .25rem;border-bottom:1px solid var(--line)}
|
|
2807
|
+
.capxul-doc .doc-li-qty,.capxul-doc .doc-li-unit,.capxul-doc .doc-li-total{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
|
|
2808
|
+
.capxul-doc .doc-li-desc{width:100%}
|
|
2809
|
+
.capxul-doc .doc-totals{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem}
|
|
2810
|
+
.capxul-doc .doc-total-line{display:flex;justify-content:space-between;align-items:baseline;gap:1rem}
|
|
2811
|
+
.capxul-doc .doc-total-label{color:var(--muted)}
|
|
2812
|
+
.capxul-doc .doc-total-deduction .doc-amount-value{color:var(--muted)}
|
|
2813
|
+
.capxul-doc .doc-total-grand{border-top:2px solid var(--ink);margin-top:.5rem;padding-top:.75rem;font-size:1.15rem}
|
|
2814
|
+
.capxul-doc .doc-total-grand .doc-amount-value{font-weight:700}
|
|
2815
|
+
.capxul-doc .doc-amount-value{font-variant-numeric:tabular-nums;font-weight:600}
|
|
2816
|
+
.capxul-doc .doc-hero{text-align:center;padding:1.5rem 0 2rem}
|
|
2817
|
+
.capxul-doc .doc-hero-label{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}
|
|
2818
|
+
.capxul-doc .doc-hero-amount{font-size:2.6rem;font-weight:700;letter-spacing:-.02em;margin-top:.35rem}
|
|
2819
|
+
.capxul-doc .doc-note{color:var(--ink);background:#f7f7f8;border-radius:10px;padding:.9rem 1.1rem;margin:0}
|
|
2820
|
+
.capxul-doc .doc-dest-address{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9rem}
|
|
2821
|
+
.capxul-doc .doc-footer{margin-top:1.75rem;padding-top:1.25rem;border-top:1px solid var(--line);color:var(--muted);font-size:.9rem}
|
|
2822
|
+
.capxul-doc code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.82rem;color:var(--muted);word-break:break-all}
|
|
2823
|
+
@media (max-width:540px){.capxul-doc{padding:1.75rem 1.25rem}.capxul-doc .doc-parties{grid-template-columns:1fr}}
|
|
2824
|
+
`.trim();
|
|
2825
|
+
//#endregion
|
|
2826
|
+
//#region ../wire/src/secret-material.ts
|
|
2827
|
+
const SENSITIVE_MATERIAL_PATTERNS = [
|
|
2828
|
+
/0x[a-fA-F0-9]{40,}/u,
|
|
2829
|
+
/(?:^|[^a-fA-F0-9])[a-fA-F0-9]{64}(?:$|[^a-fA-F0-9])/u,
|
|
2830
|
+
/eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/u,
|
|
2831
|
+
/(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/iu
|
|
2832
|
+
];
|
|
2833
|
+
/**
|
|
2834
|
+
* Reject: does the value carry any known secret material? Best effort — callers
|
|
2835
|
+
* drop the whole value on a match; a false negative is a leak, a false positive
|
|
2836
|
+
* merely omits an observation field.
|
|
2837
|
+
*/
|
|
2838
|
+
function containsSensitiveMaterial(value) {
|
|
2839
|
+
return SENSITIVE_MATERIAL_PATTERNS.some((pattern) => pattern.test(value));
|
|
2840
|
+
}
|
|
2841
|
+
//#endregion
|
|
2842
|
+
//#region ../wire/src/observation-context.ts
|
|
2843
|
+
/** Single bounded HTTP carrier used before a Convex action envelope exists. */
|
|
2844
|
+
const OBSERVATION_CONTEXT_HEADER = "x-capxul-observation-context";
|
|
2845
|
+
const FIELD_RULES = {
|
|
2846
|
+
application: {
|
|
2847
|
+
maxLength: 64,
|
|
2848
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._-]*$/u
|
|
2849
|
+
},
|
|
2850
|
+
applicationId: {
|
|
2851
|
+
maxLength: 30,
|
|
2852
|
+
pattern: APP_ID_RE
|
|
2853
|
+
},
|
|
2854
|
+
release: {
|
|
2855
|
+
maxLength: 128,
|
|
2856
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._+@:/-]*$/u
|
|
2857
|
+
},
|
|
2858
|
+
sessionId: {
|
|
2859
|
+
maxLength: 128,
|
|
2860
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
2861
|
+
},
|
|
2862
|
+
organizationId: {
|
|
2863
|
+
maxLength: 128,
|
|
2864
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
2865
|
+
},
|
|
2866
|
+
journeyId: {
|
|
2867
|
+
maxLength: 128,
|
|
2868
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
2869
|
+
},
|
|
2870
|
+
correlationId: {
|
|
2871
|
+
maxLength: 128,
|
|
2872
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
2873
|
+
},
|
|
2874
|
+
anonymousId: {
|
|
2875
|
+
maxLength: 128,
|
|
2876
|
+
pattern: /^anon_[A-Za-z0-9-]+$/u
|
|
2877
|
+
},
|
|
2878
|
+
traceparent: {
|
|
2879
|
+
maxLength: 55,
|
|
2880
|
+
pattern: /^00-[0-9a-f]{32}-[0-9a-f]{16}-0[01]$/u
|
|
2881
|
+
}
|
|
2882
|
+
};
|
|
2883
|
+
/**
|
|
2884
|
+
* Copy only the canonical allowlist and silently omit malformed/sensitive
|
|
2885
|
+
* values. Observation metadata is best effort and may never reject a domain
|
|
2886
|
+
* operation.
|
|
2887
|
+
*/
|
|
2888
|
+
function sanitizeObservationContext(input) {
|
|
2889
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return void 0;
|
|
2890
|
+
const source = input;
|
|
2891
|
+
const sanitized = {};
|
|
2892
|
+
for (const field of Object.keys(FIELD_RULES)) {
|
|
2893
|
+
const value = source[field];
|
|
2894
|
+
if (!isSafeField(field, value)) continue;
|
|
2895
|
+
sanitized[field] = value;
|
|
2896
|
+
}
|
|
2897
|
+
return Object.keys(sanitized).length === 0 ? void 0 : sanitized;
|
|
2898
|
+
}
|
|
2899
|
+
/** Encode only the sanitized allowlist; absence stays absence. */
|
|
2900
|
+
function encodeObservationContextHeader(input) {
|
|
2901
|
+
const sanitized = sanitizeObservationContext(input);
|
|
2902
|
+
return sanitized === void 0 ? void 0 : JSON.stringify(sanitized);
|
|
2903
|
+
}
|
|
2904
|
+
function isSafeField(field, value) {
|
|
2905
|
+
if (typeof value !== "string") return false;
|
|
2906
|
+
const rule = FIELD_RULES[field];
|
|
2907
|
+
return value.length > 0 && value.length <= rule.maxLength && value === value.trim() && !value.includes("://") && !containsSensitiveMaterial(value) && rule.pattern.test(value);
|
|
2908
|
+
}
|
|
2909
|
+
//#endregion
|
|
2910
|
+
//#region src/surface/_shared/provisioning-telemetry.ts
|
|
2911
|
+
async function emitProvisioningTelemetry(telemetry, smartAccount) {
|
|
2912
|
+
if (telemetry === void 0) return;
|
|
2913
|
+
await Effect.runPromise(telemetry.emit({
|
|
2914
|
+
name: "provisioning_safe_created",
|
|
2915
|
+
props: { safe_address: smartAccount.smartAccountAddress }
|
|
2916
|
+
}).pipe(Effect.catch((cause) => Effect.sync(() => reportProvisioningTelemetryFailure("error", smartAccount, cause))), Effect.catchDefect((cause) => Effect.sync(() => reportProvisioningTelemetryFailure("defect", smartAccount, cause)))));
|
|
2917
|
+
}
|
|
2918
|
+
function reportProvisioningTelemetryFailure(kind, smartAccount, cause) {
|
|
2919
|
+
if (!isProvisioningTelemetryDebugEnabled()) return;
|
|
2920
|
+
globalThis.console?.warn?.("[capxul] provisioning telemetry dropped", {
|
|
2921
|
+
kind,
|
|
2922
|
+
safeAddress: smartAccount.smartAccountAddress,
|
|
2923
|
+
cause
|
|
2924
|
+
});
|
|
2925
|
+
}
|
|
2926
|
+
function isProvisioningTelemetryDebugEnabled() {
|
|
2927
|
+
return globalThis.process?.env?.CAPXUL_DEBUG_TELEMETRY === "1";
|
|
2928
|
+
}
|
|
2929
|
+
//#endregion
|
|
2930
|
+
//#region src/telemetry/get-failure-mode.ts
|
|
2931
|
+
/**
|
|
2932
|
+
* The five canonical {@link FailureMode} members, for runtime membership checks.
|
|
2933
|
+
* Single source of truth — tests assert against this exact set so the taxonomy
|
|
2934
|
+
* and its guard can never drift apart.
|
|
2935
|
+
*/
|
|
2936
|
+
const FAILURE_MODES = new Set([
|
|
2937
|
+
"auth-origin-mismatch",
|
|
2938
|
+
"stale-openfort-cache",
|
|
2939
|
+
"app-env-allowlist",
|
|
2940
|
+
"no-secure-context",
|
|
2941
|
+
"unknown"
|
|
2942
|
+
]);
|
|
2943
|
+
function isFailureMode(value) {
|
|
2944
|
+
return typeof value === "string" && FAILURE_MODES.has(value);
|
|
2945
|
+
}
|
|
2946
|
+
/**
|
|
2947
|
+
* Extract the structured {@link FailureMode} from a CapxulError.
|
|
2948
|
+
*
|
|
2949
|
+
* Reads `details.failure_mode` and returns it only when it is one of the five
|
|
2950
|
+
* canonical members; any other value (a legacy free string, a typo, a
|
|
2951
|
+
* non-string) yields `undefined` so downstream telemetry never reports an
|
|
2952
|
+
* unrecognised cause.
|
|
2953
|
+
*/
|
|
2954
|
+
function getFailureMode(error) {
|
|
2955
|
+
if (!isCapxulError(error)) return void 0;
|
|
2956
|
+
const details = error.details;
|
|
2957
|
+
if (details === void 0) return void 0;
|
|
2958
|
+
return isFailureMode(details.failure_mode) ? details.failure_mode : void 0;
|
|
2959
|
+
}
|
|
2960
|
+
/**
|
|
2961
|
+
* Resolve the canonical {@link FailureMode} for a `$exception`, guaranteeing a
|
|
2962
|
+
* taxonomy member is always returned — never `undefined`, never a free string.
|
|
2963
|
+
*
|
|
2964
|
+
* Resolution order:
|
|
2965
|
+
* 1. the structured mode on the error's `details.failure_mode` (already guarded);
|
|
2966
|
+
* 2. a caller-supplied `contextFailureMode`, but ONLY when it passes the same
|
|
2967
|
+
* runtime membership check — the static `FailureMode` type is erased at
|
|
2968
|
+
* runtime, so an operation string (e.g. `"signer-get-address"`) injected via
|
|
2969
|
+
* a JS caller or `as` cast is rejected here rather than leaking to telemetry;
|
|
2970
|
+
* 3. `"unknown"` otherwise, so an unclassifiable error is still tagged with a
|
|
2971
|
+
* canonical value instead of being emitted with no `failure_mode` at all.
|
|
2972
|
+
*/
|
|
2973
|
+
function resolveFailureMode(error, contextFailureMode) {
|
|
2974
|
+
return getFailureMode(error) ?? (isFailureMode(contextFailureMode) ? contextFailureMode : "unknown");
|
|
2975
|
+
}
|
|
2976
|
+
//#endregion
|
|
2977
|
+
//#region src/signer.ts
|
|
2978
|
+
const EVM_ADDRESS_HEX = /^0x[0-9a-fA-F]{40}$/;
|
|
2979
|
+
const ECDSA_SIGNATURE_HEX = /^0x[0-9a-fA-F]{130}$/;
|
|
2980
|
+
const SAFE_OP_DIGEST_HEX = /^0x[0-9a-fA-F]{64}$/;
|
|
2981
|
+
/**
|
|
2982
|
+
* The readiness store a signer reports when it runs no readiness cycle. It
|
|
2983
|
+
* fails CLOSED: a node key signer or an injected wallet never claims `ready`,
|
|
2984
|
+
* so a gate that reads this never opens on a guess.
|
|
2985
|
+
*/
|
|
2986
|
+
const UNOBSERVABLE_SIGNER_STATUS = {
|
|
2987
|
+
status: () => "unknown",
|
|
2988
|
+
subscribe: () => () => void 0
|
|
2989
|
+
};
|
|
2990
|
+
/**
|
|
2991
|
+
* Walk an error and its `cause` links once each. A self-referential chain
|
|
2992
|
+
* terminates. Both signer predicates read the chain, so they read it here.
|
|
2993
|
+
*/
|
|
2994
|
+
function* causeChain(cause) {
|
|
2995
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2996
|
+
let current = cause;
|
|
2997
|
+
while (typeof current === "object" && current !== null && !seen.has(current)) {
|
|
2998
|
+
seen.add(current);
|
|
2999
|
+
yield current;
|
|
3000
|
+
current = current.cause;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
/** Fold a signer throw into the public error contract. */
|
|
3004
|
+
function signerFailure(source, operation, cause) {
|
|
3005
|
+
let failureMode;
|
|
3006
|
+
for (const link of causeChain(cause)) {
|
|
3007
|
+
if (link instanceof CapxulError && link.code === "SIGNER_REJECTED") return link;
|
|
3008
|
+
failureMode = getFailureMode(link) ?? failureMode;
|
|
3009
|
+
const error = link;
|
|
3010
|
+
if (error.code === 4001 || error.error === "passkey_user_cancelled") return Errors.signerRejected({
|
|
3011
|
+
source,
|
|
3012
|
+
cause
|
|
3013
|
+
});
|
|
3014
|
+
}
|
|
3015
|
+
return Errors.providerError("signer", operation, cause, failureMode === void 0 ? void 0 : { failure_mode: failureMode });
|
|
3016
|
+
}
|
|
3017
|
+
/**
|
|
3018
|
+
* Browser `CapxulSigner` backed by an injected EIP-1193 wallet (MetaMask, etc.).
|
|
3019
|
+
* Signs the SafeOp digest via `eth_sign`, then verifies the returned signature
|
|
3020
|
+
* recovers the selected account against that raw digest. Wallets that prefix
|
|
3021
|
+
* `eth_sign` payloads are rejected before the backend submits an invalid SafeOp.
|
|
3022
|
+
* The node key signer lives in `@capxul/sdk/node` (`localPrivateKeySigner`).
|
|
3023
|
+
*/
|
|
3024
|
+
function injectedWalletSigner(provider) {
|
|
3025
|
+
const resolveAddress = async () => {
|
|
3026
|
+
const accounts = await provider.request({ method: "eth_requestAccounts" });
|
|
3027
|
+
const first = Array.isArray(accounts) ? accounts[0] : void 0;
|
|
3028
|
+
if (typeof first !== "string") throw new Error("injectedWalletSigner: wallet returned no accounts");
|
|
3029
|
+
if (!EVM_ADDRESS_HEX.test(first)) throw new Error("injectedWalletSigner: wallet returned invalid address format");
|
|
3030
|
+
return toAddress(first);
|
|
3031
|
+
};
|
|
3032
|
+
return {
|
|
3033
|
+
source: "injected-eip1193",
|
|
3034
|
+
getAddress: resolveAddress,
|
|
3035
|
+
async signUserOpHash(hash) {
|
|
3036
|
+
if (!SAFE_OP_DIGEST_HEX.test(hash)) throw new Error("injectedWalletSigner: SafeOp digest must be a 0x-prefixed 32-byte hex");
|
|
3037
|
+
const address = await resolveAddress();
|
|
3038
|
+
let signature;
|
|
3039
|
+
try {
|
|
3040
|
+
signature = await provider.request({
|
|
3041
|
+
method: "eth_sign",
|
|
3042
|
+
params: [address, hash]
|
|
3043
|
+
});
|
|
3044
|
+
} catch (cause) {
|
|
3045
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
3046
|
+
throw new Error(`injectedWalletSigner: eth_sign failed; enable raw-hash signing for deployment (${detail})`, { cause });
|
|
3047
|
+
}
|
|
3048
|
+
if (typeof signature !== "string") throw new Error("injectedWalletSigner: wallet returned a non-string signature");
|
|
3049
|
+
if (!ECDSA_SIGNATURE_HEX.test(signature)) throw new Error("injectedWalletSigner: wallet returned invalid signature format");
|
|
3050
|
+
if ((await recoverRawDigestSigner({
|
|
3051
|
+
hash,
|
|
3052
|
+
signature
|
|
3053
|
+
})).toLowerCase() !== address.toLowerCase()) throw new Error("injectedWalletSigner: wallet signature did not recover the selected account for the raw SafeOp digest; use a raw-hash-capable wallet or @capxul/sdk/node localPrivateKeySigner for deployed flows");
|
|
3054
|
+
return signature;
|
|
3055
|
+
}
|
|
3056
|
+
};
|
|
3057
|
+
}
|
|
3058
|
+
async function recoverRawDigestSigner(input) {
|
|
3059
|
+
try {
|
|
3060
|
+
return toAddress(await recoverAddress(input));
|
|
3061
|
+
} catch (cause) {
|
|
3062
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
3063
|
+
throw new Error(`injectedWalletSigner: could not verify raw SafeOp digest signature (${detail})`, { cause });
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
//#endregion
|
|
3067
|
+
//#region src/internal/invocation-observation.ts
|
|
3068
|
+
const INVOCATION_OBSERVATION = Symbol("capxul.invocation-observation");
|
|
3069
|
+
const FAILURE_INVOCATION_SNAPSHOT = Symbol("capxul.failure-invocation-snapshot");
|
|
3070
|
+
/** @internal Attach one immutable, non-wire invocation snapshot to an SDK-owned object. */
|
|
3071
|
+
function attachInvocationObservation(target, source) {
|
|
3072
|
+
const snapshot = Object.freeze(source.context === void 0 ? { active: source.active } : {
|
|
3073
|
+
active: source.active,
|
|
3074
|
+
context: Object.freeze({ ...source.context })
|
|
3075
|
+
});
|
|
3076
|
+
Object.defineProperty(target, INVOCATION_OBSERVATION, {
|
|
3077
|
+
configurable: false,
|
|
3078
|
+
enumerable: false,
|
|
3079
|
+
value: snapshot,
|
|
3080
|
+
writable: false
|
|
3081
|
+
});
|
|
3082
|
+
return target;
|
|
3083
|
+
}
|
|
3084
|
+
/** @internal Read the snapshot without exposing its symbol or adding a wire field. */
|
|
3085
|
+
function readInvocationObservation(source) {
|
|
3086
|
+
if (typeof source !== "object" || source === null) return void 0;
|
|
3087
|
+
return source[INVOCATION_OBSERVATION];
|
|
3088
|
+
}
|
|
3089
|
+
/** @internal Carry a snapshot across an SDK adapter projection without resolving again. */
|
|
3090
|
+
function copyInvocationObservation(source, target) {
|
|
3091
|
+
const snapshot = readInvocationObservation(source);
|
|
3092
|
+
return snapshot === void 0 ? target : attachInvocationObservation(target, snapshot);
|
|
3093
|
+
}
|
|
3094
|
+
/** @internal Carry the public call-start delivery decision with its failure envelope. */
|
|
3095
|
+
function markFailureInvocationSnapshot(failure, snapshot) {
|
|
3096
|
+
Object.defineProperty(failure, FAILURE_INVOCATION_SNAPSHOT, {
|
|
3097
|
+
configurable: false,
|
|
3098
|
+
enumerable: false,
|
|
3099
|
+
value: Object.freeze(snapshot),
|
|
3100
|
+
writable: false
|
|
3101
|
+
});
|
|
3102
|
+
return failure;
|
|
3103
|
+
}
|
|
3104
|
+
/** @internal Read the call-start delivery decision; undefined means a direct adapter call. */
|
|
3105
|
+
function readFailureInvocationSnapshot(failure) {
|
|
3106
|
+
if (typeof failure !== "object" || failure === null) return void 0;
|
|
3107
|
+
const snapshot = failure[FAILURE_INVOCATION_SNAPSHOT];
|
|
3108
|
+
return typeof snapshot === "object" && snapshot !== null && "active" in snapshot ? snapshot : void 0;
|
|
3109
|
+
}
|
|
3110
|
+
//#endregion
|
|
3111
|
+
//#region src/domain/machine/telemetry.ts
|
|
3112
|
+
const definedEntries = (values) => Object.fromEntries(Object.entries(values).filter((entry) => entry[1] !== void 0));
|
|
3113
|
+
const SAFE_ENGINEERING_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
3114
|
+
const safeEngineeringIdentifier = (value) => value !== void 0 && SAFE_ENGINEERING_ID.test(value) ? value : void 0;
|
|
3115
|
+
const portFailureOutcome = (code) => code === "CANCELLED" ? "cancelled" : "failed";
|
|
3116
|
+
function formatTraceparent(span) {
|
|
3117
|
+
const traceId = span.traceId.toLowerCase();
|
|
3118
|
+
const spanId = span.spanId.toLowerCase();
|
|
3119
|
+
if (!/^[0-9a-f]{32}$/u.test(traceId) || !/^[0-9a-f]{16}$/u.test(spanId)) return void 0;
|
|
3120
|
+
if (/^0+$/u.test(traceId) || /^0+$/u.test(spanId)) return void 0;
|
|
3121
|
+
return `00-${traceId}-${spanId}-${span.sampled ? "01" : "00"}`;
|
|
3122
|
+
}
|
|
3123
|
+
/** Canonical, bounded fields for the one wide engineering log owned by P3. */
|
|
3124
|
+
const transitionLogFields = (record) => {
|
|
3125
|
+
const { correlation_id, journey_id, ...canonical } = record;
|
|
3126
|
+
const safeCorrelation = safeEngineeringIdentifier(correlation_id);
|
|
3127
|
+
const safeJourney = safeEngineeringIdentifier(journey_id);
|
|
3128
|
+
return definedEntries({
|
|
3129
|
+
...canonical,
|
|
3130
|
+
...safeCorrelation === void 0 ? {} : { correlation_id: safeCorrelation },
|
|
3131
|
+
...safeJourney === void 0 ? {} : { journey_id: safeJourney }
|
|
3132
|
+
});
|
|
3133
|
+
};
|
|
3134
|
+
/** The shell span owns transition/refusal classification and no other seam's fields. */
|
|
3135
|
+
const transitionSpanFields = (record) => {
|
|
3136
|
+
if (record.outcome === "applied") return {
|
|
3137
|
+
machine: record.machine,
|
|
3138
|
+
from: record.from,
|
|
3139
|
+
event: record.event,
|
|
3140
|
+
to: record.to
|
|
3141
|
+
};
|
|
3142
|
+
return {
|
|
3143
|
+
machine: record.machine,
|
|
3144
|
+
state: record.state,
|
|
3145
|
+
event: record.event,
|
|
3146
|
+
...record.outcome === "refused" ? { refused: record.refusal_code } : {}
|
|
3147
|
+
};
|
|
3148
|
+
};
|
|
3149
|
+
/**
|
|
3150
|
+
* Emit engineering telemetry as an isolated side effect. Exporter/logger defects
|
|
3151
|
+
* can never change the actor's transition, reply, or P3 observer cardinality.
|
|
3152
|
+
*/
|
|
3153
|
+
const logIdentityTransition = (record) => (record.outcome === "applied" ? Effect.logInfo("identity.transition") : record.outcome === "failed" ? Effect.logError("identity.transition") : Effect.logWarning("identity.transition")).pipe(Effect.annotateLogs(transitionLogFields(record)), Effect.andThen(record.outcome === "applied" ? Effect.void : Effect.fail({ code: record.outcome === "refused" ? record.refusal_code : record.error_code })), Effect.withSpan("identity.transition"), Effect.annotateSpans(transitionSpanFields(record)), Effect.catchCause(() => Effect.void));
|
|
3154
|
+
//#endregion
|
|
3155
|
+
//#region src/domain/machine/shell.ts
|
|
3156
|
+
const INVOCATION_PARENT_SPAN = Symbol("@capxul/sdk/identity-invocation-parent-span");
|
|
3157
|
+
function carryInvocationParentSpan(controls, parent) {
|
|
3158
|
+
return {
|
|
3159
|
+
...controls,
|
|
3160
|
+
[INVOCATION_PARENT_SPAN]: parent
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
function withInvocationParentSpan(effect, controls) {
|
|
3164
|
+
const parent = controls?.[INVOCATION_PARENT_SPAN];
|
|
3165
|
+
return parent === void 0 ? effect : effect.pipe(Effect.withParentSpan(parent));
|
|
3166
|
+
}
|
|
3167
|
+
var ActorFailure = class extends Error {
|
|
3168
|
+
reason;
|
|
3169
|
+
machine;
|
|
3170
|
+
state;
|
|
3171
|
+
event;
|
|
3172
|
+
details;
|
|
3173
|
+
_tag = "ActorFailure";
|
|
3174
|
+
constructor(reason, machine, state, event, details) {
|
|
3175
|
+
super(`${machine}:${state}:${event} ${reason}`);
|
|
3176
|
+
this.reason = reason;
|
|
3177
|
+
this.machine = machine;
|
|
3178
|
+
this.state = state;
|
|
3179
|
+
this.event = event;
|
|
3180
|
+
this.details = details;
|
|
3181
|
+
this.name = "ActorFailure";
|
|
3182
|
+
}
|
|
3183
|
+
};
|
|
3184
|
+
const duration = (startedAt) => Math.max(0, Date.now() - startedAt);
|
|
3185
|
+
const failReply = (reply, failure) => reply === null ? Effect.void : Effect.asVoid(Deferred.fail(reply, failure));
|
|
3186
|
+
const withCarriage = (controls) => ({
|
|
3187
|
+
...controls.correlation_id === void 0 ? {} : { correlation_id: controls.correlation_id },
|
|
3188
|
+
...controls.journey_id === void 0 ? {} : { journey_id: controls.journey_id }
|
|
3596
3189
|
});
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3190
|
+
const timeoutFailure = {
|
|
3191
|
+
code: "PROVIDER_ERROR",
|
|
3192
|
+
message: "Identity work timed out"
|
|
3193
|
+
};
|
|
3194
|
+
const cancelledFailure = {
|
|
3195
|
+
code: "CANCELLED",
|
|
3196
|
+
message: "Identity work cancelled"
|
|
3197
|
+
};
|
|
3198
|
+
const abort = (signal) => signal.aborted ? Effect.fail(cancelledFailure) : Effect.callback((resume) => {
|
|
3199
|
+
const onAbort = () => resume(Effect.fail(cancelledFailure));
|
|
3200
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
3201
|
+
return Effect.sync(() => signal.removeEventListener("abort", onAbort));
|
|
3609
3202
|
});
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3203
|
+
const control = (effect, controls) => {
|
|
3204
|
+
let controlled = effect;
|
|
3205
|
+
const deadlineDelay = controls.deadlineMs === void 0 ? void 0 : Math.max(0, controls.deadlineMs - Date.now());
|
|
3206
|
+
const timeoutMs = controls.timeoutMs === void 0 ? deadlineDelay : deadlineDelay === void 0 ? controls.timeoutMs : Math.min(controls.timeoutMs, deadlineDelay);
|
|
3207
|
+
if (timeoutMs !== void 0) controlled = controlled.pipe(Effect.timeoutOrElse({
|
|
3208
|
+
duration: `${timeoutMs} millis`,
|
|
3209
|
+
orElse: () => Effect.fail(timeoutFailure)
|
|
3210
|
+
}));
|
|
3211
|
+
if (controls.signal !== void 0) controlled = Effect.raceFirst(controlled, abort(controls.signal));
|
|
3212
|
+
return controlled;
|
|
3213
|
+
};
|
|
3214
|
+
const boot = (spec, options = {}) => Effect.gen(function* () {
|
|
3215
|
+
const mailbox = yield* Queue.unbounded();
|
|
3216
|
+
const cell = yield* Ref.make(spec.initial);
|
|
3217
|
+
const slotEpochs = /* @__PURE__ */ new Map();
|
|
3218
|
+
const slots = /* @__PURE__ */ new Map();
|
|
3219
|
+
const stateObservers = /* @__PURE__ */ new Set();
|
|
3220
|
+
const transitionObservers = /* @__PURE__ */ new Set();
|
|
3221
|
+
let bootTransitionObserver = options.onTransition;
|
|
3222
|
+
let stopped = false;
|
|
3223
|
+
const defect = (cause) => {
|
|
3224
|
+
try {
|
|
3225
|
+
options.onDefect?.(cause);
|
|
3226
|
+
} catch {}
|
|
3227
|
+
};
|
|
3228
|
+
const emit = (record) => Effect.sync(() => {
|
|
3229
|
+
if (bootTransitionObserver !== void 0) try {
|
|
3230
|
+
bootTransitionObserver(record);
|
|
3231
|
+
} catch (cause) {
|
|
3232
|
+
bootTransitionObserver = void 0;
|
|
3233
|
+
defect(cause);
|
|
3234
|
+
}
|
|
3235
|
+
for (const observer of transitionObservers) try {
|
|
3236
|
+
observer(record);
|
|
3237
|
+
} catch (cause) {
|
|
3238
|
+
transitionObservers.delete(observer);
|
|
3239
|
+
defect(cause);
|
|
3240
|
+
}
|
|
3241
|
+
}).pipe(Effect.andThen(logIdentityTransition(record)));
|
|
3242
|
+
const notify = (state) => {
|
|
3243
|
+
for (const observer of stateObservers) try {
|
|
3244
|
+
observer(state);
|
|
3245
|
+
} catch (cause) {
|
|
3246
|
+
stateObservers.delete(observer);
|
|
3247
|
+
defect(cause);
|
|
3248
|
+
}
|
|
3249
|
+
};
|
|
3250
|
+
const makeFailure = (state, event, reason, details) => new ActorFailure(reason, spec.machine, spec.label(state), event._tag, details);
|
|
3251
|
+
const nonApplied = (state, event, reason, outcome, env) => {
|
|
3252
|
+
const slot = env.origin?.slot ?? spec.slot(event);
|
|
3253
|
+
return copyInvocationObservation(env.invocation, {
|
|
3254
|
+
machine: spec.machine,
|
|
3255
|
+
state: spec.label(state),
|
|
3256
|
+
event: event._tag,
|
|
3257
|
+
slot,
|
|
3258
|
+
epoch: env.origin?.epoch ?? slotEpochs.get(slot) ?? 0,
|
|
3259
|
+
outcome,
|
|
3260
|
+
duration_ms: duration(env.startedAt),
|
|
3261
|
+
...withCarriage(env.invocation),
|
|
3262
|
+
...outcome === "refused" ? { refusal_code: reason } : { error_code: reason }
|
|
3263
|
+
});
|
|
3264
|
+
};
|
|
3265
|
+
const applied = (from, to, event, slot, epoch, env) => copyInvocationObservation(env.invocation, {
|
|
3266
|
+
machine: spec.machine,
|
|
3267
|
+
from: spec.label(from),
|
|
3268
|
+
event: event._tag,
|
|
3269
|
+
to: spec.label(to),
|
|
3270
|
+
slot,
|
|
3271
|
+
epoch,
|
|
3272
|
+
outcome: "applied",
|
|
3273
|
+
duration_ms: duration(env.startedAt),
|
|
3274
|
+
...withCarriage(env.invocation)
|
|
3275
|
+
});
|
|
3276
|
+
const finish = (cursor, state) => {
|
|
3277
|
+
const held = slots.get(cursor.slot);
|
|
3278
|
+
if (held === void 0 || held.cursor !== cursor) return Effect.void;
|
|
3279
|
+
slots.delete(cursor.slot);
|
|
3280
|
+
return Effect.asVoid(Deferred.succeed(held.reply, state));
|
|
3281
|
+
};
|
|
3282
|
+
const recoverFailure = (state, cursor, failure) => {
|
|
3283
|
+
const event = spec.recoverFailure?.(state, cursor.event, failure);
|
|
3284
|
+
if (event === void 0) return Effect.succeed(state);
|
|
3285
|
+
const outcome = spec.transition(state, event);
|
|
3286
|
+
if ("refused" in outcome) return Effect.succeed(state);
|
|
3287
|
+
return Ref.set(cell, outcome.next).pipe(Effect.tap(() => {
|
|
3288
|
+
return emit(applied(state, outcome.next, event, cursor.slot, cursor.epoch, {
|
|
3289
|
+
invocation: cursor.invocation,
|
|
3290
|
+
startedAt: cursor.startedAt
|
|
3291
|
+
})).pipe(Effect.andThen(Effect.sync(() => {
|
|
3292
|
+
notify(outcome.next);
|
|
3293
|
+
})));
|
|
3294
|
+
}), Effect.as(outcome.next));
|
|
3295
|
+
};
|
|
3296
|
+
const step = (env) => Effect.gen(function* () {
|
|
3297
|
+
const state = yield* Ref.get(cell);
|
|
3298
|
+
if (env.origin !== null && (slotEpochs.get(env.origin.slot) ?? 0) !== env.origin.epoch) {
|
|
3299
|
+
const held = slots.get(env.origin.slot);
|
|
3300
|
+
if (held === void 0 || held.cursor !== env.origin) return;
|
|
3301
|
+
slots.delete(env.origin.slot);
|
|
3302
|
+
const event = { _tag: env.origin.event };
|
|
3303
|
+
yield* emit(nonApplied(state, event, "STALE_EPOCH", "refused", env));
|
|
3304
|
+
yield* failReply(held.reply, makeFailure(state, event, "STALE_EPOCH"));
|
|
3305
|
+
return;
|
|
3306
|
+
}
|
|
3307
|
+
if (env.origin !== null && env.event._tag === "~lane/exit") {
|
|
3308
|
+
const exit = env.event;
|
|
3309
|
+
const held = slots.get(env.origin.slot);
|
|
3310
|
+
if (held === void 0 || held.cursor !== env.origin) return;
|
|
3311
|
+
if (exit.defect !== void 0) {
|
|
3312
|
+
slots.delete(env.origin.slot);
|
|
3313
|
+
defect(exit.defect);
|
|
3314
|
+
const event = { _tag: env.origin.event };
|
|
3315
|
+
const failedState = yield* recoverFailure(state, env.origin, {
|
|
3316
|
+
code: "WORK_DIED",
|
|
3317
|
+
message: "Identity work died"
|
|
3318
|
+
});
|
|
3319
|
+
yield* emit(nonApplied(failedState, event, "WORK_DIED", "failed", env));
|
|
3320
|
+
yield* failReply(held.reply, makeFailure(failedState, event, "WORK_DIED"));
|
|
3321
|
+
return;
|
|
3322
|
+
}
|
|
3323
|
+
if (exit.failure !== void 0) {
|
|
3324
|
+
slots.delete(env.origin.slot);
|
|
3325
|
+
const outcome = exit.failure.code === "CANCELLED" ? "cancelled" : "failed";
|
|
3326
|
+
const event = { _tag: env.origin.event };
|
|
3327
|
+
const failedState = exit.failure === timeoutFailure || exit.failure === cancelledFailure ? yield* recoverFailure(state, env.origin, exit.failure) : state;
|
|
3328
|
+
yield* emit(nonApplied(failedState, event, exit.failure.code, outcome, env));
|
|
3329
|
+
const details = exit.failure === timeoutFailure ? { reason: "timeout" } : void 0;
|
|
3330
|
+
yield* failReply(held.reply, makeFailure(failedState, event, exit.failure.code, details));
|
|
3331
|
+
return;
|
|
3332
|
+
}
|
|
3333
|
+
yield* finish(env.origin, state);
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
const outcome = spec.transition(state, env.event);
|
|
3337
|
+
if ("refused" in outcome) {
|
|
3338
|
+
yield* emit(nonApplied(state, env.event, outcome.refused, "refused", env));
|
|
3339
|
+
yield* failReply(env.reply, makeFailure(state, env.event, outcome.refused));
|
|
3340
|
+
return;
|
|
3341
|
+
}
|
|
3342
|
+
const next = outcome.next;
|
|
3343
|
+
yield* Ref.set(cell, next);
|
|
3344
|
+
const emitApplied = (slot, epoch) => emit(applied(state, next, env.event, slot, epoch, env));
|
|
3345
|
+
if (env.origin !== null) {
|
|
3346
|
+
yield* emitApplied(env.origin.slot, env.origin.epoch);
|
|
3347
|
+
notify(next);
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
for (const slot of spec.invalidates?.(env.event) ?? []) {
|
|
3351
|
+
if (!slots.has(slot)) continue;
|
|
3352
|
+
slotEpochs.set(slot, (slotEpochs.get(slot) ?? 0) + 1);
|
|
3353
|
+
}
|
|
3354
|
+
const work = env.reply === null ? void 0 : spec.work?.({
|
|
3355
|
+
state: next,
|
|
3356
|
+
event: env.event
|
|
3357
|
+
});
|
|
3358
|
+
if (work === void 0 || env.reply === null) {
|
|
3359
|
+
const slot = spec.slot(env.event);
|
|
3360
|
+
yield* emitApplied(slot, slotEpochs.get(slot) ?? 0);
|
|
3361
|
+
notify(next);
|
|
3362
|
+
if (env.reply !== null) yield* Deferred.succeed(env.reply, next).pipe(Effect.asVoid);
|
|
3363
|
+
return;
|
|
3364
|
+
}
|
|
3365
|
+
const previous = slots.get(work.slot);
|
|
3366
|
+
if (previous !== void 0) {
|
|
3367
|
+
slots.delete(work.slot);
|
|
3368
|
+
yield* Fiber.interrupt(previous.fiber);
|
|
3369
|
+
const previousEvent = { _tag: previous.cursor.event };
|
|
3370
|
+
const reason = previous.cursor.epoch === (slotEpochs.get(work.slot) ?? 0) ? "SUPERSEDED" : "STALE_EPOCH";
|
|
3371
|
+
yield* emit(nonApplied(next, previousEvent, reason, "refused", {
|
|
3372
|
+
origin: previous.cursor,
|
|
3373
|
+
invocation: previous.cursor.invocation,
|
|
3374
|
+
startedAt: previous.cursor.startedAt
|
|
3375
|
+
}));
|
|
3376
|
+
yield* failReply(previous.reply, makeFailure(next, previousEvent, reason));
|
|
3377
|
+
}
|
|
3378
|
+
const epoch = (slotEpochs.get(work.slot) ?? 0) + 1;
|
|
3379
|
+
slotEpochs.set(work.slot, epoch);
|
|
3380
|
+
const cursor = {
|
|
3381
|
+
slot: work.slot,
|
|
3382
|
+
epoch,
|
|
3383
|
+
event: env.event._tag,
|
|
3384
|
+
startedAt: env.startedAt,
|
|
3385
|
+
invocation: env.invocation
|
|
3386
|
+
};
|
|
3387
|
+
yield* emitApplied(work.slot, epoch);
|
|
3388
|
+
notify(next);
|
|
3389
|
+
const send = (event) => Queue.offer(mailbox, {
|
|
3390
|
+
event,
|
|
3391
|
+
reply: null,
|
|
3392
|
+
origin: cursor,
|
|
3393
|
+
invocation: env.invocation,
|
|
3394
|
+
startedAt: env.startedAt
|
|
3395
|
+
}).pipe(Effect.asVoid);
|
|
3396
|
+
const complete = (event) => Queue.offer(mailbox, {
|
|
3397
|
+
event,
|
|
3398
|
+
reply: null,
|
|
3399
|
+
origin: cursor,
|
|
3400
|
+
invocation: env.invocation,
|
|
3401
|
+
startedAt: env.startedAt
|
|
3402
|
+
}).pipe(Effect.asVoid);
|
|
3403
|
+
const guarded = Effect.gen(function* () {
|
|
3404
|
+
yield* Effect.annotateCurrentSpan({
|
|
3405
|
+
slot: work.slot,
|
|
3406
|
+
epoch,
|
|
3407
|
+
port: work.port
|
|
3408
|
+
});
|
|
3409
|
+
return yield* control(work.run(send, env.invocation), env.invocation);
|
|
3410
|
+
}).pipe(Effect.withSpan(`identity.work.${work.slot}`)).pipe(Effect.matchEffect({
|
|
3411
|
+
onFailure: (failure) => complete({
|
|
3412
|
+
_tag: "~lane/exit",
|
|
3413
|
+
failure
|
|
3414
|
+
}),
|
|
3415
|
+
onSuccess: () => complete({ _tag: "~lane/exit" })
|
|
3416
|
+
}), Effect.catchDefect((cause) => complete({
|
|
3417
|
+
_tag: "~lane/exit",
|
|
3418
|
+
defect: cause
|
|
3419
|
+
})));
|
|
3420
|
+
const fiber = yield* Effect.forkChild(guarded, { startImmediately: true });
|
|
3421
|
+
slots.set(work.slot, {
|
|
3422
|
+
fiber,
|
|
3423
|
+
reply: env.reply,
|
|
3424
|
+
cursor
|
|
3425
|
+
});
|
|
3426
|
+
});
|
|
3427
|
+
const loop = yield* Effect.forkScoped(Effect.forever(Queue.take(mailbox).pipe(Effect.flatMap((env) => withInvocationParentSpan(step(env), env.invocation)))));
|
|
3428
|
+
yield* Effect.addFinalizer(() => Effect.gen(function* () {
|
|
3429
|
+
stopped = true;
|
|
3430
|
+
yield* Fiber.interrupt(loop);
|
|
3431
|
+
const state = yield* Ref.get(cell);
|
|
3432
|
+
const outstanding = [...slots.values()];
|
|
3433
|
+
slots.clear();
|
|
3434
|
+
for (const held of outstanding) {
|
|
3435
|
+
yield* Fiber.interrupt(held.fiber);
|
|
3436
|
+
const env = {
|
|
3437
|
+
origin: held.cursor,
|
|
3438
|
+
invocation: held.cursor.invocation,
|
|
3439
|
+
startedAt: held.cursor.startedAt
|
|
3440
|
+
};
|
|
3441
|
+
const event = { _tag: held.cursor.event };
|
|
3442
|
+
yield* emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", env));
|
|
3443
|
+
yield* failReply(held.reply, makeFailure(state, event, "ACTOR_STOPPED"));
|
|
3444
|
+
}
|
|
3445
|
+
stateObservers.clear();
|
|
3446
|
+
transitionObservers.clear();
|
|
3447
|
+
yield* Queue.shutdown(mailbox);
|
|
3448
|
+
}));
|
|
3449
|
+
const offer = (event, reply, invocation) => {
|
|
3450
|
+
const state = Ref.getUnsafe(cell);
|
|
3451
|
+
if (stopped) return withInvocationParentSpan(emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", {
|
|
3452
|
+
origin: null,
|
|
3453
|
+
invocation,
|
|
3454
|
+
startedAt: Date.now()
|
|
3455
|
+
})).pipe(Effect.andThen(Effect.fail(makeFailure(state, event, "ACTOR_STOPPED")))), invocation);
|
|
3456
|
+
const startedAt = Date.now();
|
|
3457
|
+
return Queue.offer(mailbox, {
|
|
3458
|
+
event,
|
|
3459
|
+
reply,
|
|
3460
|
+
origin: null,
|
|
3461
|
+
invocation,
|
|
3462
|
+
startedAt
|
|
3463
|
+
}).pipe(Effect.asVoid);
|
|
3464
|
+
};
|
|
3465
|
+
return {
|
|
3466
|
+
ask: (event, invocation = {}) => Effect.gen(function* () {
|
|
3467
|
+
const reply = yield* Deferred.make();
|
|
3468
|
+
yield* offer(event, reply, invocation);
|
|
3469
|
+
return yield* Deferred.await(reply);
|
|
3470
|
+
}),
|
|
3471
|
+
tell: (event, invocation = {}) => offer(event, null, invocation),
|
|
3472
|
+
snapshot: () => Ref.getUnsafe(cell),
|
|
3473
|
+
subscribe: (observer) => {
|
|
3474
|
+
stateObservers.add(observer);
|
|
3475
|
+
return () => {
|
|
3476
|
+
stateObservers.delete(observer);
|
|
3477
|
+
};
|
|
3478
|
+
},
|
|
3479
|
+
subscribeTransitions: (observer) => {
|
|
3480
|
+
transitionObservers.add(observer);
|
|
3481
|
+
return () => {
|
|
3482
|
+
transitionObservers.delete(observer);
|
|
3483
|
+
};
|
|
3484
|
+
}
|
|
3485
|
+
};
|
|
3616
3486
|
});
|
|
3617
|
-
`
|
|
3618
|
-
.capxul-doc{--ink:#1d1d1f;--muted:#6e6e73;--line:#e7e7ea;--accent:#0a7d4b;--bg:#fff;
|
|
3619
|
-
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;
|
|
3620
|
-
color:var(--ink);background:var(--bg);max-width:44rem;margin:0 auto;padding:2.75rem 3rem;
|
|
3621
|
-
border:1px solid var(--line);border-radius:16px;box-shadow:0 1px 2px rgba(0,0,0,.04),0 12px 32px rgba(0,0,0,.06);
|
|
3622
|
-
line-height:1.5;font-size:15px;overflow-wrap:anywhere;word-break:break-word}
|
|
3623
|
-
.capxul-doc *{box-sizing:border-box;min-width:0}
|
|
3624
|
-
.capxul-doc .doc-header{display:flex;flex-direction:column;gap:1.25rem;padding-bottom:1.5rem;border-bottom:1px solid var(--line);margin-bottom:1.75rem}
|
|
3625
|
-
.capxul-doc .doc-brand{display:flex;align-items:center;gap:.5rem;color:var(--accent);font-weight:600}
|
|
3626
|
-
.capxul-doc .doc-brand-mark{font-size:1.1rem}
|
|
3627
|
-
.capxul-doc .doc-brand-name{letter-spacing:.02em}
|
|
3628
|
-
.capxul-doc .doc-headline{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;flex-wrap:wrap}
|
|
3629
|
-
.capxul-doc .doc-title{font-size:1.9rem;font-weight:700;letter-spacing:-.02em;margin:0}
|
|
3630
|
-
.capxul-doc .doc-badge{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
|
|
3631
|
-
color:var(--accent);background:rgba(10,125,75,.1);padding:.3rem .6rem;border-radius:999px;max-width:100%;text-align:right}
|
|
3632
|
-
.capxul-doc .doc-parties{display:grid;grid-template-columns:1fr 1fr;gap:1.25rem;margin-bottom:1.75rem}
|
|
3633
|
-
.capxul-doc .doc-party{display:flex;flex-direction:column;gap:.15rem}
|
|
3634
|
-
.capxul-doc .doc-party-label{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}
|
|
3635
|
-
.capxul-doc .doc-party-name{font-weight:600}
|
|
3636
|
-
.capxul-doc .doc-meta{display:flex;flex-direction:column;gap:.4rem;margin-bottom:1.75rem}
|
|
3637
|
-
.capxul-doc .doc-meta-row{display:flex;justify-content:space-between;gap:1rem;font-size:.92rem}
|
|
3638
|
-
.capxul-doc .doc-meta-label{color:var(--muted);flex-shrink:0}
|
|
3639
|
-
.capxul-doc .doc-meta-value{font-weight:500;text-align:right}
|
|
3640
|
-
.capxul-doc time{color:var(--ink);font-variant-numeric:tabular-nums}
|
|
3641
|
-
.capxul-doc .doc-line-items{width:100%;border-collapse:collapse;margin:.5rem 0 1.5rem;font-size:.92rem}
|
|
3642
|
-
.capxul-doc .doc-line-items th{text-align:left;font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;
|
|
3643
|
-
color:var(--muted);font-weight:600;padding:.5rem .25rem;border-bottom:1px solid var(--line)}
|
|
3644
|
-
.capxul-doc .doc-line-items td{padding:.7rem .25rem;border-bottom:1px solid var(--line)}
|
|
3645
|
-
.capxul-doc .doc-li-qty,.capxul-doc .doc-li-unit,.capxul-doc .doc-li-total{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
|
|
3646
|
-
.capxul-doc .doc-li-desc{width:100%}
|
|
3647
|
-
.capxul-doc .doc-totals{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem}
|
|
3648
|
-
.capxul-doc .doc-total-line{display:flex;justify-content:space-between;align-items:baseline;gap:1rem}
|
|
3649
|
-
.capxul-doc .doc-total-label{color:var(--muted)}
|
|
3650
|
-
.capxul-doc .doc-total-deduction .doc-amount-value{color:var(--muted)}
|
|
3651
|
-
.capxul-doc .doc-total-grand{border-top:2px solid var(--ink);margin-top:.5rem;padding-top:.75rem;font-size:1.15rem}
|
|
3652
|
-
.capxul-doc .doc-total-grand .doc-amount-value{font-weight:700}
|
|
3653
|
-
.capxul-doc .doc-amount-value{font-variant-numeric:tabular-nums;font-weight:600}
|
|
3654
|
-
.capxul-doc .doc-hero{text-align:center;padding:1.5rem 0 2rem}
|
|
3655
|
-
.capxul-doc .doc-hero-label{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}
|
|
3656
|
-
.capxul-doc .doc-hero-amount{font-size:2.6rem;font-weight:700;letter-spacing:-.02em;margin-top:.35rem}
|
|
3657
|
-
.capxul-doc .doc-note{color:var(--ink);background:#f7f7f8;border-radius:10px;padding:.9rem 1.1rem;margin:0}
|
|
3658
|
-
.capxul-doc .doc-dest-address{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9rem}
|
|
3659
|
-
.capxul-doc .doc-footer{margin-top:1.75rem;padding-top:1.25rem;border-top:1px solid var(--line);color:var(--muted);font-size:.9rem}
|
|
3660
|
-
.capxul-doc code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.82rem;color:var(--muted);word-break:break-all}
|
|
3661
|
-
@media (max-width:540px){.capxul-doc{padding:1.75rem 1.25rem}.capxul-doc .doc-parties{grid-template-columns:1fr}}
|
|
3662
|
-
`.trim();
|
|
3663
3487
|
//#endregion
|
|
3664
|
-
//#region
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3488
|
+
//#region src/surface/to-capxul-result.ts
|
|
3489
|
+
/** The one place an Effect becomes a Promise on the public surface. */
|
|
3490
|
+
async function runProgram(program, runPromise = Effect.runPromise) {
|
|
3491
|
+
const result = await runPromise(program.pipe(Effect.catchDefect((defect) => Effect.fail(Errors.unknown(defect))), Effect.result));
|
|
3492
|
+
if (Result.isFailure(result)) return {
|
|
3493
|
+
ok: false,
|
|
3494
|
+
error: result.failure
|
|
3495
|
+
};
|
|
3496
|
+
return {
|
|
3497
|
+
ok: true,
|
|
3498
|
+
value: result.success
|
|
3499
|
+
};
|
|
3500
|
+
}
|
|
3501
|
+
async function toCapxulResult(program, layer) {
|
|
3502
|
+
return runProgram(program.pipe(Effect.provide(layer)));
|
|
3503
|
+
}
|
|
3504
|
+
/**
|
|
3505
|
+
* Signal-aware bridge for the Convex-backed method bundles. The adapters fail
|
|
3506
|
+
* with `{ publicError }` rather than a bare `CapxulError`, so the wrapper is
|
|
3507
|
+
* unwrapped here — once — instead of at every call site.
|
|
3508
|
+
*/
|
|
3509
|
+
async function runIfActive(signal, operation, effect, runPromise = Effect.runPromise) {
|
|
3510
|
+
if (signal?.aborted === true) return {
|
|
3511
|
+
ok: false,
|
|
3512
|
+
error: Errors.cancelled({ operation })
|
|
3513
|
+
};
|
|
3514
|
+
const program = effect().pipe(Effect.mapError((error) => error.publicError));
|
|
3515
|
+
return runProgram(signal === void 0 ? program : Effect.raceFirst(program, Effect.callback((resume) => {
|
|
3516
|
+
if (signal.aborted) {
|
|
3517
|
+
resume(Effect.fail(Errors.cancelled({ operation })));
|
|
3518
|
+
return Effect.void;
|
|
3519
|
+
}
|
|
3520
|
+
const onAbort = () => resume(Effect.fail(Errors.cancelled({ operation })));
|
|
3521
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
3522
|
+
return Effect.sync(() => signal.removeEventListener("abort", onAbort));
|
|
3523
|
+
})), runPromise);
|
|
3524
|
+
}
|
|
3525
|
+
//#endregion
|
|
3526
|
+
//#region src/surface/_shared/effect-actor-bridge.ts
|
|
3527
|
+
const facadeOutcome = (value) => {
|
|
3528
|
+
if (typeof value !== "object" || value === null || !("ok" in value) || value.ok !== false) return "succeeded";
|
|
3529
|
+
const reason = value.reason;
|
|
3530
|
+
if (reason === "CANCELLED") return "cancelled";
|
|
3531
|
+
return typeof reason === "string" && EXPECTED_OPERATION_OUTCOMES.has(reason) ? "refused" : "failed";
|
|
3532
|
+
};
|
|
3533
|
+
const rejectedFacadeOutcome = (cause) => {
|
|
3534
|
+
if (cause instanceof CapxulError) return cause.code === "CANCELLED" ? "cancelled" : "failed";
|
|
3535
|
+
if (typeof cause !== "object" || cause === null) return "failed";
|
|
3536
|
+
const candidate = cause;
|
|
3537
|
+
return candidate.code === "CANCELLED" || candidate.reason === "CANCELLED" || candidate.error?.code === "CANCELLED" || candidate.publicError?.code === "CANCELLED" ? "cancelled" : "failed";
|
|
3538
|
+
};
|
|
3539
|
+
const RESOLVED_FACADE_SPAN_FAILURE = Symbol("resolved facade span failure");
|
|
3540
|
+
/** The single facade-span bridge consumed by the renderer-neutral React facade. */
|
|
3541
|
+
async function runIdentityFacade(verb, controls, run, runPromise = Effect.runPromise) {
|
|
3542
|
+
const correlationId = safeEngineeringIdentifier(controls?.correlation_id);
|
|
3543
|
+
let resolvedFailure;
|
|
3544
|
+
return runPromise(Effect.gen(function* () {
|
|
3545
|
+
const parent = yield* Effect.currentSpan;
|
|
3546
|
+
return yield* Effect.tryPromise({
|
|
3547
|
+
try: () => run(carryInvocationParentSpan(controls, parent)),
|
|
3548
|
+
catch: (cause) => cause
|
|
3549
|
+
});
|
|
3550
|
+
}).pipe(Effect.tapError((cause) => Effect.annotateCurrentSpan({ outcome: rejectedFacadeOutcome(cause) })), Effect.flatMap((value) => {
|
|
3551
|
+
const outcome = facadeOutcome(value);
|
|
3552
|
+
return Effect.annotateCurrentSpan({ outcome }).pipe(Effect.andThen(outcome === "failed" || outcome === "cancelled" ? Effect.sync(() => {
|
|
3553
|
+
resolvedFailure = value;
|
|
3554
|
+
}).pipe(Effect.andThen(Effect.fail(RESOLVED_FACADE_SPAN_FAILURE))) : Effect.succeed(value)));
|
|
3555
|
+
}), Effect.withSpan(`identity.${verb}`), Effect.annotateSpans({
|
|
3556
|
+
verb,
|
|
3557
|
+
...correlationId === void 0 ? {} : { correlation_id: correlationId }
|
|
3558
|
+
}), Effect.catch((cause) => cause === RESOLVED_FACADE_SPAN_FAILURE ? Effect.succeed(resolvedFailure) : Effect.fail(cause))));
|
|
3559
|
+
}
|
|
3560
|
+
/**
|
|
3561
|
+
* Bridge an Effect whose typed failure carries `{ publicError: CapxulError }`
|
|
3562
|
+
* into the `Promise<CapxulResult<T>>` shape the consumer-facing method bundles
|
|
3563
|
+
* return. Applies to every port that surfaces a `publicError` (smart-account,
|
|
3564
|
+
* identity, auth-cache, account provision/deploy).
|
|
3565
|
+
*/
|
|
3566
|
+
async function runPortEffect(effect, controls, operation = "port", runPromise = Effect.runPromise) {
|
|
3567
|
+
const deadlineDelay = controls?.deadlineMs === void 0 ? void 0 : Math.max(0, controls.deadlineMs - Date.now());
|
|
3568
|
+
const timeoutMs = controls?.timeoutMs === void 0 ? deadlineDelay : deadlineDelay === void 0 ? controls.timeoutMs : Math.min(controls.timeoutMs, deadlineDelay);
|
|
3569
|
+
const observed = (timeoutMs === void 0 ? effect : effect.pipe(Effect.timeoutOrElse({
|
|
3570
|
+
duration: `${timeoutMs} millis`,
|
|
3571
|
+
orElse: () => Effect.fail({ publicError: Errors.providerTimeout("sdk", operation, timeoutMs) })
|
|
3572
|
+
}))).pipe(Effect.tap(() => Effect.annotateCurrentSpan({ outcome: "succeeded" })), Effect.tapError((failure) => {
|
|
3573
|
+
const mode = failure.publicError.details?.failure_mode;
|
|
3574
|
+
return Effect.annotateCurrentSpan({
|
|
3575
|
+
outcome: portFailureOutcome(failure.publicError.code),
|
|
3576
|
+
failure_code: failure.publicError.code,
|
|
3577
|
+
...typeof mode === "string" ? { mode } : {}
|
|
3578
|
+
});
|
|
3579
|
+
}), Effect.tapDefect(() => Effect.annotateCurrentSpan({
|
|
3580
|
+
outcome: "failed",
|
|
3581
|
+
failure_code: "UNKNOWN"
|
|
3582
|
+
})), Effect.withSpan(`identity.port.${operation}`));
|
|
3583
|
+
return runIfActive(controls?.signal, operation, () => withInvocationParentSpan(observed, controls), runPromise);
|
|
3584
|
+
}
|
|
3585
|
+
/** Map machine-internal failure state onto the public SDK error vocabulary. */
|
|
3586
|
+
function publicIdentityFailure(failure) {
|
|
3587
|
+
if (failure.error !== void 0) return failure.error;
|
|
3588
|
+
return new CapxulError(failure.code === "WORK_DIED" ? "UNKNOWN" : failure.code, failure.message, {
|
|
3589
|
+
...failure.mode === void 0 ? {} : { details: { failure_mode: failure.mode } },
|
|
3590
|
+
layer: "identity"
|
|
3591
|
+
});
|
|
3592
|
+
}
|
|
3593
|
+
/** The session carried by the actor's current snapshot, or `null`. */
|
|
3594
|
+
function sessionFromActor(actor) {
|
|
3595
|
+
return actor.authSession();
|
|
3596
|
+
}
|
|
3597
|
+
//#endregion
|
|
3598
|
+
//#region src/surface/account-deps.ts
|
|
3599
|
+
/** The single Context tag the account atom + `account.getStatus` resolve. */
|
|
3600
|
+
var AccountDepsTag = class extends Context.Service()("@capxul/sdk/AccountDeps") {};
|
|
3601
|
+
/** Wrap a bundle as the `Layer<AccountDepsTag>` the React provider consumes. */
|
|
3602
|
+
function accountDepsLayer(deps) {
|
|
3603
|
+
return Layer.succeed(AccountDepsTag, deps);
|
|
3604
|
+
}
|
|
3671
3605
|
/**
|
|
3672
|
-
*
|
|
3673
|
-
*
|
|
3674
|
-
*
|
|
3606
|
+
* `account.getStatus` as a single Effect requiring ONLY `AccountDepsTag`.
|
|
3607
|
+
* Faithful transcription of the current public method body (`account.ts`):
|
|
3608
|
+
* resolve the session (actor first, then the resume-path `authCache`), then
|
|
3609
|
+
* walk the readiness ladder. The `SmartAccountPort` failure narrows to its
|
|
3610
|
+
* `publicError`; a consumer `AccountProvider.getAddress` rejection becomes a
|
|
3611
|
+
* `providerError` (defending the public `CapxulResult` contract).
|
|
3675
3612
|
*/
|
|
3676
|
-
function
|
|
3677
|
-
|
|
3613
|
+
const accountStatusProgram = Effect.gen(function* () {
|
|
3614
|
+
const deps = yield* AccountDepsTag;
|
|
3615
|
+
const session = yield* Effect.promise(() => currentSession(deps.actor, deps.authCache));
|
|
3616
|
+
if (session === null) return { status: "notAuthenticated" };
|
|
3617
|
+
const current = yield* deps.smartAccountPort.loadByAuthUserId(session.authUserId).pipe(Effect.mapError((failure) => failure.publicError));
|
|
3618
|
+
if (current !== null) return statusFromAccount(current, deps.requirement);
|
|
3619
|
+
if (deps.requirement === "none") return {
|
|
3620
|
+
status: "accountReady",
|
|
3621
|
+
requirement: deps.requirement,
|
|
3622
|
+
account: null,
|
|
3623
|
+
deployment: { status: "counterfactual" }
|
|
3624
|
+
};
|
|
3625
|
+
const signer = deps.signer;
|
|
3626
|
+
if (signer === void 0) return {
|
|
3627
|
+
status: "accountRequired",
|
|
3628
|
+
requirement: deps.requirement,
|
|
3629
|
+
chainId: deps.chainId
|
|
3630
|
+
};
|
|
3631
|
+
const signerAddress = yield* Effect.tryPromise({
|
|
3632
|
+
try: () => signer.getAddress(),
|
|
3633
|
+
catch: (cause) => signerFailure(signer.source, "getAddress", cause)
|
|
3634
|
+
});
|
|
3635
|
+
return {
|
|
3636
|
+
status: "accountProviderReady",
|
|
3637
|
+
requirement: deps.requirement,
|
|
3638
|
+
chainId: deps.chainId,
|
|
3639
|
+
source: signer.source,
|
|
3640
|
+
signerAddress
|
|
3641
|
+
};
|
|
3642
|
+
});
|
|
3643
|
+
/**
|
|
3644
|
+
* Resume-path session resolution. Consults the actor first; if the actor has
|
|
3645
|
+
* no session (e.g. page refresh before any sign-in event), reads
|
|
3646
|
+
* `authCache.getSession`. `AuthCacheError` is non-fatal at this read point —
|
|
3647
|
+
* treat as "no session" and let the consumer's `auth.signIn` path resolve.
|
|
3648
|
+
*/
|
|
3649
|
+
async function currentSession(actor, authCache) {
|
|
3650
|
+
const fromActor = sessionFromActor(actor);
|
|
3651
|
+
if (fromActor !== null) return fromActor;
|
|
3652
|
+
if (authCache === void 0) return null;
|
|
3653
|
+
const cached = await Effect.runPromise(Effect.result(authCache.getSession));
|
|
3654
|
+
if (Result.isSuccess(cached)) {
|
|
3655
|
+
if (cached.success !== null && typeof actor.restoreAuthSession === "function") {
|
|
3656
|
+
const restored = await Effect.runPromise(Effect.result(actor.restoreAuthSession(cached.success)));
|
|
3657
|
+
if (Result.isFailure(restored)) return null;
|
|
3658
|
+
}
|
|
3659
|
+
return cached.success;
|
|
3660
|
+
}
|
|
3661
|
+
return null;
|
|
3662
|
+
}
|
|
3663
|
+
/** Map a backend `SmartAccount` row onto the readiness `AccountStatus`. */
|
|
3664
|
+
function statusFromAccount(account, requirement) {
|
|
3665
|
+
if (requirement === "deployed") {
|
|
3666
|
+
if (account.deployedAt === null) return {
|
|
3667
|
+
status: "accountPrepared",
|
|
3668
|
+
requirement,
|
|
3669
|
+
account,
|
|
3670
|
+
deployment: { status: "counterfactual" }
|
|
3671
|
+
};
|
|
3672
|
+
return {
|
|
3673
|
+
status: "accountReady",
|
|
3674
|
+
requirement,
|
|
3675
|
+
account,
|
|
3676
|
+
deployment: {
|
|
3677
|
+
status: "deployed",
|
|
3678
|
+
deployedAt: account.deployedAt
|
|
3679
|
+
}
|
|
3680
|
+
};
|
|
3681
|
+
}
|
|
3682
|
+
return {
|
|
3683
|
+
status: "accountReady",
|
|
3684
|
+
requirement,
|
|
3685
|
+
account,
|
|
3686
|
+
deployment: account.deployedAt === null ? { status: "counterfactual" } : {
|
|
3687
|
+
status: "deployed",
|
|
3688
|
+
deployedAt: account.deployedAt
|
|
3689
|
+
}
|
|
3690
|
+
};
|
|
3678
3691
|
}
|
|
3679
3692
|
//#endregion
|
|
3680
|
-
//#region
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
},
|
|
3696
|
-
sessionId: {
|
|
3697
|
-
maxLength: 128,
|
|
3698
|
-
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
3699
|
-
},
|
|
3700
|
-
organizationId: {
|
|
3701
|
-
maxLength: 128,
|
|
3702
|
-
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
3703
|
-
},
|
|
3704
|
-
journeyId: {
|
|
3705
|
-
maxLength: 128,
|
|
3706
|
-
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
3707
|
-
},
|
|
3708
|
-
correlationId: {
|
|
3709
|
-
maxLength: 128,
|
|
3710
|
-
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
3711
|
-
},
|
|
3712
|
-
anonymousId: {
|
|
3713
|
-
maxLength: 128,
|
|
3714
|
-
pattern: /^anon_[A-Za-z0-9-]+$/u
|
|
3715
|
-
},
|
|
3716
|
-
traceparent: {
|
|
3717
|
-
maxLength: 55,
|
|
3718
|
-
pattern: /^00-[0-9a-f]{32}-[0-9a-f]{16}-0[01]$/u
|
|
3693
|
+
//#region src/surface/account-lifecycle.ts
|
|
3694
|
+
function isActiveProvisioningPhase(phase) {
|
|
3695
|
+
return phase.status === "wallet" || phase.status === "identity" || phase.status === "provision" || phase.status === "deploy";
|
|
3696
|
+
}
|
|
3697
|
+
function isRequirementMet(status, requirement) {
|
|
3698
|
+
if (requirement === "none") return status.status !== "notAuthenticated";
|
|
3699
|
+
if (status.status !== "accountReady") return false;
|
|
3700
|
+
return requirement !== "deployed" || status.deployment.status === "deployed";
|
|
3701
|
+
}
|
|
3702
|
+
function mapProvisioningFailureStep(step) {
|
|
3703
|
+
switch (step) {
|
|
3704
|
+
case "wallet": return "connecting";
|
|
3705
|
+
case "identity": return "registering";
|
|
3706
|
+
case "provision":
|
|
3707
|
+
case "deploy": return "activating";
|
|
3719
3708
|
}
|
|
3720
|
-
}
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
for (const field of Object.keys(FIELD_RULES)) {
|
|
3731
|
-
const value = source[field];
|
|
3732
|
-
if (!isSafeField(field, value)) continue;
|
|
3733
|
-
sanitized[field] = value;
|
|
3709
|
+
}
|
|
3710
|
+
function mapProvisioningPhaseToSetupStep(phase) {
|
|
3711
|
+
switch (phase.status) {
|
|
3712
|
+
case "idle":
|
|
3713
|
+
case "wallet": return "connecting";
|
|
3714
|
+
case "identity": return "registering";
|
|
3715
|
+
case "provision":
|
|
3716
|
+
case "deploy": return "activating";
|
|
3717
|
+
case "ready":
|
|
3718
|
+
case "failed": return "connecting";
|
|
3734
3719
|
}
|
|
3735
|
-
return Object.keys(sanitized).length === 0 ? void 0 : sanitized;
|
|
3736
3720
|
}
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
const sanitized = sanitizeObservationContext(input);
|
|
3740
|
-
return sanitized === void 0 ? void 0 : JSON.stringify(sanitized);
|
|
3721
|
+
function isSettingUpLifecycle(lifecycle) {
|
|
3722
|
+
return lifecycle.status === "settingUp";
|
|
3741
3723
|
}
|
|
3742
|
-
function
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3724
|
+
function readyLifecycle(status, accountId) {
|
|
3725
|
+
return {
|
|
3726
|
+
status: "ready",
|
|
3727
|
+
accountId,
|
|
3728
|
+
canTransact: status.status === "accountReady" && status.deployment.status === "deployed"
|
|
3729
|
+
};
|
|
3730
|
+
}
|
|
3731
|
+
function mapAccountLifecycle(input) {
|
|
3732
|
+
const { status, phase, requirement, accountId } = input;
|
|
3733
|
+
if (status.status === "notAuthenticated") return { status: "loading" };
|
|
3734
|
+
if (phase.status === "failed") return {
|
|
3735
|
+
status: "failed",
|
|
3736
|
+
at: mapProvisioningFailureStep(phase.at),
|
|
3737
|
+
error: phase.error
|
|
3738
|
+
};
|
|
3739
|
+
if (isRequirementMet(status, requirement) || phase.status === "ready") {
|
|
3740
|
+
if (accountId === void 0) return { status: "loading" };
|
|
3741
|
+
return readyLifecycle(status, accountId);
|
|
3742
|
+
}
|
|
3743
|
+
if (isActiveProvisioningPhase(phase) || phase.status === "idle") return {
|
|
3744
|
+
status: "settingUp",
|
|
3745
|
+
step: mapProvisioningPhaseToSetupStep(phase)
|
|
3746
|
+
};
|
|
3747
|
+
return { status: "loading" };
|
|
3746
3748
|
}
|
|
3747
3749
|
//#endregion
|
|
3748
3750
|
//#region src/contract/actor-scope.ts
|
|
@@ -3979,7 +3981,7 @@ function mapActorRequest(request, payer) {
|
|
|
3979
3981
|
...resolvedPayer === void 0 ? {} : { payer: resolvedPayer },
|
|
3980
3982
|
amount: request.amount,
|
|
3981
3983
|
reference: request.reference,
|
|
3982
|
-
status:
|
|
3984
|
+
status: request.status,
|
|
3983
3985
|
expiresAt: request.expiresAt ?? null
|
|
3984
3986
|
};
|
|
3985
3987
|
}
|
|
@@ -4037,28 +4039,17 @@ function mapApprovedInboxPayment(command, input) {
|
|
|
4037
4039
|
};
|
|
4038
4040
|
}
|
|
4039
4041
|
}
|
|
4040
|
-
function
|
|
4042
|
+
function mapInboxStatus(status) {
|
|
4041
4043
|
switch (status) {
|
|
4042
4044
|
case "draft":
|
|
4043
4045
|
case "sent":
|
|
4044
|
-
case "viewed":
|
|
4046
|
+
case "viewed": return "open";
|
|
4047
|
+
case "pending_settlement": return "approved";
|
|
4045
4048
|
case "paid":
|
|
4046
|
-
case "pending_settlement":
|
|
4047
|
-
case "declined":
|
|
4048
|
-
case "cancelled":
|
|
4049
|
-
case "expired": return status;
|
|
4050
|
-
default: return "sent";
|
|
4051
|
-
}
|
|
4052
|
-
}
|
|
4053
|
-
function mapInboxStatus(status) {
|
|
4054
|
-
switch (status) {
|
|
4055
|
-
case "approved":
|
|
4056
4049
|
case "declined":
|
|
4057
|
-
case "paid":
|
|
4058
4050
|
case "cancelled":
|
|
4059
4051
|
case "expired": return status;
|
|
4060
|
-
|
|
4061
|
-
default: return "open";
|
|
4052
|
+
default: throw Errors.invalidInput("status", "unknown request status");
|
|
4062
4053
|
}
|
|
4063
4054
|
}
|
|
4064
4055
|
function normalizeRefForBackend$1(ref, field) {
|
|
@@ -4377,6 +4368,7 @@ const financialOpsContract = {
|
|
|
4377
4368
|
listPayments: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].listPayments),
|
|
4378
4369
|
getPayment: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].getPayment),
|
|
4379
4370
|
activityList: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].list),
|
|
4371
|
+
activitySummary: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].summary),
|
|
4380
4372
|
activityGet: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].get),
|
|
4381
4373
|
activityAnnotate: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].annotate),
|
|
4382
4374
|
verifyPaymentDocument: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].verifyPaymentDocument),
|
|
@@ -4628,6 +4620,40 @@ function actorReferenceToBackend(actor) {
|
|
|
4628
4620
|
case "org": return actor;
|
|
4629
4621
|
}
|
|
4630
4622
|
}
|
|
4623
|
+
/**
|
|
4624
|
+
* The activity backend gives its actor a different name than the other
|
|
4625
|
+
* financialOps functions: `{kind: "organization", orgId}`, and not
|
|
4626
|
+
* `{kind: "org", orgId}`. Thus it does not use `actorReferenceToBackend`.
|
|
4627
|
+
*
|
|
4628
|
+
* The `personal` case sends NO actor. This is correct, and it does not lose
|
|
4629
|
+
* data. `requireActivityActor` uses the signed-in Account when the actor is
|
|
4630
|
+
* absent, and `canReadActor` refuses all other account identifiers
|
|
4631
|
+
* (`packages/backend/convex/movement/activityAuth.ts`). Thus "personal" and
|
|
4632
|
+
* "absent" both name the one Account that an SDK caller can read. The SDK does
|
|
4633
|
+
* not hold an account identifier to send.
|
|
4634
|
+
*
|
|
4635
|
+
* The `switch` is exhaustive. A new `ActorReference` variant is a compile error
|
|
4636
|
+
* here. The conditional expression that this function replaced removed each
|
|
4637
|
+
* value that was not an organization.
|
|
4638
|
+
*/
|
|
4639
|
+
function activityActorField(actor) {
|
|
4640
|
+
if (actor === void 0) return {};
|
|
4641
|
+
switch (actor.kind) {
|
|
4642
|
+
case "personal": return {};
|
|
4643
|
+
case "organization": return { actor: {
|
|
4644
|
+
kind: "organization",
|
|
4645
|
+
orgId: actor.organizationId
|
|
4646
|
+
} };
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
/** An absent actor and a `personal` actor both name the signed-in Account. */
|
|
4650
|
+
function actorScopeKey(actor) {
|
|
4651
|
+
return actor === void 0 || actor.kind === "personal" ? "personal" : `org:${actor.organizationId}`;
|
|
4652
|
+
}
|
|
4653
|
+
/** Two actor references name the same scope. */
|
|
4654
|
+
function sameActor(left, right) {
|
|
4655
|
+
return actorScopeKey(left) === actorScopeKey(right);
|
|
4656
|
+
}
|
|
4631
4657
|
const memoryPaymentRequestKeys = /* @__PURE__ */ new Map();
|
|
4632
4658
|
const paymentAttemptReleases = /* @__PURE__ */ new Set();
|
|
4633
4659
|
let paymentAttemptPagehideInstalled = false;
|
|
@@ -5058,31 +5084,48 @@ function makeFinancialOpsMethods(deps) {
|
|
|
5058
5084
|
},
|
|
5059
5085
|
activity: {
|
|
5060
5086
|
list: (params, options) => runIfActive(options?.signal, "activity.list", () => deps.convexCall.query(fns.activityList, { input: {
|
|
5061
|
-
...params?.actor
|
|
5062
|
-
kind: "organization",
|
|
5063
|
-
orgId: params.actor.organizationId
|
|
5064
|
-
} } : {},
|
|
5087
|
+
...activityActorField(params?.actor),
|
|
5065
5088
|
...params?.cursor === void 0 ? {} : { cursor: params.cursor },
|
|
5066
|
-
...params?.limit === void 0 ? {} : { limit: params.limit }
|
|
5089
|
+
...params?.limit === void 0 ? {} : { limit: params.limit },
|
|
5090
|
+
...params?.range === void 0 ? {} : { range: params.range },
|
|
5091
|
+
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
5067
5092
|
} })),
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
...reference.kind === "payment" && reference.actor?.kind === "organization" ? { actor: {
|
|
5072
|
-
kind: "organization",
|
|
5073
|
-
orgId: reference.actor.organizationId
|
|
5074
|
-
} } : {}
|
|
5093
|
+
summary: (params, options) => runIfActive(options?.signal, "activity.summary", () => deps.convexCall.query(fns.activitySummary, { input: {
|
|
5094
|
+
...activityActorField(params?.actor),
|
|
5095
|
+
...params?.window === void 0 ? {} : { window: params.window }
|
|
5075
5096
|
} })),
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5097
|
+
get: (reference, options) => {
|
|
5098
|
+
const actor = actorReferenceToBackend(options?.actor);
|
|
5099
|
+
return runIfActive(options?.signal, "activity.get", () => deps.convexCall.query(fns.activityGet, {
|
|
5100
|
+
input: {
|
|
5101
|
+
kind: reference.kind,
|
|
5102
|
+
id: reference.id,
|
|
5103
|
+
...reference.kind === "payment" ? activityActorField(reference.actor) : {}
|
|
5104
|
+
},
|
|
5105
|
+
...actor === void 0 ? {} : { actor }
|
|
5106
|
+
}));
|
|
5107
|
+
},
|
|
5108
|
+
annotate: (input, options) => {
|
|
5109
|
+
if (input.actor !== void 0 && input.reference.kind === "payment" && input.reference.actor !== void 0 && !sameActor(input.actor, input.reference.actor)) return Promise.resolve({
|
|
5110
|
+
ok: false,
|
|
5111
|
+
error: Errors.invalidInput("reference.actor", "must match the annotation actor")
|
|
5112
|
+
});
|
|
5113
|
+
const referenceActor = input.reference.kind === "payment" ? activityActorField(input.reference.actor).actor : void 0;
|
|
5114
|
+
return runIfActive(options?.signal, "activity.annotate", () => deps.convexCall.mutation(fns.activityAnnotate, { input: {
|
|
5115
|
+
...activityActorField(input.actor),
|
|
5116
|
+
reference: input.reference.kind === "payment" && referenceActor !== void 0 ? {
|
|
5117
|
+
kind: "payment",
|
|
5118
|
+
id: input.reference.id,
|
|
5119
|
+
actor: referenceActor
|
|
5120
|
+
} : {
|
|
5121
|
+
kind: input.reference.kind,
|
|
5122
|
+
id: input.reference.id
|
|
5123
|
+
},
|
|
5124
|
+
...input.counterpartyLabel === void 0 ? {} : { counterpartyLabel: input.counterpartyLabel },
|
|
5125
|
+
...input.accountingCategory === void 0 ? {} : { accountingCategory: input.accountingCategory },
|
|
5126
|
+
...input.memo === void 0 ? {} : { memo: input.memo }
|
|
5127
|
+
} }));
|
|
5128
|
+
}
|
|
5086
5129
|
},
|
|
5087
5130
|
offramp: {
|
|
5088
5131
|
quote: (input, options) => {
|
|
@@ -5410,7 +5453,7 @@ function mapOk(result, f) {
|
|
|
5410
5453
|
}
|
|
5411
5454
|
//#endregion
|
|
5412
5455
|
//#region package.json
|
|
5413
|
-
var version = "2.
|
|
5456
|
+
var version = "2.4.0";
|
|
5414
5457
|
//#endregion
|
|
5415
5458
|
//#region src/ports/auth-client.ts
|
|
5416
5459
|
var AuthClientError = class extends Data.TaggedError("AuthClientError") {};
|
|
@@ -5584,7 +5627,8 @@ const TelemetryEnvelopeProps = {
|
|
|
5584
5627
|
"development",
|
|
5585
5628
|
"staging",
|
|
5586
5629
|
"production",
|
|
5587
|
-
"unknown"
|
|
5630
|
+
"unknown",
|
|
5631
|
+
"local"
|
|
5588
5632
|
]),
|
|
5589
5633
|
producer: Schema.Literals([
|
|
5590
5634
|
"server",
|
|
@@ -8163,13 +8207,74 @@ const organizationPaymentExecutionUnavailable = () => Promise.resolve({
|
|
|
8163
8207
|
//#region src/contract/holdings.ts
|
|
8164
8208
|
const holdingsContract = { current: makeFunctionReference(CAPXUL_FUNCTIONS["holdings/actions"].current) };
|
|
8165
8209
|
//#endregion
|
|
8210
|
+
//#region src/domain/money/primary-holding.ts
|
|
8211
|
+
const CURRENCY_BY_TOKEN_SYMBOL = new Map([["USDX", toCurrencyCode("USD")]]);
|
|
8212
|
+
/**
|
|
8213
|
+
* The designated display asset: the payable row holding the most money, or
|
|
8214
|
+
* `null` when no row is payable.
|
|
8215
|
+
*
|
|
8216
|
+
* A `HoldingRow` (`packages/wire/src/money.ts`) is a RAW CHAIN ROW, and three
|
|
8217
|
+
* of its shapes cannot become a `Holding`. Each is SKIPPED rather than
|
|
8218
|
+
* reported, because a row nobody can render is not a failure of the read:
|
|
8219
|
+
*
|
|
8220
|
+
* - no `decimals` — the raw balance cannot be lifted into major units;
|
|
8221
|
+
* - no `symbol` — the asset cannot be named;
|
|
8222
|
+
* - a symbol that stands for no supported `CurrencyCode` — the balance
|
|
8223
|
+
* cannot be stated as `Money` without inventing a currency for it.
|
|
8224
|
+
*
|
|
8225
|
+
* This replaces the app's hand pick in `formatCurrentHoldingsBalance`
|
|
8226
|
+
* (`dashboard-formatters.ts`): a `.find()` over a hardcoded `["USDX","USDC"]`
|
|
8227
|
+
* list falling back to `rows[0]`. Ties keep the earlier row, so the same
|
|
8228
|
+
* snapshot always yields the same answer.
|
|
8229
|
+
*/
|
|
8230
|
+
function primaryHolding(rows) {
|
|
8231
|
+
let primary = null;
|
|
8232
|
+
for (const row of rows) {
|
|
8233
|
+
const holding = toHolding(row);
|
|
8234
|
+
if (holding === null) continue;
|
|
8235
|
+
if (primary === null || compareDecimal(holding.available.value, primary.available.value) > 0) primary = holding;
|
|
8236
|
+
}
|
|
8237
|
+
return primary;
|
|
8238
|
+
}
|
|
8239
|
+
function toHolding(row) {
|
|
8240
|
+
if (row.symbol === void 0 || row.decimals === void 0) return null;
|
|
8241
|
+
const currency = currencyForTokenSymbol(row.symbol);
|
|
8242
|
+
if (currency === null) return null;
|
|
8243
|
+
return {
|
|
8244
|
+
id: row.assetKind === "native" ? "native" : row.tokenAddress.toLowerCase(),
|
|
8245
|
+
symbol: row.symbol,
|
|
8246
|
+
available: fromWei(row.rawBalance, row.decimals, currency)
|
|
8247
|
+
};
|
|
8248
|
+
}
|
|
8249
|
+
function currencyForTokenSymbol(symbol) {
|
|
8250
|
+
return CURRENCY_BY_TOKEN_SYMBOL.get(symbol.toUpperCase()) ?? null;
|
|
8251
|
+
}
|
|
8252
|
+
/**
|
|
8253
|
+
* Order two non-negative major-unit decimal strings without converting either
|
|
8254
|
+
* to a JavaScript number. Rows carry different `decimals`, so the raw integers
|
|
8255
|
+
* are not comparable: 1999999999 at 9 decimals is LESS than 2000000 at 6.
|
|
8256
|
+
*/
|
|
8257
|
+
function compareDecimal(first, second) {
|
|
8258
|
+
const [firstWhole = "0", firstFraction = ""] = first.split(".");
|
|
8259
|
+
const [secondWhole = "0", secondFraction = ""] = second.split(".");
|
|
8260
|
+
const width = Math.max(firstFraction.length, secondFraction.length);
|
|
8261
|
+
const firstScaled = BigInt(firstWhole + firstFraction.padEnd(width, "0"));
|
|
8262
|
+
const secondScaled = BigInt(secondWhole + secondFraction.padEnd(width, "0"));
|
|
8263
|
+
if (firstScaled === secondScaled) return 0;
|
|
8264
|
+
return firstScaled < secondScaled ? -1 : 1;
|
|
8265
|
+
}
|
|
8266
|
+
//#endregion
|
|
8166
8267
|
//#region src/surface/holdings.ts
|
|
8167
8268
|
function makeHoldingsMethods(deps) {
|
|
8168
8269
|
const functions = deps.functions ?? holdingsContract;
|
|
8169
|
-
|
|
8270
|
+
const read = (operation, actor, signal, pick) => runIfActive(signal, operation, () => deps.convexCall.action(functions.current, actor?.kind === "organization" ? { actor: {
|
|
8170
8271
|
kind: "organization",
|
|
8171
|
-
orgId:
|
|
8172
|
-
} } : {}))
|
|
8272
|
+
orgId: actor.organizationId
|
|
8273
|
+
} } : {}).pipe(Effect.map(pick)));
|
|
8274
|
+
return {
|
|
8275
|
+
current: (input, options) => read("holdings.current", input?.actor, options?.signal, (snapshot) => snapshot),
|
|
8276
|
+
primary: (input, options) => read("holdings.primary", input?.actor, options?.signal, (snapshot) => primaryHolding(snapshot.rows))
|
|
8277
|
+
};
|
|
8173
8278
|
}
|
|
8174
8279
|
//#endregion
|
|
8175
8280
|
//#region src/surface/factory.ts
|
|
@@ -8998,4 +9103,4 @@ function withHostObservation(actor, snapshot) {
|
|
|
8998
9103
|
};
|
|
8999
9104
|
}
|
|
9000
9105
|
//#endregion
|
|
9001
|
-
export { fingerprintPaymentIntent as A,
|
|
9106
|
+
export { isClaimed as $, fingerprintPaymentIntent as A, OBSERVATION_CONTEXT_HEADER as B, ClockPortTag as C, AuthClientError as D, authClientPortFromPromiseAdapter as E, copyInvocationObservation as F, CAPXUL_FUNCTIONS as G, sanitizeObservationContext as H, readInvocationObservation as I, CAPXUL_PAYMENTS_V2_ADDRESS as J, BootstrapEnvelope as K, causeChain as L, fromWei as M, isSettingUpLifecycle as N, AuthClientPortTag as O, formatTraceparent as P, destination as Q, injectedWalletSigner as R, ClockError as S, bootstrapErrorFromCapxul as T, PAYMENT_DIRECTIONS as U, encodeObservationContextHeader as V, PAYMENT_STATUSES as W, BASE_SEPOLIA_CHAIN_ID as X, normalizeBindingEmail as Y, deriveCapxulSafeAddress as Z, wireChainId as _, observeFailedResult as a, ConvexCallPortTag as b, captureExceptionSync as c, TelemetryPortTag as d, isRestoring as et, redactTelemetryEvent as f, accountReadErrorFromCapxul as g, AccountReadPortTag as h, observationContextProps as i, toWei as j, version as k, detectAuthCacheAdapter as l, smartAccountErrorFromCapxul as m, postHogProductTelemetry as n, postHogFailureObservation as o, SmartAccountPortTag as p, EngineeringTelemetryBootstrapPolicy as q, CAPXUL_SDK_EXPECTED_OUTCOME_EVENT as r, captureException as s, assembleCapxulClient as t, PostHogTelemetryLayer as u, IdentityPortTag as v, BootstrapPortTag as w, convexCallErrorFromCapxul as x, identityErrorFromCapxul as y, signerFailure as z };
|