@capxul/sdk 4.20.0-beta.24 → 4.20.0-beta.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { $ as moneySafeOpDigest, $i as actorReferenceToBackend, $r as runIfActive, A as logDroppedEvent, Ai as globalLocalStorage, An as configurations, Ar as ChainIdSchema, At as SAFE_SENTINEL_OWNERS, B as makeTargetsMethods, Bn as PayrollRunDetailSchema, Br as absurd, Bt as observeSdkClient, C as retryIdempotentRead, Ci as toAllowedOrigin, Cn as isCapxulOperation, Cr as EpochMsSchema, Ct as deriveCapxulSafeAddress, D as makeApprovalsMethods, Di as AuthClientError, Dn as onboardings, Dr as toPermissionId, Dt as buildSafeMultiSendExecTransactionCall, En as details, Er as toPermissionAssignmentId, Et as safe4337MultiSendUserOpCallData, F as paymentRequestKeyLifecycle, Fn as runs, Ft as failureDetail, Gi as CapxulError, Gn as members, Gr as runIdentityFacade, H as executePrepared, Hi as isNonEmptyString, Hn as PayrollRunItemInputSchema, Hr as makeAuthMethods, Ht as withCapturedAttemptContext, I as makeDestinationsMethods, Ii as toEpochMs, In as terms, Ir as isEvmAddress, It as failureEvidenceProps, Ji as isFailureMode, Jn as treasuries, Jr as observationOnlyControls, Ki as isCapxulError, Kn as roles, Kr as runPortEffect, L as makeActorRelationshipMethods, Ln as AuthorizePayrollRunExecutionInputSchema, Lr as toAddress, M as makeOfframpMethods, Mn as PreparedPermissionExecutionSchema, Mt as asCapxulError, N as makePaymentsMethods, Nn as PermissionCommandViewSchema, Nt as mapPage, O as makeAccountMethods, Oi as AuthClientPortTag, On as authorities, Or as toRoleKey, Ot as BASE_SEPOLIA_CHAIN_ID, P as waitForPaymentSubmission, Pn as groups, Pr as toExactAssetAmount, Pt as toPublicError, Qi as AuthCachePortTag, Qn as ORGANIZATION_SETUP_STATUSES, Qr as defectAsCapxulError, Qt as causeChain, R as makeCheckoutMethods, Ri as toAuthUserId, Rt as postHogFailureObservation, S as convexCallErrorFromCapxul, Si as isCredentialField, Sn as CAPXUL_OPERATIONS, Sr as DurationMsSchema, St as configuredMoneyAssetById, T as registerProductTelemetryInvocation, Ti as toPublishableKey, Tn as OPERATIONS, Tr as toBudgetId, Tt as safe4337ExecuteUserOpCallData, Ui as toNonEmptyStringBrand, Ur as askIdentity, V as subscribeFreshQuery, Vn as PayrollRunIdSchema, Vr as reference, Vt as observationContextProps, Wi as Errors, Wn as invitations, Wr as publicIdentityFailure, Xi as readField, Xn as OrganizationSetupSchema, Xr as withVerifiedOrganizationScope, Yi as isRecord, Yn as workspaces, Yr as runCapxulEffect, Yt as version$1, Z as canonicalJson, Zn as ORGANIZATION_SETUP_REASONS, Zr as awaitCapxulResult, Zt as UNOBSERVABLE_SIGNER_STATUS, _i as copyInvocationObservation, _n as CLI_CAPABILITIES, _r as documents, _t as SEND_SIGNATURE, a as ConvexSmartAccountLayer, ai as ActorFailure, ar as CurrentPolicySchema, at as MODULE_PROXY_FACTORY_ABI, b as TelemetryPortTag, bi as redactSecrets, bt as CONFIGURED_MONEY_ASSETS, c as makeHoldingsMethods, cn as toCountryCode, cr as images, ct as PARAMETER_TYPE, d as ConvexAccountLayer, di as ClockPortTag, dr as isBytes32, dt as ROLES_SET_ALLOWANCE_ABI, ea as sameActor, ei as toCapxulResult, en as isEcdsaSignatureHex, er as decodeChainCause, et as ORG_BUDGET_ACTIONS, f as accountFromAccountPositions, fn as toHandle, fr as toExactHex, ft as ROLES_SET_TRANSACTION_UNWRAPPER_ABI, gi as attachInvocationObservation, gn as redactTelemetryProps, gr as annotations, gt as SEND_BATCH_SIGNATURE, h as MONEY_READ_RATES, hi as observationRequestHeaders, hr as toLowerHex, ht as SAFE_ENABLE_MODULE_ABI, i as makeSmartAccountMethods, in as signerFailure, ir as decodePermissionIntent$1, it as CREATE_COMMITMENT_SIGNATURE, j as makeOrganizationPaymentsMethods, jn as replacements, jt as callOperation, k as emitDepositInitiated, ki as detectAuthCacheAdapter, kn as commands, kr as PaymentDocumentReadRefSchema, kt as MULTI_SEND_CALL_ONLY, l as PonderIndexerAdapter, li as SystemClockLayer, ln as toKycTier, lr as toAccountId, lt as ROLES_ASSIGN_ROLES_ABI, mi as BetterAuthBrowserLayer, mn as validateHandle, mr as toLowerBytes32, mt as ROLE_MANAGEMENT_SIGNATURES, n as bootIdentityFlow, ni as safeEngineeringIdentifier, nn as openfortSignerNotReadyCode, nr as InvitationViewSchema, nt as ZODIAC_MODULE_PROXY_FACTORY, o as SmartAccountPortTag, oi as carryInvocationParentSpan, on as makeCurrentUserMethods, or as diagnostics, ot as MULTI_SEND_SELECTOR, p as accountPositionsFromWire, pi as BetterAuthNodeLayer, pn as toPartyId, pr as toLowerAddress, pt as ROLES_SET_UP_ABI, qi as FAILURE_MODES, qn as setups, qr as sessionFromActor, r as makeMeMethods, ri as CAPXUL_ERROR_CODES, rn as recoverRawDigestSigner, rr as hashPermissionReplacementPreview, rt as resolveOrgPermissionIdentity, si as withInvocationParentSpan, sn as makeClientRequestMethods, sr as AppIdSchema, st as OPERATOR, t as executeIdentityProductObservation, ti as formatTraceparent, tn as isSignerNotReadySignal, tr as toOrgId, tt as roleKeyForPermission, u as makeAccountsMethods, un as toTesterKind, ut as ROLES_EXEC_TRANSACTION_WITH_ROLE_ABI, v as AccountReadPortTag, vi as readInvocationObservation, vn as CLI_COMMAND_NAMES, vr as items, vt as CAPXUL_PAYMENTS_V2_ADDRESS, w as bindProductTelemetryInvocation, wn as normalizeCapxulOperation, wr as ActorRefSchema, wt as normalizeSafeSaltEmail, x as ConvexCallPortTag, xi as redactUrlSecrets, xn as stampTelemetryEnvelope, xr as PAYMENT_STATUSES, yi as sanitizeObservationContext, yn as CLI_INVOCATION_KINDS, yr as overlays, yt as hashAdjustmentsV3, zi as toEmail, zt as observeFailedResult } from "./access-CCRHHGfU.mjs";
1
+ import { $n as InvitationViewSchema, $r as SystemClockLayer, $t as version$1, A as registerProductTelemetryInvocation, An as details, At as safe4337ExecuteUserOpCallData, B as makeOrganizationPaymentsMethods, Bi as AuthCachePortTag, Br as runCapxulEffect, Bt as failureEvidenceProps, C as AccountReadPortTag, Cn as CLI_INVOCATION_KINDS, Ct as SEND_BATCH_SIGNATURE, D as convexCallErrorFromCapxul, Dn as isCapxulOperation, Dr as isEvmAddress, E as ConvexCallPortTag, Ei as toEpochMs, En as CAPXUL_OPERATIONS, Et as CONFIGURED_MONEY_ASSETS, Fi as FAILURE_MODES, Fn as replacements, Fr as publicIdentityFailure, Ft as SAFE_SENTINEL_OWNERS, G as makeActorRelationshipMethods, Gn as treasuries, Gr as toCapxulResult, Gt as observationContextProps, H as makePaymentsMethods, Hn as members, Hr as awaitCapxulResult, Ht as postHogFailureObservation, Ii as isFailureMode, In as PreparedPermissionExecutionSchema, Ir as runIdentityFacade, It as callOperation, J as makeTargetsMethods, Jn as ORGANIZATION_SETUP_REASONS, K as makeCheckoutMethods, Kn as workspaces, Kr as formatTraceparent, Kt as withCapturedAttemptContext, L as makeAccountMethods, Li as isRecord, Ln as PermissionCommandViewSchema, Lr as runPortEffect, Lt as asCapxulError, M as makeApprovalsMethods, Mi as Errors, Mn as authorities, Mr as reference, Mt as buildSafeMultiSendExecTransactionCall, N as makePayrollMethods, Ni as CapxulError, Nn as commands, Nr as makeAuthMethods, Nt as BASE_SEPOLIA_CHAIN_ID, O as retryIdempotentRead, Oi as toAuthUserId, On as normalizeCapxulOperation, Or as toAddress, Ot as deriveCapxulSafeAddress, P as makeUnavailablePayrollMethods, Pi as isCapxulError, Pn as configurations, Pr as askIdentity, Pt as MULTI_SEND_CALL_ONLY, Qn as toOrgId, R as emitDepositInitiated, Ri as readField, Rr as sessionFromActor, Rt as toPublicError, Sn as CLI_COMMAND_NAMES, St as SAFE_ENABLE_MODULE_ABI, T as TelemetryPortTag, Tn as stampTelemetryEnvelope, Tr as toExactAssetAmount, Tt as CAPXUL_PAYMENTS_V2_ADDRESS, U as waitForPaymentSubmission, Un as roles, Ur as defectAsCapxulError, Ut as observeFailedResult, V as makeOfframpMethods, Vn as invitations, Vr as withVerifiedOrganizationScope, W as makeDestinationsMethods, Wn as setups, Wr as runIfActive, Wt as observeSdkClient, Xr as carryInvocationParentSpan, Y as subscribeFreshQuery, Yn as ORGANIZATION_SETUP_STATUSES, Yr as ActorFailure, Zn as decodeChainCause, Zr as withInvocationParentSpan, _ as accountFromAccountPositions, _i as AuthClientError, _n as toPartyId, _r as toBudgetId, _t as ROLES_EXEC_TRANSACTION_WITH_ROLE_ABI, ai as observationRequestHeaders, an as isSignerNotReadySignal, ar as images, b as MONEY_READ_RATES, bi as globalLocalStorage, bn as redactTelemetryProps, br as toRoleKey, bt as ROLES_SET_UP_ABI, c as makePaymentDocumentsMethods, ci as readInvocationObservation, cn as signerFailure, cr as isBytes32, ct as roleKeyForPermission, d as SmartAccountPortTag, di as redactUrlSecrets, dn as makeClientRequestMethods, dr as toLowerBytes32, dt as CREATE_COMMITMENT_SIGNATURE, er as hashPermissionReplacementPreview, fi as isCredentialField, fn as toCountryCode, fr as toLowerHex, ft as MODULE_PROXY_FACTORY_ABI, g as ConvexAccountLayer, gn as toHandle, gr as EpochMsSchema, gt as ROLES_ASSIGN_ROLES_ABI, h as makeAccountsMethods, hi as toPublishableKey, hr as DurationMsSchema, ht as PARAMETER_TYPE, ii as BetterAuthBrowserLayer, in as isEcdsaSignatureHex, ir as AppIdSchema, it as canonicalJson, jn as onboardings, jr as absurd, jt as safe4337MultiSendUserOpCallData, k as bindProductTelemetryInvocation, ki as toEmail, kn as OPERATIONS, kt as normalizeSafeSaltEmail, l as makeSmartAccountMethods, li as sanitizeObservationContext, lr as toExactHex, lt as ZODIAC_MODULE_PROXY_FACTORY, m as PonderIndexerAdapter, mn as toTesterKind, mt as OPERATOR, n as bootIdentityFlow, nn as causeChain, nr as CurrentPolicySchema, oi as attachInvocationObservation, on as openfortSignerNotReadyCode, or as toAccountId, ot as moneySafeOpDigest, p as makeHoldingsMethods, pi as toAllowedOrigin, pn as toKycTier, pt as MULTI_SEND_SELECTOR, qn as OrganizationSetupSchema, qr as CAPXUL_ERROR_CODES, r as makeMeMethods, ri as BetterAuthNodeLayer, rr as diagnostics, s as makeActivityMethods, si as copyInvocationObservation, sn as recoverRawDigestSigner, st as ORG_BUDGET_ACTIONS, t as executeIdentityProductObservation, ti as ClockPortTag, tn as UNOBSERVABLE_SIGNER_STATUS, tr as decodePermissionIntent$1, u as ConvexSmartAccountLayer, ui as redactSecrets, un as makeCurrentUserMethods, ur as toLowerAddress, ut as resolveOrgPermissionIdentity, v as accountPositionsFromWire, vi as AuthClientPortTag, vn as validateHandle, vr as toPermissionAssignmentId, vt as ROLES_SET_ALLOWANCE_ABI, wt as SEND_SIGNATURE, xn as CLI_CAPABILITIES, xr as ChainIdSchema, xt as ROLE_MANAGEMENT_SIGNATURES, yi as detectAuthCacheAdapter, yr as toPermissionId, yt as ROLES_SET_TRANSACTION_UNWRAPPER_ABI, z as logDroppedEvent, zr as observationOnlyControls, zt as failureDetail } from "./access-BnlgphFT.mjs";
2
2
  import { Cause, Clock, Context, Data, Deferred, Duration, Effect, Exit, FiberSet, Layer, Logger, Metric, Option, Predicate, References, Result, Schedule, Schema, SchemaIssue, SchemaParser, Scope, Stream, Tracer } from "effect";
