@capxul/mcp 4.2.0-rc.7 → 4.2.0-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capxul-source.json +3 -3
- package/dist/main.mjs +609 -18
- package/package.json +2 -2
package/dist/capxul-source.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": {
|
|
3
|
-
"commit": "
|
|
4
|
-
"tree": "
|
|
3
|
+
"commit": "1c96a26356e8f36402e47eb4ac60bb89d6aeed7f",
|
|
4
|
+
"tree": "375f49f76377eeec331fd44ce17238d4a6f02ad4",
|
|
5
5
|
"branch": "codex/programme-rc",
|
|
6
6
|
"repository": "https://github.com/Xelmar-tech/infrastructure",
|
|
7
7
|
"lockfileSha256": "5870f53b72706d103220c1bcf733cb4fb71196aea32a9cfbdc277d73cc959264"
|
|
8
8
|
},
|
|
9
9
|
"name": "@capxul/mcp",
|
|
10
|
-
"version": "4.2.0-rc.
|
|
10
|
+
"version": "4.2.0-rc.9"
|
|
11
11
|
}
|
package/dist/main.mjs
CHANGED
|
@@ -852,6 +852,7 @@ const COUNTRY_CODE_RE = /^[A-Z]{2}$/;
|
|
|
852
852
|
const ACCOUNT_ID_RE = /^account_[0-9A-Za-z]+$/;
|
|
853
853
|
const PARTY_ID_RE$1 = /^party_[0-9A-Za-z]+$/;
|
|
854
854
|
const APP_ID_RE = /^app_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
|
|
855
|
+
const TX_HASH_RE = /^0x[0-9a-f]{64}$/i;
|
|
855
856
|
const WEI_RE = /^[0-9]+$/;
|
|
856
857
|
Math.floor(Number.MAX_SAFE_INTEGER / 1e3);
|
|
857
858
|
function toAddress(raw) {
|
|
@@ -903,6 +904,10 @@ function toPublishableKey(raw) {
|
|
|
903
904
|
if (typeof raw !== "string" || !PUBLISHABLE_KEY_PATTERN.test(raw)) throw Errors.invalidInput("publishableKey", invalidValueReason("must match cap_pk_(test|live) plus 32 Crockford base32 chars", raw));
|
|
904
905
|
return raw;
|
|
905
906
|
}
|
|
907
|
+
function toTxHash(raw) {
|
|
908
|
+
if (typeof raw !== "string" || !TX_HASH_RE.test(raw)) throw Errors.invalidInput("txHash", invalidValueReason("must be 0x + 64 hex chars", raw));
|
|
909
|
+
return raw.toLowerCase();
|
|
910
|
+
}
|
|
906
911
|
function toEpochMs(raw) {
|
|
907
912
|
assertSafeNonNegativeInteger(raw, "epochMs");
|
|
908
913
|
return raw;
|
|
@@ -982,19 +987,108 @@ function invalidValueReason(prefix, raw) {
|
|
|
982
987
|
//#region ../../packages/config/src/tokens.ts
|
|
983
988
|
/** `TestUSDC` ("USDX") — Base Sepolia, 6 decimals, open `mint`. (Canon §1.) */
|
|
984
989
|
const USDX_ADDRESS_BASE_SEPOLIA = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
|
|
990
|
+
const USDX_DEPLOYMENT_START_BLOCK = 39860173;
|
|
991
|
+
/**
|
|
992
|
+
* The ERC-20 `symbol()` the deployed `TestUSDC` contract returns
|
|
993
|
+
* (`packages/contracts/src/TestUSDC.sol:10`). It travels with the address and
|
|
994
|
+
* the decimals so a holdings row can never carry one token's address with
|
|
995
|
+
* another token's display symbol. This is the display symbol, not the
|
|
996
|
+
* currency: see `USDX_CURRENCY` below.
|
|
997
|
+
*/
|
|
998
|
+
const USDX_SYMBOL = "USDX";
|
|
999
|
+
const SYNTHETIC_TEST_ASSET_SOURCE = "synthetic-test-liquidity";
|
|
985
1000
|
const USDX_ASSET = {
|
|
986
1001
|
assetId: assetIdFor(84532, USDX_ADDRESS_BASE_SEPOLIA),
|
|
987
1002
|
chainId: toChainId(84532),
|
|
988
|
-
symbol:
|
|
1003
|
+
symbol: USDX_SYMBOL,
|
|
989
1004
|
peg: toCurrencyCode("USD"),
|
|
990
1005
|
tokenAddress: USDX_ADDRESS_BASE_SEPOLIA,
|
|
991
|
-
deploymentStartBlock:
|
|
992
|
-
decimals: 6
|
|
1006
|
+
deploymentStartBlock: USDX_DEPLOYMENT_START_BLOCK,
|
|
1007
|
+
decimals: 6,
|
|
1008
|
+
source: "settlement"
|
|
993
1009
|
};
|
|
994
|
-
|
|
1010
|
+
/**
|
|
1011
|
+
* The settlement tier: rows the exact-transfer path and the Movement scanner
|
|
1012
|
+
* admit, each carrying the legacy `currency` projection those readers expect.
|
|
1013
|
+
* Adding an owned test asset here would put candidate liquidity on the
|
|
1014
|
+
* money-write path, so M03's rows go to the candidate tier below instead.
|
|
1015
|
+
*/
|
|
1016
|
+
const CONFIGURED_MONEY_ASSETS = [{
|
|
995
1017
|
...USDX_ASSET,
|
|
996
1018
|
currency: USDX_ASSET.peg
|
|
997
|
-
}]
|
|
1019
|
+
}];
|
|
1020
|
+
/**
|
|
1021
|
+
* The candidate tier: five owned test assets, every field read back from Base
|
|
1022
|
+
* Sepolia after the deployment that created it (chain 84532, blocks
|
|
1023
|
+
* 46,465,978–46,465,982). Real contracts, authored liquidity, no settlement
|
|
1024
|
+
* admission — so rollback is removing consumers, never relabelling a
|
|
1025
|
+
* deployed address or deleting its history.
|
|
1026
|
+
*
|
|
1027
|
+
* `symbol` is the display string the owned contract returns. A
|
|
1028
|
+
* provider-supported USDC would be a different contract at a different
|
|
1029
|
+
* address returning the same symbol, which is why the resolvers below key on
|
|
1030
|
+
* `assetId`, or on chain plus address, and never on a symbol.
|
|
1031
|
+
*/
|
|
1032
|
+
const SYNTHETIC_TEST_ASSETS = [
|
|
1033
|
+
{
|
|
1034
|
+
assetId: assetIdFor(84532, "0xb3d8566fb90f7df939f4bc08a09b19047117f549"),
|
|
1035
|
+
chainId: toChainId(84532),
|
|
1036
|
+
tokenAddress: "0xb3d8566fb90f7df939f4bc08a09b19047117f549",
|
|
1037
|
+
symbol: "USDC",
|
|
1038
|
+
decimals: 6,
|
|
1039
|
+
peg: toCurrencyCode("USD"),
|
|
1040
|
+
deploymentStartBlock: 46465978,
|
|
1041
|
+
deploymentTransaction: "0xa7fb8bb9d1fd1cc5fe2fc42fa4cdccace7742efae8ea99f091a283e5709343de",
|
|
1042
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1043
|
+
},
|
|
1044
|
+
{
|
|
1045
|
+
assetId: assetIdFor(84532, "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01"),
|
|
1046
|
+
chainId: toChainId(84532),
|
|
1047
|
+
tokenAddress: "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01",
|
|
1048
|
+
symbol: "USDT",
|
|
1049
|
+
decimals: 6,
|
|
1050
|
+
peg: toCurrencyCode("USD"),
|
|
1051
|
+
deploymentStartBlock: 46465979,
|
|
1052
|
+
deploymentTransaction: "0xa17ad76fe8b24cdf1fbcff245c3eb022444710cf9fa7b5ee59818be654f4ae10",
|
|
1053
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
assetId: assetIdFor(84532, "0x1d93d525f73453fe18eebf044e8a3954bfe3721e"),
|
|
1057
|
+
chainId: toChainId(84532),
|
|
1058
|
+
tokenAddress: "0x1d93d525f73453fe18eebf044e8a3954bfe3721e",
|
|
1059
|
+
symbol: "WETH",
|
|
1060
|
+
decimals: 18,
|
|
1061
|
+
peg: null,
|
|
1062
|
+
deploymentStartBlock: 46465980,
|
|
1063
|
+
deploymentTransaction: "0xcb9b2d024686fac93ec1243ccf02d3567fd531da46176115660cba2ba644b09d",
|
|
1064
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1065
|
+
},
|
|
1066
|
+
{
|
|
1067
|
+
assetId: assetIdFor(84532, "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624"),
|
|
1068
|
+
chainId: toChainId(84532),
|
|
1069
|
+
tokenAddress: "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624",
|
|
1070
|
+
symbol: "WBTC",
|
|
1071
|
+
decimals: 8,
|
|
1072
|
+
peg: null,
|
|
1073
|
+
deploymentStartBlock: 46465981,
|
|
1074
|
+
deploymentTransaction: "0x728ba7b430ffd141f83d0ed5ce429d050049d7b097c936fd11e9c572d1243854",
|
|
1075
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
assetId: assetIdFor(84532, "0x5865fe9787ac214feb5facaebaec06969ac0a9a3"),
|
|
1079
|
+
chainId: toChainId(84532),
|
|
1080
|
+
tokenAddress: "0x5865fe9787ac214feb5facaebaec06969ac0a9a3",
|
|
1081
|
+
symbol: "cNGN",
|
|
1082
|
+
decimals: 6,
|
|
1083
|
+
peg: toCurrencyCode("NGN"),
|
|
1084
|
+
deploymentStartBlock: 46465982,
|
|
1085
|
+
deploymentTransaction: "0xcedf254bcef57fc64189261a5ab1161bc4fde370e470933f627c4d8ddb5a2e26",
|
|
1086
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1087
|
+
}
|
|
1088
|
+
];
|
|
1089
|
+
toChainId(84532);
|
|
1090
|
+
[...CONFIGURED_MONEY_ASSETS, ...SYNTHETIC_TEST_ASSETS];
|
|
1091
|
+
CONFIGURED_MONEY_ASSETS.map((asset) => asset.tokenAddress.toLowerCase());
|
|
998
1092
|
//#endregion
|
|
999
1093
|
//#region ../../node_modules/.pnpm/abitype@1.2.3_typescript@7.0.2_zod@4.4.3/node_modules/abitype/dist/esm/regex.js
|
|
1000
1094
|
function execTyped(regex, string) {
|
|
@@ -6794,6 +6888,8 @@ const CAPXUL_OPERATIONS = {
|
|
|
6794
6888
|
emitAnnotationSaved: "activity.emitAnnotationSaved",
|
|
6795
6889
|
get: "activity.get",
|
|
6796
6890
|
list: "activity.list",
|
|
6891
|
+
/** The SDK's live Activity read over the indexer's SSE stream. */
|
|
6892
|
+
subscribe: "activity.subscribe",
|
|
6797
6893
|
summary: "activity.summary"
|
|
6798
6894
|
},
|
|
6799
6895
|
addressBook: {
|
|
@@ -7277,6 +7373,17 @@ const AuthFailedProps = Schema.Struct({
|
|
|
7277
7373
|
auth_type: OptionalString,
|
|
7278
7374
|
reason: OptionalString
|
|
7279
7375
|
});
|
|
7376
|
+
const IdentityRefusedProps = Schema.Struct({
|
|
7377
|
+
...TelemetryEnvelopeProps,
|
|
7378
|
+
/** The refused identity event tag, for example CreateOrganization. */
|
|
7379
|
+
event: OptionalString,
|
|
7380
|
+
/** The machine's refusal code: WRONG_STATE, SUPERSEDED or STALE_EPOCH. */
|
|
7381
|
+
refusal_code: OptionalString,
|
|
7382
|
+
/** The machine slot the request targeted, for example identity:org. */
|
|
7383
|
+
slot: OptionalString,
|
|
7384
|
+
/** The machine state at refusal, for example authenticated:claiming. */
|
|
7385
|
+
state: OptionalString
|
|
7386
|
+
});
|
|
7280
7387
|
const AuthSignedOutProps = Schema.Struct(TelemetryEnvelopeProps);
|
|
7281
7388
|
const ProvisioningSafeCreatedProps = Schema.Struct({
|
|
7282
7389
|
...TelemetryEnvelopeProps,
|
|
@@ -7727,6 +7834,10 @@ Schema.Struct({
|
|
|
7727
7834
|
name: Schema.Literal("auth_failed"),
|
|
7728
7835
|
props: AuthFailedProps
|
|
7729
7836
|
});
|
|
7837
|
+
Schema.Struct({
|
|
7838
|
+
name: Schema.Literal("identity_refused"),
|
|
7839
|
+
props: IdentityRefusedProps
|
|
7840
|
+
});
|
|
7730
7841
|
Schema.Struct({
|
|
7731
7842
|
name: Schema.Literal("auth_signed_out"),
|
|
7732
7843
|
props: AuthSignedOutProps
|
|
@@ -8536,6 +8647,10 @@ const BootstrapEnvelope = Schema.Struct({
|
|
|
8536
8647
|
siteBaseUrl: Schema.String,
|
|
8537
8648
|
openfortPublishableKey: Schema.String,
|
|
8538
8649
|
shieldPublishableKey: Schema.String,
|
|
8650
|
+
/** Which Openfort third-party auth provider the signer presents; absent means better-auth. */
|
|
8651
|
+
openfortAuthProvider: Schema.optional(Schema.Literals(["better-auth", "oidc"])),
|
|
8652
|
+
/** Origin of the Ponder indexer the SDK reads directly; absent means no indexer reads. */
|
|
8653
|
+
indexerUrl: Schema.optional(Schema.String),
|
|
8539
8654
|
engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
|
|
8540
8655
|
})
|
|
8541
8656
|
});
|
|
@@ -8610,6 +8725,7 @@ const CAPXUL_FUNCTIONS = {
|
|
|
8610
8725
|
prepareOrganizationPaymentExecution: "moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
|
|
8611
8726
|
submitPaymentCommandExecution: "moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
|
|
8612
8727
|
},
|
|
8728
|
+
"moneyExecution/providerSendWitness": { read: "moneyExecution/providerSendWitness:read" },
|
|
8613
8729
|
media: {
|
|
8614
8730
|
generateUploadUrl: "media:generateUploadUrl",
|
|
8615
8731
|
setOrgLogo: "media:setOrgLogo",
|
|
@@ -8658,8 +8774,7 @@ const CAPXUL_FUNCTIONS = {
|
|
|
8658
8774
|
listMine: "org/queries:listMine",
|
|
8659
8775
|
listRolesByOrgId: "org/queries:listRolesByOrgId",
|
|
8660
8776
|
loadByOrgId: "org/queries:loadByOrgId",
|
|
8661
|
-
me: "org/queries:me"
|
|
8662
|
-
pendingSetup: "org/queries:pendingSetup"
|
|
8777
|
+
me: "org/queries:me"
|
|
8663
8778
|
},
|
|
8664
8779
|
"smartAccount/actions": {
|
|
8665
8780
|
claim: "smartAccount/actions:claim",
|
|
@@ -8670,6 +8785,11 @@ const CAPXUL_FUNCTIONS = {
|
|
|
8670
8785
|
loadByAuthUserId: "smartAccount/queries:loadByAuthUserId",
|
|
8671
8786
|
loadBySmartAccountAddress: "smartAccount/queries:loadBySmartAccountAddress"
|
|
8672
8787
|
},
|
|
8788
|
+
e2eControls: {
|
|
8789
|
+
holdUserOperationReceipt: "e2eControls:holdUserOperationReceipt",
|
|
8790
|
+
isUserOperationReceiptHeld: "e2eControls:isUserOperationReceiptHeld",
|
|
8791
|
+
releaseUserOperationReceipt: "e2eControls:releaseUserOperationReceipt"
|
|
8792
|
+
},
|
|
8673
8793
|
system: { health: "system:health" }
|
|
8674
8794
|
};
|
|
8675
8795
|
//#endregion
|
|
@@ -12258,7 +12378,7 @@ const moneyExecutionContract = {
|
|
|
12258
12378
|
};
|
|
12259
12379
|
//#endregion
|
|
12260
12380
|
//#region ../../packages/sdk/package.json
|
|
12261
|
-
var version$1 = "4.2.0-rc.
|
|
12381
|
+
var version$1 = "4.2.0-rc.9";
|
|
12262
12382
|
//#endregion
|
|
12263
12383
|
//#region ../../packages/sdk/src/telemetry/exception-projection.ts
|
|
12264
12384
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -12355,6 +12475,17 @@ function observeFailedResult(result, adapter, operation, origin) {
|
|
|
12355
12475
|
return result;
|
|
12356
12476
|
}
|
|
12357
12477
|
/**
|
|
12478
|
+
* @internal Reports a failure that can never become a method result.
|
|
12479
|
+
*
|
|
12480
|
+
* A reactive stream is torn down after its call already returned, so nothing
|
|
12481
|
+
* downstream observes it. `captureException` is the right kind: the caller did
|
|
12482
|
+
* not ask for the end, so it is not an expected product outcome.
|
|
12483
|
+
*/
|
|
12484
|
+
function observeStreamFailure(adapter, operation, cause) {
|
|
12485
|
+
if (adapter === void 0) return;
|
|
12486
|
+
report(adapter, "exception", operation, cause, resolveAdapterSnapshot(adapter));
|
|
12487
|
+
}
|
|
12488
|
+
/**
|
|
12358
12489
|
* One failure, one event. The identity machine reports a failed transition
|
|
12359
12490
|
* first; when the same `CapxulError` then surfaces as a public method result
|
|
12360
12491
|
* (directly or down its `cause` chain), the boundary drops that second report.
|
|
@@ -15272,7 +15403,7 @@ function isTransientBootstrapFailure(error) {
|
|
|
15272
15403
|
const status = error.details?.httpStatus;
|
|
15273
15404
|
return typeof status === "number" && isTransientHttpStatus(status);
|
|
15274
15405
|
}
|
|
15275
|
-
async function safeText(res) {
|
|
15406
|
+
async function safeText$1(res) {
|
|
15276
15407
|
try {
|
|
15277
15408
|
return await res.text();
|
|
15278
15409
|
} catch {
|
|
@@ -15345,6 +15476,8 @@ var HttpBootstrapAdapter = class {
|
|
|
15345
15476
|
siteBaseUrl: normalizeRuntimeUrl("siteBaseUrl", decodedState.siteBaseUrl),
|
|
15346
15477
|
openfortPublishableKey: decodedState.openfortPublishableKey,
|
|
15347
15478
|
shieldPublishableKey: decodedState.shieldPublishableKey,
|
|
15479
|
+
...decodedState.openfortAuthProvider === void 0 ? {} : { openfortAuthProvider: decodedState.openfortAuthProvider },
|
|
15480
|
+
...decodedState.indexerUrl === void 0 ? {} : { indexerUrl: decodedState.indexerUrl },
|
|
15348
15481
|
...decodedEngineeringTelemetry !== void 0 && Result.isSuccess(decodedEngineeringTelemetry) ? { engineeringTelemetry: decodedEngineeringTelemetry.success } : {}
|
|
15349
15482
|
};
|
|
15350
15483
|
},
|
|
@@ -15353,7 +15486,7 @@ var HttpBootstrapAdapter = class {
|
|
|
15353
15486
|
return bootstrapErrorFromCapxul("malformedBody", Errors.providerError("convex", "bootstrap", cause instanceof Error ? cause : new Error(String(cause))));
|
|
15354
15487
|
}
|
|
15355
15488
|
});
|
|
15356
|
-
return Effect.promise(() => safeText(res)).pipe(Effect.flatMap((body) => {
|
|
15489
|
+
return Effect.promise(() => safeText$1(res)).pipe(Effect.flatMap((body) => {
|
|
15357
15490
|
if (res.status === 401 || body.startsWith("NOT_AUTHENTICATED")) return Effect.fail(bootstrapErrorFromCapxul("notAuthenticated", Errors.notAuthenticated()));
|
|
15358
15491
|
if (res.status === 400 || body.startsWith("INVALID_INPUT")) return Effect.fail(bootstrapErrorFromCapxul("invalidInput", Errors.invalidInput("publishableKey", "rejected by bootstrap")));
|
|
15359
15492
|
const responseBody = body.slice(0, 300);
|
|
@@ -18536,6 +18669,12 @@ function makeConnectionId() {
|
|
|
18536
18669
|
}
|
|
18537
18670
|
//#endregion
|
|
18538
18671
|
//#region ../../packages/sdk/src/adapters/convex-call/ConvexCallAdapter.ts
|
|
18672
|
+
/**
|
|
18673
|
+
* A socket that has not emitted `close` within this window is not going to.
|
|
18674
|
+
* ponytail: one fixed bound; `closeTimeoutMs` is the knob if a slow runtime
|
|
18675
|
+
* ever needs a different one.
|
|
18676
|
+
*/
|
|
18677
|
+
const DEFAULT_CLOSE_TIMEOUT_MS = 5e3;
|
|
18539
18678
|
/** Exact floor-first allowlist; every additional handler must migrate its validator first. */
|
|
18540
18679
|
const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
|
|
18541
18680
|
"payroll/actions:authorizeRun",
|
|
@@ -18604,7 +18743,9 @@ var ConvexCallAdapter = class {
|
|
|
18604
18743
|
#applicationId;
|
|
18605
18744
|
#observation;
|
|
18606
18745
|
#connectionMonitor;
|
|
18746
|
+
#closeTimeoutMs;
|
|
18607
18747
|
constructor(deps) {
|
|
18748
|
+
this.#closeTimeoutMs = deps.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS;
|
|
18608
18749
|
this.#connectionMonitor = new ConvexConnectionMonitor();
|
|
18609
18750
|
const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
|
|
18610
18751
|
this.#client = deps.client ?? new ConvexClient(deps.convexUrl, webSocketConstructor === void 0 ? {} : { webSocketConstructor });
|
|
@@ -18703,11 +18844,33 @@ var ConvexCallAdapter = class {
|
|
|
18703
18844
|
callback({ status: "loading" });
|
|
18704
18845
|
})));
|
|
18705
18846
|
}
|
|
18847
|
+
/**
|
|
18848
|
+
* Teardown always completes. `ConvexClient.close()` resolves only when the
|
|
18849
|
+
* socket emits `close`, and `WebSocketManager.close()` in the `connecting`
|
|
18850
|
+
* state arms `ws.onopen = () => ws.close()` without ever closing the socket
|
|
18851
|
+
* itself — so a handshake that stalls right after a server-initiated close
|
|
18852
|
+
* (code 1012, which is what a Convex deploy sends) leaves that promise
|
|
18853
|
+
* pending for as long as the page lives. Convex marks the client closed and
|
|
18854
|
+
* terminates the socket manager synchronously before returning it, so
|
|
18855
|
+
* abandoning it drops nothing. A rejection is dropped for the same reason —
|
|
18856
|
+
* the layer release turns it into a defect that fails the whole scope close,
|
|
18857
|
+
* and teardown has no caller who can act on it.
|
|
18858
|
+
*/
|
|
18706
18859
|
async close() {
|
|
18707
18860
|
this.#connectionMonitor.close();
|
|
18708
|
-
await this.#client.close();
|
|
18861
|
+
await settledWithin(this.#client.close(), this.#closeTimeoutMs);
|
|
18709
18862
|
}
|
|
18710
18863
|
};
|
|
18864
|
+
function settledWithin(promise, timeoutMs) {
|
|
18865
|
+
return new Promise((resolve) => {
|
|
18866
|
+
const timer = setTimeout(resolve, timeoutMs);
|
|
18867
|
+
const finish = () => {
|
|
18868
|
+
clearTimeout(timer);
|
|
18869
|
+
resolve();
|
|
18870
|
+
};
|
|
18871
|
+
promise.then(finish, finish);
|
|
18872
|
+
});
|
|
18873
|
+
}
|
|
18711
18874
|
function ConvexCallLayer(deps) {
|
|
18712
18875
|
return Layer.effect(ConvexCallPortTag, Effect.acquireRelease(Effect.sync(() => new ConvexCallAdapter(deps)), (adapter) => Effect.promise(() => adapter.close()).pipe(Effect.orDie)));
|
|
18713
18876
|
}
|
|
@@ -42764,8 +42927,9 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
42764
42927
|
});
|
|
42765
42928
|
throw failOpenfort("configure", "no-secure-context", /* @__PURE__ */ new Error("Web Crypto unavailable: browser is not a secure context"));
|
|
42766
42929
|
}
|
|
42767
|
-
|
|
42768
|
-
|
|
42930
|
+
const authProvider = bootstrap.openfortAuthProvider ?? "better-auth";
|
|
42931
|
+
function accessTokenUrl() {
|
|
42932
|
+
return authProvider === "oidc" ? `${authBaseUrl}/convex/token` : `${authBaseUrl}/get-session`;
|
|
42769
42933
|
}
|
|
42770
42934
|
function encryptionSessionUrl() {
|
|
42771
42935
|
return `${authBaseUrl}/encryption-session`;
|
|
@@ -42788,7 +42952,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
42788
42952
|
tokenCalls += 1;
|
|
42789
42953
|
const tokenStartedAt = Date.now();
|
|
42790
42954
|
try {
|
|
42791
|
-
const response = await fetch(
|
|
42955
|
+
const response = await fetch(accessTokenUrl(), {
|
|
42792
42956
|
credentials: "include",
|
|
42793
42957
|
cache: "no-store"
|
|
42794
42958
|
});
|
|
@@ -42801,7 +42965,8 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
42801
42965
|
});
|
|
42802
42966
|
return null;
|
|
42803
42967
|
}
|
|
42804
|
-
const
|
|
42968
|
+
const body = await response.json();
|
|
42969
|
+
const token = (authProvider === "oidc" ? body.token : body.session?.token)?.trim();
|
|
42805
42970
|
if (token === void 0 || token.length === 0) {
|
|
42806
42971
|
diagnostic?.trace("openfort.token", {
|
|
42807
42972
|
ok: false,
|
|
@@ -42813,6 +42978,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
42813
42978
|
diagnostic?.trace("openfort.token", {
|
|
42814
42979
|
ok: true,
|
|
42815
42980
|
tokenPresent: true,
|
|
42981
|
+
provider: authProvider,
|
|
42816
42982
|
call: tokenCalls,
|
|
42817
42983
|
duration_ms: Date.now() - tokenStartedAt,
|
|
42818
42984
|
elapsed_ms: sinceCycleStart()
|
|
@@ -42831,7 +42997,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
42831
42997
|
baseConfiguration: { publishableKey: bootstrap.openfortPublishableKey },
|
|
42832
42998
|
shieldConfiguration: { shieldPublishableKey: bootstrap.shieldPublishableKey },
|
|
42833
42999
|
thirdPartyAuth: {
|
|
42834
|
-
provider: o$13.BETTER_AUTH,
|
|
43000
|
+
provider: authProvider === "oidc" ? o$13.OIDC : o$13.BETTER_AUTH,
|
|
42835
43001
|
getAccessToken: fetchBetterAuthAccessToken
|
|
42836
43002
|
}
|
|
42837
43003
|
});
|
|
@@ -45940,11 +46106,28 @@ const authFailure = (record, reason, mode) => ({
|
|
|
45940
46106
|
...linkage(record)
|
|
45941
46107
|
}
|
|
45942
46108
|
});
|
|
46109
|
+
const REFUSAL_EVENTS = /* @__PURE__ */ new Set([
|
|
46110
|
+
"ClaimAccount",
|
|
46111
|
+
"RetryAccount",
|
|
46112
|
+
"CreateOrganization",
|
|
46113
|
+
"AttachOrganization",
|
|
46114
|
+
"RetryOrganization"
|
|
46115
|
+
]);
|
|
45943
46116
|
function mapIdentityProductObservation(record, state) {
|
|
45944
46117
|
if (record.machine !== "identity" || !IDENTITY_EVENT_TAG_SET.has(record.event)) return NONE;
|
|
45945
46118
|
if (record.outcome === "cancelled") return NONE;
|
|
45946
46119
|
if (record.slot === "identity:auth" && record.outcome === "refused") return authFailure(record, record.refusal_code, void 0);
|
|
45947
46120
|
if (record.slot === "identity:auth" && record.outcome === "failed") return authFailure(record, record.error_code, record.failure?.mode);
|
|
46121
|
+
if (record.outcome === "refused" && REFUSAL_EVENTS.has(record.event)) return {
|
|
46122
|
+
name: "identity_refused",
|
|
46123
|
+
props: {
|
|
46124
|
+
event: record.event,
|
|
46125
|
+
refusal_code: record.refusal_code,
|
|
46126
|
+
slot: record.slot,
|
|
46127
|
+
state: record.state,
|
|
46128
|
+
...linkage(record)
|
|
46129
|
+
}
|
|
46130
|
+
};
|
|
45948
46131
|
if (record.outcome !== "applied") return NONE;
|
|
45949
46132
|
switch (APPLIED_ACTION_BY_EVENT[record.event]) {
|
|
45950
46133
|
case "none": return NONE;
|
|
@@ -46270,6 +46453,7 @@ function assembleCapxulClient(input) {
|
|
|
46270
46453
|
identity: identityRuntime,
|
|
46271
46454
|
bootstrap: input.bootstrap,
|
|
46272
46455
|
accounts: { fund: accounts.fund },
|
|
46456
|
+
...input.ports.indexerRead === void 0 ? {} : { indexer: input.ports.indexerRead },
|
|
46273
46457
|
organizationSetup: { getProofReceipt: (orgId) => runPortEffect(input.ports.convexCall.query(organizationSetupProofReceiptQuery, { orgId })) },
|
|
46274
46458
|
telemetry: input.ports.telemetry,
|
|
46275
46459
|
captureExternalAddressAcknowledged: (address, organizationId) => {
|
|
@@ -46351,6 +46535,382 @@ function snapshotHostObservability(observability) {
|
|
|
46351
46535
|
};
|
|
46352
46536
|
}
|
|
46353
46537
|
//#endregion
|
|
46538
|
+
//#region ../../packages/sdk/src/ports/indexer-read.ts
|
|
46539
|
+
/** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
|
|
46540
|
+
const INDEXER_MAX_LIMIT = 1e3;
|
|
46541
|
+
const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
|
|
46542
|
+
/**
|
|
46543
|
+
* Refuse a page the indexer would refuse, before any implementation spends a
|
|
46544
|
+
* round trip on it. Every implementation calls this, so a consumer test written
|
|
46545
|
+
* against the hermetic adapter cannot pass on input production rejects. Throws
|
|
46546
|
+
* a `CapxulError`; callers convert it to an `IndexerReadError`.
|
|
46547
|
+
*/
|
|
46548
|
+
function assertIndexerPage(input) {
|
|
46549
|
+
const safe = String(input.safe).toLowerCase();
|
|
46550
|
+
if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
|
|
46551
|
+
if (!Number.isSafeInteger(input.limit) || input.limit < 1 || input.limit > 1e3) throw Errors.invalidInput("limit", `must be an integer from 1 to ${INDEXER_MAX_LIMIT}`);
|
|
46552
|
+
const offset = input.offset ?? 0;
|
|
46553
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
|
|
46554
|
+
return safe;
|
|
46555
|
+
}
|
|
46556
|
+
var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
|
|
46557
|
+
function indexerReadErrorFromCapxul(operation, error, cause = error) {
|
|
46558
|
+
return new IndexerReadError({
|
|
46559
|
+
operation,
|
|
46560
|
+
publicCode: error.code,
|
|
46561
|
+
publicError: error,
|
|
46562
|
+
cause,
|
|
46563
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
46564
|
+
});
|
|
46565
|
+
}
|
|
46566
|
+
Context.Service()("@capxul/sdk/ports/IndexerReadPort");
|
|
46567
|
+
//#endregion
|
|
46568
|
+
//#region ../../packages/sdk/src/adapters/ponder/decode.ts
|
|
46569
|
+
/**
|
|
46570
|
+
* Decode the indexer's rows at the trust boundary.
|
|
46571
|
+
*
|
|
46572
|
+
* Ponder returns the raw PostgreSQL result: `integer` arrives as a number,
|
|
46573
|
+
* `numeric(78)` and `text` arrive as strings. Amounts, block numbers and
|
|
46574
|
+
* timestamps are `numeric(78)`, so they become `bigint` here and never pass
|
|
46575
|
+
* through `Number`. Every decoder throws a `CapxulError`; the adapter turns
|
|
46576
|
+
* that into a typed `IndexerReadError`.
|
|
46577
|
+
*/
|
|
46578
|
+
const HEX = /^0x[0-9a-fA-F]+$/;
|
|
46579
|
+
function field(row, key) {
|
|
46580
|
+
if (!(key in row)) throw Errors.invalidInput(key, "missing from the indexer row");
|
|
46581
|
+
return row[key];
|
|
46582
|
+
}
|
|
46583
|
+
function text(row, key) {
|
|
46584
|
+
const value = field(row, key);
|
|
46585
|
+
if (typeof value !== "string") throw Errors.invalidInput(key, "must be a string");
|
|
46586
|
+
return value;
|
|
46587
|
+
}
|
|
46588
|
+
function hex(row, key) {
|
|
46589
|
+
const value = text(row, key);
|
|
46590
|
+
if (!HEX.test(value)) throw Errors.invalidInput(key, "must be a hex string");
|
|
46591
|
+
return value.toLowerCase();
|
|
46592
|
+
}
|
|
46593
|
+
function integer(row, key) {
|
|
46594
|
+
const value = field(row, key);
|
|
46595
|
+
const parsed = typeof value === "string" ? Number(value) : value;
|
|
46596
|
+
if (typeof parsed !== "number" || !Number.isSafeInteger(parsed)) throw Errors.invalidInput(key, "must be an integer");
|
|
46597
|
+
return parsed;
|
|
46598
|
+
}
|
|
46599
|
+
/** `numeric(78)` crosses the wire as a decimal string; keep every digit. */
|
|
46600
|
+
function big(row, key) {
|
|
46601
|
+
const value = field(row, key);
|
|
46602
|
+
if (typeof value === "bigint") return value;
|
|
46603
|
+
const raw = typeof value === "number" ? String(value) : value;
|
|
46604
|
+
if (typeof raw !== "string" || !/^-?[0-9]+$/.test(raw)) throw Errors.invalidInput(key, "must be an exact integer amount");
|
|
46605
|
+
return BigInt(raw);
|
|
46606
|
+
}
|
|
46607
|
+
function asRow(value) {
|
|
46608
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw Errors.invalidInput("row", "must be an object");
|
|
46609
|
+
return value;
|
|
46610
|
+
}
|
|
46611
|
+
function identity(row) {
|
|
46612
|
+
return {
|
|
46613
|
+
id: text(row, "id"),
|
|
46614
|
+
chainId: toChainId(integer(row, "chain_id")),
|
|
46615
|
+
txHash: toTxHash(hex(row, "tx_hash")),
|
|
46616
|
+
transactionLogOrdinal: integer(row, "transaction_log_ordinal"),
|
|
46617
|
+
blockNumber: big(row, "block_number"),
|
|
46618
|
+
blockHash: hex(row, "block_hash"),
|
|
46619
|
+
timestamp: big(row, "timestamp")
|
|
46620
|
+
};
|
|
46621
|
+
}
|
|
46622
|
+
function decodeTransfer(value) {
|
|
46623
|
+
const row = asRow(value);
|
|
46624
|
+
return {
|
|
46625
|
+
...identity(row),
|
|
46626
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
46627
|
+
token: toAddress(hex(row, "token")),
|
|
46628
|
+
from: toAddress(hex(row, "from")),
|
|
46629
|
+
to: toAddress(hex(row, "to")),
|
|
46630
|
+
amount: big(row, "amount")
|
|
46631
|
+
};
|
|
46632
|
+
}
|
|
46633
|
+
function decodePayment(value) {
|
|
46634
|
+
const row = asRow(value);
|
|
46635
|
+
return {
|
|
46636
|
+
...identity(row),
|
|
46637
|
+
contract: toAddress(hex(row, "contract")),
|
|
46638
|
+
settlementId: hex(row, "settlement_id"),
|
|
46639
|
+
sender: toAddress(hex(row, "sender")),
|
|
46640
|
+
recipient: toAddress(hex(row, "recipient")),
|
|
46641
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
46642
|
+
token: toAddress(hex(row, "token")),
|
|
46643
|
+
amount: big(row, "amount"),
|
|
46644
|
+
kind: integer(row, "kind"),
|
|
46645
|
+
documentHash: hex(row, "document_hash")
|
|
46646
|
+
};
|
|
46647
|
+
}
|
|
46648
|
+
/** The `balance` view exposes the signed sum under the underlying column name `to`. */
|
|
46649
|
+
function decodeBalance(value) {
|
|
46650
|
+
const row = asRow(value);
|
|
46651
|
+
return {
|
|
46652
|
+
chainId: toChainId(integer(row, "chain_id")),
|
|
46653
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
46654
|
+
address: toAddress(hex(row, "to")),
|
|
46655
|
+
amount: big(row, "amount")
|
|
46656
|
+
};
|
|
46657
|
+
}
|
|
46658
|
+
const DECODERS = {
|
|
46659
|
+
transfer: decodeTransfer,
|
|
46660
|
+
payment: decodePayment,
|
|
46661
|
+
balance: decodeBalance
|
|
46662
|
+
};
|
|
46663
|
+
/** Decode one `/sql/db` or `/sql/live` payload. Ponder returns `{ rows: [...] }`. */
|
|
46664
|
+
function decodeRows(table, body) {
|
|
46665
|
+
const rows = asRow(body).rows;
|
|
46666
|
+
if (!Array.isArray(rows)) throw Errors.invalidInput("rows", "must be an array");
|
|
46667
|
+
const decode = DECODERS[table];
|
|
46668
|
+
return rows.map(decode);
|
|
46669
|
+
}
|
|
46670
|
+
/** Decode Ponder's native `/status`: `{ [chainName]: { id, block: { number, timestamp } } }`. */
|
|
46671
|
+
function decodeStatus(body) {
|
|
46672
|
+
const chains = asRow(body);
|
|
46673
|
+
return Object.entries(chains).map(([chain, value]) => {
|
|
46674
|
+
const entry = asRow(value);
|
|
46675
|
+
const block = asRow(field(entry, "block"));
|
|
46676
|
+
return {
|
|
46677
|
+
chain,
|
|
46678
|
+
chainId: toChainId(integer(entry, "id")),
|
|
46679
|
+
blockNumber: big(block, "number"),
|
|
46680
|
+
blockTimestamp: big(block, "timestamp")
|
|
46681
|
+
};
|
|
46682
|
+
});
|
|
46683
|
+
}
|
|
46684
|
+
//#endregion
|
|
46685
|
+
//#region ../../packages/sdk/src/adapters/ponder/statements.ts
|
|
46686
|
+
/**
|
|
46687
|
+
* The exact statements the indexer accepts.
|
|
46688
|
+
*
|
|
46689
|
+
* `apps/indexer/src/queries.ts` compiles its allowlist with `@ponder/client`
|
|
46690
|
+
* and compares `sql`, `params` and `typings` byte for byte against the caller's
|
|
46691
|
+
* statement. Anything else gets 403. The SDK therefore reproduces the compiled
|
|
46692
|
+
* text here rather than pulling Ponder and Drizzle into a published browser
|
|
46693
|
+
* bundle to regenerate three fixed statements.
|
|
46694
|
+
*
|
|
46695
|
+
* `statements.test.ts` pins the text. When the indexer schema or a query in
|
|
46696
|
+
* that allowlist changes, this file changes with it or every read fails 403.
|
|
46697
|
+
*/
|
|
46698
|
+
const HEADS = {
|
|
46699
|
+
transfer: "select \"id\", \"chain_id\", \"tx_hash\", \"transaction_log_ordinal\", \"block_number\", \"block_hash\", \"timestamp\", \"asset_id\", \"token\", \"from\", \"to\", \"amount\" from \"transfer\" where (\"transfer\".\"from\" = $1 or \"transfer\".\"to\" = $2) order by \"transfer\".\"block_number\" desc, \"transfer\".\"id\" desc",
|
|
46700
|
+
payment: "select \"id\", \"chain_id\", \"tx_hash\", \"transaction_log_ordinal\", \"block_number\", \"block_hash\", \"timestamp\", \"contract\", \"settlement_id\", \"sender\", \"recipient\", \"asset_id\", \"token\", \"amount\", \"kind\", \"document_hash\" from \"payment\" where (\"payment\".\"sender\" = $1 or \"payment\".\"recipient\" = $2) order by \"payment\".\"block_number\" desc, \"payment\".\"id\" desc",
|
|
46701
|
+
balance: "select \"chain_id\", \"asset_id\", \"to\", \"amount\" from \"balance\" where \"balance\".\"to\" = $1"
|
|
46702
|
+
};
|
|
46703
|
+
/** How many times the statement binds the Safe address. */
|
|
46704
|
+
const ACTOR_PARAMS = {
|
|
46705
|
+
transfer: 2,
|
|
46706
|
+
payment: 2,
|
|
46707
|
+
balance: 1
|
|
46708
|
+
};
|
|
46709
|
+
/**
|
|
46710
|
+
* Build the compiled query for one table and page. Throws a `CapxulError` for
|
|
46711
|
+
* a page or address the indexer would refuse, so the caller never spends a
|
|
46712
|
+
* round trip to learn it built the request wrong.
|
|
46713
|
+
*/
|
|
46714
|
+
function ponderQuery(table, input) {
|
|
46715
|
+
const safe = assertIndexerPage(input);
|
|
46716
|
+
const offset = input.offset ?? 0;
|
|
46717
|
+
const actors = ACTOR_PARAMS[table];
|
|
46718
|
+
const params = [];
|
|
46719
|
+
for (let i = 0; i < actors; i++) params.push(safe);
|
|
46720
|
+
params.push(input.limit);
|
|
46721
|
+
let sql = `${HEADS[table]} limit $${actors + 1}`;
|
|
46722
|
+
if (offset > 0) {
|
|
46723
|
+
params.push(offset);
|
|
46724
|
+
sql = `${sql} offset $${actors + 2}`;
|
|
46725
|
+
}
|
|
46726
|
+
return {
|
|
46727
|
+
sql,
|
|
46728
|
+
params,
|
|
46729
|
+
typings: params.map(() => "none")
|
|
46730
|
+
};
|
|
46731
|
+
}
|
|
46732
|
+
/**
|
|
46733
|
+
* The `?sql=` value. `@ponder/client` sends `superjson.stringify(query)`; for a
|
|
46734
|
+
* payload of strings and numbers superjson emits no `meta`, so the wire form is
|
|
46735
|
+
* exactly this envelope.
|
|
46736
|
+
*/
|
|
46737
|
+
function ponderQueryParam(query) {
|
|
46738
|
+
return JSON.stringify({ json: query });
|
|
46739
|
+
}
|
|
46740
|
+
//#endregion
|
|
46741
|
+
//#region ../../packages/sdk/src/adapters/ponder/sse.ts
|
|
46742
|
+
/**
|
|
46743
|
+
* The smallest SSE reader that can carry an `Authorization` header.
|
|
46744
|
+
*
|
|
46745
|
+
* `@ponder/client` opens its live query with `EventSource`, which accepts no
|
|
46746
|
+
* headers, so it cannot reach the guarded indexer at all. The indexer's CORS
|
|
46747
|
+
* policy allows exactly one request header (`Authorization`), so this reader
|
|
46748
|
+
* sends nothing else. Ponder writes one `data:` line per frame.
|
|
46749
|
+
*/
|
|
46750
|
+
async function readEventStream(body, onData, stopped) {
|
|
46751
|
+
const reader = body.getReader();
|
|
46752
|
+
const decoder = new TextDecoder();
|
|
46753
|
+
let buffer = "";
|
|
46754
|
+
try {
|
|
46755
|
+
for (;;) {
|
|
46756
|
+
const { done, value } = await reader.read();
|
|
46757
|
+
if (done || stopped()) return;
|
|
46758
|
+
buffer += decoder.decode(value, { stream: true });
|
|
46759
|
+
let split = buffer.indexOf("\n\n");
|
|
46760
|
+
while (split !== -1) {
|
|
46761
|
+
const frame = buffer.slice(0, split);
|
|
46762
|
+
buffer = buffer.slice(split + 2);
|
|
46763
|
+
const data = frameData(frame);
|
|
46764
|
+
if (data !== void 0) onData(data);
|
|
46765
|
+
if (stopped()) return;
|
|
46766
|
+
split = buffer.indexOf("\n\n");
|
|
46767
|
+
}
|
|
46768
|
+
}
|
|
46769
|
+
} finally {
|
|
46770
|
+
await reader.cancel().catch(() => void 0);
|
|
46771
|
+
}
|
|
46772
|
+
}
|
|
46773
|
+
function frameData(frame) {
|
|
46774
|
+
const lines = [];
|
|
46775
|
+
for (const line of frame.split("\n")) {
|
|
46776
|
+
if (!line.startsWith("data:")) continue;
|
|
46777
|
+
lines.push(line.slice(line.startsWith("data: ") ? 6 : 5));
|
|
46778
|
+
}
|
|
46779
|
+
return lines.length === 0 ? void 0 : lines.join("\n");
|
|
46780
|
+
}
|
|
46781
|
+
//#endregion
|
|
46782
|
+
//#region ../../packages/sdk/src/adapters/ponder/PonderIndexerAdapter.ts
|
|
46783
|
+
var PonderIndexerAdapter = class {
|
|
46784
|
+
#baseUrl;
|
|
46785
|
+
#token;
|
|
46786
|
+
#fetch;
|
|
46787
|
+
#observation;
|
|
46788
|
+
constructor(deps) {
|
|
46789
|
+
this.#baseUrl = deps.baseUrl.replace(/\/$/, "");
|
|
46790
|
+
this.#token = deps.token;
|
|
46791
|
+
this.#observation = deps.observation;
|
|
46792
|
+
this.#fetch = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
46793
|
+
}
|
|
46794
|
+
read(table, input) {
|
|
46795
|
+
const operation = `indexer.read.${table}`;
|
|
46796
|
+
return Effect.tryPromise({
|
|
46797
|
+
try: async () => {
|
|
46798
|
+
return decodeRows(table, await (await this.#request("db", table, input, operation)).json());
|
|
46799
|
+
},
|
|
46800
|
+
catch: (cause) => toIndexerReadError(operation, cause)
|
|
46801
|
+
});
|
|
46802
|
+
}
|
|
46803
|
+
subscribe(table, input, callback) {
|
|
46804
|
+
const operation = CAPXUL_OPERATIONS.activity.subscribe;
|
|
46805
|
+
return Effect.sync(() => {
|
|
46806
|
+
let stopped = false;
|
|
46807
|
+
const unsubscribe = () => {
|
|
46808
|
+
stopped = true;
|
|
46809
|
+
};
|
|
46810
|
+
callback({ status: "loading" });
|
|
46811
|
+
this.#stream(table, input, operation, callback, () => stopped).catch(() => void 0);
|
|
46812
|
+
return unsubscribe;
|
|
46813
|
+
});
|
|
46814
|
+
}
|
|
46815
|
+
status = Effect.tryPromise({
|
|
46816
|
+
try: async () => {
|
|
46817
|
+
const response = await this.#fetch(`${this.#baseUrl}/status`, { method: "GET" });
|
|
46818
|
+
if (!response.ok) throw httpError("indexer.status", response.status, await safeText(response));
|
|
46819
|
+
return decodeStatus(await response.json());
|
|
46820
|
+
},
|
|
46821
|
+
catch: (cause) => toIndexerReadError("indexer.status", cause)
|
|
46822
|
+
});
|
|
46823
|
+
/** One authenticated `/sql/:method` call. Throws a `CapxulError` on refusal. */
|
|
46824
|
+
async #request(method, table, input, operation) {
|
|
46825
|
+
const query = ponderQuery(table, input);
|
|
46826
|
+
const token = await this.#token({ forceRefreshToken: false });
|
|
46827
|
+
if (token === null || token === "") throw Errors.notAuthenticated();
|
|
46828
|
+
const url = `${this.#baseUrl}/sql/${method}?sql=${encodeURIComponent(ponderQueryParam(query))}`;
|
|
46829
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
46830
|
+
if (method === "live") headers.accept = "text/event-stream";
|
|
46831
|
+
let response;
|
|
46832
|
+
try {
|
|
46833
|
+
response = await this.#fetch(url, {
|
|
46834
|
+
method: "GET",
|
|
46835
|
+
headers
|
|
46836
|
+
});
|
|
46837
|
+
} catch (cause) {
|
|
46838
|
+
throw Errors.networkError(operation, cause, {
|
|
46839
|
+
provider: "indexer",
|
|
46840
|
+
failure_mode: "upstream-down"
|
|
46841
|
+
});
|
|
46842
|
+
}
|
|
46843
|
+
if (!response.ok) throw httpError(operation, response.status, await safeText(response));
|
|
46844
|
+
return response;
|
|
46845
|
+
}
|
|
46846
|
+
/**
|
|
46847
|
+
* Follow the live stream until the caller unsubscribes or the indexer closes
|
|
46848
|
+
* it. The indexer closes the stream on token expiry or revoked authority
|
|
46849
|
+
* with no frame, so any end the caller did not ask for is a teardown: it
|
|
46850
|
+
* reaches `captureException` under `activity.subscribe` and the caller sees
|
|
46851
|
+
* one `error` snapshot.
|
|
46852
|
+
*/
|
|
46853
|
+
async #stream(table, input, operation, callback, stopped) {
|
|
46854
|
+
let failure;
|
|
46855
|
+
try {
|
|
46856
|
+
const response = await this.#request("live", table, input, operation);
|
|
46857
|
+
if (response.body === null) throw Errors.providerError("indexer", operation, "empty stream");
|
|
46858
|
+
await readEventStream(response.body, (data) => {
|
|
46859
|
+
try {
|
|
46860
|
+
callback({
|
|
46861
|
+
status: "ok",
|
|
46862
|
+
value: decodeRows(table, JSON.parse(data))
|
|
46863
|
+
});
|
|
46864
|
+
} catch (cause) {
|
|
46865
|
+
failure = toPublicError$1(operation, cause);
|
|
46866
|
+
}
|
|
46867
|
+
}, () => stopped() || failure !== void 0);
|
|
46868
|
+
} catch (cause) {
|
|
46869
|
+
failure = toPublicError$1(operation, cause);
|
|
46870
|
+
}
|
|
46871
|
+
if (stopped()) return;
|
|
46872
|
+
const error = failure ?? Errors.capabilityUnavailable("indexer", operation);
|
|
46873
|
+
observeStreamFailure(this.#observation, CAPXUL_OPERATIONS.activity.subscribe, error);
|
|
46874
|
+
callback({
|
|
46875
|
+
status: "error",
|
|
46876
|
+
error
|
|
46877
|
+
});
|
|
46878
|
+
}
|
|
46879
|
+
};
|
|
46880
|
+
/**
|
|
46881
|
+
* Map an indexer refusal into the shared error vocabulary.
|
|
46882
|
+
*
|
|
46883
|
+
* 401 is the expected outcome of an expired or revoked session. 403 is not:
|
|
46884
|
+
* the indexer accepts only its own compiled statements for a Safe in the
|
|
46885
|
+
* claim, so a refusal means this SDK and that deployment disagree, or the
|
|
46886
|
+
* session gained a Safe and still holds the older token. Both must be loud.
|
|
46887
|
+
*/
|
|
46888
|
+
function httpError(operation, status, body) {
|
|
46889
|
+
if (status === 401) return Errors.notAuthenticated();
|
|
46890
|
+
if (status === 429) return Errors.rateLimited({ resource: "indexer" });
|
|
46891
|
+
if (status === 503) return Errors.capabilityUnavailable("indexer", operation);
|
|
46892
|
+
return Errors.providerError("indexer", operation, body, {
|
|
46893
|
+
httpStatus: status,
|
|
46894
|
+
...status >= 500 ? { failure_mode: "upstream-down" } : {},
|
|
46895
|
+
details: { reason: status === 403 ? "scope-refused" : "http-status" }
|
|
46896
|
+
});
|
|
46897
|
+
}
|
|
46898
|
+
function toPublicError$1(operation, cause) {
|
|
46899
|
+
if (cause instanceof CapxulError) return cause;
|
|
46900
|
+
return Errors.providerError("indexer", operation, cause);
|
|
46901
|
+
}
|
|
46902
|
+
function toIndexerReadError(operation, cause) {
|
|
46903
|
+
if (cause instanceof IndexerReadError) return cause;
|
|
46904
|
+
return indexerReadErrorFromCapxul(operation, toPublicError$1(operation, cause), cause);
|
|
46905
|
+
}
|
|
46906
|
+
async function safeText(response) {
|
|
46907
|
+
try {
|
|
46908
|
+
return await response.text();
|
|
46909
|
+
} catch {
|
|
46910
|
+
return "";
|
|
46911
|
+
}
|
|
46912
|
+
}
|
|
46913
|
+
//#endregion
|
|
46354
46914
|
//#region ../../packages/sdk/src/adapters/_shared/clock.ts
|
|
46355
46915
|
async function readClockNow(clock) {
|
|
46356
46916
|
try {
|
|
@@ -46807,7 +47367,16 @@ async function createProductionAdapters(input) {
|
|
|
46807
47367
|
}));
|
|
46808
47368
|
const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime));
|
|
46809
47369
|
const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
|
|
46810
|
-
const
|
|
47370
|
+
const collected = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
|
|
47371
|
+
const indexerUrl = input.indexerUrl ?? bootstrapResult.value.indexerUrl;
|
|
47372
|
+
const indexerRead = productionIndexerReadPort(indexerUrl === void 0 ? input : {
|
|
47373
|
+
...input,
|
|
47374
|
+
indexerUrl
|
|
47375
|
+
}, collected, observation);
|
|
47376
|
+
const ports = indexerRead === void 0 ? collected : {
|
|
47377
|
+
...collected,
|
|
47378
|
+
indexerRead
|
|
47379
|
+
};
|
|
46811
47380
|
const stopConnectionObservation = observeProductionConvexConnection(ports.convexCall, context);
|
|
46812
47381
|
const close = idempotentClose(async () => {
|
|
46813
47382
|
try {
|
|
@@ -47139,6 +47708,28 @@ function idempotentClose(close) {
|
|
|
47139
47708
|
await close();
|
|
47140
47709
|
};
|
|
47141
47710
|
}
|
|
47711
|
+
/**
|
|
47712
|
+
* Build the Ponder read port for this client, or `undefined` when no indexer
|
|
47713
|
+
* origin was supplied. The token provider is the same
|
|
47714
|
+
* `AuthClientPort.getConvexJwt` closure `ConvexCallPort` uses, so the indexer
|
|
47715
|
+
* and Convex see one session with one scope.
|
|
47716
|
+
*/
|
|
47717
|
+
function productionIndexerReadPort(input, ports, observation) {
|
|
47718
|
+
if (input.indexerUrl === void 0 || input.indexerUrl === "") return void 0;
|
|
47719
|
+
return new PonderIndexerAdapter({
|
|
47720
|
+
baseUrl: normalizeHttpUrl("indexerUrl", input.indexerUrl),
|
|
47721
|
+
token: async ({ forceRefreshToken }) => {
|
|
47722
|
+
const token = await Effect.runPromise(Effect.result(ports.authClient.getConvexJwt({
|
|
47723
|
+
forceRefresh: forceRefreshToken,
|
|
47724
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
47725
|
+
})));
|
|
47726
|
+
if (Result.isFailure(token)) return null;
|
|
47727
|
+
return String(token.success.token);
|
|
47728
|
+
},
|
|
47729
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
47730
|
+
...observation === void 0 ? {} : { observation }
|
|
47731
|
+
});
|
|
47732
|
+
}
|
|
47142
47733
|
//#endregion
|
|
47143
47734
|
//#region src/tools/account.ts
|
|
47144
47735
|
const READ = "account:read";
|
|
@@ -47750,7 +48341,7 @@ function stdioIdentity(env = process.env, options = {}) {
|
|
|
47750
48341
|
}
|
|
47751
48342
|
//#endregion
|
|
47752
48343
|
//#region package.json
|
|
47753
|
-
var version = "4.2.0-rc.
|
|
48344
|
+
var version = "4.2.0-rc.9";
|
|
47754
48345
|
//#endregion
|
|
47755
48346
|
//#region src/deps.ts
|
|
47756
48347
|
const NO_IDENTITY = async () => void 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capxul/mcp",
|
|
3
|
-
"version": "4.2.0-rc.
|
|
3
|
+
"version": "4.2.0-rc.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Capxul MCP server for auth, Account readiness, identity, Organizations, relationships, requests, destinations, and testnet funding over Streamable HTTP.",
|
|
6
6
|
"keywords": [
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
|
|
52
52
|
"vite-plus": "0.3.0",
|
|
53
53
|
"@capxul/config": "0.3.0",
|
|
54
|
-
"@capxul/sdk": "4.2.0-rc.
|
|
54
|
+
"@capxul/sdk": "4.2.0-rc.9",
|
|
55
55
|
"@capxul/typescript-config": "0.0.0"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|