@capxul/sdk 4.20.0-beta.1 → 4.20.0-beta.3
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/{OAuthBearerAuthClient-DDD0JlaI.mjs → OAuthBearerAuthClient-DWkofxdZ.mjs} +9 -2
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/node/index.mjs +1 -1
- package/dist/{production-DvGDNIwu.d.mts → production-CJwxgI8u.d.mts} +55 -6
- package/dist/{production-BmsFhDUr.mjs → production-M4dWz5g4.mjs} +1141 -215
- package/dist/testing/index.d.mts +1 -1
- package/dist/testing/index.mjs +2 -2
- package/package.json +5 -5
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as toPartyId, A as SUPPORTED_CURRENCY_CODES, B as toAuthUserId, C as ASSET_ID_RE, Ct as isChainUpstream, D as COUNTRY_CODE_RE, Dt as isCredentialField, E as CLIENT_REQUEST_ID_RE, Et as containsSensitiveMaterial, F as toAccountId, G as toDurationMs, H as toChainId, I as toAddress, J as toEpochSeconds, K as toEmail, L as toAllowedOrigin, M as ZERO_BYTES32, O as EMAIL_RE$1, Ot as redactSecrets, Q as toOrgId, S as APP_ID_RE, St as failureFingerprint, T as CLIENT_GRANT_ID_RE, Tt as revertSummaryText, U as toCountryCode, V as toBudgetId, W as toCurrencyCode, X as toJwtToken, Y as toHandle, Z as toKycTier, _ as normalizeBindingEmail, _t as FAILURE_MODES$1, at as toTesterKind, b as configuredMoneyAssetById, bt as chainEvidenceLabel, c as AuthCachePortTag, ct as validateHandle, d as authClientPortFromPromiseAdapter, dt as CAPXUL_ERROR_CODES, et as toPayrollGroupId, f as AuthClientError, ft as CapxulError, gt as CHAIN_UPSTREAMS, h as orgRoleKeyForLabel, ht as isCapxulError, i as BrowserAuthCacheAdapter, it as toSessionToken, j as WEI_RE, k as EVM_ADDRESS_RE$1, kt as redactUrlSecrets, l as SystemClockLayer, lt as HANDLE_RE$1, m as ADMIN_ROLE_LABEL, mt as Errors, nt as toPublishableKey, ot as toTxHash, p as AuthClientPortTag, pt as EXPECTED_OPERATION_OUTCOMES, q as toEpochMs, r as InMemoryAuthCacheAdapter, rt as toRoleKey, st as toWeiAmount, tt as toPayrollRunId, u as ClockPortTag, ut as decodeConvexError, v as BASE_SEPOLIA_CHAIN_ID, vt as boundedResponseHeaders, w as BYTES32_RE, wt as isFailureMode, x as ACCOUNT_ID_RE, xt as decodeChainCause, y as deriveCapxulSafeAddress, yt as chainCauseProperties, z as toAssetId } from "./OAuthBearerAuthClient-DWkofxdZ.mjs";
|
|
2
2
|
import { formatUnits, keccak256, parseUnits, recoverAddress, stringToHex, toBytes } from "viem";
|
|
3
|
-
import { Cause, Clock, Context, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, Layer, Logger, Option, Queue, Ref, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stream, Tracer } from "effect";
|
|
3
|
+
import { Cause, Clock, Context, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, Layer, Logger, Metric, Option, Queue, Ref, References, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stream, Tracer } from "effect";
|
|
4
4
|
import { getFunctionName, makeFunctionReference } from "convex/server";
|
|
5
5
|
import { privateKeyToAccount } from "viem/accounts";
|
|
6
|
-
import { FetchHttpClient, Headers, HttpBody, HttpClient, HttpClientResponse, HttpTraceContext } from "effect/unstable/http";
|
|
6
|
+
import { FetchHttpClient, Headers, HttpBody, HttpClient, HttpClientError, HttpClientResponse, HttpTraceContext } from "effect/unstable/http";
|
|
7
7
|
import { OtlpExporter, OtlpLogger, OtlpMetrics, OtlpSerialization, OtlpTracer } from "effect/unstable/observability";
|
|
8
8
|
import { ConvexClient } from "convex/browser";
|
|
9
9
|
import { AccountTypeEnum, ChainTypeEnum, EmbeddedState, Openfort, RecoveryMethod, ThirdPartyOAuthProvider } from "@openfort/openfort-js";
|
|
@@ -1735,6 +1735,92 @@ const ContactActionProps = {
|
|
|
1735
1735
|
party_id: OptionalString,
|
|
1736
1736
|
reason_code: OptionalReasonCode
|
|
1737
1737
|
};
|
|
1738
|
+
/** One completed online CLI diagnostic. No command input or provider text. */
|
|
1739
|
+
const CLI_COMMAND_NAMES = [
|
|
1740
|
+
"capxul",
|
|
1741
|
+
"doctor",
|
|
1742
|
+
"telemetry",
|
|
1743
|
+
"telemetry.status",
|
|
1744
|
+
"telemetry.enable",
|
|
1745
|
+
"telemetry.disable",
|
|
1746
|
+
"unknown"
|
|
1747
|
+
];
|
|
1748
|
+
const CLI_CAPABILITIES = [
|
|
1749
|
+
"cli",
|
|
1750
|
+
"diagnostics",
|
|
1751
|
+
"collection"
|
|
1752
|
+
];
|
|
1753
|
+
const CLI_INVOCATION_KINDS = [
|
|
1754
|
+
"command",
|
|
1755
|
+
"help",
|
|
1756
|
+
"version",
|
|
1757
|
+
"completion",
|
|
1758
|
+
"parse_refusal",
|
|
1759
|
+
"collection_control"
|
|
1760
|
+
];
|
|
1761
|
+
const CliRelease = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/u));
|
|
1762
|
+
const CliInvocationProps = {
|
|
1763
|
+
...TelemetryEnvelopeProps,
|
|
1764
|
+
schema_version: Schema.Literal(1),
|
|
1765
|
+
command: Schema.Literals(CLI_COMMAND_NAMES),
|
|
1766
|
+
capability: Schema.Literals(CLI_CAPABILITIES),
|
|
1767
|
+
invocation_kind: Schema.Literals(CLI_INVOCATION_KINDS),
|
|
1768
|
+
invocation_id: Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u)),
|
|
1769
|
+
cli_identity_kind: Schema.optional(Schema.Literal("anonymous")),
|
|
1770
|
+
anonymous_id: Schema.optional(Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u))),
|
|
1771
|
+
occurred_at: Schema.Number,
|
|
1772
|
+
cli_version: CliRelease,
|
|
1773
|
+
sdk_version: CliRelease,
|
|
1774
|
+
release: CliRelease,
|
|
1775
|
+
platform: Schema.Literals([
|
|
1776
|
+
"darwin",
|
|
1777
|
+
"linux",
|
|
1778
|
+
"unknown"
|
|
1779
|
+
]),
|
|
1780
|
+
architecture: Schema.Literals([
|
|
1781
|
+
"arm64",
|
|
1782
|
+
"x64",
|
|
1783
|
+
"unknown"
|
|
1784
|
+
]),
|
|
1785
|
+
ci: Schema.Boolean,
|
|
1786
|
+
tty: Schema.Boolean,
|
|
1787
|
+
output_mode: Schema.Literals(["human", "json"])
|
|
1788
|
+
};
|
|
1789
|
+
Schema.Struct({
|
|
1790
|
+
name: Schema.Literal("cli_first_used"),
|
|
1791
|
+
props: Schema.Struct(CliInvocationProps)
|
|
1792
|
+
});
|
|
1793
|
+
Schema.Struct({
|
|
1794
|
+
name: Schema.Literal("cli_command_started"),
|
|
1795
|
+
props: Schema.Struct({
|
|
1796
|
+
...CliInvocationProps,
|
|
1797
|
+
invocation_kind: Schema.Literals([
|
|
1798
|
+
"command",
|
|
1799
|
+
"help",
|
|
1800
|
+
"version"
|
|
1801
|
+
])
|
|
1802
|
+
})
|
|
1803
|
+
});
|
|
1804
|
+
Schema.Struct({
|
|
1805
|
+
name: Schema.Literal("cli_command_completed"),
|
|
1806
|
+
props: Schema.Struct({
|
|
1807
|
+
...CliInvocationProps,
|
|
1808
|
+
outcome: Schema.Literals([
|
|
1809
|
+
"succeeded",
|
|
1810
|
+
"refused",
|
|
1811
|
+
"failed",
|
|
1812
|
+
"cancelled"
|
|
1813
|
+
]),
|
|
1814
|
+
duration_ms: Schema.Number,
|
|
1815
|
+
error_code: Schema.optional(Schema.Literals([
|
|
1816
|
+
...CAPXUL_ERROR_CODES,
|
|
1817
|
+
"CLI_USAGE",
|
|
1818
|
+
"CLI_CONFIG",
|
|
1819
|
+
"CLI_STORAGE",
|
|
1820
|
+
"CLI_DEFECT"
|
|
1821
|
+
]))
|
|
1822
|
+
})
|
|
1823
|
+
});
|
|
1738
1824
|
Schema.Struct({
|
|
1739
1825
|
name: Schema.Literal("cli_diagnostic_completed"),
|
|
1740
1826
|
props: Schema.Struct({
|
|
@@ -2607,6 +2693,30 @@ const PAYMENT_DOCUMENT_KINDS = [
|
|
|
2607
2693
|
"receipt",
|
|
2608
2694
|
"withdrawal"
|
|
2609
2695
|
];
|
|
2696
|
+
/** Native browser projection, distinct from the issuer's persisted request state. */
|
|
2697
|
+
const CLIENT_AUTHORIZATION_PHASE = {
|
|
2698
|
+
OTP_REQUIRED: "otp_required",
|
|
2699
|
+
REVIEW: "review",
|
|
2700
|
+
PROCESSING: "processing",
|
|
2701
|
+
CONNECTED: "connected",
|
|
2702
|
+
REFUSED: "refused"
|
|
2703
|
+
};
|
|
2704
|
+
CLIENT_AUTHORIZATION_PHASE.OTP_REQUIRED, CLIENT_AUTHORIZATION_PHASE.REVIEW, CLIENT_AUTHORIZATION_PHASE.PROCESSING, CLIENT_AUTHORIZATION_PHASE.CONNECTED, CLIENT_AUTHORIZATION_PHASE.REFUSED;
|
|
2705
|
+
/** Closed native refusals; existing Capxul error codes retain their own meaning. */
|
|
2706
|
+
const CLIENT_AUTHORIZATION_REFUSALS = [
|
|
2707
|
+
"signup_required",
|
|
2708
|
+
"setup_required",
|
|
2709
|
+
"already_signed_up",
|
|
2710
|
+
"identity_mismatch",
|
|
2711
|
+
"invalid_request",
|
|
2712
|
+
"request_expired",
|
|
2713
|
+
"request_denied",
|
|
2714
|
+
"cancelled",
|
|
2715
|
+
"grant_revoked",
|
|
2716
|
+
"grant_expired",
|
|
2717
|
+
"refresh_reused",
|
|
2718
|
+
"revoke_unconfirmed"
|
|
2719
|
+
];
|
|
2610
2720
|
//#endregion
|
|
2611
2721
|
//#region ../wire/src/financial-ops.ts
|
|
2612
2722
|
const MINOR_UNIT_STRING_RE = /^[0-9]+$/;
|
|
@@ -4308,6 +4418,251 @@ function isSafeField(field, value) {
|
|
|
4308
4418
|
return value.length > 0 && value.length <= rule.maxLength && value === value.trim() && !value.includes("://") && !containsSensitiveMaterial(value) && rule.pattern.test(value);
|
|
4309
4419
|
}
|
|
4310
4420
|
//#endregion
|
|
4421
|
+
//#region ../wire/src/client-authorization.ts
|
|
4422
|
+
const NATIVE_CLIENT_ID = "capxul-cli";
|
|
4423
|
+
const NATIVE_CLIENT_SCOPE = "account:read";
|
|
4424
|
+
const NATIVE_CLIENT_LIMITS = {
|
|
4425
|
+
accessMs: 36e5,
|
|
4426
|
+
renewalMs: 2592e6,
|
|
4427
|
+
absoluteMs: 7776e6,
|
|
4428
|
+
requestMs: 6e5,
|
|
4429
|
+
pollSeconds: 5,
|
|
4430
|
+
imageBytes: 5242880
|
|
4431
|
+
};
|
|
4432
|
+
const strict = { parseOptions: { onExcessProperty: "error" } };
|
|
4433
|
+
const text$2 = (max) => Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(max));
|
|
4434
|
+
const nonNegativeInt = Schema.Number.check(Schema.makeFilter((n) => Number.isSafeInteger(n) && n >= 0));
|
|
4435
|
+
const normalize = (f) => Schema.String.pipe(Schema.decodeTo(Schema.String, {
|
|
4436
|
+
decode: SchemaGetter.transform(f),
|
|
4437
|
+
encode: SchemaGetter.transform((s) => s)
|
|
4438
|
+
}));
|
|
4439
|
+
const ClientRequestIdSchema = Schema.String.pipe(Schema.refine((s) => CLIENT_REQUEST_ID_RE.test(s)));
|
|
4440
|
+
const ClientGrantIdSchema = Schema.String.pipe(Schema.refine((s) => CLIENT_GRANT_ID_RE.test(s)));
|
|
4441
|
+
const authUserId = Schema.String.pipe(Schema.refine((s) => s.length > 0));
|
|
4442
|
+
const keyId = Schema.String.pipe(Schema.refine((s) => s.length > 0));
|
|
4443
|
+
const email = normalize((s) => s.toLowerCase()).pipe(Schema.refine((s) => EMAIL_RE$1.test(s))).check(Schema.isMaxLength(254));
|
|
4444
|
+
const handle = Schema.String.pipe(Schema.refine((s) => HANDLE_RE$1.test(s)));
|
|
4445
|
+
const country = normalize((s) => s.toUpperCase()).pipe(Schema.refine((s) => COUNTRY_CODE_RE.test(s)));
|
|
4446
|
+
const credential = Schema.RedactedFromValue(Schema.String.check(Schema.isMinLength(32), Schema.isMaxLength(8192), Schema.isPattern(/^[A-Za-z0-9._~-]+$/u)), { label: "native authorization credential" });
|
|
4447
|
+
const otp = Schema.RedactedFromValue(Schema.String.check(Schema.isPattern(/^[0-9]{6}$/u)), { label: "OTP" });
|
|
4448
|
+
const scopes = Schema.Tuple([Schema.Literal(NATIVE_CLIENT_SCOPE)]);
|
|
4449
|
+
/** Exact HTTPS origin, no credentials/path/query and no retired cloud deployment. */
|
|
4450
|
+
const NativeHttpsOriginSchema = Schema.String.check(Schema.makeFilter((value) => {
|
|
4451
|
+
try {
|
|
4452
|
+
const url = new URL(value);
|
|
4453
|
+
const host = url.hostname.toLowerCase().replace(/\.$/u, "");
|
|
4454
|
+
return url.protocol === "https:" && value === url.origin && !url.username && !url.password && !url.search && !url.hash && host !== "convex.cloud" && !host.endsWith(".convex.cloud") && host !== "convex.site" && !host.endsWith(".convex.site");
|
|
4455
|
+
} catch {
|
|
4456
|
+
return false;
|
|
4457
|
+
}
|
|
4458
|
+
}, { message: "must be a canonical credential-free HTTPS origin outside retired Convex cloud hosts" }));
|
|
4459
|
+
const NativeClientConfigurationSchema = Schema.Struct({
|
|
4460
|
+
clientId: Schema.Literal(NATIVE_CLIENT_ID),
|
|
4461
|
+
applicationId: AppIdSchema,
|
|
4462
|
+
keyId,
|
|
4463
|
+
environment: Schema.Literals([
|
|
4464
|
+
"development",
|
|
4465
|
+
"staging",
|
|
4466
|
+
"production"
|
|
4467
|
+
]),
|
|
4468
|
+
keyEnvironment: Schema.Literals(["test", "live"]),
|
|
4469
|
+
issuer: NativeHttpsOriginSchema,
|
|
4470
|
+
verificationOrigin: NativeHttpsOriginSchema
|
|
4471
|
+
}).annotate(strict).check(Schema.makeFilter((c) => c.keyEnvironment === (c.environment === "production" ? "live" : "test"), { message: "key environment must match the configured deployment" })).annotate(strict);
|
|
4472
|
+
const NativeSignupProfileSchema = Schema.Struct({
|
|
4473
|
+
displayName: normalize((s) => s.trim()).check(Schema.isMinLength(1), Schema.isMaxLength(200)),
|
|
4474
|
+
country,
|
|
4475
|
+
handle,
|
|
4476
|
+
payoutAddresses: Schema.optional(Schema.Array(Schema.Struct({
|
|
4477
|
+
chain: Schema.Literals([
|
|
4478
|
+
"evm",
|
|
4479
|
+
"solana",
|
|
4480
|
+
"starknet"
|
|
4481
|
+
]),
|
|
4482
|
+
address: text$2(256)
|
|
4483
|
+
}).annotate(strict)).check(Schema.isMaxLength(10))),
|
|
4484
|
+
image: Schema.optional(Schema.Struct({
|
|
4485
|
+
mediaType: Schema.Literals([
|
|
4486
|
+
"image/jpeg",
|
|
4487
|
+
"image/png",
|
|
4488
|
+
"image/webp"
|
|
4489
|
+
]),
|
|
4490
|
+
byteLength: Schema.Number.check(Schema.isInt(), Schema.isBetween({
|
|
4491
|
+
minimum: 1,
|
|
4492
|
+
maximum: NATIVE_CLIENT_LIMITS.imageBytes
|
|
4493
|
+
})),
|
|
4494
|
+
sha256: Schema.String.check(Schema.isPattern(/^[0-9a-f]{64}$/u))
|
|
4495
|
+
}).annotate(strict))
|
|
4496
|
+
}).annotate(strict);
|
|
4497
|
+
const ClientAuthorizationIntentSchema = Schema.Union([Schema.Struct({ mode: Schema.Literal("login") }).annotate(strict), Schema.Struct({
|
|
4498
|
+
mode: Schema.Literal("signup"),
|
|
4499
|
+
profile: NativeSignupProfileSchema
|
|
4500
|
+
}).annotate(strict)]).annotate(strict);
|
|
4501
|
+
Schema.Struct({
|
|
4502
|
+
clientId: Schema.Literal(NATIVE_CLIENT_ID),
|
|
4503
|
+
email,
|
|
4504
|
+
intent: ClientAuthorizationIntentSchema,
|
|
4505
|
+
waitMs: Schema.Number.check(Schema.isInt(), Schema.isBetween({
|
|
4506
|
+
minimum: 1,
|
|
4507
|
+
maximum: NATIVE_CLIENT_LIMITS.requestMs
|
|
4508
|
+
})),
|
|
4509
|
+
continuityToken: Schema.optional(credential)
|
|
4510
|
+
}).annotate(strict);
|
|
4511
|
+
const request = {
|
|
4512
|
+
requestId: ClientRequestIdSchema,
|
|
4513
|
+
revision: nonNegativeInt,
|
|
4514
|
+
expiresAt: EpochMsSchema
|
|
4515
|
+
};
|
|
4516
|
+
const principal = {
|
|
4517
|
+
authUserId,
|
|
4518
|
+
email
|
|
4519
|
+
};
|
|
4520
|
+
const profileSummary = {
|
|
4521
|
+
displayName: text$2(200),
|
|
4522
|
+
handle,
|
|
4523
|
+
country
|
|
4524
|
+
};
|
|
4525
|
+
Schema.Union([
|
|
4526
|
+
Schema.Struct({
|
|
4527
|
+
...request,
|
|
4528
|
+
phase: Schema.Literal(CLIENT_AUTHORIZATION_PHASE.OTP_REQUIRED),
|
|
4529
|
+
email,
|
|
4530
|
+
resendAt: EpochMsSchema
|
|
4531
|
+
}).annotate(strict),
|
|
4532
|
+
Schema.Struct({
|
|
4533
|
+
...request,
|
|
4534
|
+
phase: Schema.Literal(CLIENT_AUTHORIZATION_PHASE.REVIEW),
|
|
4535
|
+
...principal,
|
|
4536
|
+
clientLabel: Schema.Literal("Capxul CLI"),
|
|
4537
|
+
scopes,
|
|
4538
|
+
intent: ClientAuthorizationIntentSchema,
|
|
4539
|
+
setupState: Schema.Literals([
|
|
4540
|
+
"not_required",
|
|
4541
|
+
"new",
|
|
4542
|
+
"resumable"
|
|
4543
|
+
])
|
|
4544
|
+
}).annotate(strict).check(Schema.makeFilter((v) => v.intent.mode === "login" === (v.setupState === "not_required"), { message: "login review cannot permit account setup" })),
|
|
4545
|
+
Schema.Struct({
|
|
4546
|
+
...request,
|
|
4547
|
+
phase: Schema.Literal(CLIENT_AUTHORIZATION_PHASE.PROCESSING),
|
|
4548
|
+
...principal,
|
|
4549
|
+
purpose: Schema.Literals(["login", "signup"]),
|
|
4550
|
+
step: Schema.Literals([
|
|
4551
|
+
"profile",
|
|
4552
|
+
"wallet",
|
|
4553
|
+
"account",
|
|
4554
|
+
"delivery"
|
|
4555
|
+
])
|
|
4556
|
+
}).annotate(strict).check(Schema.makeFilter((v) => v.purpose === "signup" || v.step === "delivery", { message: "login may wait for delivery but cannot enter signup processing" })),
|
|
4557
|
+
Schema.Struct({
|
|
4558
|
+
...request,
|
|
4559
|
+
phase: Schema.Literal(CLIENT_AUTHORIZATION_PHASE.CONNECTED),
|
|
4560
|
+
...principal,
|
|
4561
|
+
...profileSummary,
|
|
4562
|
+
accountId: AccountIdSchema,
|
|
4563
|
+
grantId: ClientGrantIdSchema
|
|
4564
|
+
}).annotate(strict),
|
|
4565
|
+
Schema.Struct({
|
|
4566
|
+
...request,
|
|
4567
|
+
phase: Schema.Literal(CLIENT_AUTHORIZATION_PHASE.REFUSED),
|
|
4568
|
+
reason: Schema.Literals(CLIENT_AUTHORIZATION_REFUSALS),
|
|
4569
|
+
code: Schema.Literals(CAPXUL_ERROR_CODES),
|
|
4570
|
+
field: Schema.optional(Schema.Literals([
|
|
4571
|
+
"email",
|
|
4572
|
+
"displayName",
|
|
4573
|
+
"country",
|
|
4574
|
+
"handle",
|
|
4575
|
+
"image",
|
|
4576
|
+
"payoutAddresses"
|
|
4577
|
+
])),
|
|
4578
|
+
retryable: Schema.Boolean
|
|
4579
|
+
}).annotate(strict)
|
|
4580
|
+
]).annotate(strict);
|
|
4581
|
+
Schema.Struct({
|
|
4582
|
+
requestId: ClientRequestIdSchema,
|
|
4583
|
+
revision: nonNegativeInt
|
|
4584
|
+
}).annotate(strict);
|
|
4585
|
+
Schema.Struct({
|
|
4586
|
+
requestId: ClientRequestIdSchema,
|
|
4587
|
+
code: otp
|
|
4588
|
+
}).annotate(strict);
|
|
4589
|
+
Schema.Struct({
|
|
4590
|
+
client_id: Schema.Literal(NATIVE_CLIENT_ID),
|
|
4591
|
+
grant_type: Schema.Literal("refresh_token"),
|
|
4592
|
+
refresh_token: credential,
|
|
4593
|
+
refreshAttemptId: Schema.RedactedFromValue(Schema.String.check(Schema.isPattern(/^[A-Za-z0-9_-]{32,128}$/u)), { label: "refresh attempt" })
|
|
4594
|
+
}).annotate(strict);
|
|
4595
|
+
Schema.Struct({
|
|
4596
|
+
client_id: Schema.Literal(NATIVE_CLIENT_ID),
|
|
4597
|
+
applicationId: AppIdSchema,
|
|
4598
|
+
resource: text$2(2048),
|
|
4599
|
+
token_type: Schema.Literal("Bearer"),
|
|
4600
|
+
access_token: credential,
|
|
4601
|
+
refresh_token: credential,
|
|
4602
|
+
scope: Schema.Literal(NATIVE_CLIENT_SCOPE),
|
|
4603
|
+
expires_in: Schema.Number.check(Schema.isInt(), Schema.isBetween({
|
|
4604
|
+
minimum: 1,
|
|
4605
|
+
maximum: NATIVE_CLIENT_LIMITS.accessMs / 1e3
|
|
4606
|
+
})),
|
|
4607
|
+
grantId: ClientGrantIdSchema,
|
|
4608
|
+
...principal,
|
|
4609
|
+
accountId: AccountIdSchema,
|
|
4610
|
+
issuedAt: EpochMsSchema,
|
|
4611
|
+
grantCreatedAt: EpochMsSchema,
|
|
4612
|
+
accessExpiresAt: EpochMsSchema,
|
|
4613
|
+
renewalExpiresAt: EpochMsSchema,
|
|
4614
|
+
absoluteExpiresAt: EpochMsSchema,
|
|
4615
|
+
revision: nonNegativeInt
|
|
4616
|
+
}).annotate(strict).check(Schema.makeFilter((t) => t.grantCreatedAt <= t.issuedAt && t.absoluteExpiresAt - t.grantCreatedAt === NATIVE_CLIENT_LIMITS.absoluteMs && t.renewalExpiresAt <= Math.min(t.issuedAt + NATIVE_CLIENT_LIMITS.renewalMs, t.absoluteExpiresAt) && t.accessExpiresAt <= Math.min(t.issuedAt + NATIVE_CLIENT_LIMITS.accessMs, t.renewalExpiresAt) && t.expires_in === Math.floor((t.accessExpiresAt - t.issuedAt) / 1e3), { message: "native credentials must obey the approved access, renewal and absolute deadlines" })).annotate(strict);
|
|
4617
|
+
/** One endpoint mapping; consumers cannot substitute callback or resource URLs. */
|
|
4618
|
+
function nativeClientEndpoints(config) {
|
|
4619
|
+
const resource = `${config.issuer}/api/auth/native`;
|
|
4620
|
+
return {
|
|
4621
|
+
resource,
|
|
4622
|
+
device_authorization_endpoint: `${resource}/device/code`,
|
|
4623
|
+
token_endpoint: `${resource}/token`,
|
|
4624
|
+
revocation_endpoint: `${resource}/revoke`,
|
|
4625
|
+
session_endpoint: `${resource}/session`,
|
|
4626
|
+
verification_uri: `${config.verificationOrigin}/connect`
|
|
4627
|
+
};
|
|
4628
|
+
}
|
|
4629
|
+
Schema.Struct({
|
|
4630
|
+
protocol: Schema.Literal("capxul.client-authorization"),
|
|
4631
|
+
version: Schema.Literal(1),
|
|
4632
|
+
configuration: NativeClientConfigurationSchema,
|
|
4633
|
+
scopes_supported: scopes,
|
|
4634
|
+
grant_types_supported: Schema.Tuple([Schema.Literal("urn:ietf:params:oauth:grant-type:device_code"), Schema.Literal("refresh_token")]),
|
|
4635
|
+
resource: text$2(2048),
|
|
4636
|
+
device_authorization_endpoint: text$2(2048),
|
|
4637
|
+
token_endpoint: text$2(2048),
|
|
4638
|
+
revocation_endpoint: text$2(2048),
|
|
4639
|
+
session_endpoint: text$2(2048),
|
|
4640
|
+
verification_uri: text$2(2048)
|
|
4641
|
+
}).annotate(strict).check(Schema.makeFilter((m) => {
|
|
4642
|
+
const expected = nativeClientEndpoints(m.configuration);
|
|
4643
|
+
return m.resource === expected.resource && m.device_authorization_endpoint === expected.device_authorization_endpoint && m.token_endpoint === expected.token_endpoint && m.revocation_endpoint === expected.revocation_endpoint && m.session_endpoint === expected.session_endpoint && m.verification_uri === expected.verification_uri;
|
|
4644
|
+
}, { message: "native endpoints must match their configured issuer and verification origin" })).annotate(strict);
|
|
4645
|
+
Schema.Struct({
|
|
4646
|
+
client_id: Schema.Literal(NATIVE_CLIENT_ID),
|
|
4647
|
+
grant_type: Schema.Literal("urn:ietf:params:oauth:grant-type:device_code"),
|
|
4648
|
+
device_code: credential
|
|
4649
|
+
}).annotate(strict);
|
|
4650
|
+
Schema.Struct({
|
|
4651
|
+
requestId: ClientRequestIdSchema,
|
|
4652
|
+
device_code: credential,
|
|
4653
|
+
user_code: Schema.RedactedFromValue(Schema.String.check(Schema.isPattern(/^[A-Z0-9]{4}-[A-Z0-9]{4}$/u)), { label: "device user code" }),
|
|
4654
|
+
continuityToken: credential,
|
|
4655
|
+
verification_uri: text$2(2048),
|
|
4656
|
+
expires_in: Schema.Number.check(Schema.isInt(), Schema.isBetween({
|
|
4657
|
+
minimum: 1,
|
|
4658
|
+
maximum: NATIVE_CLIENT_LIMITS.requestMs / 1e3
|
|
4659
|
+
})),
|
|
4660
|
+
interval: Schema.Number.check(Schema.isInt(), Schema.isBetween({
|
|
4661
|
+
minimum: NATIVE_CLIENT_LIMITS.pollSeconds,
|
|
4662
|
+
maximum: NATIVE_CLIENT_LIMITS.requestMs / 1e3
|
|
4663
|
+
}))
|
|
4664
|
+
}).annotate(strict);
|
|
4665
|
+
//#endregion
|
|
4311
4666
|
//#region src/internal/invocation-observation.ts
|
|
4312
4667
|
const INVOCATION_OBSERVATION = Symbol("capxul.invocation-observation");
|
|
4313
4668
|
const FAILURE_INVOCATION_SNAPSHOT = Symbol("capxul.failure-invocation-snapshot");
|
|
@@ -5894,6 +6249,9 @@ const moneyExecutionContract = {
|
|
|
5894
6249
|
//#region src/ports/indexer-read.ts
|
|
5895
6250
|
/** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
|
|
5896
6251
|
const INDEXER_MAX_LIMIT = 1e3;
|
|
6252
|
+
function validTime(value) {
|
|
6253
|
+
return Number.isSafeInteger(value) && value >= 0 && value <= Math.floor(Number.MAX_SAFE_INTEGER / 1e3);
|
|
6254
|
+
}
|
|
5897
6255
|
const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
|
|
5898
6256
|
/**
|
|
5899
6257
|
* Refuse a page the indexer would refuse, before any implementation spends a
|
|
@@ -5901,12 +6259,21 @@ const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
|
|
|
5901
6259
|
* against the hermetic adapter cannot pass on input production rejects. Throws
|
|
5902
6260
|
* a `CapxulError`; callers convert it to an `IndexerReadError`.
|
|
5903
6261
|
*/
|
|
6262
|
+
function assertEventLookup(input, id) {
|
|
6263
|
+
if (input.limit !== 1 || (input.offset ?? 0) !== 0 || input.timeline !== void 0 || input.settlements !== void 0 || !/^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(id)) throw Errors.invalidInput("eventId", "must identify one logical event without another cursor");
|
|
6264
|
+
}
|
|
5904
6265
|
function assertIndexerPage(input) {
|
|
5905
6266
|
const safe = String(input.safe).toLowerCase();
|
|
5906
6267
|
if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
|
|
5907
6268
|
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}`);
|
|
5908
6269
|
const offset = input.offset ?? 0;
|
|
5909
6270
|
if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
|
|
6271
|
+
const timeline = input.timeline;
|
|
6272
|
+
if (input.eventId !== void 0) assertEventLookup(input, input.eventId);
|
|
6273
|
+
if (input.settlements !== void 0 && (offset !== 0 || timeline !== void 0 || input.settlements.length < 1 || input.settlements.length > 50 || !input.settlements.every((id) => /^0x[0-9a-f]{64}$/.test(id)))) throw Errors.invalidInput("settlements", "must hold 1 to 50 settlement IDs without another cursor");
|
|
6274
|
+
if (timeline !== void 0) {
|
|
6275
|
+
if (offset !== 0 || !validTime(timeline.from) || !validTime(timeline.to) || timeline.from > timeline.to || timeline.before !== void 0 && (!validTime(timeline.before.timestamp) || !/^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(timeline.before.id))) throw Errors.invalidInput("timeline", "must be a bounded time window with a logical event cursor and no offset");
|
|
6276
|
+
}
|
|
5910
6277
|
return safe;
|
|
5911
6278
|
}
|
|
5912
6279
|
var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
|
|
@@ -5936,20 +6303,6 @@ function directionFor(safe, transfer) {
|
|
|
5936
6303
|
if (from === safe && to === safe) return "self";
|
|
5937
6304
|
return to === safe ? "in" : "out";
|
|
5938
6305
|
}
|
|
5939
|
-
/**
|
|
5940
|
-
* The Transfer a protocol Payment moved is the same money as its `payment`
|
|
5941
|
-
* row. One actor sees one item for it, so the Transfer is claimed and does not
|
|
5942
|
-
* become a second row. Another transfer in the same transaction that this
|
|
5943
|
-
* payment did not move stays unclaimed and keeps its own row.
|
|
5944
|
-
*/
|
|
5945
|
-
function claimedTransferIds(transfers, payments) {
|
|
5946
|
-
const claimed = /* @__PURE__ */ new Set();
|
|
5947
|
-
for (const payment of payments) {
|
|
5948
|
-
const match = transfers.find((transfer) => !claimed.has(transfer.id) && transfer.txHash === payment.txHash && transfer.token.toLowerCase() === payment.token.toLowerCase() && transfer.amount === payment.amount && transfer.to.toLowerCase() === payment.recipient.toLowerCase());
|
|
5949
|
-
if (match !== void 0) claimed.add(match.id);
|
|
5950
|
-
}
|
|
5951
|
-
return claimed;
|
|
5952
|
-
}
|
|
5953
6306
|
function withAnnotation(item, annotation) {
|
|
5954
6307
|
return annotation?.counterpartyLabel === void 0 ? item : {
|
|
5955
6308
|
...item,
|
|
@@ -5960,10 +6313,14 @@ function withAnnotation(item, annotation) {
|
|
|
5960
6313
|
* An indexed settlement confirms the intent. A missing row can be outside
|
|
5961
6314
|
* this page or still backfilling; only the correction owner can prove rollback.
|
|
5962
6315
|
*/
|
|
5963
|
-
function confirmPayment(intent, chain) {
|
|
5964
|
-
if (chain !== void 0) return {
|
|
6316
|
+
function confirmPayment(intent, chain, expected) {
|
|
6317
|
+
if (chain !== void 0 && matchesPaymentFact(chain, expected)) return {
|
|
5965
6318
|
...intent,
|
|
5966
6319
|
status: "settled",
|
|
6320
|
+
...intent.execution === void 0 ? {} : { execution: {
|
|
6321
|
+
status: "settled",
|
|
6322
|
+
retryRequired: false
|
|
6323
|
+
} },
|
|
5967
6324
|
receipt: {
|
|
5968
6325
|
chainId: chain.chainId,
|
|
5969
6326
|
transactionHash: chain.txHash
|
|
@@ -5972,16 +6329,22 @@ function confirmPayment(intent, chain) {
|
|
|
5972
6329
|
};
|
|
5973
6330
|
return intent;
|
|
5974
6331
|
}
|
|
6332
|
+
function matchesPaymentFact(chain, expected) {
|
|
6333
|
+
return chain !== void 0 && expected !== void 0 && chain.chainId === expected.chainId && chain.contract.toLowerCase() === expected.contract.toLowerCase() && chain.sender.toLowerCase() === expected.sender.toLowerCase() && chain.recipient.toLowerCase() === expected.recipient.toLowerCase() && chain.token.toLowerCase() === expected.token.toLowerCase() && chain.amount.toString() === expected.rawAmount && chain.documentHash.toLowerCase() === expected.documentHash.toLowerCase() && chain.kind === expected.kind;
|
|
6334
|
+
}
|
|
5975
6335
|
/**
|
|
5976
6336
|
* One unclaimed Transfer as an Activity row, or `null` when this SDK cannot
|
|
5977
6337
|
* denominate its asset. The indexer serves only configured assets, so a row
|
|
5978
6338
|
* the registry does not know means the SDK and that deployment disagree; it is
|
|
5979
6339
|
* dropped rather than shown with a guessed currency.
|
|
5980
6340
|
*/
|
|
6341
|
+
function isActivityVisible(item) {
|
|
6342
|
+
return !(item.kind === "payment" && item.direction === "in" && item.execution?.notSent);
|
|
6343
|
+
}
|
|
5981
6344
|
function transferItem(safe, transfer, annotation) {
|
|
5982
6345
|
const asset = configuredMoneyAssetById(transfer.assetId);
|
|
5983
6346
|
if (asset === null) return null;
|
|
5984
|
-
const direction = directionFor(safe, transfer);
|
|
6347
|
+
const direction = directionFor(safe.toLowerCase(), transfer);
|
|
5985
6348
|
const counterparty = direction === "in" ? transfer.from : transfer.to;
|
|
5986
6349
|
const at = blockMillis(transfer);
|
|
5987
6350
|
return withAnnotation({
|
|
@@ -6003,47 +6366,6 @@ function transferItem(safe, transfer, annotation) {
|
|
|
6003
6366
|
updatedAt: at
|
|
6004
6367
|
}, annotation);
|
|
6005
6368
|
}
|
|
6006
|
-
/** Newest first. A Payment precedes a Movement at one instant, as the ledger reader orders them. */
|
|
6007
|
-
function compareItems(left, right) {
|
|
6008
|
-
if (right.createdAt !== left.createdAt) return right.createdAt - left.createdAt;
|
|
6009
|
-
if (left.kind !== right.kind) return left.kind === "payment" ? -1 : 1;
|
|
6010
|
-
if (left.id === right.id) return 0;
|
|
6011
|
-
return left.id < right.id ? 1 : -1;
|
|
6012
|
-
}
|
|
6013
|
-
function stitchActivity(input) {
|
|
6014
|
-
const safe = input.safe.toLowerCase();
|
|
6015
|
-
const chainBySettlement = /* @__PURE__ */ new Map();
|
|
6016
|
-
for (const row of input.payments) chainBySettlement.set(row.settlementId.toLowerCase(), row);
|
|
6017
|
-
const chainByPaymentId = /* @__PURE__ */ new Map();
|
|
6018
|
-
for (const link of input.links) {
|
|
6019
|
-
const row = chainBySettlement.get(link.settlementId.toLowerCase());
|
|
6020
|
-
if (row !== void 0) chainByPaymentId.set(link.paymentId, row);
|
|
6021
|
-
}
|
|
6022
|
-
const annotations = /* @__PURE__ */ new Map();
|
|
6023
|
-
for (const annotation of input.annotations) annotations.set(`${annotation.reference.kind}:${annotation.reference.id}`, annotation);
|
|
6024
|
-
const items = [];
|
|
6025
|
-
const claiming = [];
|
|
6026
|
-
for (const intent of input.intents) {
|
|
6027
|
-
if (intent.kind !== "payment") continue;
|
|
6028
|
-
const chain = chainByPaymentId.get(intent.id);
|
|
6029
|
-
if (chain !== void 0) claiming.push(chain);
|
|
6030
|
-
items.push(withAnnotation(confirmPayment(intent, chain), annotations.get(`payment:${intent.id}`)));
|
|
6031
|
-
}
|
|
6032
|
-
const claimed = claimedTransferIds(input.transfers, claiming);
|
|
6033
|
-
for (const transfer of input.transfers) {
|
|
6034
|
-
if (claimed.has(transfer.id)) continue;
|
|
6035
|
-
const item = transferItem(safe, transfer, annotations.get(`movement:${transfer.id}`));
|
|
6036
|
-
if (item !== null) items.push(item);
|
|
6037
|
-
}
|
|
6038
|
-
return items.sort(compareItems);
|
|
6039
|
-
}
|
|
6040
|
-
/** The reference ids one stitched page needs its Convex overlay for. */
|
|
6041
|
-
function overlayReferences(items) {
|
|
6042
|
-
return items.map((item) => ({
|
|
6043
|
-
kind: item.kind,
|
|
6044
|
-
id: item.id
|
|
6045
|
-
}));
|
|
6046
|
-
}
|
|
6047
6369
|
/**
|
|
6048
6370
|
* Money in, money out and the pending share, one bucket per currency and
|
|
6049
6371
|
* precision. Two assets that peg to the same currency at different precision
|
|
@@ -6062,7 +6384,7 @@ function summarizeItems(items) {
|
|
|
6062
6384
|
};
|
|
6063
6385
|
const raw = rawUnits(item.amount.value, item.amount.decimals);
|
|
6064
6386
|
if (item.kind === "payment" && item.status !== "settled") {
|
|
6065
|
-
if (item.direction === "in" && item.status !== "cancelled" && item.status !== "failed") bucket.pendingIn += raw;
|
|
6387
|
+
if (item.direction === "in" && item.status !== "cancelled" && item.status !== "failed" && !item.execution?.notSent) bucket.pendingIn += raw;
|
|
6066
6388
|
} else if (item.direction === "in") bucket.in += raw;
|
|
6067
6389
|
else if (item.direction === "out") bucket.out += raw;
|
|
6068
6390
|
buckets.set(key, bucket);
|
|
@@ -6085,179 +6407,412 @@ function rawUnits(value, decimals) {
|
|
|
6085
6407
|
}
|
|
6086
6408
|
//#endregion
|
|
6087
6409
|
//#region src/surface/activity-read.ts
|
|
6088
|
-
|
|
6089
|
-
const INTENT_WINDOW = 100;
|
|
6090
|
-
/** movement/annotations:overlay bounds each identifier array to 200. */
|
|
6410
|
+
const BATCH = 100;
|
|
6091
6411
|
const MAX_OVERLAY_IDS = 200;
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6412
|
+
function actorKey(actor) {
|
|
6413
|
+
return actor.kind === "account" ? `account:${actor.accountId}` : `organization:${actor.orgId}`;
|
|
6414
|
+
}
|
|
6415
|
+
function scopeKey(scope) {
|
|
6416
|
+
return `${scope.principalAccountId ?? "legacy"}|${actorKey(scope.actor)}|${scope.safe?.toLowerCase() ?? "none"}`;
|
|
6417
|
+
}
|
|
6418
|
+
function selectionKey(params) {
|
|
6419
|
+
const filter = params?.filter;
|
|
6420
|
+
return JSON.stringify([
|
|
6421
|
+
params?.range?.from ?? null,
|
|
6422
|
+
params?.range?.to ?? null,
|
|
6423
|
+
filter?.kind ?? null,
|
|
6424
|
+
filter?.direction ?? null,
|
|
6425
|
+
filter?.status === void 0 ? null : [...new Set(filter.status)].sort()
|
|
6426
|
+
]);
|
|
6427
|
+
}
|
|
6428
|
+
function validString(value, max) {
|
|
6429
|
+
return typeof value === "string" && value.length <= max;
|
|
6430
|
+
}
|
|
6431
|
+
function validPosition(position) {
|
|
6432
|
+
return position === null || position !== void 0 && typeof position === "object" && Number.isSafeInteger(position.timestamp) && position.timestamp >= 0 && /^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(position.id);
|
|
6433
|
+
}
|
|
6434
|
+
function parseCursor(value, scope, selection) {
|
|
6435
|
+
if (value === void 0) return null;
|
|
6436
|
+
if (value.length > 4e4) throw Errors.invalidInput("cursor", "invalid Activity cursor");
|
|
6437
|
+
const cursor = JSON.parse(value);
|
|
6438
|
+
if (cursor === null || typeof cursor !== "object" || cursor.version !== 1 || cursor.scope !== scope || cursor.selection !== selection || !Number.isSafeInteger(cursor.checkpoint) || cursor.checkpoint < 0 || cursor.source !== "indexed" && cursor.source !== "fallback" || cursor.paymentCursor !== null && !validString(cursor.paymentCursor, 2048) || typeof cursor.paymentDone !== "boolean" || typeof cursor.transferDone !== "boolean" || typeof cursor.degraded !== "boolean" || !Array.isArray(cursor.paymentPending) || cursor.paymentPending.length > BATCH || new Set(cursor.paymentPending).size !== cursor.paymentPending.length || !cursor.paymentPending.every((id) => validString(id, 256)) || !validPosition(cursor.transferBefore)) throw Errors.invalidInput("cursor", "Activity scope or filters changed; restart from the first page");
|
|
6439
|
+
return cursor;
|
|
6440
|
+
}
|
|
6441
|
+
function requireParams(params) {
|
|
6442
|
+
const limit = params?.limit ?? 25;
|
|
6443
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > BATCH) throw Errors.invalidInput("limit", "must be an integer from 1 to 100");
|
|
6444
|
+
for (const value of [params?.range?.from, params?.range?.to]) if (value !== void 0 && !Number.isSafeInteger(value)) throw Errors.invalidInput("range", "must contain integer epoch milliseconds");
|
|
6445
|
+
const f = params?.filter;
|
|
6446
|
+
if (f?.kind !== void 0 && f.kind !== "payment" && f.kind !== "movement" || f?.direction !== void 0 && ![
|
|
6447
|
+
"in",
|
|
6448
|
+
"out",
|
|
6449
|
+
"self"
|
|
6450
|
+
].includes(f.direction) || f?.status !== void 0 && (!Array.isArray(f.status) || !f.status.every((status) => PAYMENT_STATUSES.includes(status)))) throw Errors.invalidInput("filter", "invalid Activity filter");
|
|
6106
6451
|
}
|
|
6107
|
-
/** The caller's filter applied to the merged page. The join owns the order. */
|
|
6108
6452
|
function matchesFilter(item, filter) {
|
|
6109
|
-
|
|
6110
|
-
if (filter.kind !== void 0 && item.kind !== filter.kind) return false;
|
|
6111
|
-
if (filter.direction !== void 0 && item.direction !== filter.direction) return false;
|
|
6112
|
-
if (filter.status === void 0) return true;
|
|
6113
|
-
return item.kind === "payment" && filter.status.includes(item.status);
|
|
6453
|
+
return (filter?.kind === void 0 || item.kind === filter.kind) && (filter?.direction === void 0 || item.direction === filter.direction) && (filter?.status === void 0 || item.kind === "payment" && filter.status.includes(item.status));
|
|
6114
6454
|
}
|
|
6115
6455
|
function overlay(deps, actor, controls, input) {
|
|
6116
6456
|
const count = Math.max(input.references?.length ?? 0, input.settlementIds?.length ?? 0);
|
|
6117
|
-
const offsets = Array.from({ length: Math.max(1, Math.ceil(count / MAX_OVERLAY_IDS)) }, (_,
|
|
6457
|
+
const offsets = Array.from({ length: Math.max(1, Math.ceil(count / MAX_OVERLAY_IDS)) }, (_, i) => i * MAX_OVERLAY_IDS);
|
|
6118
6458
|
return Effect.forEach(offsets, (offset) => deps.convexCall.query(deps.functions.activityOverlay, copyInvocationObservation(controls, { input: {
|
|
6119
6459
|
...actor === void 0 ? {} : { actor },
|
|
6120
6460
|
...input.references === void 0 ? {} : { references: input.references.slice(offset, offset + MAX_OVERLAY_IDS) },
|
|
6121
6461
|
...input.settlementIds === void 0 ? {} : { settlementIds: input.settlementIds.slice(offset, offset + MAX_OVERLAY_IDS) }
|
|
6122
|
-
} })), { concurrency: 2 }).pipe(Effect.
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6462
|
+
} })), { concurrency: 2 }).pipe(Effect.flatMap((pages) => {
|
|
6463
|
+
const first = pages[0];
|
|
6464
|
+
if (pages.some((page) => scopeKey(page) !== scopeKey(first))) return Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during the read") });
|
|
6465
|
+
return Effect.succeed({
|
|
6466
|
+
...first,
|
|
6467
|
+
links: pages.flatMap((page) => page.links),
|
|
6468
|
+
annotations: pages.flatMap((page) => page.annotations),
|
|
6469
|
+
...pages.every((page) => page.payments !== void 0) ? { payments: pages.flatMap((page) => page.payments ?? []) } : {}
|
|
6470
|
+
});
|
|
6471
|
+
}));
|
|
6127
6472
|
}
|
|
6128
6473
|
function convexPage(deps, actor, controls, input) {
|
|
6129
6474
|
return deps.convexCall.query(deps.functions.activityList, copyInvocationObservation(controls, { input: {
|
|
6130
|
-
...
|
|
6131
|
-
|
|
6132
|
-
...input.limit === void 0 ? {} : { limit: input.limit },
|
|
6133
|
-
...input.range === void 0 ? {} : { range: input.range }
|
|
6475
|
+
...input,
|
|
6476
|
+
actor
|
|
6134
6477
|
} }));
|
|
6135
6478
|
}
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6479
|
+
function chainRead(deps, table, input) {
|
|
6480
|
+
return deps.indexer.read(table, input).pipe(Effect.catch((error) => {
|
|
6481
|
+
if (error.publicError.details?.reason === "scope-refused" || ![
|
|
6482
|
+
"NETWORK_ERROR",
|
|
6483
|
+
"RATE_LIMITED",
|
|
6484
|
+
"PROVIDER_ERROR",
|
|
6485
|
+
"CAPABILITY_UNAVAILABLE",
|
|
6486
|
+
"TIMEOUT"
|
|
6487
|
+
].includes(error.publicCode)) return Effect.fail({ publicError: error.publicError });
|
|
6488
|
+
return Effect.succeed(null);
|
|
6489
|
+
}));
|
|
6146
6490
|
}
|
|
6147
|
-
|
|
6148
|
-
function stitchedWindow(deps, actor, controls, window, params) {
|
|
6491
|
+
function fallbackPage(deps, scope, controls, params, state, degraded) {
|
|
6149
6492
|
return Effect.gen(function* () {
|
|
6150
|
-
const
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6493
|
+
const page = yield* convexPage(deps, scope.actor, controls, {
|
|
6494
|
+
limit: params?.limit ?? 25,
|
|
6495
|
+
...state.paymentCursor === null ? {} : { cursor: state.paymentCursor },
|
|
6496
|
+
range: {
|
|
6497
|
+
...params?.range?.from === void 0 ? {} : { from: params.range.from },
|
|
6498
|
+
to: Math.min(state.checkpoint, params?.range?.to ?? state.checkpoint)
|
|
6499
|
+
},
|
|
6500
|
+
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
6154
6501
|
});
|
|
6155
|
-
const
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6502
|
+
const decoration = yield* overlay(deps, scope.actor, controls, { references: page.items.map((item) => ({
|
|
6503
|
+
kind: item.kind,
|
|
6504
|
+
id: item.id
|
|
6505
|
+
})) });
|
|
6506
|
+
if (scopeKey(decoration) !== scopeKey(scope)) return yield* Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during fallback") });
|
|
6507
|
+
const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
|
|
6508
|
+
yield* Effect.annotateCurrentSpan({
|
|
6509
|
+
read_source: "convex",
|
|
6510
|
+
...degraded ? { read_warning: "indexer-unavailable" } : {}
|
|
6511
|
+
});
|
|
6512
|
+
return {
|
|
6513
|
+
...page,
|
|
6514
|
+
items: page.items.filter(isActivityVisible).map((item) => withAnnotation(item, annotations.get(`${item.kind}:${item.id}`))),
|
|
6515
|
+
checkpoint: state.checkpoint,
|
|
6516
|
+
cursor: page.cursor === null ? null : JSON.stringify({
|
|
6517
|
+
...state,
|
|
6518
|
+
source: "fallback",
|
|
6519
|
+
paymentCursor: page.cursor,
|
|
6520
|
+
degraded
|
|
6521
|
+
}),
|
|
6522
|
+
...degraded ? { readWarning: "indexer-unavailable" } : {}
|
|
6523
|
+
};
|
|
6524
|
+
});
|
|
6525
|
+
}
|
|
6526
|
+
function boundedRange(range, checkpoint) {
|
|
6527
|
+
return {
|
|
6528
|
+
...range?.from === void 0 ? {} : { from: range.from },
|
|
6529
|
+
to: Math.min(checkpoint, range?.to ?? checkpoint)
|
|
6530
|
+
};
|
|
6531
|
+
}
|
|
6532
|
+
function unavailablePage(deps, scope, controls, params, state, first) {
|
|
6533
|
+
if (first) return fallbackPage(deps, scope, controls, params, state, true);
|
|
6534
|
+
return Effect.annotateCurrentSpan({
|
|
6535
|
+
read_source: "indexer",
|
|
6536
|
+
read_warning: "indexer-unavailable"
|
|
6537
|
+
}).pipe(Effect.as({
|
|
6538
|
+
items: [],
|
|
6539
|
+
cursor: null,
|
|
6540
|
+
checkpoint: state.checkpoint,
|
|
6541
|
+
observedAt: Date.now(),
|
|
6542
|
+
readWarning: "indexer-unavailable"
|
|
6543
|
+
}));
|
|
6544
|
+
}
|
|
6545
|
+
function readTransfers(deps, safe, range, state, wanted, first) {
|
|
6546
|
+
const from = Math.max(0, Math.ceil((range.from ?? 0) / 1e3));
|
|
6547
|
+
const to = Math.floor(range.to / 1e3);
|
|
6548
|
+
if (state.transferDone || from > to || !wanted && !first) return Effect.succeed([]);
|
|
6549
|
+
return chainRead(deps, "transfer", {
|
|
6550
|
+
safe: toAddress(safe),
|
|
6551
|
+
limit: wanted ? BATCH : 1,
|
|
6552
|
+
timeline: {
|
|
6553
|
+
from,
|
|
6554
|
+
to,
|
|
6555
|
+
...state.transferBefore === null ? {} : { before: state.transferBefore }
|
|
6166
6556
|
}
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6557
|
+
});
|
|
6558
|
+
}
|
|
6559
|
+
function readPayments(deps, scope, controls, range, state, wanted) {
|
|
6560
|
+
const done = state.paymentDone || !wanted;
|
|
6561
|
+
if (state.paymentPending.length > 0 || done) return Effect.succeed({
|
|
6562
|
+
items: [],
|
|
6563
|
+
pending: state.paymentPending,
|
|
6564
|
+
cursor: state.paymentCursor,
|
|
6565
|
+
done
|
|
6566
|
+
});
|
|
6567
|
+
return convexPage(deps, scope.actor, controls, {
|
|
6568
|
+
limit: BATCH,
|
|
6569
|
+
range,
|
|
6570
|
+
filter: { kind: "payment" },
|
|
6571
|
+
...state.paymentCursor === null ? {} : { cursor: state.paymentCursor }
|
|
6572
|
+
}).pipe(Effect.map((page) => ({
|
|
6573
|
+
items: page.items,
|
|
6574
|
+
pending: page.items.map((item) => item.id),
|
|
6575
|
+
cursor: page.cursor,
|
|
6576
|
+
done: page.cursor === null
|
|
6577
|
+
})));
|
|
6578
|
+
}
|
|
6579
|
+
function readPaymentFacts(deps, safe, settlements) {
|
|
6580
|
+
const offsets = Array.from({ length: Math.ceil(settlements.length / 50) }, (_, i) => i * 50);
|
|
6581
|
+
return Effect.forEach(offsets, (offset) => chainRead(deps, "payment", {
|
|
6582
|
+
safe: toAddress(safe),
|
|
6583
|
+
limit: 50,
|
|
6584
|
+
settlements: settlements.slice(offset, offset + 50)
|
|
6585
|
+
}), { concurrency: 2 }).pipe(Effect.map((pages) => pages.some((page) => page === null) ? null : pages.flatMap((page) => page ?? [])));
|
|
6586
|
+
}
|
|
6587
|
+
function decoratePage(deps, scope, controls, transfers, page, range) {
|
|
6588
|
+
return Effect.gen(function* () {
|
|
6589
|
+
const decoration = yield* overlay(deps, scope.actor, controls, {
|
|
6590
|
+
references: [...page.pending.map((id) => ({
|
|
6591
|
+
kind: "payment",
|
|
6592
|
+
id
|
|
6593
|
+
})), ...transfers.map((row) => ({
|
|
6170
6594
|
kind: "movement",
|
|
6171
6595
|
id: row.id
|
|
6172
6596
|
}))],
|
|
6173
|
-
settlementIds:
|
|
6597
|
+
settlementIds: [...new Set(transfers.flatMap((row) => row.paymentSettlementId ? [row.paymentSettlementId] : []))]
|
|
6174
6598
|
});
|
|
6175
|
-
|
|
6176
|
-
const
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6599
|
+
if (scopeKey(decoration) !== scopeKey(scope)) return yield* Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during the read") });
|
|
6600
|
+
const byId = new Map([...page.items, ...decoration.payments ?? []].map((item) => [item.id, item]));
|
|
6601
|
+
const settlements = [...new Set(decoration.links.map((link) => link.settlementId))];
|
|
6602
|
+
const facts = yield* readPaymentFacts(deps, scope.safe, settlements);
|
|
6603
|
+
if (facts === null) return null;
|
|
6604
|
+
const bySettlement = new Map(facts.map((row) => [row.settlementId, row]));
|
|
6605
|
+
const byPayment = new Map(decoration.links.map((link) => [link.paymentId, {
|
|
6606
|
+
fact: bySettlement.get(link.settlementId),
|
|
6607
|
+
expected: link.expected
|
|
6608
|
+
}]));
|
|
6609
|
+
const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
|
|
6610
|
+
return {
|
|
6611
|
+
payments: page.pending.flatMap((id) => {
|
|
6612
|
+
const item = byId.get(id);
|
|
6613
|
+
if (item === void 0 || item.kind !== "payment" || item.createdAt > range.to || range.from !== void 0 && item.createdAt < range.from) return [];
|
|
6614
|
+
const confirmed = withAnnotation(confirmPayment(item, byPayment.get(id)?.fact, byPayment.get(id)?.expected), annotations.get(`payment:${id}`));
|
|
6615
|
+
return isActivityVisible(confirmed) ? [confirmed] : [];
|
|
6616
|
+
}),
|
|
6617
|
+
annotations,
|
|
6618
|
+
linked: new Set(decoration.links.filter((link) => matchesPaymentFact(bySettlement.get(link.settlementId), link.expected)).map((link) => link.settlementId))
|
|
6619
|
+
};
|
|
6620
|
+
});
|
|
6621
|
+
}
|
|
6622
|
+
function mergePage(safe, filter, limit, state, page, decoration, transfers, wantsTransfers) {
|
|
6623
|
+
const { payments, annotations, linked } = decoration;
|
|
6624
|
+
let pi = 0;
|
|
6625
|
+
let ti = 0;
|
|
6626
|
+
const items = [];
|
|
6627
|
+
const transferEnd = !wantsTransfers || transfers.length < BATCH;
|
|
6628
|
+
let transferBefore = state.transferBefore;
|
|
6629
|
+
while (items.length < limit) {
|
|
6630
|
+
if (pi === payments.length && !page.done || ti === transfers.length && !transferEnd) break;
|
|
6631
|
+
const payment = payments[pi];
|
|
6632
|
+
const row = transfers[ti];
|
|
6633
|
+
if (payment === void 0 && row === void 0) break;
|
|
6634
|
+
if (payment !== void 0 && (row === void 0 || payment.createdAt >= Number(row.timestamp) * 1e3)) {
|
|
6635
|
+
pi += 1;
|
|
6636
|
+
if (matchesFilter(payment, filter)) items.push(payment);
|
|
6637
|
+
} else if (row !== void 0) {
|
|
6638
|
+
ti += 1;
|
|
6639
|
+
transferBefore = {
|
|
6640
|
+
timestamp: Number(row.timestamp),
|
|
6641
|
+
id: row.id
|
|
6642
|
+
};
|
|
6643
|
+
if (row.paymentSettlementId && linked.has(row.paymentSettlementId)) continue;
|
|
6644
|
+
const item = transferItem(safe, row, annotations.get(`movement:${row.id}`));
|
|
6645
|
+
if (item !== null && matchesFilter(item, filter)) items.push(item);
|
|
6187
6646
|
}
|
|
6188
|
-
|
|
6189
|
-
|
|
6647
|
+
}
|
|
6648
|
+
return {
|
|
6649
|
+
items,
|
|
6650
|
+
next: {
|
|
6651
|
+
...state,
|
|
6652
|
+
paymentCursor: page.cursor,
|
|
6653
|
+
paymentDone: page.done,
|
|
6654
|
+
paymentPending: payments.slice(pi).map((item) => item.id),
|
|
6655
|
+
transferBefore,
|
|
6656
|
+
transferDone: ti === transfers.length && transferEnd
|
|
6657
|
+
}
|
|
6658
|
+
};
|
|
6659
|
+
}
|
|
6660
|
+
function indexedPage(deps, scope, controls, params, state, first) {
|
|
6661
|
+
return Effect.gen(function* () {
|
|
6662
|
+
const range = boundedRange(params?.range, state.checkpoint);
|
|
6663
|
+
const wantsTransfers = params?.filter?.kind !== "payment" && params?.filter?.status === void 0;
|
|
6664
|
+
const found = yield* readTransfers(deps, scope.safe, range, state, wantsTransfers, first);
|
|
6665
|
+
if (found === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
|
|
6666
|
+
const transfers = wantsTransfers ? found : [];
|
|
6667
|
+
const page = yield* readPayments(deps, scope, controls, range, state, params?.filter?.kind !== "movement");
|
|
6668
|
+
const decoration = yield* decoratePage(deps, scope, controls, transfers, page, range);
|
|
6669
|
+
if (decoration === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
|
|
6670
|
+
const { items, next } = mergePage(scope.safe, params?.filter, params?.limit ?? 25, state, page, decoration, transfers, wantsTransfers);
|
|
6671
|
+
const cursor = next.paymentDone && next.paymentPending.length === 0 && next.transferDone ? null : JSON.stringify(next);
|
|
6672
|
+
if (cursor !== null && cursor === params?.cursor) return yield* Effect.fail({ publicError: Errors.invalidInput("cursor", "Activity source did not advance") });
|
|
6673
|
+
yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
|
|
6190
6674
|
return {
|
|
6191
|
-
items
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
intents,
|
|
6196
|
-
links: decoration.links,
|
|
6197
|
-
annotations: [...decoration.annotations, ...olderDecoration]
|
|
6198
|
-
}).filter((item) => inRange(item, params.range) && matchesFilter(item, params.filter)),
|
|
6199
|
-
stitched: true
|
|
6675
|
+
items,
|
|
6676
|
+
cursor,
|
|
6677
|
+
checkpoint: state.checkpoint,
|
|
6678
|
+
observedAt: Date.now()
|
|
6200
6679
|
};
|
|
6201
6680
|
});
|
|
6202
6681
|
}
|
|
6203
6682
|
function readActivityPage(deps, actor, controls, params) {
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
const
|
|
6211
|
-
|
|
6212
|
-
|
|
6683
|
+
return Effect.gen(function* () {
|
|
6684
|
+
yield* Effect.try({
|
|
6685
|
+
try: () => requireParams(params),
|
|
6686
|
+
catch: (cause) => ({ publicError: cause })
|
|
6687
|
+
});
|
|
6688
|
+
const scope = yield* overlay(deps, actor, controls, {});
|
|
6689
|
+
const selection = selectionKey(params);
|
|
6690
|
+
const cursor = yield* Effect.try({
|
|
6691
|
+
try: () => parseCursor(params?.cursor, scopeKey(scope), selection),
|
|
6692
|
+
catch: () => ({ publicError: Errors.invalidInput("cursor", "invalid or obsolete Activity cursor; restart the read") })
|
|
6693
|
+
});
|
|
6694
|
+
const state = cursor ?? {
|
|
6695
|
+
version: 1,
|
|
6696
|
+
scope: scopeKey(scope),
|
|
6697
|
+
selection,
|
|
6698
|
+
checkpoint: Date.now(),
|
|
6699
|
+
source: "indexed",
|
|
6700
|
+
paymentCursor: null,
|
|
6701
|
+
paymentDone: false,
|
|
6702
|
+
paymentPending: [],
|
|
6703
|
+
transferBefore: null,
|
|
6704
|
+
transferDone: false,
|
|
6705
|
+
degraded: false
|
|
6706
|
+
};
|
|
6707
|
+
const range = boundedRange(params?.range, state.checkpoint);
|
|
6708
|
+
if (range.from !== void 0 && range.from > range.to) return {
|
|
6709
|
+
items: [],
|
|
6213
6710
|
cursor: null,
|
|
6214
|
-
checkpoint:
|
|
6215
|
-
observedAt
|
|
6216
|
-
...readWarning === void 0 ? {} : { readWarning }
|
|
6711
|
+
checkpoint: state.checkpoint,
|
|
6712
|
+
observedAt: Date.now()
|
|
6217
6713
|
};
|
|
6714
|
+
if (state.source === "fallback" || scope.safe === null) return yield* fallbackPage(deps, scope, controls, params, state, state.degraded);
|
|
6715
|
+
if (scope.payments === void 0) return yield* unavailablePage(deps, scope, controls, params, state, cursor === null);
|
|
6716
|
+
return yield* indexedPage(deps, scope, controls, params, state, cursor === null);
|
|
6717
|
+
});
|
|
6718
|
+
}
|
|
6719
|
+
function readActivitySummary(deps, actor, controls, params) {
|
|
6720
|
+
return Effect.gen(function* () {
|
|
6721
|
+
const totals = /* @__PURE__ */ new Map();
|
|
6722
|
+
let cursor;
|
|
6723
|
+
let count = 0;
|
|
6724
|
+
let readWarning;
|
|
6725
|
+
do {
|
|
6726
|
+
const page = yield* readActivityPage(deps, actor, controls, {
|
|
6727
|
+
limit: BATCH,
|
|
6728
|
+
...cursor === void 0 ? {} : { cursor },
|
|
6729
|
+
...params?.window === void 0 ? {} : { range: params.window }
|
|
6730
|
+
});
|
|
6731
|
+
count += page.items.length;
|
|
6732
|
+
readWarning ??= page.readWarning;
|
|
6733
|
+
for (const total of summarizeItems(page.items)) {
|
|
6734
|
+
const key = `${total.currency}:${total.decimals}`;
|
|
6735
|
+
const old = totals.get(key);
|
|
6736
|
+
totals.set(key, old === void 0 ? total : {
|
|
6737
|
+
...total,
|
|
6738
|
+
in: String(BigInt(old.in) + BigInt(total.in)),
|
|
6739
|
+
out: String(BigInt(old.out) + BigInt(total.out)),
|
|
6740
|
+
net: String(BigInt(old.net) + BigInt(total.net)),
|
|
6741
|
+
pendingIn: String(BigInt(old.pendingIn) + BigInt(total.pendingIn))
|
|
6742
|
+
});
|
|
6743
|
+
}
|
|
6744
|
+
cursor = page.cursor ?? void 0;
|
|
6745
|
+
} while (cursor !== void 0);
|
|
6218
6746
|
return {
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6747
|
+
window: {
|
|
6748
|
+
from: params?.window?.from ?? null,
|
|
6749
|
+
to: params?.window?.to ?? null
|
|
6750
|
+
},
|
|
6751
|
+
totals: [...totals.values()],
|
|
6752
|
+
count,
|
|
6753
|
+
observedAt: Date.now(),
|
|
6754
|
+
...readWarning === void 0 ? {} : { readWarning }
|
|
6223
6755
|
};
|
|
6224
|
-
})
|
|
6756
|
+
});
|
|
6225
6757
|
}
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6758
|
+
/** An indexed Movement has no Convex materialization. Read its exact native row. */
|
|
6759
|
+
function readIndexedMovement(deps, actor, controls, eventId) {
|
|
6760
|
+
return Effect.gen(function* () {
|
|
6761
|
+
const scope = yield* overlay(deps, actor, controls, { references: [{
|
|
6762
|
+
kind: "movement",
|
|
6763
|
+
id: eventId
|
|
6764
|
+
}] });
|
|
6765
|
+
if (scope.safe === null) return null;
|
|
6766
|
+
const safe = scope.safe.toLowerCase();
|
|
6767
|
+
const row = (yield* deps.indexer.read("transfer", {
|
|
6768
|
+
safe: toAddress(safe),
|
|
6769
|
+
limit: 1,
|
|
6770
|
+
eventId
|
|
6771
|
+
}).pipe(Effect.catch((error) => Effect.fail({ publicError: error.publicError }))))[0];
|
|
6772
|
+
if (row === void 0) return null;
|
|
6773
|
+
const blockNumber = Number(row.blockNumber);
|
|
6774
|
+
if (row.id !== eventId || row.from.toLowerCase() !== safe && row.to.toLowerCase() !== safe || !Number.isSafeInteger(blockNumber) || blockNumber < 0 || !Number.isSafeInteger(row.logIndex) || row.logIndex < 0) return yield* Effect.fail({ publicError: Errors.invalidInput("activity", "indexed Movement does not match the requested scope or evidence") });
|
|
6775
|
+
yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
|
|
6776
|
+
return {
|
|
6777
|
+
kind: "movement",
|
|
6778
|
+
movementId: row.id,
|
|
6779
|
+
viewer: scope.actor,
|
|
6780
|
+
annotation: scope.annotations.find((annotation) => annotation.reference.kind === "movement" && annotation.reference.id === row.id) ?? null,
|
|
6781
|
+
evidence: {
|
|
6782
|
+
chainId: row.chainId,
|
|
6783
|
+
contractAddress: row.token,
|
|
6784
|
+
tokenAddress: row.token,
|
|
6785
|
+
transactionHash: row.txHash,
|
|
6786
|
+
blockNumber,
|
|
6787
|
+
blockHash: row.blockHash,
|
|
6788
|
+
inclusion: "included",
|
|
6789
|
+
verification: "verified",
|
|
6790
|
+
canonicalState: "finalized",
|
|
6791
|
+
transferLogIndex: row.logIndex,
|
|
6792
|
+
source: row.from,
|
|
6793
|
+
destination: row.to,
|
|
6794
|
+
rawAmount: row.amount.toString(),
|
|
6795
|
+
link: {
|
|
6796
|
+
movementId: row.id,
|
|
6797
|
+
depositPaymentId: null
|
|
6798
|
+
}
|
|
6799
|
+
}
|
|
6800
|
+
};
|
|
6801
|
+
});
|
|
6237
6802
|
}
|
|
6238
|
-
/**
|
|
6239
|
-
* Tell the caller when the indexed page for this Safe changed.
|
|
6240
|
-
*
|
|
6241
|
-
* The transport is the indexer's one SSE connection, wired in the Ponder
|
|
6242
|
-
* adapter under `activity.subscribe`. A reactive snapshot is not a product
|
|
6243
|
-
* event, so nothing here emits telemetry: the adapter reports a teardown, and
|
|
6244
|
-
* the caller re-reads.
|
|
6245
|
-
*/
|
|
6246
6803
|
function subscribeActivity(deps, actor, controls, onChange) {
|
|
6247
6804
|
return Effect.gen(function* () {
|
|
6248
6805
|
const scope = yield* overlay(deps, actor, controls, {});
|
|
6249
6806
|
if (scope.safe === null) return () => {};
|
|
6250
|
-
const page = {
|
|
6251
|
-
safe: scope.safe,
|
|
6252
|
-
limit: INTENT_WINDOW
|
|
6253
|
-
};
|
|
6254
6807
|
let delivered = false;
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
}
|
|
6259
|
-
|
|
6260
|
-
|
|
6808
|
+
return yield* deps.indexer.subscribe("transfer", {
|
|
6809
|
+
safe: toAddress(scope.safe),
|
|
6810
|
+
limit: BATCH
|
|
6811
|
+
}, (snapshot) => {
|
|
6812
|
+
if (snapshot.status === "ok") {
|
|
6813
|
+
if (delivered) onChange();
|
|
6814
|
+
delivered = true;
|
|
6815
|
+
}
|
|
6261
6816
|
if (snapshot.status === "error") {
|
|
6262
6817
|
delivered = true;
|
|
6263
6818
|
onChange();
|
|
@@ -6267,7 +6822,7 @@ function subscribeActivity(deps, actor, controls, onChange) {
|
|
|
6267
6822
|
}
|
|
6268
6823
|
//#endregion
|
|
6269
6824
|
//#region package.json
|
|
6270
|
-
var version = "4.20.0-beta.
|
|
6825
|
+
var version = "4.20.0-beta.3";
|
|
6271
6826
|
//#endregion
|
|
6272
6827
|
//#region src/telemetry/exception-projection.ts
|
|
6273
6828
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -7833,7 +8388,10 @@ function makeFinancialOpsMethods(deps) {
|
|
|
7833
8388
|
...params?.limit === void 0 ? {} : { limit: params.limit },
|
|
7834
8389
|
...params?.range === void 0 ? {} : { range: params.range },
|
|
7835
8390
|
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
7836
|
-
} })) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.
|
|
8391
|
+
} })) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.map((page) => ({
|
|
8392
|
+
...page,
|
|
8393
|
+
items: page.items.filter(isActivityVisible)
|
|
8394
|
+
})), Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
|
|
7837
8395
|
},
|
|
7838
8396
|
summary: (params, options) => {
|
|
7839
8397
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
@@ -7854,7 +8412,10 @@ function makeFinancialOpsMethods(deps) {
|
|
|
7854
8412
|
actor: scope,
|
|
7855
8413
|
referenceActor: reference.kind === "payment" ? activityActorField(reference.actor).actor : void 0
|
|
7856
8414
|
});
|
|
7857
|
-
}).pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() =>
|
|
8415
|
+
}).pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() => indexerDeps !== void 0 && reference.kind === "movement" && /^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(reference.id) ? readIndexedMovement(indexerDeps, actor?.kind === "org" ? {
|
|
8416
|
+
kind: "organization",
|
|
8417
|
+
orgId: actor.orgId
|
|
8418
|
+
} : void 0, controls, reference.id) : deps.convexCall.query(fns.activityGet, copyInvocationObservation(controls, {
|
|
7858
8419
|
input: {
|
|
7859
8420
|
kind: reference.kind,
|
|
7860
8421
|
id: reference.id,
|
|
@@ -8375,6 +8936,277 @@ async function recoverRawDigestSigner(input) {
|
|
|
8375
8936
|
}
|
|
8376
8937
|
}
|
|
8377
8938
|
//#endregion
|
|
8939
|
+
//#region ../observability/src/engineering-delivery.ts
|
|
8940
|
+
/** Fixed admission limits for one CLI invocation and its SDK client. */
|
|
8941
|
+
const CLI_ENGINEERING_LIMITS = Object.freeze({
|
|
8942
|
+
records: 80,
|
|
8943
|
+
ordinaryRecords: 64,
|
|
8944
|
+
recordBytes: 16384,
|
|
8945
|
+
requests: 5,
|
|
8946
|
+
ordinaryRequests: 2,
|
|
8947
|
+
requestBytes: 1572864,
|
|
8948
|
+
metricSeries: 16,
|
|
8949
|
+
requestTimeoutMs: 1e3
|
|
8950
|
+
});
|
|
8951
|
+
const text$1 = (value) => typeof value === "string" ? value.slice(0, 128) : "capxul.engineering";
|
|
8952
|
+
const attributes = (input, limit = 32) => {
|
|
8953
|
+
const out = {};
|
|
8954
|
+
let count = 0;
|
|
8955
|
+
let bytes = 0;
|
|
8956
|
+
try {
|
|
8957
|
+
for (const key in input) {
|
|
8958
|
+
if (count++ >= limit) break;
|
|
8959
|
+
if (key.length > 64) continue;
|
|
8960
|
+
const value = Object.getOwnPropertyDescriptor(input, key)?.value;
|
|
8961
|
+
const scalar = typeof value === "string" ? value.slice(0, 128) : typeof value === "boolean" || typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
8962
|
+
if (scalar === void 0) continue;
|
|
8963
|
+
bytes += new TextEncoder().encode(JSON.stringify([key, scalar])).length;
|
|
8964
|
+
if (bytes > (limit >= 24 ? 2e3 : 300)) break;
|
|
8965
|
+
out[key] = scalar;
|
|
8966
|
+
}
|
|
8967
|
+
} catch {}
|
|
8968
|
+
return out;
|
|
8969
|
+
};
|
|
8970
|
+
const terminalName = (name) => name === "cli.command.completed" || CLI_COMMAND_NAMES.some((command) => name === `cli.command.${command}`);
|
|
8971
|
+
/** Native exporters retain their serialization and scheduling; this owns admission only. */
|
|
8972
|
+
function cliEngineeringDelivery(budget, ownsMetrics) {
|
|
8973
|
+
const closed = Deferred.makeUnsafe();
|
|
8974
|
+
let isClosed = false;
|
|
8975
|
+
let metricsRunning = false;
|
|
8976
|
+
const logs = [];
|
|
8977
|
+
const spans = /* @__PURE__ */ new Map();
|
|
8978
|
+
const terminalBodies = /* @__PURE__ */ new WeakSet();
|
|
8979
|
+
const bodyLeases = /* @__PURE__ */ new WeakMap();
|
|
8980
|
+
const pendingBodies = /* @__PURE__ */ new Set();
|
|
8981
|
+
const holdBody = (body, releases) => {
|
|
8982
|
+
if (releases.length === 0) return;
|
|
8983
|
+
const lease = {
|
|
8984
|
+
started: false,
|
|
8985
|
+
release: () => {
|
|
8986
|
+
if (!pendingBodies.delete(lease)) return;
|
|
8987
|
+
bodyLeases.delete(body);
|
|
8988
|
+
for (const release of releases) release();
|
|
8989
|
+
}
|
|
8990
|
+
};
|
|
8991
|
+
bodyLeases.set(body, lease);
|
|
8992
|
+
pendingBodies.add(lease);
|
|
8993
|
+
};
|
|
8994
|
+
const discardBody = (body) => bodyLeases.get(body)?.release();
|
|
8995
|
+
const drainLogs = () => {
|
|
8996
|
+
for (const release of logs.splice(0)) release();
|
|
8997
|
+
};
|
|
8998
|
+
const close = () => {
|
|
8999
|
+
isClosed = true;
|
|
9000
|
+
Deferred.doneUnsafe(closed, Effect.void);
|
|
9001
|
+
for (const lease of pendingBodies) if (!lease.started) lease.release();
|
|
9002
|
+
drainLogs();
|
|
9003
|
+
for (const release of spans.values()) release();
|
|
9004
|
+
spans.clear();
|
|
9005
|
+
if (ownsMetrics) budget.metricRegistry.clear();
|
|
9006
|
+
};
|
|
9007
|
+
return {
|
|
9008
|
+
close,
|
|
9009
|
+
discardBody,
|
|
9010
|
+
metrics(run) {
|
|
9011
|
+
return Effect.suspend(() => {
|
|
9012
|
+
if (isClosed || metricsRunning) return Effect.void;
|
|
9013
|
+
metricsRunning = true;
|
|
9014
|
+
return run.pipe(Effect.ensuring(Effect.sync(() => {
|
|
9015
|
+
metricsRunning = false;
|
|
9016
|
+
})));
|
|
9017
|
+
});
|
|
9018
|
+
},
|
|
9019
|
+
logger(delegate) {
|
|
9020
|
+
return Logger.make((options) => {
|
|
9021
|
+
if (isClosed) return;
|
|
9022
|
+
let release;
|
|
9023
|
+
try {
|
|
9024
|
+
const message = text$1(Array.isArray(options.message) ? Object.getOwnPropertyDescriptor(options.message, "0")?.value : options.message);
|
|
9025
|
+
release = budget.admitRecord(terminalName(message));
|
|
9026
|
+
if (release === void 0) return;
|
|
9027
|
+
const context = Context.add(options.fiber.context, References.CurrentLogAnnotations, attributes(options.fiber.getRef(References.CurrentLogAnnotations)));
|
|
9028
|
+
const fiber = Object.create(options.fiber);
|
|
9029
|
+
Object.defineProperty(fiber, "getRef", { value: (ref) => Context.get(context, ref) });
|
|
9030
|
+
logs.push(release);
|
|
9031
|
+
delegate.log({
|
|
9032
|
+
...options,
|
|
9033
|
+
message,
|
|
9034
|
+
cause: Cause.empty,
|
|
9035
|
+
fiber
|
|
9036
|
+
});
|
|
9037
|
+
} catch {
|
|
9038
|
+
if (release !== void 0) {
|
|
9039
|
+
const index = logs.indexOf(release);
|
|
9040
|
+
if (index !== -1) logs.splice(index, 1);
|
|
9041
|
+
release();
|
|
9042
|
+
}
|
|
9043
|
+
budget.drop();
|
|
9044
|
+
}
|
|
9045
|
+
});
|
|
9046
|
+
},
|
|
9047
|
+
tracer(delegate) {
|
|
9048
|
+
return Tracer.make({
|
|
9049
|
+
span(options) {
|
|
9050
|
+
const name = text$1(options.name);
|
|
9051
|
+
const release = isClosed ? void 0 : budget.admitRecord(terminalName(name));
|
|
9052
|
+
const spanOptions = {
|
|
9053
|
+
...options,
|
|
9054
|
+
name,
|
|
9055
|
+
annotations: Context.empty(),
|
|
9056
|
+
links: []
|
|
9057
|
+
};
|
|
9058
|
+
const span = release === void 0 ? new Tracer.NativeSpan({
|
|
9059
|
+
...spanOptions,
|
|
9060
|
+
sampled: false
|
|
9061
|
+
}) : delegate.span(spanOptions);
|
|
9062
|
+
if (release !== void 0) spans.set(span.spanId, release);
|
|
9063
|
+
const wrapped = Object.create(span);
|
|
9064
|
+
let fields = 0, events = 0, links = 0, attributeBytes = 0;
|
|
9065
|
+
let ended = false;
|
|
9066
|
+
Object.defineProperties(wrapped, {
|
|
9067
|
+
attribute: { value: (key, value) => {
|
|
9068
|
+
if (fields >= 32 && !span.attributes.has(key)) {
|
|
9069
|
+
budget.drop();
|
|
9070
|
+
return;
|
|
9071
|
+
}
|
|
9072
|
+
const safe = attributes({ [key]: value }, 1);
|
|
9073
|
+
if (!Object.hasOwn(safe, key)) return;
|
|
9074
|
+
const previous = span.attributes.has(key) ? new TextEncoder().encode(JSON.stringify([key, span.attributes.get(key)])).length : 0;
|
|
9075
|
+
const next = new TextEncoder().encode(JSON.stringify([key, safe[key]])).length;
|
|
9076
|
+
if (attributeBytes - previous + next > 2e3) {
|
|
9077
|
+
budget.drop();
|
|
9078
|
+
return;
|
|
9079
|
+
}
|
|
9080
|
+
attributeBytes += next - previous;
|
|
9081
|
+
if (!span.attributes.has(key)) fields += 1;
|
|
9082
|
+
span.attribute(key, safe[key]);
|
|
9083
|
+
} },
|
|
9084
|
+
event: { value: (eventName, time, attrs) => {
|
|
9085
|
+
if (events++ >= 2) {
|
|
9086
|
+
budget.drop();
|
|
9087
|
+
return;
|
|
9088
|
+
}
|
|
9089
|
+
span.event(text$1(eventName), time, attributes(attrs ?? {}, 4));
|
|
9090
|
+
} },
|
|
9091
|
+
addLinks: { value: (items) => {
|
|
9092
|
+
for (const item of items.slice(0, Math.max(0, 2 - links))) {
|
|
9093
|
+
if (!/^[a-f0-9]{32}$/u.test(item.span.traceId) || !/^[a-f0-9]{16}$/u.test(item.span.spanId)) {
|
|
9094
|
+
budget.drop();
|
|
9095
|
+
continue;
|
|
9096
|
+
}
|
|
9097
|
+
span.addLinks([{
|
|
9098
|
+
span: Tracer.externalSpan({
|
|
9099
|
+
traceId: item.span.traceId,
|
|
9100
|
+
spanId: item.span.spanId,
|
|
9101
|
+
sampled: item.span.sampled
|
|
9102
|
+
}),
|
|
9103
|
+
attributes: attributes(item.attributes, 4)
|
|
9104
|
+
}]);
|
|
9105
|
+
links += 1;
|
|
9106
|
+
}
|
|
9107
|
+
} },
|
|
9108
|
+
end: { value: (time, exit) => {
|
|
9109
|
+
if (ended) return;
|
|
9110
|
+
ended = true;
|
|
9111
|
+
span.end(time, Exit.isFailure(exit) ? Exit.fail("CLI operation failed") : Exit.void);
|
|
9112
|
+
} }
|
|
9113
|
+
});
|
|
9114
|
+
wrapped.addLinks(options.links ?? []);
|
|
9115
|
+
return wrapped;
|
|
9116
|
+
},
|
|
9117
|
+
...delegate.context === void 0 ? {} : { context: delegate.context.bind(delegate) }
|
|
9118
|
+
});
|
|
9119
|
+
},
|
|
9120
|
+
serialization(delegate) {
|
|
9121
|
+
return {
|
|
9122
|
+
logs(data) {
|
|
9123
|
+
const releases = logs.splice(0);
|
|
9124
|
+
try {
|
|
9125
|
+
const body = delegate.logs(data);
|
|
9126
|
+
if (data.resourceLogs.some((r) => r.scopeLogs.some((s) => s.logRecords?.some((l) => l.body?.stringValue === "cli.command.completed")))) terminalBodies.add(body);
|
|
9127
|
+
holdBody(body, releases);
|
|
9128
|
+
return body;
|
|
9129
|
+
} catch (error) {
|
|
9130
|
+
for (const release of releases) release();
|
|
9131
|
+
throw error;
|
|
9132
|
+
}
|
|
9133
|
+
},
|
|
9134
|
+
traces(data) {
|
|
9135
|
+
const releases = [];
|
|
9136
|
+
for (const resource of data.resourceSpans) for (const scope of resource.scopeSpans) for (const span of scope.spans) {
|
|
9137
|
+
const release = spans.get(span.spanId);
|
|
9138
|
+
if (release !== void 0) releases.push(release);
|
|
9139
|
+
spans.delete(span.spanId);
|
|
9140
|
+
}
|
|
9141
|
+
try {
|
|
9142
|
+
const body = delegate.traces(data);
|
|
9143
|
+
if (data.resourceSpans.some((r) => r.scopeSpans.some((s) => s.spans.some((span) => terminalName(span.name))))) terminalBodies.add(body);
|
|
9144
|
+
holdBody(body, releases);
|
|
9145
|
+
return body;
|
|
9146
|
+
} catch (error) {
|
|
9147
|
+
for (const release of releases) release();
|
|
9148
|
+
throw error;
|
|
9149
|
+
}
|
|
9150
|
+
},
|
|
9151
|
+
metrics(data) {
|
|
9152
|
+
const body = delegate.metrics(data);
|
|
9153
|
+
terminalBodies.add(body);
|
|
9154
|
+
return body;
|
|
9155
|
+
}
|
|
9156
|
+
};
|
|
9157
|
+
},
|
|
9158
|
+
transport(client, enabled = () => true) {
|
|
9159
|
+
const retrying = HttpClient.transform(client, (response, request) => Effect.suspend(() => {
|
|
9160
|
+
if (enabled()) return response;
|
|
9161
|
+
budget.drop();
|
|
9162
|
+
return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
|
|
9163
|
+
})).pipe(HttpClient.transformResponse((response) => Effect.tap(response, (result) => {
|
|
9164
|
+
let received = 0;
|
|
9165
|
+
return Stream.runForEach(result.stream, (chunk) => Effect.sync(() => {
|
|
9166
|
+
received += chunk.byteLength;
|
|
9167
|
+
if (received > 65536) throw new Error("Engineering response limit exceeded");
|
|
9168
|
+
})).pipe(Effect.catchTag("HttpClientError", (error) => error.reason._tag === "EmptyBodyError" ? Effect.void : Effect.fail(error)));
|
|
9169
|
+
}))).pipe(HttpClient.filterStatusOk, HttpClient.retryTransient({
|
|
9170
|
+
times: 1,
|
|
9171
|
+
schedule: Schedule.spaced("100 millis").pipe(Schedule.addDelay(({ input }) => Effect.map(Clock.currentTimeMillis, (now) => {
|
|
9172
|
+
if (!HttpClientError.isHttpClientError(input) || input.reason._tag !== "StatusCodeError" || input.reason.response.status !== 429) return Duration.zero;
|
|
9173
|
+
const header = input.reason.response.headers["retry-after"];
|
|
9174
|
+
const numeric = Number(header);
|
|
9175
|
+
const delay = header === void 0 ? 2e3 : Number.isFinite(numeric) ? numeric * 1e3 : Date.parse(header) - now;
|
|
9176
|
+
return Duration.millis(Number.isFinite(delay) ? Math.min(2e3, Math.max(0, delay)) : 2e3);
|
|
9177
|
+
})))
|
|
9178
|
+
}));
|
|
9179
|
+
return HttpClient.transform(retrying, (response, request) => Effect.suspend(() => {
|
|
9180
|
+
if (isClosed) {
|
|
9181
|
+
discardBody(request.body);
|
|
9182
|
+
return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null)));
|
|
9183
|
+
}
|
|
9184
|
+
const droppedResponse = () => HttpClientResponse.fromWeb(request, new Response(null, { status: 200 }));
|
|
9185
|
+
if (request.body._tag === "Empty") {
|
|
9186
|
+
discardBody(request.body);
|
|
9187
|
+
return Effect.succeed(droppedResponse());
|
|
9188
|
+
}
|
|
9189
|
+
const bytes = request.body.contentLength;
|
|
9190
|
+
const release = budget.admitRequest(bytes ?? Number.POSITIVE_INFINITY, terminalBodies.has(request.body));
|
|
9191
|
+
if (release === void 0) {
|
|
9192
|
+
discardBody(request.body);
|
|
9193
|
+
return Effect.succeed(droppedResponse());
|
|
9194
|
+
}
|
|
9195
|
+
const lease = bodyLeases.get(request.body);
|
|
9196
|
+
if (lease !== void 0) lease.started = true;
|
|
9197
|
+
return response.pipe(Effect.asVoid, Effect.timeoutOption(CLI_ENGINEERING_LIMITS.requestTimeoutMs), Effect.raceFirst(Deferred.await(closed).pipe(Effect.andThen(Effect.interrupt))), Effect.tap((result) => Effect.sync(() => {
|
|
9198
|
+
if (Option.isNone(result)) budget.drop();
|
|
9199
|
+
})), Effect.catchCause(() => Effect.sync(() => {
|
|
9200
|
+
budget.drop();
|
|
9201
|
+
})), Effect.as(droppedResponse()), Effect.ensuring(Effect.sync(() => {
|
|
9202
|
+
release();
|
|
9203
|
+
discardBody(request.body);
|
|
9204
|
+
})));
|
|
9205
|
+
}));
|
|
9206
|
+
}
|
|
9207
|
+
};
|
|
9208
|
+
}
|
|
9209
|
+
//#endregion
|
|
8378
9210
|
//#region ../observability/src/engineering.ts
|
|
8379
9211
|
const ENGINEERING_CAPXUL_ENVS = [
|
|
8380
9212
|
"development",
|
|
@@ -8549,10 +9381,13 @@ const MINIMAL_NUMBERS = /* @__PURE__ */ new Set([
|
|
|
8549
9381
|
const MINIMAL_CORRELATIONS = /* @__PURE__ */ new Set([
|
|
8550
9382
|
"correlation_id",
|
|
8551
9383
|
"journey_id",
|
|
8552
|
-
"invocation_id"
|
|
9384
|
+
"invocation_id",
|
|
9385
|
+
"posthogDistinctId"
|
|
8553
9386
|
]);
|
|
8554
9387
|
const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
|
|
8555
9388
|
"command",
|
|
9389
|
+
"capability",
|
|
9390
|
+
"invocation_kind",
|
|
8556
9391
|
"outcome",
|
|
8557
9392
|
"capxul_env",
|
|
8558
9393
|
"unit"
|
|
@@ -8560,7 +9395,7 @@ const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
|
|
|
8560
9395
|
const MINIMAL_METRIC_NAMES = /* @__PURE__ */ new Set(["capxul.cli.command.count", "capxul.cli.command.duration"]);
|
|
8561
9396
|
const traceId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{32}$/u.test(value);
|
|
8562
9397
|
const spanId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{16}$/u.test(value);
|
|
8563
|
-
const minimalName = (value) => typeof value === "string" && (MINIMAL_LOG_NAMES.has(value) || isCapxulOperation(value)) ? value : "capxul.engineering";
|
|
9398
|
+
const minimalName = (value) => typeof value === "string" && (MINIMAL_LOG_NAMES.has(value) || isCapxulOperation(value) || CLI_COMMAND_NAMES.some((command) => value === `cli.command.${command}`)) ? value : "capxul.engineering";
|
|
8564
9399
|
const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
|
|
8565
9400
|
["outcome", [
|
|
8566
9401
|
"succeeded",
|
|
@@ -8573,7 +9408,10 @@ const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
|
|
|
8573
9408
|
["failure_mode", FAILURE_MODES$1],
|
|
8574
9409
|
["capxul_env", ENGINEERING_CAPXUL_ENVS],
|
|
8575
9410
|
["producer", ["browser", "server"]],
|
|
8576
|
-
["command",
|
|
9411
|
+
["command", CLI_COMMAND_NAMES],
|
|
9412
|
+
["capability", CLI_CAPABILITIES],
|
|
9413
|
+
["invocation_kind", CLI_INVOCATION_KINDS],
|
|
9414
|
+
["cli_identity_kind", ["anonymous"]],
|
|
8577
9415
|
["output_mode", ["human", "json"]],
|
|
8578
9416
|
["unit", [
|
|
8579
9417
|
"1",
|
|
@@ -8790,10 +9628,12 @@ function minimalSerialization(config) {
|
|
|
8790
9628
|
};
|
|
8791
9629
|
}
|
|
8792
9630
|
Effect.flatMap(OtlpExporter.Flusher, (flusher) => flusher.flush);
|
|
9631
|
+
var EngineeringLog = class extends Context.Service()("capxul/EngineeringLog") {};
|
|
8793
9632
|
/** Shared browser/server OTLP layer. OtlpLogger merges with incumbent loggers once. */
|
|
8794
9633
|
const makeEngineeringTelemetryLayer = (config) => {
|
|
8795
9634
|
const validated = validateEngineeringTelemetryConfig(config);
|
|
8796
9635
|
return Layer.suspend(() => {
|
|
9636
|
+
const bounded = validated.deliveryBudget === void 0 ? void 0 : cliEngineeringDelivery(validated.deliveryBudget, validated.serviceName === "capxul-cli");
|
|
8797
9637
|
const endpoints = postHogOtlpEndpoints(validated.host);
|
|
8798
9638
|
let disabled = false;
|
|
8799
9639
|
const enabled = () => {
|
|
@@ -8807,7 +9647,12 @@ const makeEngineeringTelemetryLayer = (config) => {
|
|
|
8807
9647
|
};
|
|
8808
9648
|
const delivery = {
|
|
8809
9649
|
...validated.shutdownTimeout === void 0 ? {} : { shutdownTimeout: validated.shutdownTimeout },
|
|
8810
|
-
...validated.exportInterval === void 0 ? {} : { exportInterval: validated.exportInterval }
|
|
9650
|
+
...validated.exportInterval === void 0 ? {} : { exportInterval: validated.exportInterval },
|
|
9651
|
+
...bounded === void 0 ? {} : {
|
|
9652
|
+
maxBatchSize: Number.MAX_SAFE_INTEGER,
|
|
9653
|
+
exportInterval: 1e3,
|
|
9654
|
+
shutdownTimeout: validated.serviceName === "capxul-cli" ? 0 : 500
|
|
9655
|
+
}
|
|
8811
9656
|
};
|
|
8812
9657
|
const resource = {
|
|
8813
9658
|
serviceName: validated.serviceName ?? "capxul-sdk",
|
|
@@ -8823,22 +9668,38 @@ const makeEngineeringTelemetryLayer = (config) => {
|
|
|
8823
9668
|
headers: validated.headers,
|
|
8824
9669
|
resource,
|
|
8825
9670
|
...delivery
|
|
8826
|
-
}).pipe(Effect.map((tracer) => makeLeakSafeEngineeringTracer(tracer, enabled)))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
|
|
8827
|
-
const
|
|
9671
|
+
}).pipe(Effect.map((tracer) => makeLeakSafeEngineeringTracer(bounded?.tracer(tracer) ?? tracer, enabled)))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
|
|
9672
|
+
const ownedLogging = Layer.effect(EngineeringLog, OtlpLogger.make({
|
|
8828
9673
|
url: endpoints.logs,
|
|
8829
9674
|
headers: validated.headers,
|
|
8830
9675
|
resource,
|
|
8831
|
-
...delivery
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
9676
|
+
...delivery,
|
|
9677
|
+
...bounded === void 0 ? {} : { excludeLogSpans: true }
|
|
9678
|
+
}).pipe(Effect.map((nativeLogger) => {
|
|
9679
|
+
const logger = bounded?.logger(nativeLogger) ?? nativeLogger;
|
|
9680
|
+
return Logger.make((options) => {
|
|
9681
|
+
if (enabled()) logger.log(options);
|
|
9682
|
+
});
|
|
9683
|
+
}))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
|
|
9684
|
+
const logging = Logger.layer([EngineeringLog], { mergeWithExisting: true }).pipe(Layer.provideMerge(ownedLogging));
|
|
9685
|
+
const metricConfig = {
|
|
8836
9686
|
url: endpoints.metrics,
|
|
8837
9687
|
headers: validated.headers,
|
|
8838
9688
|
resource,
|
|
8839
9689
|
temporality: "delta",
|
|
8840
9690
|
...delivery
|
|
8841
|
-
}
|
|
9691
|
+
};
|
|
9692
|
+
const metrics = bounded === void 0 ? OtlpMetrics.layer(metricConfig) : validated.serviceName !== "capxul-cli" ? Layer.empty : Layer.effectDiscard(Effect.gen(function* () {
|
|
9693
|
+
const flusher = yield* OtlpExporter.Flusher;
|
|
9694
|
+
yield* OtlpMetrics.make({
|
|
9695
|
+
...metricConfig,
|
|
9696
|
+
exportInterval: Number.POSITIVE_INFINITY,
|
|
9697
|
+
shutdownTimeout: 0
|
|
9698
|
+
}).pipe(Effect.provideService(OtlpExporter.Flusher, {
|
|
9699
|
+
...flusher,
|
|
9700
|
+
register: (run) => flusher.register(bounded.metrics(run))
|
|
9701
|
+
}));
|
|
9702
|
+
})).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
|
|
8842
9703
|
const headerPolicy = Layer.merge(Layer.succeed(HttpClient.TracerHeaderFilter, traceHeaderFilter), Layer.succeed(Headers.CurrentRedactedNames, [credentialHeaderPattern]));
|
|
8843
9704
|
const browserLifecycle = Layer.effectDiscard(Effect.gen(function* () {
|
|
8844
9705
|
if (validated.producer !== "browser" || !hasBrowserDocument()) return;
|
|
@@ -8861,17 +9722,25 @@ const makeEngineeringTelemetryLayer = (config) => {
|
|
|
8861
9722
|
}));
|
|
8862
9723
|
}));
|
|
8863
9724
|
const transport = Layer.effect(HttpClient.HttpClient, Effect.map(HttpClient.HttpClient, (client) => {
|
|
8864
|
-
const
|
|
8865
|
-
|
|
9725
|
+
const originalTransport = validated.producer === "browser" && hasBrowserDocument() ? browserExportClient(client) : client;
|
|
9726
|
+
const selectedTransport = bounded?.transport(originalTransport, enabled) ?? originalTransport;
|
|
9727
|
+
return HttpClient.transform(selectedTransport, (response, request) => Effect.suspend(() => {
|
|
9728
|
+
if (enabled() && request.body._tag !== "Empty") return response;
|
|
9729
|
+
bounded?.discardBody(request.body);
|
|
9730
|
+
return Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null, { status: 200 })));
|
|
9731
|
+
}));
|
|
8866
9732
|
})).pipe(Layer.provide(FetchHttpClient.layer));
|
|
8867
9733
|
const serialization = Layer.effect(OtlpSerialization.OtlpSerialization, Effect.map(OtlpSerialization.OtlpSerialization, (json) => {
|
|
8868
9734
|
const projected = minimalSerialization(validated);
|
|
8869
|
-
|
|
9735
|
+
const selected = validated.privacy === "minimal" ? projected : {
|
|
8870
9736
|
...json,
|
|
8871
9737
|
metrics: projected.metrics
|
|
8872
9738
|
};
|
|
9739
|
+
return bounded?.serialization(selected) ?? selected;
|
|
8873
9740
|
})).pipe(Layer.provide(OtlpSerialization.layerJson));
|
|
8874
|
-
|
|
9741
|
+
const combined = browserLifecycle.pipe(Layer.provideMerge(Layer.mergeAll(tracing, logging, metrics, headerPolicy)), Layer.provide(serialization), Layer.provide(transport));
|
|
9742
|
+
const scoped = bounded === void 0 ? combined : combined.pipe(Layer.provideMerge(Layer.effectDiscard(Effect.addFinalizer(() => Effect.sync(bounded.close)))));
|
|
9743
|
+
return validated.deliveryBudget === void 0 ? scoped : scoped.pipe(Layer.provideMerge(Layer.succeed(Metric.MetricRegistry, validated.serviceName === "capxul-cli" ? validated.deliveryBudget.metricRegistry : validated.deliveryBudget.emptyMetricRegistry())));
|
|
8875
9744
|
});
|
|
8876
9745
|
};
|
|
8877
9746
|
//#endregion
|
|
@@ -15155,6 +16024,7 @@ function postHogObservability(client, options = {}) {
|
|
|
15155
16024
|
product: postHogProductTelemetry(policy)
|
|
15156
16025
|
};
|
|
15157
16026
|
Object.defineProperty(module, HOST_ENGINEERING_POLICY, { value: Object.freeze({
|
|
16027
|
+
...options.engineeringDeliveryBudget === void 0 ? {} : { engineeringDeliveryBudget: options.engineeringDeliveryBudget },
|
|
15158
16028
|
...options.engineeringEnabled === void 0 ? {} : { engineeringEnabled: options.engineeringEnabled },
|
|
15159
16029
|
...options.engineeringPrivacy === void 0 ? {} : { engineeringPrivacy: options.engineeringPrivacy }
|
|
15160
16030
|
}) });
|
|
@@ -15192,6 +16062,7 @@ function isPromiseLike(value) {
|
|
|
15192
16062
|
function hostEngineeringPolicy(observability) {
|
|
15193
16063
|
const policy = observability?.[HOST_ENGINEERING_POLICY];
|
|
15194
16064
|
return {
|
|
16065
|
+
...policy?.engineeringDeliveryBudget === void 0 ? {} : { deliveryBudget: policy.engineeringDeliveryBudget },
|
|
15195
16066
|
...policy?.engineeringEnabled === void 0 ? {} : { enabled: policy.engineeringEnabled },
|
|
15196
16067
|
...policy?.engineeringPrivacy === void 0 ? {} : { privacy: policy.engineeringPrivacy }
|
|
15197
16068
|
};
|
|
@@ -15260,7 +16131,8 @@ function decodeTransfer(value) {
|
|
|
15260
16131
|
token: toAddress(hex(row, "token")),
|
|
15261
16132
|
from: toAddress(hex(row, "from")),
|
|
15262
16133
|
to: toAddress(hex(row, "to")),
|
|
15263
|
-
amount: big(row, "amount")
|
|
16134
|
+
amount: big(row, "amount"),
|
|
16135
|
+
..."payment_settlement_id" in row ? { paymentSettlementId: row.payment_settlement_id === null ? null : hex(row, "payment_settlement_id") } : {}
|
|
15264
16136
|
};
|
|
15265
16137
|
}
|
|
15266
16138
|
function decodePayment(value) {
|
|
@@ -15346,6 +16218,60 @@ const ACTOR_PARAMS = {
|
|
|
15346
16218
|
*/
|
|
15347
16219
|
function ponderQuery(table, input) {
|
|
15348
16220
|
const safe = assertIndexerPage(input);
|
|
16221
|
+
if (input.eventId !== void 0) {
|
|
16222
|
+
if (table !== "transfer") throw Errors.invalidInput("eventId", "only transfer reads accept a logical event ID");
|
|
16223
|
+
return {
|
|
16224
|
+
sql: `${HEADS.transfer.slice(0, HEADS.transfer.indexOf(" from \"transfer\""))} from "transfer" where (("transfer"."from" = $1 or "transfer"."to" = $2) and "transfer"."id" = $3) limit $4`,
|
|
16225
|
+
params: [
|
|
16226
|
+
safe,
|
|
16227
|
+
safe,
|
|
16228
|
+
input.eventId,
|
|
16229
|
+
1
|
|
16230
|
+
],
|
|
16231
|
+
typings: [
|
|
16232
|
+
"none",
|
|
16233
|
+
"none",
|
|
16234
|
+
"none",
|
|
16235
|
+
"none"
|
|
16236
|
+
]
|
|
16237
|
+
};
|
|
16238
|
+
}
|
|
16239
|
+
if (input.settlements !== void 0) {
|
|
16240
|
+
if (table !== "payment") throw Errors.invalidInput("settlements", "only Payment reads accept settlement IDs");
|
|
16241
|
+
const params = [
|
|
16242
|
+
safe,
|
|
16243
|
+
safe,
|
|
16244
|
+
...input.settlements,
|
|
16245
|
+
input.limit
|
|
16246
|
+
];
|
|
16247
|
+
return {
|
|
16248
|
+
sql: `${HEADS.payment.slice(0, HEADS.payment.indexOf("where "))}where (("payment"."sender" = $1 or "payment"."recipient" = $2) and "payment"."settlement_id" in (${input.settlements.map((_, i) => `$${i + 3}`).join(", ")})) limit $${params.length}`,
|
|
16249
|
+
params,
|
|
16250
|
+
typings: params.map(() => "none")
|
|
16251
|
+
};
|
|
16252
|
+
}
|
|
16253
|
+
if (input.timeline !== void 0) {
|
|
16254
|
+
if (table !== "transfer") throw Errors.invalidInput("timeline", "only transfer reads have a timeline");
|
|
16255
|
+
const { from, to, before } = input.timeline;
|
|
16256
|
+
const params = [
|
|
16257
|
+
safe,
|
|
16258
|
+
safe,
|
|
16259
|
+
from,
|
|
16260
|
+
to
|
|
16261
|
+
];
|
|
16262
|
+
const select = HEADS.transfer.slice(0, HEADS.transfer.indexOf(" from \"transfer\"")) + ", \"payment_settlement_id\" from \"activity_transfer\" ";
|
|
16263
|
+
let where = "(\"activity_transfer\".\"from\" = $1 or \"activity_transfer\".\"to\" = $2) and \"activity_transfer\".\"timestamp\" >= $3 and \"activity_transfer\".\"timestamp\" <= $4";
|
|
16264
|
+
if (before !== void 0) {
|
|
16265
|
+
params.push(before.timestamp, before.timestamp, before.id);
|
|
16266
|
+
where += " and (\"activity_transfer\".\"timestamp\" < $5 or (\"activity_transfer\".\"timestamp\" = $6 and \"activity_transfer\".\"id\" < $7))";
|
|
16267
|
+
}
|
|
16268
|
+
params.push(input.limit);
|
|
16269
|
+
return {
|
|
16270
|
+
sql: `${select}where (${where}) order by "activity_transfer"."timestamp" desc, "activity_transfer"."id" desc limit $${params.length}`,
|
|
16271
|
+
params,
|
|
16272
|
+
typings: params.map(() => "none")
|
|
16273
|
+
};
|
|
16274
|
+
}
|
|
15349
16275
|
const offset = input.offset ?? 0;
|
|
15350
16276
|
const actors = ACTOR_PARAMS[table];
|
|
15351
16277
|
const params = [];
|