3
3
  import { getFunctionName } from "convex/server";
4
4
  import { BaseError, decodeFunctionData, encodeAbiParameters, encodeFunctionData, formatUnits, getContractAddress, keccak256, padHex, parseAbiParameters, stringToHex, toBytes, toFunctionSelector, toHex } from "viem";
@@ -76,798 +76,6 @@ function isPostHogUrlProperty(key) {
76
76
  return normalized.includes("url") || normalized.includes("referrer") || normalized.includes("path") || normalized.includes("filename");
77
77
  }
78
78
  //#endregion
79
- //#region src/activity/documents.ts
80
- /** A reply its row does not admit is refused without the document body its decode error would carry. */
81
- function withoutDocumentBody(error) {
82
- return error.details?.step === "decodeOutput" ? Errors.invalidInput("documentHash", "invalid document read") : error;
83
- }
84
- /** Schema errors must not retain document bodies. */
85
- function decodeDocumentRead(schema, input) {
86
- return Effect.fromOption(Schema.decodeUnknownOption(schema)(input)).pipe(Effect.mapError(() => ({ publicError: Errors.invalidInput("documentHash", "invalid document read") })));
87
- }
88
- function makePaymentDocumentsMethods(deps) {
89
- /**
90
- * One document read by its row. The pair is decoded before anything is sent,
91
- * and a reply the row does not admit is refused without the body it carried.
92
- */
93
- const readDocument = (row, ref, options) => {
94
- const controls = deps.invocationControls?.(options) ?? options;
95
- return runPortEffect(Effect.gen(function* () {
96
- const pair = yield* decodeDocumentRead(PaymentDocumentReadRefSchema, ref);
97
- return yield* callOperation(deps.convexCall, row, copyInvocationObservation(controls, pair)).pipe(Effect.mapError((error) => ({ publicError: withoutDocumentBody(error) })));
98
- }), controls, row.name, deps.runPromise);
99
- };
100
- return {
101
- get: (ref, options) => readDocument(documents.get, ref, options),
102
- verify: (ref, options) => readDocument(documents.verify, ref, options),
103
- render: (ref, options) => readDocument(documents.render, ref, options)
104
- };
105
- }
106
- //#endregion
107
- //#region src/activity/stitching.ts
108
- /** Epoch milliseconds of the block that carried the row. */
109
- function blockMillis(row) {
110
- return Number(row.timestamp) * 1e3;
111
- }
112
- /** `0x1234…abcd` — the counterparty of an unclaimed transfer is an address. */
113
- function shortAddressLabel(address) {
114
- return address.length <= 12 ? address : `${address.slice(0, 6)}…${address.slice(-4)}`;
115
- }
116
- function directionFor(safe, transfer) {
117
- const from = transfer.from.toLowerCase();
118
- const to = transfer.to.toLowerCase();
119
- if (from === safe && to === safe) return "self";
120
- return to === safe ? "in" : "out";
121
- }
122
- function withAnnotation(item, annotation) {
123
- return annotation?.counterpartyLabel === void 0 ? item : {
124
- ...item,
125
- counterpartyLabel: annotation.counterpartyLabel
126
- };
127
- }
128
- /**
129
- * An indexed settlement confirms the intent. A missing row can be outside
130
- * this page or still backfilling; only the correction owner can prove rollback.
131
- */
132
- function confirmPayment(intent, chain, expected) {
133
- if (chain !== void 0 && matchesPaymentFact(chain, expected)) return {
134
- ...intent,
135
- status: "settled",
136
- ...intent.execution === void 0 ? {} : { execution: {
137
- status: "settled",
138
- retryRequired: false
139
- } },
140
- receipt: {
141
- chainId: chain.chainId,
142
- transactionHash: chain.txHash
143
- },
144
- updatedAt: blockMillis(chain)
145
- };
146
- return intent;
147
- }
148
- function matchesPaymentFact(chain, expected) {
149
- 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;
150
- }
151
- /**
152
- * One unclaimed Transfer as an Activity row, or `null` when this SDK cannot
153
- * denominate its asset. The indexer serves only configured assets, so a row
154
- * the registry does not know means the SDK and that deployment disagree; it is
155
- * dropped rather than shown with a guessed currency.
156
- */
157
- function isActivityVisible(item) {
158
- return !(item.kind === "payment" && item.direction === "in" && item.execution?.notSent);
159
- }
160
- function transferItem(safe, transfer, annotation) {
161
- const asset = configuredMoneyAssetById(transfer.assetId);
162
- if (asset === null) return null;
163
- const direction = directionFor(safe.toLowerCase(), transfer);
164
- const counterparty = direction === "in" ? transfer.from : transfer.to;
165
- const at = blockMillis(transfer);
166
- return withAnnotation({
167
- kind: "movement",
168
- id: transfer.id,
169
- classification: "movement_only_outflow",
170
- amount: {
171
- asset: asset.assetId,
172
- value: formatUnits(transfer.amount, asset.decimals)
173
- },
174
- direction,
175
- counterpartyLabel: shortAddressLabel(counterparty),
176
- receipt: {
177
- chainId: transfer.chainId,
178
- transactionHash: transfer.txHash
179
- },
180
- createdAt: at,
181
- updatedAt: at
182
- }, annotation);
183
- }
184
- /**
185
- * Money in, money out and the pending share, one bucket per exact asset.
186
- */
187
- function summarizeItems(items) {
188
- const buckets = /* @__PURE__ */ new Map();
189
- for (const item of items) {
190
- const metadata = configuredMoneyAssetById(item.amount.asset);
191
- if (metadata === null) continue;
192
- const key = item.amount.asset;
193
- const bucket = buckets.get(key) ?? {
194
- asset: item.amount.asset,
195
- in: 0n,
196
- out: 0n,
197
- pendingIn: 0n
198
- };
199
- const raw = rawUnits(item.amount.value, metadata.decimals);
200
- if (item.kind === "payment" && item.status !== "settled") {
201
- if (item.direction === "in" && item.status !== "cancelled" && item.status !== "failed" && !item.execution?.notSent) bucket.pendingIn += raw;
202
- } else if (item.direction === "in") bucket.in += raw;
203
- else if (item.direction === "out") bucket.out += raw;
204
- buckets.set(key, bucket);
205
- }
206
- return [...buckets.values()].map((bucket) => ({
207
- asset: bucket.asset,
208
- in: bucket.in.toString(),
209
- out: bucket.out.toString(),
210
- net: (bucket.in - bucket.out).toString(),
211
- pendingIn: bucket.pendingIn.toString()
212
- }));
213
- }
214
- /** The inverse of `formatUnits`, kept exact: `"1.25"` at 6 decimals is `1250000n`. */
215
- function rawUnits(value, decimals) {
216
- const negative = value.startsWith("-");
217
- const [whole = "0", fraction = ""] = (negative ? value.slice(1) : value).split(".");
218
- const raw = BigInt(`${whole}${fraction.padEnd(decimals, "0").slice(0, decimals)}`);
219
- return negative ? -raw : raw;
220
- }
221
- //#endregion
222
- //#region src/activity/cursor.ts
223
- function actorKey(actor) {
224
- return actor.kind === "account" ? `account:${actor.accountId}` : `organization:${actor.orgId}`;
225
- }
226
- function scopeKey(scope) {
227
- return `${scope.principalAccountId}|${actorKey(scope.actor)}|${scope.safe?.toLowerCase() ?? "none"}`;
228
- }
229
- function selectionKey(params) {
230
- const filter = params?.filter;
231
- return JSON.stringify([
232
- params?.range?.from ?? null,
233
- params?.range?.to ?? null,
234
- filter?.kind ?? null,
235
- filter?.direction ?? null,
236
- filter?.status === void 0 ? null : [...new Set(filter.status)].sort()
237
- ]);
238
- }
239
- function validString(value, max) {
240
- return typeof value === "string" && value.length <= max;
241
- }
242
- function validPosition(position) {
243
- return position === null || typeof position === "object" && "timestamp" in position && typeof position.timestamp === "number" && Number.isSafeInteger(position.timestamp) && position.timestamp >= 0 && "id" in position && typeof position.id === "string" && /^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(position.id);
244
- }
245
- function isActivityCursor(cursor, scope, selection) {
246
- return typeof cursor === "object" && cursor !== null && cursorScopeMatches(cursor, scope, selection) && cursorProgressIsValid(cursor);
247
- }
248
- /** The cursor was issued for this scope and filter selection, at a valid checkpoint. */
249
- function cursorScopeMatches(cursor, scope, selection) {
250
- return "version" in cursor && cursor.version === 1 && "scope" in cursor && cursor.scope === scope && "selection" in cursor && cursor.selection === selection && "checkpoint" in cursor && typeof cursor.checkpoint === "number" && Number.isSafeInteger(cursor.checkpoint) && cursor.checkpoint >= 0 && "source" in cursor && (cursor.source === "indexed" || cursor.source === "fallback");
251
- }
252
- /** Each source's progress is well-formed and bounded. */
253
- function cursorProgressIsValid(cursor) {
254
- return "paymentCursor" in cursor && (cursor.paymentCursor === null || validString(cursor.paymentCursor, 2048)) && "paymentDone" in cursor && typeof cursor.paymentDone === "boolean" && "transferDone" in cursor && typeof cursor.transferDone === "boolean" && "degraded" in cursor && typeof cursor.degraded === "boolean" && "paymentPending" in cursor && Array.isArray(cursor.paymentPending) && cursor.paymentPending.length <= 100 && new Set(cursor.paymentPending).size === cursor.paymentPending.length && cursor.paymentPending.every((id) => validString(id, 256)) && "transferBefore" in cursor && validPosition(cursor.transferBefore);
255
- }
256
- function parseCursor(value, scope, selection) {
257
- if (value === void 0) return null;
258
- if (value.length > 4e4) throw Errors.invalidInput("cursor", "invalid Activity cursor");
259
- const cursor = JSON.parse(value);
260
- if (!isActivityCursor(cursor, scope, selection)) throw Errors.invalidInput("cursor", "Activity scope or filters changed; restart from the first page");
261
- return cursor;
262
- }
263
- function requireParams(params) {
264
- const limit = params?.limit ?? 25;
265
- if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw Errors.invalidInput("limit", "must be an integer from 1 to 100");
266
- 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");
267
- const f = params?.filter;
268
- if (f?.kind !== void 0 && f.kind !== "payment" && f.kind !== "movement" || f?.direction !== void 0 && ![
269
- "in",
270
- "out",
271
- "self"
272
- ].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");
273
- }
274
- function matchesFilter(item, filter) {
275
- 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));
276
- }
277
- //#endregion
278
- //#region src/activity/indexed-pages.ts
279
- function chainRead(deps, table, input) {
280
- return deps.indexer.read(table, input).pipe(Effect.catch((error) => {
281
- if (error.publicError.details?.reason === "scope-refused" || ![
282
- "NETWORK_ERROR",
283
- "RATE_LIMITED",
284
- "PROVIDER_ERROR",
285
- "CAPABILITY_UNAVAILABLE",
286
- "TIMEOUT"
287
- ].includes(error.publicCode)) return Effect.fail({ publicError: error.publicError });
288
- return Effect.succeed(null);
289
- }));
290
- }
291
- function boundedRange(range, checkpoint) {
292
- return {
293
- ...range?.from === void 0 ? {} : { from: range.from },
294
- to: Math.min(checkpoint, range?.to ?? checkpoint)
295
- };
296
- }
297
- function unavailablePage(deps, scope, controls, params, state, first) {
298
- if (first) return fallbackPage(deps, scope, controls, params, state, true);
299
- return Effect.annotateCurrentSpan({
300
- read_source: "indexer",
301
- read_warning: "indexer-unavailable"
302
- }).pipe(Effect.as({
303
- items: [],
304
- nextCursor: null,
305
- checkpoint: state.checkpoint,
306
- observedAt: Date.now(),
307
- readWarning: "indexer-unavailable"
308
- }));
309
- }
310
- function readTransfers(deps, safe, range, state, wanted, first) {
311
- const from = Math.max(0, Math.ceil((range.from ?? 0) / 1e3));
312
- const to = Math.floor(range.to / 1e3);
313
- if (state.transferDone || from > to || !wanted && !first) return Effect.succeed([]);
314
- return chainRead(deps, "transfer", {
315
- safe: toAddress(safe),
316
- limit: wanted ? 100 : 1,
317
- timeline: {
318
- from,
319
- to,
320
- ...state.transferBefore === null ? {} : { before: state.transferBefore }
321
- }
322
- });
323
- }
324
- function readPayments(deps, scope, controls, range, state, wanted) {
325
- const done = state.paymentDone || !wanted;
326
- if (state.paymentPending.length > 0 || done) return Effect.succeed({
327
- items: [],
328
- pending: state.paymentPending,
329
- cursor: state.paymentCursor,
330
- done
331
- });
332
- return convexPage(deps, scope.actor, controls, {
333
- limit: 100,
334
- range,
335
- filter: { kind: "payment" },
336
- ...state.paymentCursor === null ? {} : { cursor: state.paymentCursor }
337
- }).pipe(Effect.map((page) => ({
338
- items: page.items,
339
- pending: page.items.map((item) => item.id),
340
- cursor: page.nextCursor,
341
- done: page.nextCursor === null
342
- })));
343
- }
344
- function readPaymentFacts(deps, safe, settlements) {
345
- const offsets = Array.from({ length: Math.ceil(settlements.length / 50) }, (_, i) => i * 50);
346
- return Effect.forEach(offsets, (offset) => chainRead(deps, "payment", {
347
- safe: toAddress(safe),
348
- limit: 50,
349
- settlements: settlements.slice(offset, offset + 50)
350
- }), { concurrency: 2 }).pipe(Effect.map((pages) => pages.some((page) => page === null) ? null : pages.flatMap((page) => page ?? [])));
351
- }
352
- function indexedPage(deps, scope, controls, params, state, first) {
353
- return Effect.gen(function* () {
354
- const range = boundedRange(params?.range, state.checkpoint);
355
- const wantsTransfers = params?.filter?.kind !== "payment" && params?.filter?.status === void 0;
356
- const found = yield* readTransfers(deps, scope.safe, range, state, wantsTransfers, first);
357
- if (found === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
358
- const transfers = wantsTransfers ? found : [];
359
- const page = yield* readPayments(deps, scope, controls, range, state, params?.filter?.kind !== "movement");
360
- const decoration = yield* decoratePage(deps, scope, controls, transfers, page, range);
361
- if (decoration === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
362
- const { items, next } = mergePage(scope.safe, params?.filter, params?.limit ?? 25, state, page, decoration, transfers, wantsTransfers);
363
- const cursor = next.paymentDone && next.paymentPending.length === 0 && next.transferDone ? null : JSON.stringify(next);
364
- if (cursor !== null && cursor === params?.cursor) return yield* Effect.fail({ publicError: Errors.invalidInput("cursor", "Activity source did not advance") });
365
- yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
366
- return {
367
- items,
368
- nextCursor: cursor,
369
- checkpoint: state.checkpoint,
370
- observedAt: Date.now()
371
- };
372
- });
373
- }
374
- //#endregion
375
- //#region src/activity/page-overlay.ts
376
- const MAX_OVERLAY_IDS = 200;
377
- function overlay(deps, actor, controls, input) {
378
- const count = Math.max(input.references?.length ?? 0, input.settlementIds?.length ?? 0);
379
- const offsets = Array.from({ length: Math.max(1, Math.ceil(count / MAX_OVERLAY_IDS)) }, (_, i) => i * MAX_OVERLAY_IDS);
380
- return Effect.forEach(offsets, (offset) => callOperation(deps.convexCall, overlays.get, copyInvocationObservation(controls, {
381
- ...actor === void 0 ? {} : { actor },
382
- ...input.references === void 0 ? {} : { references: input.references.slice(offset, offset + MAX_OVERLAY_IDS) },
383
- ...input.settlementIds === void 0 ? {} : { settlementIds: input.settlementIds.slice(offset, offset + MAX_OVERLAY_IDS) }
384
- })).pipe(Effect.mapError((publicError) => ({ publicError }))), { concurrency: 2 }).pipe(Effect.flatMap((pages) => {
385
- const first = pages[0];
386
- if (pages.some((page) => scopeKey(page) !== scopeKey(first))) return Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during the read") });
387
- return Effect.succeed({
388
- ...first,
389
- links: pages.flatMap((page) => page.links),
390
- annotations: pages.flatMap((page) => page.annotations),
391
- payments: pages.flatMap((page) => page.payments)
392
- });
393
- }));
394
- }
395
- function decoratePage(deps, scope, controls, transfers, page, range) {
396
- return Effect.gen(function* () {
397
- const decoration = yield* overlay(deps, scope.actor, controls, {
398
- references: [...page.pending.map((id) => ({
399
- kind: "payment",
400
- id
401
- })), ...transfers.map((row) => ({
402
- kind: "movement",
403
- id: row.id
404
- }))],
405
- settlementIds: [...new Set(transfers.flatMap((row) => row.paymentSettlementId ? [row.paymentSettlementId] : []))]
406
- });
407
- if (scopeKey(decoration) !== scopeKey(scope)) return yield* Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during the read") });
408
- const byId = new Map([...page.items, ...decoration.payments].map((item) => [item.id, item]));
409
- const settlements = [...new Set(decoration.links.map((link) => link.settlementId))];
410
- const facts = yield* readPaymentFacts(deps, scope.safe, settlements);
411
- if (facts === null) return null;
412
- const bySettlement = new Map(facts.map((row) => [row.settlementId, row]));
413
- const byPayment = new Map(decoration.links.map((link) => [link.paymentId, {
414
- fact: bySettlement.get(link.settlementId),
415
- expected: link.expected
416
- }]));
417
- const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
418
- return {
419
- payments: page.pending.flatMap((id) => {
420
- const item = byId.get(id);
421
- if (item === void 0 || item.kind !== "payment" || item.createdAt > range.to || range.from !== void 0 && item.createdAt < range.from) return [];
422
- const confirmed = withAnnotation(confirmPayment(item, byPayment.get(id)?.fact, byPayment.get(id)?.expected), annotations.get(`payment:${id}`));
423
- return isActivityVisible(confirmed) ? [confirmed] : [];
424
- }),
425
- annotations,
426
- linked: new Set(decoration.links.filter((link) => matchesPaymentFact(bySettlement.get(link.settlementId), link.expected)).map((link) => link.settlementId))
427
- };
428
- });
429
- }
430
- function mergePage(safe, filter, limit, state, page, decoration, transfers, wantsTransfers) {
431
- const { payments, annotations, linked } = decoration;
432
- let pi = 0;
433
- let ti = 0;
434
- const items = [];
435
- const transferEnd = !wantsTransfers || transfers.length < 100;
436
- let transferBefore = state.transferBefore;
437
- while (items.length < limit) {
438
- if (pi === payments.length && !page.done || ti === transfers.length && !transferEnd) break;
439
- const payment = payments[pi];
440
- const row = transfers[ti];
441
- if (payment === void 0 && row === void 0) break;
442
- if (payment !== void 0 && (row === void 0 || payment.createdAt >= Number(row.timestamp) * 1e3)) {
443
- pi += 1;
444
- if (matchesFilter(payment, filter)) items.push(payment);
445
- } else if (row !== void 0) {
446
- ti += 1;
447
- transferBefore = {
448
- timestamp: Number(row.timestamp),
449
- id: row.id
450
- };
451
- if (row.paymentSettlementId && linked.has(row.paymentSettlementId)) continue;
452
- const item = transferItem(safe, row, annotations.get(`movement:${row.id}`));
453
- if (item !== null && matchesFilter(item, filter)) items.push(item);
454
- }
455
- }
456
- return {
457
- items,
458
- next: {
459
- ...state,
460
- paymentCursor: page.cursor,
461
- paymentDone: page.done,
462
- paymentPending: payments.slice(pi).map((item) => item.id),
463
- transferBefore,
464
- transferDone: ti === transfers.length && transferEnd
465
- }
466
- };
467
- }
468
- function convexPage(deps, actor, controls, input) {
469
- return callOperation(deps.convexCall, items.list, copyInvocationObservation(controls, {
470
- ...input,
471
- actor
472
- })).pipe(Effect.mapError((publicError) => ({ publicError })));
473
- }
474
- function fallbackPage(deps, scope, controls, params, state, degraded) {
475
- return Effect.gen(function* () {
476
- const page = yield* convexPage(deps, scope.actor, controls, {
477
- limit: params?.limit ?? 25,
478
- ...state.paymentCursor === null ? {} : { cursor: state.paymentCursor },
479
- range: {
480
- ...params?.range?.from === void 0 ? {} : { from: params.range.from },
481
- to: Math.min(state.checkpoint, params?.range?.to ?? state.checkpoint)
482
- },
483
- ...params?.filter === void 0 ? {} : { filter: params.filter }
484
- });
485
- const decoration = yield* overlay(deps, scope.actor, controls, { references: page.items.map((item) => ({
486
- kind: item.kind,
487
- id: item.id
488
- })) });
489
- if (scopeKey(decoration) !== scopeKey(scope)) return yield* Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during fallback") });
490
- const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
491
- yield* Effect.annotateCurrentSpan({
492
- read_source: "convex",
493
- ...degraded ? { read_warning: "indexer-unavailable" } : {}
494
- });
495
- return {
496
- ...page,
497
- items: page.items.filter(isActivityVisible).map((item) => withAnnotation(item, annotations.get(`${item.kind}:${item.id}`))),
498
- checkpoint: state.checkpoint,
499
- nextCursor: page.nextCursor === null ? null : JSON.stringify({
500
- ...state,
501
- source: "fallback",
502
- paymentCursor: page.nextCursor,
503
- degraded
504
- }),
505
- ...degraded ? { readWarning: "indexer-unavailable" } : {}
506
- };
507
- });
508
- }
509
- function readActivityPage(deps, actor, controls, params) {
510
- return Effect.gen(function* () {
511
- yield* Effect.try({
512
- try: () => requireParams(params),
513
- catch: (cause) => ({ publicError: asCapxulError(cause) })
514
- });
515
- const scope = yield* overlay(deps, actor, controls, {});
516
- const selection = selectionKey(params);
517
- const cursor = yield* Effect.try({
518
- try: () => parseCursor(params?.cursor, scopeKey(scope), selection),
519
- catch: () => ({ publicError: Errors.invalidInput("cursor", "invalid or obsolete Activity cursor; restart the read") })
520
- });
521
- const state = cursor ?? {
522
- version: 1,
523
- scope: scopeKey(scope),
524
- selection,
525
- checkpoint: Date.now(),
526
- source: "indexed",
527
- paymentCursor: null,
528
- paymentDone: false,
529
- paymentPending: [],
530
- transferBefore: null,
531
- transferDone: false,
532
- degraded: false
533
- };
534
- const range = boundedRange(params?.range, state.checkpoint);
535
- if (range.from !== void 0 && range.from > range.to) return {
536
- items: [],
537
- nextCursor: null,
538
- checkpoint: state.checkpoint,
539
- observedAt: Date.now()
540
- };
541
- if (state.source === "fallback" || scope.safe === null) return yield* fallbackPage(deps, scope, controls, params, state, state.degraded);
542
- return yield* indexedPage(deps, scope, controls, params, state, cursor === null);
543
- });
544
- }
545
- function readActivitySummary(deps, actor, controls, params) {
546
- return Effect.gen(function* () {
547
- const totals = /* @__PURE__ */ new Map();
548
- let cursor;
549
- let count = 0;
550
- let readWarning;
551
- do {
552
- const page = yield* readActivityPage(deps, actor, controls, {
553
- limit: 100,
554
- ...cursor === void 0 ? {} : { cursor },
555
- ...params?.window === void 0 ? {} : { range: params.window }
556
- });
557
- count += page.items.length;
558
- readWarning ??= page.readWarning;
559
- for (const total of summarizeItems(page.items)) {
560
- const key = total.asset;
561
- const old = totals.get(key);
562
- totals.set(key, old === void 0 ? total : {
563
- ...total,
564
- in: String(BigInt(old.in) + BigInt(total.in)),
565
- out: String(BigInt(old.out) + BigInt(total.out)),
566
- net: String(BigInt(old.net) + BigInt(total.net)),
567
- pendingIn: String(BigInt(old.pendingIn) + BigInt(total.pendingIn))
568
- });
569
- }
570
- cursor = page.nextCursor ?? void 0;
571
- } while (cursor !== void 0);
572
- return {
573
- window: {
574
- from: params?.window?.from ?? null,
575
- to: params?.window?.to ?? null
576
- },
577
- totals: [...totals.values()],
578
- count,
579
- observedAt: Date.now(),
580
- ...readWarning === void 0 ? {} : { readWarning }
581
- };
582
- });
583
- }
584
- function subscribeActivity(deps, actor, controls, onChange) {
585
- return Effect.gen(function* () {
586
- const scope = yield* overlay(deps, actor, controls, {});
587
- if (scope.safe === null) return () => {};
588
- let delivered = false;
589
- return yield* deps.indexer.subscribe("transfer", {
590
- safe: toAddress(scope.safe),
591
- limit: 100
592
- }, (snapshot) => {
593
- if (snapshot.status === "ok") {
594
- if (delivered) onChange();
595
- delivered = true;
596
- }
597
- if (snapshot.status === "error") {
598
- delivered = true;
599
- onChange();
600
- }
601
- });
602
- });
603
- }
604
- //#endregion
605
- //#region src/activity/movement-detail.ts
606
- /** An indexed Movement has no Convex materialization. Read its exact native row. */
607
- function readIndexedMovement(deps, actor, controls, eventId) {
608
- return Effect.gen(function* () {
609
- const scope = yield* overlay(deps, actor, controls, { references: [{
610
- kind: "movement",
611
- id: eventId
612
- }] });
613
- if (scope.safe === null) return null;
614
- const safe = scope.safe.toLowerCase();
615
- const row = (yield* deps.indexer.read("transfer", {
616
- safe: toAddress(safe),
617
- limit: 1,
618
- eventId
619
- }).pipe(Effect.mapError((error) => ({ publicError: error.publicError }))))[0];
620
- if (row === void 0) return null;
621
- const blockNumber = Number(row.blockNumber);
622
- 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") });
623
- yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
624
- const documentSet = scope.movementDocuments.find((set) => set.movementId === row.id);
625
- return {
626
- kind: "movement",
627
- movementId: row.id,
628
- documents: documentSet?.documents ?? [],
629
- documentState: documentSet?.state ?? "pending",
630
- viewer: scope.actor,
631
- annotation: scope.annotations.find((annotation) => annotation.reference.kind === "movement" && annotation.reference.id === row.id) ?? null,
632
- evidence: {
633
- chainId: row.chainId,
634
- contractAddress: row.token,
635
- tokenAddress: row.token,
636
- transactionHash: row.txHash,
637
- blockNumber,
638
- blockHash: row.blockHash,
639
- inclusion: "included",
640
- verification: "verified",
641
- canonicalState: "finalized",
642
- transferLogIndex: row.logIndex,
643
- source: row.from,
644
- destination: row.to,
645
- rawAmount: row.amount.toString(),
646
- link: {
647
- movementId: row.id,
648
- depositPaymentId: null
649
- }
650
- }
651
- };
652
- });
653
- }
654
- //#endregion
655
- //#region src/activity/scope.ts
656
- /**
657
- * The activity backend gives its actor a different name than the other
658
- * financialOps functions: `{kind: "organization", orgId}`, and not
659
- * `{kind: "org", orgId}`. Thus it does not use `actorReferenceToBackend`.
660
- *
661
- * The `personal` case sends NO actor. This is correct, and it does not lose
662
- * data. `requireActivityActor` uses the signed-in Account when the actor is
663
- * absent, and `canReadActor` refuses all other account identifiers
664
- * (`packages/backend/convex/movement/activityAuth.ts`). Thus "personal" and
665
- * "absent" both name the one Account that an SDK caller can read. The SDK does
666
- * not hold an account identifier to send.
667
- *
668
- * The `switch` is exhaustive. A new `ActorReference` variant is a compile error
669
- * here. The conditional expression that this function replaced removed each
670
- * value that was not an organization.
671
- */
672
- function activityActorField(actor) {
673
- if (actor === void 0 || actor.kind === "personal") return {};
674
- return { actor: {
675
- kind: "organization",
676
- orgId: actor.organizationId
677
- } };
678
- }
679
- /** Capture scope now; let the observed result boundary handle a malformed caller value. */
680
- function captureActivityInput(prepare) {
681
- try {
682
- return prepare();
683
- } catch (cause) {
684
- return Effect.die(cause);
685
- }
686
- }
687
- function observeActivityScope(actor) {
688
- const organizationId = actor?.kind === "organization" || actor?.kind === "org" ? safeEngineeringIdentifier(actor.orgId) : void 0;
689
- return Effect.annotateCurrentSpan({
690
- scope_kind: actor?.kind === "organization" || actor?.kind === "org" ? "organization" : "account",
691
- ...organizationId === void 0 ? {} : { verified_organization_id: organizationId }
692
- });
693
- }
694
- function decodeActivityViewer(value) {
695
- const viewer = Option.getOrUndefined(Schema.decodeUnknownOption(ActorRefSchema)(value));
696
- if (viewer === void 0) return void 0;
697
- const id = viewer.kind === "organization" ? viewer.orgId : viewer.accountId;
698
- return safeEngineeringIdentifier(id) === void 0 ? void 0 : viewer;
699
- }
700
- /** The detail without the scope it was read for, which the SDK reports as telemetry instead. */
701
- function normalizeActivityDetail(response) {
702
- if (response === null) return null;
703
- const { viewer: _viewer, ...detail } = response;
704
- return detail;
705
- }
706
- //#endregion
707
- //#region src/activity/methods.ts
708
- function makeActivityMethods(deps) {
709
- const indexerDeps = deps.indexerRead === void 0 ? void 0 : {
710
- indexer: deps.indexerRead,
711
- convexCall: deps.convexCall
712
- };
713
- /** One public call for the caller's chosen actor, read once at call start. */
714
- const forActor = (actorRef, options, operation, program) => {
715
- const controls = deps.invocationControls?.(options) ?? options;
716
- return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(actorRef).actor)).pipe(Effect.flatMap((actor) => program(actor, controls))), controls, operation, deps.runPromise);
717
- };
718
- return {
719
- subscribe: (onChange, params, options) => forActor(params?.actor, options, CAPXUL_OPERATIONS.activity.subscribe, (actor, controls) => indexerDeps === void 0 ? Effect.succeed(() => {}) : Effect.suspend(() => subscribeActivity(indexerDeps, actor, controls, onChange)).pipe(Effect.tap(() => observeActivityScope(actor)))),
720
- list: (params, options) => forActor(params?.actor, options, items.list.name, (actor, controls) => Effect.suspend(() => indexerDeps === void 0 ? callOperation(deps.convexCall, items.list, copyInvocationObservation(controls, {
721
- ...actor === void 0 ? {} : { actor },
722
- ...params?.cursor === void 0 ? {} : { cursor: params.cursor },
723
- ...params?.limit === void 0 ? {} : { limit: params.limit },
724
- ...params?.range === void 0 ? {} : { range: params.range },
725
- ...params?.filter === void 0 ? {} : { filter: params.filter }
726
- })).pipe(Effect.mapError((publicError) => ({ publicError }))) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.map((page) => ({
727
- ...page,
728
- items: page.items.filter(isActivityVisible)
729
- })), Effect.tap(() => observeActivityScope(actor)))),
730
- summary: (params, options) => forActor(params?.actor, options, items.summary.name, (actor, controls) => Effect.suspend(() => indexerDeps === void 0 ? callOperation(deps.convexCall, items.summary, copyInvocationObservation(controls, {
731
- ...actor === void 0 ? {} : { actor },
732
- ...params?.window === void 0 ? {} : { window: params.window }
733
- })).pipe(Effect.mapError((publicError) => ({ publicError }))) : readActivitySummary(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor)))),
734
- get: (reference, options) => {
735
- const controls = deps.invocationControls?.(options) ?? options;
736
- const requestedActor = actorReferenceToBackend(options?.actor);
737
- const capturedScope = captureActivityInput(() => {
738
- const scope = requestedActor === void 0 ? void 0 : requestedActor.kind === "org" ? {
739
- kind: "org",
740
- orgId: requestedActor.orgId
741
- } : { kind: "account" };
742
- return Effect.succeed({
743
- actor: scope,
744
- referenceActor: reference.kind === "payment" ? activityActorField(reference.actor).actor : void 0
745
- });
746
- });
747
- return runPortEffect(capturedScope.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" ? {
748
- kind: "organization",
749
- orgId: actor.orgId
750
- } : void 0, controls, reference.id) : callOperation(deps.convexCall, items.get, copyInvocationObservation(controls, {
751
- reference: {
752
- kind: reference.kind,
753
- id: reference.id,
754
- ...referenceActor === void 0 ? {} : { actor: referenceActor }
755
- },
756
- ...actor === void 0 ? {} : { actor }
757
- })).pipe(Effect.mapError((publicError) => ({ publicError })))).pipe(Effect.flatMap((response) => {
758
- const detail = normalizeActivityDetail(response);
759
- if (response === null) return Effect.succeed(detail);
760
- const viewer = response.viewer === void 0 ? actor ?? referenceActor : decodeActivityViewer(response.viewer);
761
- return viewer === void 0 ? Effect.succeed(detail) : observeActivityScope(viewer).pipe(Effect.as(detail));
762
- })))), controls, items.get.name, deps.runPromise);
763
- },
764
- annotate: (input, options) => {
765
- const controls = deps.invocationControls?.(options) ?? options;
766
- const capturedScope = captureActivityInput(() => {
767
- const actor = activityActorField(input.actor).actor;
768
- if (input.actor !== void 0 && input.reference.kind === "payment" && input.reference.actor !== void 0 && !sameActor(input.actor, input.reference.actor)) return Effect.fail({ publicError: Errors.invalidInput("reference.actor", "must match the annotation actor") });
769
- return Effect.succeed({
770
- actor,
771
- referenceActor: input.reference.kind === "payment" ? activityActorField(input.reference.actor).actor : void 0
772
- });
773
- });
774
- return runPortEffect(capturedScope.pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() => callOperation(deps.convexCall, annotations.save, copyInvocationObservation(controls, {
775
- ...actor === void 0 ? {} : { actor },
776
- reference: input.reference.kind === "payment" && referenceActor !== void 0 ? {
777
- kind: "payment",
778
- id: input.reference.id,
779
- actor: referenceActor
780
- } : {
781
- kind: input.reference.kind,
782
- id: input.reference.id
783
- },
784
- ...input.counterpartyLabel === void 0 ? {} : { counterpartyLabel: input.counterpartyLabel },
785
- ...input.accountingCategory === void 0 ? {} : { accountingCategory: input.accountingCategory },
786
- ...input.memo === void 0 ? {} : { memo: input.memo }
787
- })).pipe(Effect.mapError((publicError) => ({ publicError })))).pipe(Effect.tap((annotation) => {
788
- const scope = decodeActivityViewer(annotation.actor);
789
- return scope === void 0 ? Effect.void : observeActivityScope(scope);
790
- })))), controls, annotations.save.name, deps.runPromise);
791
- }
792
- };
793
- }
794
- //#endregion
795
- //#region src/activity/phases.ts
796
- const PENDING = Object.freeze({
797
- terminal: false,
798
- outcome: null,
799
- needsAction: false
800
- });
801
- const AWAITING_ACTOR = Object.freeze({
802
- terminal: false,
803
- outcome: null,
804
- needsAction: true
805
- });
806
- const COMPLETED = Object.freeze({
807
- terminal: true,
808
- outcome: "good",
809
- needsAction: false
810
- });
811
- const FAILED = Object.freeze({
812
- terminal: true,
813
- outcome: "bad",
814
- needsAction: false
815
- });
816
- const WITHDRAWN = Object.freeze({
817
- terminal: true,
818
- outcome: "neutral",
819
- needsAction: false
820
- });
821
- /** Interpret a Payment status. `pending_claim` waits for the actor who reads it. */
822
- function paymentPhase(status) {
823
- switch (status) {
824
- case "pending":
825
- case "settling":
826
- case "scheduled":
827
- case "streaming": return PENDING;
828
- case "pending_claim": return AWAITING_ACTOR;
829
- case "settled": return COMPLETED;
830
- case "cancelled": return WITHDRAWN;
831
- case "failed":
832
- case "unresolved": return FAILED;
833
- default: return absurd(status);
834
- }
835
- }
836
- /** Interpret a payment Request as its ISSUER sees it. An issuer does not wait for itself. */
837
- function requestPhase(status) {
838
- switch (status) {
839
- case "draft":
840
- case "sent":
841
- case "viewed":
842
- case "pending_settlement": return PENDING;
843
- case "paid": return COMPLETED;
844
- case "declined":
845
- case "expired": return FAILED;
846
- case "cancelled": return WITHDRAWN;
847
- default: return absurd(status);
848
- }
849
- }
850
- /** Interpret the same request as its RECIPIENT sees it. `open` waits for the recipient. */
851
- function inboxPhase(status) {
852
- switch (status) {
853
- case "open": return AWAITING_ACTOR;
854
- case "approved": return PENDING;
855
- case "paid": return COMPLETED;
856
- case "declined":
857
- case "expired": return FAILED;
858
- case "cancelled": return WITHDRAWN;
859
- default: return absurd(status);
860
- }
861
- }
862
- //#endregion
863
- //#region ../protocol/src/payroll/model.ts
864
- function toPayrollRunId(raw) {
865
- return toNonEmptyStringBrand(raw, "payrollRunId", (value) => isNonEmptyString(value));
866
- }
867
- function toPayrollGroupId(raw) {
868
- return toNonEmptyStringBrand(raw, "payrollGroupId", (value) => isNonEmptyString(value));
869
- }
870
- //#endregion
871
79
  //#region src/permissions/command-decoding.ts
872
80
  /** The caller's input as a command: ids branded and the intent decoded, or invalid input thrown. */
873
81
  const buildCommand = {
@@ -12016,411 +11224,6 @@ function makePermissionMethods(deps, orgId) {
12016
11224
  };
12017
11225
  }
12018
11226
  //#endregion
12019
- //#region src/payroll/run-checks.ts
12020
- /** Match the caller's compensation to the committed V3 Payslip. */
12021
- function payslipIdentityMatches(message, item, index, runId, period) {
12022
- return message.runId === runId && message.itemIndex === index && message.employeeRef === item.partyId && message.periodStart === period.start && message.periodEnd === period.end && message.adjustmentsHash === hashAdjustmentsV3(item.adjustments);
12023
- }
12024
- function payslipAmountMatches(message, item) {
12025
- const exact = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
12026
- return message.gross.asset === exact.asset && message.gross.minorUnits === item.gross && message.gross.decimals === exact.decimals && message.net.asset === exact.asset && message.net.minorUnits === item.net && message.net.decimals === exact.decimals;
12027
- }
12028
- function isRunItem(prepared, item, evidence, index, runId, period) {
12029
- if (prepared === void 0) return false;
12030
- const bundle = evidence.preparedCommand?.documentBundles?.[index];
12031
- const preparedItem = evidence.preparedCommand?.items[index];
12032
- const payslip = bundle?.envelope;
12033
- if (bundle === void 0 || payslip?.primaryType !== "Payslip") return false;
12034
- return canonicalJson(prepared.to) === canonicalJson(item.to) && prepared.paymentType === "payroll" && canonicalJson(prepared.amount) === canonicalJson(item.amount) && bundle.adjustments !== void 0 && canonicalJson(bundle.adjustments) === canonicalJson(item.adjustments) && payslipIdentityMatches(payslip.message, item, index, runId, period) && payslipAmountMatches(payslip.message, item) && (preparedItem?.operation === "send" || preparedItem?.operation === "create_commitment") && preparedItem.documentHash === evidence.preparedCommand?.instructionDocuments?.[index]?.documentHash;
12035
- }
12036
- /**
12037
- * The batch the backend derived from a payroll intent must be the batch this
12038
- * caller asked for, checked before the digest is signed.
12039
- *
12040
- * The payslip envelope on each item is server-built and carries the run row's
12041
- * own `issuedAt`, so the batch cannot be fingerprinted against a local copy
12042
- * (`matchesIntent` is unavailable here). What CAN be verified is every field
12043
- * the caller supplied — the org, the Budget, the request key, and per item the
12044
- * recipient, the amount and the payslip figures — plus the lineage binding the
12045
- * batch to the run just written. A prepared command that pays a different
12046
- * person, a different amount, from a different Budget, or under a different
12047
- * run never reaches the signer.
12048
- */
12049
- function isRunBatch(input) {
12050
- return (batch, evidence) => Promise.resolve(!("kind" in batch) && batch.orgId === input.orgId && batch.permissionId === input.permissionId && batch.requestKey === input.requestKey && batch.lineage?.kind === "payroll" && batch.lineage.runId === input.runId && batch.items.length === input.items.length && batch.items.every((item) => item.amount.asset === input.asset) && input.items.every((item, index) => isRunItem(batch.items[index], item, evidence, index, input.runId, input.period)));
12051
- }
12052
- /** A decoded detail is the run asked for, whole: its scope, membership and quantities agree. */
12053
- function checkPayrollRunDetail(value, orgId, runId) {
12054
- if (value === null) return {
12055
- ok: true,
12056
- value: null
12057
- };
12058
- if (value.orgId !== orgId || value.run.id !== runId || value.items.length === 0 || value.items.length !== value.run.recipientCount || new Set(value.items.map((item) => item.payment.id)).size !== value.items.length || value.items.some((item, index) => item.itemIndex !== index || item.amount.asset !== value.run.total.asset || item.payment.amount.asset !== item.amount.asset || item.payment.paymentType !== "payroll")) return {
12059
- ok: false,
12060
- error: Errors.invalidInput("run", "Payroll run scope or membership does not match")
12061
- };
12062
- try {
12063
- const total = toExactAssetAmount(value.run.total, CONFIGURED_MONEY_ASSETS);
12064
- let rawTotal = 0n;
12065
- for (const item of value.items) {
12066
- const amount = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
12067
- const payment = toExactAssetAmount(item.payment.amount, CONFIGURED_MONEY_ASSETS);
12068
- if (amount.minorUnits !== payment.minorUnits) return {
12069
- ok: false,
12070
- error: Errors.invalidInput("run", "Payroll item quantity does not match its Payment")
12071
- };
12072
- rawTotal += BigInt(amount.minorUnits);
12073
- }
12074
- if (rawTotal !== BigInt(total.minorUnits)) return {
12075
- ok: false,
12076
- error: Errors.invalidInput("run", "Payroll run total does not match its items")
12077
- };
12078
- } catch {
12079
- return {
12080
- ok: false,
12081
- error: Errors.invalidInput("run", "Payroll run quantities are invalid")
12082
- };
12083
- }
12084
- return {
12085
- ok: true,
12086
- value
12087
- };
12088
- }
12089
- //#endregion
12090
- //#region src/payroll/run-decoding.ts
12091
- function toPayrollRun(wire) {
12092
- return {
12093
- id: toPayrollRunId(wire.id),
12094
- status: wire.status,
12095
- periodStart: wire.periodStart,
12096
- periodEnd: wire.periodEnd,
12097
- total: wire.total,
12098
- recipientCount: wire.recipientCount
12099
- };
12100
- }
12101
- function toPayrollRuns(wire) {
12102
- return {
12103
- ...mapPage(wire, toPayrollRun),
12104
- settledThisMonth: wire.settledThisMonth
12105
- };
12106
- }
12107
- function toPayrollGroup(wire) {
12108
- return {
12109
- id: toPayrollGroupId(wire.id),
12110
- name: wire.name,
12111
- tone: wire.tone,
12112
- members: wire.members.map((member) => ({
12113
- partyId: member.partyId,
12114
- amount: member.amount,
12115
- currency: member.currency
12116
- }))
12117
- };
12118
- }
12119
- function validateRunItems(input) {
12120
- for (const [index, item] of input.items.entries()) {
12121
- if (Schema.decodeResult(PayrollRunItemInputSchema, { onExcessProperty: "error" })(item)._tag === "Failure") return Errors.invalidInput(`items.${index}`, "invalid Payroll item");
12122
- try {
12123
- const exact = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
12124
- const net = BigInt(item.net);
12125
- const gross = BigInt(item.gross);
12126
- const adjustments = item.adjustments.reduce((sum, row) => sum + BigInt(row.amount), 0n);
12127
- if (item.amount.asset !== input.asset || item.net !== exact.minorUnits || gross + adjustments !== net) return Errors.invalidInput(`items.${index}`, "invalid Payroll units");
12128
- } catch {
12129
- return Errors.invalidInput(`items.${index}`, "invalid Payroll units");
12130
- }
12131
- }
12132
- return null;
12133
- }
12134
- const RunInputSchema = Schema.Struct({
12135
- permissionId: AuthorizePayrollRunExecutionInputSchema.fields.permissionId,
12136
- asset: AuthorizePayrollRunExecutionInputSchema.fields.asset,
12137
- period: AuthorizePayrollRunExecutionInputSchema.fields.period,
12138
- items: AuthorizePayrollRunExecutionInputSchema.fields.items,
12139
- requestKey: Schema.optional(AuthorizePayrollRunExecutionInputSchema.fields.requestKey)
12140
- });
12141
- /** Decode caller input before session or signer work. Keep routing outside the input. */
12142
- function decodePayrollRunInput(value) {
12143
- try {
12144
- const decoded = Schema.decodeUnknownSync(RunInputSchema, { onExcessProperty: "error" })(value);
12145
- const input = {
12146
- permissionId: decoded.permissionId,
12147
- asset: decoded.asset,
12148
- period: decoded.period,
12149
- ...decoded.requestKey === void 0 ? {} : { requestKey: decoded.requestKey },
12150
- items: decoded.items.map((item) => ({
12151
- amount: item.amount,
12152
- gross: item.gross,
12153
- net: item.net,
12154
- adjustments: item.adjustments,
12155
- partyId: toPartyId(item.partyId),
12156
- to: item.to.kind === "party" ? {
12157
- kind: "party",
12158
- partyId: toPartyId(item.to.partyId)
12159
- } : item.to.kind === "address" ? {
12160
- kind: "address",
12161
- address: toAddress(item.to.address)
12162
- } : item.to
12163
- }))
12164
- };
12165
- if (input.items.length === 0 || input.period.start > input.period.end) throw Errors.invalidInput("input", "Payroll requires items and an ordered period");
12166
- const error = validateRunItems(input);
12167
- if (error !== null) throw error;
12168
- return input;
12169
- } catch (cause) {
12170
- throw cause instanceof CapxulError ? cause : Errors.invalidInput("input", "Invalid Payroll input");
12171
- }
12172
- }
12173
- /** Read only the exact backend prepare receipt, never an arbitrary signer failure. */
12174
- function payrollResolution(error, requestKey) {
12175
- const details = error.details;
12176
- if (error.code !== "WRONG_STATE" || details?.method !== runs.authorize.name || details.requestKey !== requestKey || typeof details.commandId !== "string" || typeof details.runId !== "string") return void 0;
12177
- return details.payrollResolution === "failed" || details.payrollResolution === "settled" ? details.payrollResolution : void 0;
12178
- }
12179
- function validatePayrollRunLookup(orgId, runId) {
12180
- return !Schema.is(PayrollRunIdSchema)(runId) || orgId.length === 0 ? {
12181
- ok: false,
12182
- error: Errors.invalidInput("runId", "Organization and run ID must be valid")
12183
- } : {
12184
- ok: true,
12185
- value: void 0
12186
- };
12187
- }
12188
- /** Strict saved detail decoder shared by the read and fresh stream. */
12189
- function decodePayrollRunDetail(raw, orgId, runId) {
12190
- const decoded = Schema.decodeUnknownResult(Schema.NullOr(PayrollRunDetailSchema), { onExcessProperty: "error" })(raw);
12191
- if (decoded._tag === "Failure") return {
12192
- ok: false,
12193
- error: Errors.invalidInput("run", "Invalid Payroll run detail")
12194
- };
12195
- return checkPayrollRunDetail(decoded.success, orgId, runId);
12196
- }
12197
- //#endregion
12198
- //#region src/payroll/run-subscription.ts
12199
- function detailValue(result) {
12200
- if (!result.ok) throw result.error;
12201
- return result.value;
12202
- }
12203
- function subscribePayrollRun(deps, orgId, runId, listener, options) {
12204
- const operation = CAPXUL_OPERATIONS.org.payroll.subscribeRun;
12205
- const valid = deps.validate(orgId, runId);
12206
- if (!valid.ok) return runCapxulEffect(Effect.fail(valid.error), observationOnlyControls(deps.invocationControls?.(options) ?? options), operation, deps.runPromise);
12207
- const decode = (raw) => detailValue(deps.decode(raw, orgId, runId));
12208
- return subscribeFreshQuery(deps, {
12209
- orgId,
12210
- runId
12211
- }, listener, operation, {
12212
- decode,
12213
- validateCached: (raw) => {
12214
- decode(raw);
12215
- },
12216
- invalidValue: (cause) => isCapxulError(cause) ? cause : Errors.invalidInput("run", "Invalid Payroll run detail"),
12217
- invalidScope: () => Errors.invalidInput("run", "Payroll run subscription scope does not match"),
12218
- lostValue: () => Errors.invalidInput("run", "Payroll run is no longer readable"),
12219
- unavailable: () => Errors.notImplemented("payroll", "subscribeRun")
12220
- }, options);
12221
- }
12222
- //#endregion
12223
- //#region src/payroll/run-authorization.ts
12224
- /**
12225
- * Authorize one payroll run: a stable request key, the prepared batch checked
12226
- * against the run the caller asked for, the signature, and the submission.
12227
- */
12228
- async function authorizePayrollRun(scope, input, controls, onRequestKey, onResolved) {
12229
- const { deps, orgId, requestKeyScope } = scope;
12230
- const signal = controls?.signal;
12231
- const execution = executionDependencies(deps);
12232
- if (execution === null) return {
12233
- ok: false,
12234
- error: Errors.notImplemented("payroll", "executionComposition")
12235
- };
12236
- try {
12237
- input = decodePayrollRunInput(input);
12238
- } catch (cause) {
12239
- return {
12240
- ok: false,
12241
- error: asCapxulError(cause)
12242
- };
12243
- }
12244
- const { requestKey: provided, ...canonicalInput } = input;
12245
- const actorId = onRequestKey === void 0 ? void 0 : execution.actor.authSession()?.authUserId;
12246
- if (onRequestKey !== void 0 && actorId === void 0) return {
12247
- ok: false,
12248
- error: Errors.notAuthenticated()
12249
- };
12250
- const requestKey = await paymentRequestKeyLifecycle(`${requestKeyScope}:${AUTHORIZE_RUN}`, onRequestKey === void 0 ? {
12251
- orgId,
12252
- input
12253
- } : {
12254
- actorId,
12255
- orgId,
12256
- input: canonicalInput
12257
- }, provided, onRequestKey === void 0 ? void 0 : { manageProvidedKey: true });
12258
- try {
12259
- await onRequestKey?.(requestKey.key);
12260
- if (onRequestKey !== void 0 && execution.actor.authSession()?.authUserId !== actorId) {
12261
- await requestKey.finish(false).catch(() => void 0);
12262
- return {
12263
- ok: false,
12264
- error: Errors.notAuthenticated()
12265
- };
12266
- }
12267
- } catch (cause) {
12268
- await requestKey.finish(false).catch(() => void 0);
12269
- return {
12270
- ok: false,
12271
- error: asCapxulError(cause)
12272
- };
12273
- }
12274
- let authorized;
12275
- let acceptedReplay;
12276
- let preparedClosed = false;
12277
- let terminalReplay;
12278
- const submitted = await executePrepared(execution, async (signerAddress) => {
12279
- const prepared = await runIfActive(signal, AUTHORIZE_RUN, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, runs.authorize, copyInvocationObservation(controls, {
12280
- orgId,
12281
- permissionId: input.permissionId,
12282
- asset: input.asset,
12283
- requestKey: requestKey.key,
12284
- signerAddress,
12285
- period: input.period,
12286
- items: input.items
12287
- }))).pipe(Effect.mapError((publicError) => ({ publicError }))), controls), deps.runPromise);
12288
- if (!prepared.ok) {
12289
- terminalReplay = payrollResolution(prepared.error, requestKey.key);
12290
- return prepared;
12291
- }
12292
- authorized = prepared.value.run;
12293
- acceptedReplay = prepared.value.replay.kind === "accepted" ? prepared.value.replay.submission : void 0;
12294
- return {
12295
- ok: true,
12296
- value: prepared.value.execution
12297
- };
12298
- }, (request, evidence) => authorized === void 0 || authorized.periodStart !== input.period.start || authorized.periodEnd !== input.period.end ? Promise.resolve(false) : isRunBatch({
12299
- orgId,
12300
- permissionId: input.permissionId,
12301
- requestKey: requestKey.key,
12302
- runId: authorized.id,
12303
- asset: input.asset,
12304
- period: input.period,
12305
- items: input.items
12306
- })(request, evidence), true, () => preparedClosed = true, signal, controls, void 0, () => acceptedReplay);
12307
- const resolution = preparedClosed ? "failed" : terminalReplay;
12308
- if (resolution !== void 0) try {
12309
- await onResolved?.(resolution);
12310
- } catch {}
12311
- try {
12312
- await requestKey.finish(submitted.ok || resolution !== void 0);
12313
- } catch {}
12314
- if (!submitted.ok) return submitted;
12315
- return authorized === void 0 ? {
12316
- ok: false,
12317
- error: Errors.unknown()
12318
- } : {
12319
- ok: true,
12320
- value: toPayrollRun(authorized)
12321
- };
12322
- }
12323
- const AUTHORIZE_RUN = runs.authorize.name;
12324
- function executionDependencies(deps) {
12325
- return deps.actor === void 0 || deps.chainId === void 0 || deps.signer === void 0 ? null : {
12326
- actor: deps.actor,
12327
- chainId: deps.chainId,
12328
- convexCall: deps.convexCall,
12329
- runPromise: deps.runPromise,
12330
- signer: deps.signer,
12331
- ...deps.telemetry === void 0 ? {} : { telemetry: deps.telemetry }
12332
- };
12333
- }
12334
- //#endregion
12335
- //#region src/payroll/runs.ts
12336
- function makePayrollMethods(deps, orgId) {
12337
- const requestKeyScope = deps.requestKeyScope ?? `client-${crypto.randomUUID()}`;
12338
- /** One observed call of one payroll row, its reply projected inside the call, under the row's name. */
12339
- const call = (row, args, options, project) => {
12340
- const controls = deps.invocationControls?.(options) ?? options;
12341
- return runPortEffect(callOperation(deps.convexCall, row, copyInvocationObservation(controls, args)).pipe(Effect.map(project), Effect.mapError((publicError) => ({ publicError }))), controls, row.name, deps.runPromise);
12342
- };
12343
- return {
12344
- subscribeRun: (runId, listener, options) => subscribePayrollRun({
12345
- actor: deps.actor,
12346
- convexCall: deps.convexCall,
12347
- query: reference(runs.get),
12348
- runPromise: deps.runPromise,
12349
- invocationControls: deps.invocationControls,
12350
- decode: decodePayrollRunDetail,
12351
- validate: validatePayrollRunLookup
12352
- }, orgId, runId, listener, options),
12353
- getRun: (runId, options) => {
12354
- const controls = deps.invocationControls?.(options) ?? options;
12355
- const lifetime = deps.actor?.authLifetime();
12356
- const operation = runs.get.name;
12357
- return runCapxulEffect(Effect.gen(function* () {
12358
- const lookup = validatePayrollRunLookup(orgId, runId);
12359
- if (!lookup.ok) return yield* Effect.fail(lookup.error);
12360
- if (lifetime === void 0 || lifetime.authUserId === null || deps.actor === void 0) return yield* Effect.fail(Errors.notAuthenticated());
12361
- const current = () => deps.actor?.matchesAuthLifetime(lifetime, lifetime.authUserId) === true;
12362
- if (!current()) return yield* Effect.fail(Errors.notAuthenticated());
12363
- const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
12364
- const captured = carryInvocationParentSpan(controls, parent);
12365
- const run = yield* awaitCapxulResult(() => runIfActive(controls?.signal, operation, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, runs.get, copyInvocationObservation(captured, {
12366
- orgId,
12367
- runId
12368
- }))).pipe(Effect.mapError((publicError) => ({ publicError }))), captured), deps.runPromise));
12369
- if (!current()) return yield* Effect.fail(Errors.cancelled({ operation }));
12370
- const checked = checkPayrollRunDetail(run, orgId, runId);
12371
- return yield* checked.ok ? Effect.succeed(checked.value) : Effect.fail(checked.error);
12372
- }), observationOnlyControls(controls), operation, deps.runPromise);
12373
- },
12374
- runs: (options) => call(runs.list, { orgId }, options, toPayrollRuns),
12375
- authorizeRun: (input, options) => {
12376
- const controls = deps.invocationControls?.(options) ?? options;
12377
- return runCapxulEffect(Effect.gen(function* () {
12378
- if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation: AUTHORIZE_RUN }));
12379
- const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
12380
- return yield* awaitCapxulResult(() => authorizePayrollRun({
12381
- deps,
12382
- orgId,
12383
- requestKeyScope
12384
- }, input, carryInvocationParentSpan(controls, parent), options?.onRequestKey, options?.onResolved));
12385
- }), observationOnlyControls(controls), AUTHORIZE_RUN, deps.runPromise);
12386
- },
12387
- terms: (options) => call(terms.list, { orgId }, options, (rates) => rates),
12388
- groups: {
12389
- list: (options) => call(groups.list, { orgId }, options, (saved) => mapPage(saved, toPayrollGroup)),
12390
- save: (input, options) => call(groups.save, {
12391
- orgId,
12392
- ...input.id === void 0 ? {} : { id: String(input.id) },
12393
- name: input.name,
12394
- tone: input.tone,
12395
- members: input.members
12396
- }, options, toPayrollGroup),
12397
- remove: (id, options) => call(groups.remove, {
12398
- orgId,
12399
- id
12400
- }, options, () => void 0)
12401
- }
12402
- };
12403
- }
12404
- const payrollUnavailable = () => Promise.resolve({
12405
- ok: false,
12406
- error: Errors.notImplemented("payroll", "executionComposition")
12407
- });
12408
- /** The bundle an unconfigured client exposes — every verb refuses, none throws. */
12409
- function makeUnavailablePayrollMethods() {
12410
- return {
12411
- getRun: payrollUnavailable,
12412
- subscribeRun: payrollUnavailable,
12413
- runs: payrollUnavailable,
12414
- authorizeRun: payrollUnavailable,
12415
- terms: payrollUnavailable,
12416
- groups: {
12417
- list: payrollUnavailable,
12418
- save: payrollUnavailable,
12419
- remove: payrollUnavailable
12420
- }
12421
- };
12422
- }
12423
- //#endregion
12424
11227
  //#region src/signing/embedded-wallet.ts
12425
11228
  function openfortEmbeddedWalletPort(input) {
12426
11229
  return {
@@ -15789,6 +14592,11 @@ const OBSERVED_CONVEX_QUERIES = new Set(OPERATIONS.filter((row) => row.kind ===
15789
14592
  const OBSERVED_CONVEX_MUTATIONS = new Set(OPERATIONS.filter((row) => row.kind === "mutation").map((row) => row.path));
15790
14593
  //#endregion
15791
14594
  //#region src/platform/transport/convex-errors.ts
14595
+ /**
14596
+ * All a redacting backend says about an error it raised itself: its request
14597
+ * id. The WebSocket client wraps it as `[CONVEX Q(path)] … Called by client`.
14598
+ */
14599
+ const REDACTED_ERROR = /^(?:\[CONVEX [QMA?]\([^)]*\)\] )?\[Request ID: ([^\]\s]+)\] Server Error(?:\s+Called by client)?$/u;
15792
14600
  function mapToCapxulError(operation, err, transport) {
15793
14601
  const decoded = decodeConvexError(err);
15794
14602
  if (decoded !== null) return decoded;
@@ -15813,9 +14621,26 @@ function mapToCapxulError(operation, err, transport) {
15813
14621
  * anything else (an uncaught "Server Error") is a backend defect. None is a
15814
14622
  * provider outage. The Convex request id rides in details, so the backend's
15815
14623
  * log line can be found.
14624
+ *
14625
+ * A deployed backend redacts these errors (`REDACT_LOGS_TO_CLIENT`, #2304):
14626
+ * the message is only the request id, and no structured cause exists, so the
14627
+ * message shape is the only discriminator. Neither case can be told apart, and
14628
+ * the reply says where to look instead. The text matches stay for a backend
14629
+ * that does not redact (development, local).
15816
14630
  */
15817
14631
  function backendFailure(operation, err) {
15818
14632
  const text = err instanceof Error ? err.message : String(err);
14633
+ const redacted = REDACTED_ERROR.exec(text.trim())?.[1];
14634
+ if (redacted !== void 0) return new CapxulError("UNKNOWN", `The backend refused ${operation} and gave no detail. Find Convex request ${redacted} in the backend logs. If the backend can be newer than this client, update the CLI or SDK.`, {
14635
+ cause: err,
14636
+ layer: "convex",
14637
+ details: {
14638
+ operation,
14639
+ provider: "convex",
14640
+ requestId: redacted,
14641
+ reason: "backend-error-redacted"
14642
+ }
14643
+ });
15819
14644
  const requestId = /\[Request ID: ([^\]\s]+)\]/u.exec(text)?.[1];
15820
14645
  const where = {
15821
14646
  operation,
@@ -18461,4 +17286,4 @@ async function createCapxulClient(input) {
18461
17286
  return createCapxulClientWithSignerControls(input, {});
18462
17287
  }
18463
17288
  //#endregion
18464
- export { decodePermissionIntent as _, cloneTelemetryEvent as a, paymentPhase as b, identityErrorFromCapxul as c, devPrivateKeySigner as d, createOpenfortBrowserSigner as f, decodePayrollRunInput as g, openfortEmbeddedWalletPort as h, postHogObservability as i, bootstrapErrorFromCapxul as l, openfortEmbeddedSignerFromWallet as m, createCapxulClientWithSignerControls as n, cloneTelemetryProps as o, embeddedSigner as p, assembleCapxulClient as r, IdentityPortTag as s, createCapxulClient as t, deriveDevPrivateKey as u, toPayrollGroupId as v, requestPhase as x, inboxPhase as y };
17289
+ export { cloneTelemetryEvent as a, identityErrorFromCapxul as c, devPrivateKeySigner as d, createOpenfortBrowserSigner as f, decodePermissionIntent as g, openfortEmbeddedWalletPort as h, postHogObservability as i, bootstrapErrorFromCapxul as l, openfortEmbeddedSignerFromWallet as m, createCapxulClientWithSignerControls as n, cloneTelemetryProps as o, embeddedSigner as p, assembleCapxulClient as r, IdentityPortTag as s, createCapxulClient as t, deriveDevPrivateKey as u };