@capxul/mcp 4.20.0-beta.24 → 4.20.0-beta.26
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/main.mjs +1890 -1768
- package/package.json +2 -2
package/dist/main.mjs
CHANGED
|
@@ -206,12 +206,17 @@ const Errors = {
|
|
|
206
206
|
* a PERMISSION denial — explicitly NOT an `INSUFFICIENT_BALANCE` (the treasury
|
|
207
207
|
* held the funds; the role's authority is what bound). `reason` discriminates
|
|
208
208
|
* the violated condition (`over_cap` / `daily_cap` / `not_member` /
|
|
209
|
-
* `disallowed_recipient` / `condition_violation`);
|
|
210
|
-
*
|
|
209
|
+
* `disallowed_recipient` / `condition_violation` / `budget_exhausted`);
|
|
210
|
+
* no Safe, Roles module or role key enters the details.
|
|
211
|
+
* `budget_exhausted` carries what the Budget's allowance still holds and
|
|
212
|
+
* what the spend needs, as asset amounts in major units (#2297); the asset
|
|
213
|
+
* id names the public token contract.
|
|
211
214
|
*/
|
|
212
|
-
rolePermissionDenied: (details) => new CapxulError("ROLE_PERMISSION_DENIED", `Org role denied this spend on-chain (${details.reason}).`, { details:
|
|
215
|
+
rolePermissionDenied: (details) => new CapxulError("ROLE_PERMISSION_DENIED", `Org role denied this spend on-chain (${details.reason}).`, { details: {
|
|
213
216
|
reason: details.reason,
|
|
214
|
-
operation: details.operation
|
|
217
|
+
...details.operation === void 0 ? {} : { operation: details.operation },
|
|
218
|
+
...details.remaining === void 0 ? {} : { remaining: details.remaining },
|
|
219
|
+
...details.required === void 0 ? {} : { required: details.required }
|
|
215
220
|
} }),
|
|
216
221
|
/**
|
|
217
222
|
* A transaction (or sponsored UserOp) failed. `details.reason` discriminates
|
|
@@ -4257,7 +4262,7 @@ const ActivityItemSchema = Schema.Union([Schema.Struct({
|
|
|
4257
4262
|
}), Schema.Struct({
|
|
4258
4263
|
kind: Schema.Literal("movement"),
|
|
4259
4264
|
id: Schema.String,
|
|
4260
|
-
classification: Schema.
|
|
4265
|
+
classification: Schema.Literals(["deposit", "movement_only_outflow"]),
|
|
4261
4266
|
amount: Amount,
|
|
4262
4267
|
direction: Direction,
|
|
4263
4268
|
counterpartyLabel: Schema.String,
|
|
@@ -9862,7 +9867,44 @@ const approvals = {
|
|
|
9862
9867
|
]
|
|
9863
9868
|
})
|
|
9864
9869
|
};
|
|
9870
|
+
/**
|
|
9871
|
+
* The home screen in one read. A section whose read failed is null, so the
|
|
9872
|
+
* caller shows it as unavailable, never as empty.
|
|
9873
|
+
*/
|
|
9874
|
+
const HomeSchema = Schema.Struct({
|
|
9875
|
+
email: Schema.String,
|
|
9876
|
+
actingAs: Schema.Union([Schema.Struct({ kind: Schema.Literal("personal") }), Schema.Struct({
|
|
9877
|
+
kind: Schema.Literal("organization"),
|
|
9878
|
+
orgId: Schema.String
|
|
9879
|
+
})]),
|
|
9880
|
+
/** The acting Safe's position in each settlement asset of its chain. */
|
|
9881
|
+
balances: Schema.NullOr(Schema.Array(AssetPositionSchema)),
|
|
9882
|
+
/** The ids of what waits on the caller, newest first. */
|
|
9883
|
+
needsYou: Schema.NullOr(Schema.Struct({
|
|
9884
|
+
requestsToPay: Schema.Array(Schema.String),
|
|
9885
|
+
payrollToApprove: Schema.Array(Schema.String),
|
|
9886
|
+
paymentsToRetry: Schema.Array(Schema.String)
|
|
9887
|
+
})),
|
|
9888
|
+
/** The newest rows of the actor's activity. */
|
|
9889
|
+
recentActivity: Schema.NullOr(Schema.Array(ActivityItemSchema))
|
|
9890
|
+
});
|
|
9865
9891
|
const summaries = {
|
|
9892
|
+
/**
|
|
9893
|
+
* Where the caller acts, its balances, what needs it and its newest activity,
|
|
9894
|
+
* for the person or for an Organization they take part in. One round trip.
|
|
9895
|
+
*/
|
|
9896
|
+
home: Operation.action("accounts.summaries.home", {
|
|
9897
|
+
input: Schema.Struct({
|
|
9898
|
+
chainId: ChainIdSchema$1,
|
|
9899
|
+
orgId: Schema.optionalKey(Schema.NonEmptyString)
|
|
9900
|
+
}),
|
|
9901
|
+
output: HomeSchema,
|
|
9902
|
+
errors: [
|
|
9903
|
+
...CALL$10,
|
|
9904
|
+
...DEFECTS,
|
|
9905
|
+
"ROLE_PERMISSION_DENIED"
|
|
9906
|
+
]
|
|
9907
|
+
}),
|
|
9866
9908
|
/** The signed-in person: id, verified email and Profile display name. */
|
|
9867
9909
|
me: Operation.query("accounts.summaries.me", {
|
|
9868
9910
|
input: Schema.Struct({}),
|
|
@@ -11737,7 +11779,11 @@ const OrgId$2 = Schema.NonEmptyString;
|
|
|
11737
11779
|
const CALL$4 = ["NOT_AUTHENTICATED", "INVALID_INPUT"];
|
|
11738
11780
|
/** The caller's own Account and Safe, which a Payment is signed from. */
|
|
11739
11781
|
const ACCOUNT$1 = ["SMART_ACCOUNT_MISSING", "ACCOUNT_NOT_FOUND"];
|
|
11740
|
-
/**
|
|
11782
|
+
/**
|
|
11783
|
+
* Not a participant of the Organization, no Budget in it that may pay for
|
|
11784
|
+
* this, or a Budget whose on-chain allowance holds less than the batch spends
|
|
11785
|
+
* (`budget_exhausted`, naming what is left).
|
|
11786
|
+
*/
|
|
11741
11787
|
const DENIED$1 = ["ROLE_PERMISSION_DENIED"];
|
|
11742
11788
|
/** The command, its Payment or its execution is not in a state this applies to. */
|
|
11743
11789
|
const STATE$3 = ["WRONG_STATE"];
|
|
@@ -12048,7 +12094,9 @@ const OneOrganization = Schema.Struct({ orgId: OrgId$1 });
|
|
|
12048
12094
|
const CALL$3 = ["NOT_AUTHENTICATED", "INVALID_INPUT"];
|
|
12049
12095
|
/**
|
|
12050
12096
|
* The caller does not take part in the Organization, or holds no Budget in it
|
|
12051
|
-
* that may pay for this
|
|
12097
|
+
* that may pay for this, or the Budget's on-chain allowance holds less than the
|
|
12098
|
+
* run pays (`budget_exhausted`, naming what is left). A caller outside the
|
|
12099
|
+
* Organization learns nothing more.
|
|
12052
12100
|
*/
|
|
12053
12101
|
const DENIED = ["ROLE_PERMISSION_DENIED"];
|
|
12054
12102
|
/**
|
|
@@ -13751,7 +13799,9 @@ emitSaved: "activity.annotations.emitSaved" },
|
|
|
13751
13799
|
linkObservation: "payments.commands.linkObservation",
|
|
13752
13800
|
failPrepared: "payments.commands.failPrepared",
|
|
13753
13801
|
loadContext: "payments.commands.loadContext",
|
|
13754
|
-
loadExecutionContext: "payments.commands.loadExecutionContext"
|
|
13802
|
+
loadExecutionContext: "payments.commands.loadExecutionContext",
|
|
13803
|
+
/** The capped Budget a prepared Organization command spends from, before its allowance is read on chain. */
|
|
13804
|
+
loadBudgetAllowance: "payments.commands.loadBudgetAllowance"
|
|
13755
13805
|
},
|
|
13756
13806
|
lifecycles: { prepareIntent: "payments.lifecycles.prepareIntent" },
|
|
13757
13807
|
/** The stored UserOperation of every money command: Payment, Permission and configuration. */
|
|
@@ -15241,95 +15291,83 @@ Schema.Struct({
|
|
|
15241
15291
|
});
|
|
15242
15292
|
//#endregion
|
|
15243
15293
|
//#region ../../packages/observability/src/catalog/cli.ts
|
|
15244
|
-
/**
|
|
15294
|
+
/** Every command path of the `capxul` tree, dot-separated, plus the root and `unknown`. */
|
|
15245
15295
|
const CLI_COMMAND_NAMES = [
|
|
15246
|
-
"status",
|
|
15247
|
-
"request",
|
|
15248
|
-
"request.list",
|
|
15249
|
-
"request.get",
|
|
15250
|
-
"request.cancel",
|
|
15251
|
-
"offer",
|
|
15252
|
-
"offer.create",
|
|
15253
|
-
"offer.list",
|
|
15254
|
-
"offer.get",
|
|
15255
|
-
"offer.revise",
|
|
15256
|
-
"offer.deactivate",
|
|
15257
|
-
"org.offer",
|
|
15258
|
-
"org.offer.create",
|
|
15259
|
-
"org.offer.list",
|
|
15260
|
-
"org.offer.get",
|
|
15261
|
-
"org.offer.revise",
|
|
15262
|
-
"org.offer.deactivate",
|
|
15263
|
-
"request.issue",
|
|
15264
|
-
"inbox",
|
|
15265
|
-
CAPXUL_OPERATIONS.inbox.list,
|
|
15266
|
-
"inbox.get",
|
|
15267
|
-
"inbox.pay",
|
|
15268
|
-
CAPXUL_OPERATIONS.inbox.decline,
|
|
15269
|
-
"org.request",
|
|
15270
|
-
"org.request.list",
|
|
15271
|
-
"org.request.get",
|
|
15272
|
-
"org.request.cancel",
|
|
15273
|
-
"org.request.issue",
|
|
15274
|
-
"org.inbox",
|
|
15275
|
-
CAPXUL_OPERATIONS.org.inbox.list,
|
|
15276
|
-
"org.inbox.get",
|
|
15277
|
-
"org.inbox.pay",
|
|
15278
|
-
CAPXUL_OPERATIONS.org.inbox.decline,
|
|
15279
15296
|
"capxul",
|
|
15297
|
+
"home",
|
|
15280
15298
|
"whoami",
|
|
15281
15299
|
"use",
|
|
15282
15300
|
"use.personal",
|
|
15283
15301
|
"use.org",
|
|
15284
15302
|
"use.env",
|
|
15285
|
-
"doctor",
|
|
15286
|
-
"telemetry",
|
|
15287
|
-
"telemetry.status",
|
|
15288
|
-
"telemetry.enable",
|
|
15289
|
-
"telemetry.disable",
|
|
15290
15303
|
"auth",
|
|
15291
15304
|
"auth.login",
|
|
15292
|
-
"auth.signup",
|
|
15293
|
-
"auth.send",
|
|
15294
15305
|
"auth.verify",
|
|
15295
15306
|
"auth.switch",
|
|
15296
|
-
"auth.profile",
|
|
15297
|
-
"auth.status",
|
|
15298
15307
|
"auth.logout",
|
|
15299
15308
|
"account",
|
|
15300
|
-
|
|
15301
|
-
CAPXUL_OPERATIONS.account.balance,
|
|
15302
|
-
"account.deposit",
|
|
15309
|
+
"account.show",
|
|
15303
15310
|
"account.retry",
|
|
15304
|
-
"
|
|
15305
|
-
"
|
|
15306
|
-
"document.verify",
|
|
15307
|
-
"document.render",
|
|
15308
|
-
"document.export",
|
|
15311
|
+
"balance",
|
|
15312
|
+
"address",
|
|
15309
15313
|
"payment",
|
|
15314
|
+
"payment.send",
|
|
15310
15315
|
"payment.list",
|
|
15311
15316
|
"payment.get",
|
|
15312
15317
|
"payment.wait",
|
|
15313
|
-
"payment.send",
|
|
15314
15318
|
"payment.retry",
|
|
15315
|
-
"
|
|
15316
|
-
|
|
15317
|
-
"
|
|
15318
|
-
"
|
|
15319
|
-
|
|
15319
|
+
"inbox",
|
|
15320
|
+
CAPXUL_OPERATIONS.inbox.list,
|
|
15321
|
+
"inbox.get",
|
|
15322
|
+
"inbox.pay",
|
|
15323
|
+
CAPXUL_OPERATIONS.inbox.decline,
|
|
15324
|
+
"request",
|
|
15325
|
+
"request.issue",
|
|
15326
|
+
"request.list",
|
|
15327
|
+
"request.get",
|
|
15328
|
+
"request.cancel",
|
|
15329
|
+
"offer",
|
|
15330
|
+
"offer.create",
|
|
15331
|
+
"offer.list",
|
|
15332
|
+
"offer.get",
|
|
15333
|
+
"offer.revise",
|
|
15334
|
+
"offer.deactivate",
|
|
15320
15335
|
"contact",
|
|
15336
|
+
"contact.add",
|
|
15321
15337
|
"contact.list",
|
|
15322
15338
|
"contact.get",
|
|
15323
|
-
"contact.add",
|
|
15324
15339
|
"contact.label",
|
|
15325
15340
|
"contact.hide",
|
|
15326
15341
|
"contact.unhide",
|
|
15342
|
+
"document",
|
|
15343
|
+
"document.get",
|
|
15344
|
+
"document.render",
|
|
15345
|
+
"document.export",
|
|
15346
|
+
"document.verify",
|
|
15347
|
+
"activity",
|
|
15348
|
+
"activity.list",
|
|
15349
|
+
"activity.get",
|
|
15350
|
+
"activity.summary",
|
|
15351
|
+
"activity.annotate",
|
|
15352
|
+
"invite",
|
|
15353
|
+
"invite.list",
|
|
15354
|
+
"invite.accept",
|
|
15355
|
+
"invite.decline",
|
|
15327
15356
|
"org",
|
|
15328
15357
|
CAPXUL_OPERATIONS.org.list,
|
|
15329
|
-
|
|
15358
|
+
CAPXUL_OPERATIONS.org.create,
|
|
15359
|
+
"org.show",
|
|
15330
15360
|
CAPXUL_OPERATIONS.org.me,
|
|
15331
|
-
"org.
|
|
15332
|
-
|
|
15361
|
+
"org.wait",
|
|
15362
|
+
CAPXUL_OPERATIONS.org.retry,
|
|
15363
|
+
"org.member",
|
|
15364
|
+
"org.member.list",
|
|
15365
|
+
"org.member.get",
|
|
15366
|
+
"org.member.invite",
|
|
15367
|
+
CAPXUL_OPERATIONS.org.invite,
|
|
15368
|
+
"org.invite.list",
|
|
15369
|
+
"org.invite.cancel",
|
|
15370
|
+
"org.invite.resend",
|
|
15333
15371
|
"org.permission",
|
|
15334
15372
|
"org.permission.list",
|
|
15335
15373
|
"org.permission.get",
|
|
@@ -15341,46 +15379,32 @@ const CLI_COMMAND_NAMES = [
|
|
|
15341
15379
|
"org.permission.command",
|
|
15342
15380
|
"org.permission.command.get",
|
|
15343
15381
|
"org.permission.command.wait",
|
|
15344
|
-
CAPXUL_OPERATIONS.org.payment,
|
|
15345
|
-
"org.payment.list",
|
|
15346
|
-
"org.payment.get",
|
|
15347
|
-
"org.payment.wait",
|
|
15348
|
-
"org.payment.retry",
|
|
15349
|
-
"org.payment.send",
|
|
15350
15382
|
"org.payroll",
|
|
15383
|
+
"org.payroll.run",
|
|
15384
|
+
"org.payroll.list",
|
|
15385
|
+
"org.payroll.get",
|
|
15386
|
+
"org.payroll.wait",
|
|
15387
|
+
CAPXUL_OPERATIONS.org.payroll.terms,
|
|
15351
15388
|
"org.payroll.groups",
|
|
15352
15389
|
CAPXUL_OPERATIONS.org.payroll.groups.list,
|
|
15353
15390
|
CAPXUL_OPERATIONS.org.payroll.groups.save,
|
|
15354
15391
|
CAPXUL_OPERATIONS.org.payroll.groups.remove,
|
|
15355
|
-
|
|
15356
|
-
"
|
|
15357
|
-
"
|
|
15358
|
-
"
|
|
15359
|
-
"
|
|
15360
|
-
|
|
15361
|
-
"
|
|
15362
|
-
"
|
|
15363
|
-
"org.member.invite",
|
|
15364
|
-
"org.contact",
|
|
15365
|
-
"org.contact.list",
|
|
15366
|
-
"org.contact.get",
|
|
15367
|
-
"org.contact.add",
|
|
15368
|
-
"org.contact.label",
|
|
15369
|
-
"org.contact.hide",
|
|
15370
|
-
"org.contact.unhide",
|
|
15371
|
-
CAPXUL_OPERATIONS.org.invite,
|
|
15372
|
-
"org.invite.list",
|
|
15392
|
+
"config",
|
|
15393
|
+
"config.telemetry",
|
|
15394
|
+
"config.telemetry.on",
|
|
15395
|
+
"config.telemetry.off",
|
|
15396
|
+
"config.telemetry.status",
|
|
15397
|
+
"invite.get",
|
|
15398
|
+
"invite.review",
|
|
15399
|
+
"invite.wait",
|
|
15373
15400
|
"org.invite.get",
|
|
15374
15401
|
"org.invite.wait",
|
|
15375
|
-
"org.invite.review",
|
|
15376
|
-
"org.invite.accept",
|
|
15377
|
-
"org.invite.decline",
|
|
15378
|
-
"org.invite.cancel",
|
|
15379
|
-
"org.invite.resend",
|
|
15380
15402
|
"org.invite.retry",
|
|
15381
|
-
"
|
|
15382
|
-
|
|
15383
|
-
|
|
15403
|
+
"schema",
|
|
15404
|
+
"doctor",
|
|
15405
|
+
"dev",
|
|
15406
|
+
"dev.key",
|
|
15407
|
+
"dev.fund",
|
|
15384
15408
|
"unknown"
|
|
15385
15409
|
];
|
|
15386
15410
|
const CLI_CAPABILITIES = [
|
|
@@ -17074,7 +17098,7 @@ function mapAccountLifecycle(input) {
|
|
|
17074
17098
|
}
|
|
17075
17099
|
//#endregion
|
|
17076
17100
|
//#region ../../packages/sdk/package.json
|
|
17077
|
-
var version$2 = "4.20.0-beta.
|
|
17101
|
+
var version$2 = "4.20.0-beta.26";
|
|
17078
17102
|
//#endregion
|
|
17079
17103
|
//#region ../../packages/sdk/src/observability/telemetry/exception-projection.ts
|
|
17080
17104
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -22808,63 +22832,6 @@ function makeOfframpMethods() {
|
|
|
22808
22832
|
};
|
|
22809
22833
|
}
|
|
22810
22834
|
//#endregion
|
|
22811
|
-
//#region ../../packages/sdk/src/payments/organization-payment-reads.ts
|
|
22812
|
-
const exactPaymentId = (value) => Schema.decodeEffect(PaymentIdSchema)(value).pipe(Effect.mapError(() => Errors.invalidInput("paymentId", "Provide one exact Payment ID")));
|
|
22813
|
-
/** Read the backend-authorized Organization wrapper before projecting any Payment. */
|
|
22814
|
-
function makeOrganizationPaymentReadMethods(deps, orgId) {
|
|
22815
|
-
return {
|
|
22816
|
-
previewRetry(paymentId, options) {
|
|
22817
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
22818
|
-
return runCapxulEffect(Effect.gen(function* () {
|
|
22819
|
-
const id = yield* exactPaymentId(paymentId);
|
|
22820
|
-
const preview = yield* callOperation(deps.convexCall, lifecycles.previewRetry, copyInvocationObservation(controls, {
|
|
22821
|
-
orgId,
|
|
22822
|
-
paymentId: id
|
|
22823
|
-
}));
|
|
22824
|
-
if (preview.orgId !== orgId || !preview.paymentIds.includes(id) || new Set(preview.paymentIds).size !== preview.paymentIds.length || preview.payments.length !== preview.paymentIds.length || preview.payments.some((payment, index) => payment.id !== preview.paymentIds[index] || payment.direction !== "out")) return yield* Effect.fail(Errors.invalidInput("paymentId", "Invalid Organization retry preview"));
|
|
22825
|
-
return {
|
|
22826
|
-
...preview,
|
|
22827
|
-
paymentIds: [...preview.paymentIds],
|
|
22828
|
-
payments: preview.payments.map((payment) => normalizePaymentTiming(payment))
|
|
22829
|
-
};
|
|
22830
|
-
}), controls, lifecycles.previewRetry.name, deps.runPromise);
|
|
22831
|
-
},
|
|
22832
|
-
list(options) {
|
|
22833
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
22834
|
-
return runCapxulEffect(callOperation(deps.convexCall, records.listForOrganization, copyInvocationObservation(controls, { orgId })).pipe(Effect.filterOrFail((result) => result.orgId === orgId, () => Errors.invalidInput("orgId", "Invalid Organization Payment list")), Effect.map((result) => mapPage(result, (payment) => normalizePaymentTiming(payment)))), controls, records.listForOrganization.name, deps.runPromise);
|
|
22835
|
-
},
|
|
22836
|
-
get(paymentId, options) {
|
|
22837
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
22838
|
-
return runCapxulEffect(Effect.gen(function* () {
|
|
22839
|
-
const id = yield* exactPaymentId(paymentId);
|
|
22840
|
-
const response = yield* callOperation(deps.convexCall, records.getForOrganization, copyInvocationObservation(controls, {
|
|
22841
|
-
orgId,
|
|
22842
|
-
paymentId: id
|
|
22843
|
-
}));
|
|
22844
|
-
if (response.orgId !== orgId) return yield* Effect.fail(Errors.invalidInput("orgId", "Payment read scope does not match"));
|
|
22845
|
-
const decoded = response.payment;
|
|
22846
|
-
if (decoded === null) return null;
|
|
22847
|
-
if (decoded.id !== id) return yield* Effect.fail(Errors.invalidInput("paymentId", "Invalid Organization Payment response"));
|
|
22848
|
-
return normalizePaymentTiming(decoded);
|
|
22849
|
-
}), controls, records.getForOrganization.name, deps.runPromise);
|
|
22850
|
-
},
|
|
22851
|
-
subscribe(paymentId, listener, options) {
|
|
22852
|
-
const parsed = Schema.decodeResult(PaymentIdSchema)(paymentId);
|
|
22853
|
-
if (parsed._tag === "Failure") return Promise.resolve({
|
|
22854
|
-
ok: false,
|
|
22855
|
-
error: Errors.invalidInput("paymentId", "Provide one exact Payment ID")
|
|
22856
|
-
});
|
|
22857
|
-
return subscribeOrganizationPayment({
|
|
22858
|
-
actor: deps.actor,
|
|
22859
|
-
convexCall: deps.convexCall,
|
|
22860
|
-
getPayment: reference(records.getForOrganization),
|
|
22861
|
-
runPromise: deps.runPromise,
|
|
22862
|
-
invocationControls: deps.invocationControls
|
|
22863
|
-
}, orgId, parsed.success, listener, options);
|
|
22864
|
-
}
|
|
22865
|
-
};
|
|
22866
|
-
}
|
|
22867
|
-
//#endregion
|
|
22868
22835
|
//#region ../../packages/sdk/src/payments/organization-payment-requests.ts
|
|
22869
22836
|
const organizationPaymentIntentSchema = Schema.Struct({
|
|
22870
22837
|
orgId: PrepareOrganizationPaymentExecutionInputSchema.fields.orgId,
|
|
@@ -22925,6 +22892,63 @@ async function organizationPaymentRequest(scope, actor, intent, providedKey) {
|
|
|
22925
22892
|
}
|
|
22926
22893
|
}
|
|
22927
22894
|
//#endregion
|
|
22895
|
+
//#region ../../packages/sdk/src/payments/organization-payment-reads.ts
|
|
22896
|
+
const exactPaymentId = (value) => Schema.decodeEffect(PaymentIdSchema)(value).pipe(Effect.mapError(() => Errors.invalidInput("paymentId", "Provide one exact Payment ID")));
|
|
22897
|
+
/** Read the backend-authorized Organization wrapper before projecting any Payment. */
|
|
22898
|
+
function makeOrganizationPaymentReadMethods(deps, orgId) {
|
|
22899
|
+
return {
|
|
22900
|
+
previewRetry(paymentId, options) {
|
|
22901
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
22902
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
22903
|
+
const id = yield* exactPaymentId(paymentId);
|
|
22904
|
+
const preview = yield* callOperation(deps.convexCall, lifecycles.previewRetry, copyInvocationObservation(controls, {
|
|
22905
|
+
orgId,
|
|
22906
|
+
paymentId: id
|
|
22907
|
+
}));
|
|
22908
|
+
if (preview.orgId !== orgId || !preview.paymentIds.includes(id) || new Set(preview.paymentIds).size !== preview.paymentIds.length || preview.payments.length !== preview.paymentIds.length || preview.payments.some((payment, index) => payment.id !== preview.paymentIds[index] || payment.direction !== "out")) return yield* Effect.fail(Errors.invalidInput("paymentId", "Invalid Organization retry preview"));
|
|
22909
|
+
return {
|
|
22910
|
+
...preview,
|
|
22911
|
+
paymentIds: [...preview.paymentIds],
|
|
22912
|
+
payments: preview.payments.map((payment) => normalizePaymentTiming(payment))
|
|
22913
|
+
};
|
|
22914
|
+
}), controls, lifecycles.previewRetry.name, deps.runPromise);
|
|
22915
|
+
},
|
|
22916
|
+
list(options) {
|
|
22917
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
22918
|
+
return runCapxulEffect(callOperation(deps.convexCall, records.listForOrganization, copyInvocationObservation(controls, { orgId })).pipe(Effect.filterOrFail((result) => result.orgId === orgId, () => Errors.invalidInput("orgId", "Invalid Organization Payment list")), Effect.map((result) => mapPage(result, (payment) => normalizePaymentTiming(payment)))), controls, records.listForOrganization.name, deps.runPromise);
|
|
22919
|
+
},
|
|
22920
|
+
get(paymentId, options) {
|
|
22921
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
22922
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
22923
|
+
const id = yield* exactPaymentId(paymentId);
|
|
22924
|
+
const response = yield* callOperation(deps.convexCall, records.getForOrganization, copyInvocationObservation(controls, {
|
|
22925
|
+
orgId,
|
|
22926
|
+
paymentId: id
|
|
22927
|
+
}));
|
|
22928
|
+
if (response.orgId !== orgId) return yield* Effect.fail(Errors.invalidInput("orgId", "Payment read scope does not match"));
|
|
22929
|
+
const decoded = response.payment;
|
|
22930
|
+
if (decoded === null) return null;
|
|
22931
|
+
if (decoded.id !== id) return yield* Effect.fail(Errors.invalidInput("paymentId", "Invalid Organization Payment response"));
|
|
22932
|
+
return normalizePaymentTiming(decoded);
|
|
22933
|
+
}), controls, records.getForOrganization.name, deps.runPromise);
|
|
22934
|
+
},
|
|
22935
|
+
subscribe(paymentId, listener, options) {
|
|
22936
|
+
const parsed = Schema.decodeResult(PaymentIdSchema)(paymentId);
|
|
22937
|
+
if (parsed._tag === "Failure") return Promise.resolve({
|
|
22938
|
+
ok: false,
|
|
22939
|
+
error: Errors.invalidInput("paymentId", "Provide one exact Payment ID")
|
|
22940
|
+
});
|
|
22941
|
+
return subscribeOrganizationPayment({
|
|
22942
|
+
actor: deps.actor,
|
|
22943
|
+
convexCall: deps.convexCall,
|
|
22944
|
+
getPayment: reference(records.getForOrganization),
|
|
22945
|
+
runPromise: deps.runPromise,
|
|
22946
|
+
invocationControls: deps.invocationControls
|
|
22947
|
+
}, orgId, parsed.success, listener, options);
|
|
22948
|
+
}
|
|
22949
|
+
};
|
|
22950
|
+
}
|
|
22951
|
+
//#endregion
|
|
22928
22952
|
//#region ../../packages/sdk/src/payments/organization-flights.ts
|
|
22929
22953
|
function executionDependencies$1(input) {
|
|
22930
22954
|
return input.actor === void 0 || input.chainId === void 0 || input.signer === void 0 ? null : {
|
|
@@ -23835,20 +23859,86 @@ function sleep(ms, signal) {
|
|
|
23835
23859
|
});
|
|
23836
23860
|
}
|
|
23837
23861
|
//#endregion
|
|
23862
|
+
//#region ../../packages/sdk/src/accounts/organization-approvals.ts
|
|
23863
|
+
/** The Organization Payment prepared for `signerAddress` and checked, before any approval. */
|
|
23864
|
+
async function prepareVerifiedOrganizationPayment(deps, input, signerAddress, signal) {
|
|
23865
|
+
const { organizationId, permissionId, requestKey, lineage, ...item } = input;
|
|
23866
|
+
const intent = organizationPaymentIntent(organizationId, {
|
|
23867
|
+
permissionId,
|
|
23868
|
+
items: [item],
|
|
23869
|
+
...lineage === void 0 ? {} : { lineage },
|
|
23870
|
+
...requestKey === void 0 ? {} : { requestKey }
|
|
23871
|
+
});
|
|
23872
|
+
if (!intent.ok) return intent;
|
|
23873
|
+
const session = deps.actor?.authSession() ?? null;
|
|
23874
|
+
if (session === null || deps.chainId === void 0) return {
|
|
23875
|
+
ok: false,
|
|
23876
|
+
error: Errors.notAuthenticated()
|
|
23877
|
+
};
|
|
23878
|
+
const expected = {
|
|
23879
|
+
...intent.value,
|
|
23880
|
+
requestKey: requestKey ?? `pay_${crypto.randomUUID()}`
|
|
23881
|
+
};
|
|
23882
|
+
const prepared = await runIfActive(signal, CAPXUL_OPERATIONS.organizationPayments.prepareExecution, () => callOperation(deps.convexCall, commands$1.prepareOrganization, {
|
|
23883
|
+
...expected,
|
|
23884
|
+
signerAddress
|
|
23885
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise);
|
|
23886
|
+
if (!prepared.ok) return prepared;
|
|
23887
|
+
const evidence = prepared.value;
|
|
23888
|
+
return await matchesIntent(expected)(evidence.request, evidence) && await verifiedPreparedCommand(evidence, session.email, signerAddress, deps.chainId) ? {
|
|
23889
|
+
ok: true,
|
|
23890
|
+
value: evidence
|
|
23891
|
+
} : abandonPrepared(deps, evidence.executionId, "prepared_mismatch", {
|
|
23892
|
+
ok: false,
|
|
23893
|
+
error: Errors.invalidInput("execution", "prepared Organization Payment mismatch")
|
|
23894
|
+
});
|
|
23895
|
+
}
|
|
23896
|
+
/** One fresh Payment read: the caller's own, or one in the Organization's scope. */
|
|
23897
|
+
const readPayment = (deps, paymentId, organizationId, signal) => organizationId === void 0 ? runIfActive(signal, records.get.name, () => callOperation(deps.convexCall, records.get, { paymentId }, { fresh: true }).pipe(Effect.map((payment) => ({ payment })), Effect.mapError((publicError) => ({ publicError }))), deps.runPromise) : runIfActive(signal, records.getForOrganization.name, () => callOperation(deps.convexCall, records.getForOrganization, {
|
|
23898
|
+
orgId: organizationId,
|
|
23899
|
+
paymentId
|
|
23900
|
+
}, { fresh: true }).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise);
|
|
23901
|
+
/**
|
|
23902
|
+
* The Payments an approval carries, as they stand. A personal read names only
|
|
23903
|
+
* the caller's own Account's Payments, so an Organization's is read with its
|
|
23904
|
+
* `organizationId`; one that cannot be read is refused, never taken as ended.
|
|
23905
|
+
*/
|
|
23906
|
+
async function readApprovalPayments(deps, paymentIds, organizationId, signal) {
|
|
23907
|
+
const reads = await Promise.all(paymentIds.map((paymentId) => readPayment(deps, paymentId, organizationId, signal)));
|
|
23908
|
+
const payments = [];
|
|
23909
|
+
for (const [index, read] of reads.entries()) {
|
|
23910
|
+
if (!read.ok) return read;
|
|
23911
|
+
const { orgId, payment } = read.value;
|
|
23912
|
+
if (payment === null && organizationId === void 0) return {
|
|
23913
|
+
ok: false,
|
|
23914
|
+
error: Errors.invalidInput("organizationId", "this approval pays from an Organization: wait with its organizationId")
|
|
23915
|
+
};
|
|
23916
|
+
if (payment === null || payment.id !== paymentIds[index] || orgId !== organizationId) return {
|
|
23917
|
+
ok: false,
|
|
23918
|
+
error: Errors.invalidInput("paymentId", "the approval's Payments are not this scope's")
|
|
23919
|
+
};
|
|
23920
|
+
payments.push(normalizePaymentTiming(payment));
|
|
23921
|
+
}
|
|
23922
|
+
return {
|
|
23923
|
+
ok: true,
|
|
23924
|
+
value: payments
|
|
23925
|
+
};
|
|
23926
|
+
}
|
|
23927
|
+
//#endregion
|
|
23838
23928
|
//#region ../../packages/sdk/src/accounts/approvals.ts
|
|
23839
23929
|
function makeApprovalsMethods(deps) {
|
|
23840
23930
|
const call = (name, row, args, signal) => runIfActive(signal, name, () => callOperation(deps.convexCall, row, args, { fresh: true }).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise);
|
|
23841
23931
|
const get = (approvalId) => call(approvals.get.name, approvals.get, { approvalId });
|
|
23842
|
-
|
|
23843
|
-
|
|
23844
|
-
|
|
23845
|
-
|
|
23846
|
-
|
|
23847
|
-
|
|
23932
|
+
/**
|
|
23933
|
+
* Prepare for the signer the backend recorded when the Safe was claimed (the
|
|
23934
|
+
* person's own key signs later, on the approval page) and open the approval.
|
|
23935
|
+
*/
|
|
23936
|
+
const openApproval = async (signal, prepareFor) => {
|
|
23937
|
+
const session = deps.actor?.authSession() ?? null;
|
|
23938
|
+
if (deps.actor === void 0 || deps.chainId === void 0) return {
|
|
23848
23939
|
ok: false,
|
|
23849
23940
|
error: Errors.notImplemented("approvals", "prepareComposition")
|
|
23850
23941
|
};
|
|
23851
|
-
const session = actor.authSession();
|
|
23852
23942
|
if (session === null) return {
|
|
23853
23943
|
ok: false,
|
|
23854
23944
|
error: Errors.notAuthenticated()
|
|
@@ -23867,18 +23957,33 @@ function makeApprovalsMethods(deps) {
|
|
|
23867
23957
|
validStates: ["claimed"]
|
|
23868
23958
|
})
|
|
23869
23959
|
};
|
|
23870
|
-
const prepared = await
|
|
23871
|
-
actor,
|
|
23872
|
-
chainId,
|
|
23873
|
-
convexCall: deps.convexCall,
|
|
23874
|
-
runPromise: deps.runPromise
|
|
23875
|
-
}, intent.value, safe.value.signerAddress, input.requestKey ?? `pay_${crypto.randomUUID()}`, signal);
|
|
23960
|
+
const prepared = await prepareFor(safe.value.signerAddress);
|
|
23876
23961
|
if (!prepared.ok) return prepared;
|
|
23877
|
-
await emitPreparedPaymentInitiated(deps, prepared.value);
|
|
23878
23962
|
const { executionId } = prepared.value;
|
|
23879
23963
|
const opened = await call(approvals.request.name, approvals.request, { executionId });
|
|
23880
23964
|
return opened.ok ? opened : abandonPrepared(deps, executionId, "cancelled", opened);
|
|
23881
23965
|
};
|
|
23966
|
+
const prepare = async (input, options) => {
|
|
23967
|
+
const signal = options?.signal;
|
|
23968
|
+
if ("organizationId" in input) return openApproval(signal, (signerAddress) => prepareVerifiedOrganizationPayment(deps, input, signerAddress, signal));
|
|
23969
|
+
const intent = paymentExecutionIntent(input);
|
|
23970
|
+
if (!intent.ok) return intent;
|
|
23971
|
+
const { actor, chainId } = deps;
|
|
23972
|
+
return openApproval(signal, async (signerAddress) => {
|
|
23973
|
+
if (actor === void 0 || chainId === void 0) return {
|
|
23974
|
+
ok: false,
|
|
23975
|
+
error: Errors.notImplemented("approvals", "prepareComposition")
|
|
23976
|
+
};
|
|
23977
|
+
const prepared = await prepareVerifiedPersonalPayment({
|
|
23978
|
+
actor,
|
|
23979
|
+
chainId,
|
|
23980
|
+
convexCall: deps.convexCall,
|
|
23981
|
+
runPromise: deps.runPromise
|
|
23982
|
+
}, intent.value, signerAddress, input.requestKey ?? `pay_${crypto.randomUUID()}`, signal);
|
|
23983
|
+
if (prepared.ok) await emitPreparedPaymentInitiated(deps, prepared.value);
|
|
23984
|
+
return prepared;
|
|
23985
|
+
});
|
|
23986
|
+
};
|
|
23882
23987
|
const approve = async (approvalId) => {
|
|
23883
23988
|
const { signer } = deps;
|
|
23884
23989
|
if (signer === void 0) return {
|
|
@@ -23939,28 +24044,12 @@ function makeApprovalsMethods(deps) {
|
|
|
23939
24044
|
}
|
|
23940
24045
|
};
|
|
23941
24046
|
};
|
|
23942
|
-
const readPayments = async (approval, signal) => {
|
|
23943
|
-
const reads = await Promise.all(approval.operation.paymentIds.map((paymentId) => call(records.get.name, records.get, { paymentId }, signal)));
|
|
23944
|
-
const payments = [];
|
|
23945
|
-
for (const read of reads) {
|
|
23946
|
-
if (!read.ok) return read;
|
|
23947
|
-
if (read.value === null) return {
|
|
23948
|
-
ok: false,
|
|
23949
|
-
error: Errors.notImplemented("approvals", "wait.organizationPayment")
|
|
23950
|
-
};
|
|
23951
|
-
payments.push(normalizePaymentTiming(read.value));
|
|
23952
|
-
}
|
|
23953
|
-
return {
|
|
23954
|
-
ok: true,
|
|
23955
|
-
value: payments
|
|
23956
|
-
};
|
|
23957
|
-
};
|
|
23958
24047
|
/**
|
|
23959
24048
|
* One read of the approval: its outcome when it has ended, or null to read
|
|
23960
24049
|
* again. The Payments are read before anything is sent, so a send is never
|
|
23961
24050
|
* made for Payments this caller cannot follow.
|
|
23962
24051
|
*/
|
|
23963
|
-
const step = async (approvalId, signal) => {
|
|
24052
|
+
const step = async (approvalId, signal, organizationId) => {
|
|
23964
24053
|
const read = await call(approvals.get.name, approvals.get, { approvalId }, signal);
|
|
23965
24054
|
if (!read.ok) return read;
|
|
23966
24055
|
const approval = read.value;
|
|
@@ -23968,7 +24057,7 @@ function makeApprovalsMethods(deps) {
|
|
|
23968
24057
|
ok: true,
|
|
23969
24058
|
value: null
|
|
23970
24059
|
};
|
|
23971
|
-
const payments = await
|
|
24060
|
+
const payments = await readApprovalPayments(deps, approval.operation.paymentIds, organizationId, signal);
|
|
23972
24061
|
if (!payments.ok) return payments;
|
|
23973
24062
|
if (unsent(approval, payments.value)) {
|
|
23974
24063
|
const sent = await submit(approvalId);
|
|
@@ -23989,7 +24078,7 @@ function makeApprovalsMethods(deps) {
|
|
|
23989
24078
|
const signal = options?.signal;
|
|
23990
24079
|
const intervalMs = options?.intervalMs ?? 2e3;
|
|
23991
24080
|
const poll = async () => {
|
|
23992
|
-
const stepped = await step(approvalId, signal);
|
|
24081
|
+
const stepped = await step(approvalId, signal, options?.organizationId);
|
|
23993
24082
|
if (!stepped.ok) return stepped;
|
|
23994
24083
|
if (stepped.value !== null) return {
|
|
23995
24084
|
ok: true,
|
|
@@ -25143,1656 +25232,1667 @@ function makeSmartAccountMethods(deps) {
|
|
|
25143
25232
|
};
|
|
25144
25233
|
}
|
|
25145
25234
|
//#endregion
|
|
25146
|
-
//#region ../../packages/sdk/src/
|
|
25147
|
-
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
depositInstructions: (options) => {
|
|
25151
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
25152
|
-
return runPortEffect(callOperation(deps.convexCall, summaries.depositInstructions, copyInvocationObservation(controls, {})).pipe(Effect.mapError((publicError) => ({ publicError })), Effect.tap((instructions) => emitDepositInitiated(deps.telemetry, instructions, controls))), controls, summaries.depositInstructions.name, deps.runPromise);
|
|
25153
|
-
}
|
|
25154
|
-
};
|
|
25235
|
+
//#region ../../packages/sdk/src/activity/documents.ts
|
|
25236
|
+
/** A reply its row does not admit is refused without the document body its decode error would carry. */
|
|
25237
|
+
function withoutDocumentBody(error) {
|
|
25238
|
+
return error.details?.step === "decodeOutput" ? Errors.invalidInput("documentHash", "invalid document read") : error;
|
|
25155
25239
|
}
|
|
25156
|
-
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
code: "CANCELLED",
|
|
25160
|
-
message: "Account authority changed"
|
|
25161
|
-
};
|
|
25162
|
-
const requireAccountAuthority = (isCurrent, send, step) => Effect.suspend(() => isCurrent() ? Effect.void : send({
|
|
25163
|
-
_tag: "AccountFailed",
|
|
25164
|
-
step,
|
|
25165
|
-
failure: staleAccountFailure
|
|
25166
|
-
}).pipe(Effect.andThen(Effect.fail(staleAccountFailure))));
|
|
25167
|
-
const accountCall = (name, make, isCurrent) => Effect.suspend(() => isCurrent() ? call$1(name, make()) : Effect.fail(staleAccountFailure));
|
|
25168
|
-
const modelSession = (session) => ({
|
|
25169
|
-
authUserId: String(session.authUserId),
|
|
25170
|
-
email: String(session.email)
|
|
25171
|
-
});
|
|
25172
|
-
const claimAccount = (input, authUserId, signer, isCurrent) => {
|
|
25173
|
-
const cancelled = smartAccountErrorFromCapxul("claim", Errors.cancelled({ operation: CAPXUL_OPERATIONS.smartAccount.claim }));
|
|
25174
|
-
return call$1("smart-account.claim", Effect.tryPromise({
|
|
25175
|
-
try: () => {
|
|
25176
|
-
if (!isCurrent()) throw cancelled;
|
|
25177
|
-
return signer.getAddress();
|
|
25178
|
-
},
|
|
25179
|
-
catch: (cause) => cause === cancelled ? cancelled : signerFailure(signer.source, "getAddress", cause)
|
|
25180
|
-
}).pipe(Effect.flatMap((signerAddress) => Effect.suspend(() => isCurrent() ? input.ports.smartAccount.claim({
|
|
25181
|
-
authUserId: toAuthUserId(authUserId),
|
|
25182
|
-
chainId: toChainId(input.chainId),
|
|
25183
|
-
signerAddress
|
|
25184
|
-
}) : Effect.fail(cancelled)))));
|
|
25185
|
-
};
|
|
25186
|
-
const hasDurableClaim = (account) => account.deployedAt !== null && account.claimedAt !== null && account.signerAddress !== null;
|
|
25187
|
-
const profileComplete = (profile) => profile?.onboarded === true;
|
|
25188
|
-
const sessionProfile = (input, session) => {
|
|
25189
|
-
if (session === null) return Effect.succeed({
|
|
25190
|
-
session: null,
|
|
25191
|
-
profileComplete: false
|
|
25192
|
-
});
|
|
25193
|
-
return call$1("identity.load-by-auth-user-id", input.ports.identity.loadByAuthUserId(session.authUserId)).pipe(Effect.map((profile) => ({
|
|
25194
|
-
session: modelSession(session),
|
|
25195
|
-
profileComplete: profileComplete(profile)
|
|
25196
|
-
})));
|
|
25197
|
-
};
|
|
25198
|
-
const accountLane = (input, config, session, send, isCurrent) => Effect.gen(function* () {
|
|
25199
|
-
let step = "wallet";
|
|
25200
|
-
const failAt = (at, failure) => send({
|
|
25201
|
-
_tag: "AccountFailed",
|
|
25202
|
-
step: at,
|
|
25203
|
-
failure
|
|
25204
|
-
}).pipe(Effect.andThen(Effect.fail(failure)));
|
|
25205
|
-
yield* requireAccountAuthority(isCurrent, send, step);
|
|
25206
|
-
step = "identity";
|
|
25207
|
-
yield* send({
|
|
25208
|
-
_tag: "AccountAt",
|
|
25209
|
-
step: "identity"
|
|
25210
|
-
});
|
|
25211
|
-
step = "provision";
|
|
25212
|
-
yield* send({
|
|
25213
|
-
_tag: "AccountAt",
|
|
25214
|
-
step: "provision"
|
|
25215
|
-
});
|
|
25216
|
-
const existing = yield* Effect.result(accountCall("smart-account.load-by-auth-user-id", () => input.ports.smartAccount.loadByAuthUserId(toAuthUserId(session.authUserId)), isCurrent));
|
|
25217
|
-
yield* requireAccountAuthority(isCurrent, send, step);
|
|
25218
|
-
if (Result.isFailure(existing)) return yield* failAt("provision", existing.failure);
|
|
25219
|
-
const provisioned = yield* Effect.result(existing.success === null ? accountCall("smart-account.provision", () => input.ports.smartAccount.provision({
|
|
25220
|
-
authUserId: toAuthUserId(session.authUserId),
|
|
25221
|
-
chainId: toChainId(input.chainId),
|
|
25222
|
-
email: toEmail(session.email)
|
|
25223
|
-
}), isCurrent) : Effect.succeed(existing.success));
|
|
25224
|
-
yield* requireAccountAuthority(isCurrent, send, step);
|
|
25225
|
-
if (Result.isFailure(provisioned)) return yield* failAt("provision", provisioned.failure);
|
|
25226
|
-
const account = provisioned.success;
|
|
25227
|
-
const address = String(account.smartAccountAddress);
|
|
25228
|
-
yield* send({
|
|
25229
|
-
_tag: "AccountDerived",
|
|
25230
|
-
address
|
|
25231
|
-
});
|
|
25232
|
-
yield* requireAccountAuthority(isCurrent, send, step);
|
|
25233
|
-
if (hasDurableClaim(account)) return yield* send({
|
|
25234
|
-
_tag: "AccountClaimed",
|
|
25235
|
-
address
|
|
25236
|
-
});
|
|
25237
|
-
if (config.requirement !== "deployed" || input.signer === void 0) return;
|
|
25238
|
-
yield* requireAccountAuthority(isCurrent, send, step);
|
|
25239
|
-
step = "deploy";
|
|
25240
|
-
yield* send({ _tag: "AccountClaiming" });
|
|
25241
|
-
const claimed = yield* Effect.result(claimAccount(input, session.authUserId, input.signer, isCurrent));
|
|
25242
|
-
yield* requireAccountAuthority(isCurrent, send, step);
|
|
25243
|
-
if (Result.isFailure(claimed)) return yield* failAt("deploy", claimed.failure);
|
|
25244
|
-
return yield* send(hasDurableClaim(claimed.success) ? {
|
|
25245
|
-
_tag: "AccountClaimed",
|
|
25246
|
-
address: String(claimed.success.smartAccountAddress)
|
|
25247
|
-
} : {
|
|
25248
|
-
_tag: "AccountDerived",
|
|
25249
|
-
address: String(claimed.success.smartAccountAddress)
|
|
25250
|
-
});
|
|
25251
|
-
});
|
|
25252
|
-
//#endregion
|
|
25253
|
-
//#region ../../packages/sdk/src/access/identity/spec.ts
|
|
25254
|
-
const SETTLED_BY_TRANSITION_TAGS = /* @__PURE__ */ new Set([
|
|
25255
|
-
"RestoreSession",
|
|
25256
|
-
"ResumeOtpEntry",
|
|
25257
|
-
"RestoreOtpEntry",
|
|
25258
|
-
"Reset",
|
|
25259
|
-
"SessionRestored",
|
|
25260
|
-
"SessionAbsent",
|
|
25261
|
-
"AttachOrganization",
|
|
25262
|
-
"LeaveOrganization",
|
|
25263
|
-
"ObserveOrganization"
|
|
25264
|
-
]);
|
|
25265
|
-
function isSettledByTransition(event) {
|
|
25266
|
-
return SETTLED_BY_TRANSITION_TAGS.has(event._tag);
|
|
25240
|
+
/** Schema errors must not retain document bodies. */
|
|
25241
|
+
function decodeDocumentRead(schema, input) {
|
|
25242
|
+
return Effect.fromOption(Schema.decodeUnknownOption(schema)(input)).pipe(Effect.mapError(() => ({ publicError: Errors.invalidInput("documentHash", "invalid document read") })));
|
|
25267
25243
|
}
|
|
25268
|
-
|
|
25269
|
-
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
|
|
25273
|
-
|
|
25244
|
+
function makePaymentDocumentsMethods(deps) {
|
|
25245
|
+
/**
|
|
25246
|
+
* One document read by its row. The pair is decoded before anything is sent,
|
|
25247
|
+
* and a reply the row does not admit is refused without the body it carried.
|
|
25248
|
+
*/
|
|
25249
|
+
const readDocument = (row, ref, options) => {
|
|
25250
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
25251
|
+
return runPortEffect(Effect.gen(function* () {
|
|
25252
|
+
const pair = yield* decodeDocumentRead(PaymentDocumentReadRefSchema, ref);
|
|
25253
|
+
return yield* callOperation(deps.convexCall, row, copyInvocationObservation(controls, pair)).pipe(Effect.mapError((error) => ({ publicError: withoutDocumentBody(error) })));
|
|
25254
|
+
}), controls, row.name, deps.runPromise);
|
|
25274
25255
|
};
|
|
25275
25256
|
return {
|
|
25276
|
-
|
|
25277
|
-
|
|
25278
|
-
|
|
25279
|
-
slot: identitySlot,
|
|
25280
|
-
transition: (state, event) => transition(state, event, config),
|
|
25281
|
-
recoverFailure: recoverIdentityFailure,
|
|
25282
|
-
invalidates: (event) => event._tag === "Reset" ? [
|
|
25283
|
-
IDENTITY_SLOT.auth,
|
|
25284
|
-
IDENTITY_SLOT.session,
|
|
25285
|
-
IDENTITY_SLOT.account,
|
|
25286
|
-
IDENTITY_SLOT.org
|
|
25287
|
-
] : event._tag === "SignOut" ? [
|
|
25288
|
-
IDENTITY_SLOT.session,
|
|
25289
|
-
IDENTITY_SLOT.account,
|
|
25290
|
-
IDENTITY_SLOT.org
|
|
25291
|
-
] : event._tag === "LeaveOrganization" || event._tag === "AttachOrganization" ? [IDENTITY_SLOT.org] : [],
|
|
25292
|
-
work: ({ state, event }) => {
|
|
25293
|
-
const activeSession = state.phase === "authenticated" ? state.session : null;
|
|
25294
|
-
const org = state.phase === "authenticated" && state.account.at === "claimed" ? state.account.org : null;
|
|
25295
|
-
if (isSettledByTransition(event)) return void 0;
|
|
25296
|
-
switch (event._tag) {
|
|
25297
|
-
case "RequestOtp": return {
|
|
25298
|
-
slot: IDENTITY_SLOT.auth,
|
|
25299
|
-
port: "auth-client",
|
|
25300
|
-
run: (send, controls) => call$1("auth-client.send-otp", input.ports.authClient.sendOtp(copyInvocationObservation(controls, { email: toEmail(event.email) }))).pipe(Effect.andThen(call$1("clock.now", input.ports.clock.now)), Effect.flatMap((at) => send({
|
|
25301
|
-
_tag: "OtpSent",
|
|
25302
|
-
at
|
|
25303
|
-
})), Effect.tapError((failure) => send({
|
|
25304
|
-
_tag: "OtpFailed",
|
|
25305
|
-
failure
|
|
25306
|
-
})))
|
|
25307
|
-
};
|
|
25308
|
-
case "VerifyOtp": return {
|
|
25309
|
-
slot: IDENTITY_SLOT.auth,
|
|
25310
|
-
port: "auth-client",
|
|
25311
|
-
run: (send, controls) => call$1("auth-client.verify-otp", input.ports.authClient.verifyOtp(copyInvocationObservation(controls, {
|
|
25312
|
-
email: toEmail(event.email),
|
|
25313
|
-
otp: event.otp
|
|
25314
|
-
}))).pipe(Effect.tap((session) => Effect.sync(() => {
|
|
25315
|
-
authority.recordProviderSession(session);
|
|
25316
|
-
})), Effect.flatMap((session) => sessionProfile(input, session).pipe(Effect.flatMap((loaded) => send({
|
|
25317
|
-
_tag: "Verified",
|
|
25318
|
-
session: loaded.session ?? modelSession(session),
|
|
25319
|
-
profileComplete: loaded.profileComplete
|
|
25320
|
-
})))), Effect.tapError((failure) => send({
|
|
25321
|
-
_tag: "VerifyFailed",
|
|
25322
|
-
failure
|
|
25323
|
-
})))
|
|
25324
|
-
};
|
|
25325
|
-
case "ReadSession": return {
|
|
25326
|
-
slot: IDENTITY_SLOT.session,
|
|
25327
|
-
port: "auth-client",
|
|
25328
|
-
run: (send, controls) => call$1("auth-client.get-session", input.ports.authClient.getSession(copyInvocationObservation(controls, {}))).pipe(Effect.tap((session) => Effect.sync(() => {
|
|
25329
|
-
authority.recordProviderSession(session, activeSession?.authUserId);
|
|
25330
|
-
})), Effect.flatMap((session) => sessionProfile(input, session)), Effect.flatMap((loaded) => send({
|
|
25331
|
-
_tag: "SessionRead",
|
|
25332
|
-
...loaded
|
|
25333
|
-
})), Effect.tapError((failure) => Effect.sync(() => {
|
|
25334
|
-
authority.recordProviderSession(null, failure.code === "NOT_AUTHENTICATED" ? activeSession?.authUserId : void 0);
|
|
25335
|
-
}).pipe(Effect.andThen(activeSession !== null && failure.code === "NOT_AUTHENTICATED" ? Effect.void : send({
|
|
25336
|
-
_tag: "SessionReadFailed",
|
|
25337
|
-
failure
|
|
25338
|
-
})))))
|
|
25339
|
-
};
|
|
25340
|
-
case "SignOut": return {
|
|
25341
|
-
slot: IDENTITY_SLOT.auth,
|
|
25342
|
-
port: "auth-client",
|
|
25343
|
-
run: (send, controls) => call$1("auth-client.sign-out", input.ports.authClient.signOut(copyInvocationObservation(controls, {}))).pipe(Effect.andThen(send({ _tag: "SignedOut" })), Effect.tapError((failure) => send({
|
|
25344
|
-
_tag: "SignOutFailed",
|
|
25345
|
-
failure
|
|
25346
|
-
})), Effect.ensuring(Effect.sync(() => {
|
|
25347
|
-
authority.recordProviderSession(null);
|
|
25348
|
-
})))
|
|
25349
|
-
};
|
|
25350
|
-
case "EnsureAccount":
|
|
25351
|
-
case "RetryAccount": return activeSession === null ? void 0 : {
|
|
25352
|
-
slot: IDENTITY_SLOT.account,
|
|
25353
|
-
port: "smart-account",
|
|
25354
|
-
run: (send) => {
|
|
25355
|
-
const captured = authority.snapshot();
|
|
25356
|
-
return accountLane(input, config, activeSession, send, () => authority.matches(captured, activeSession.authUserId));
|
|
25357
|
-
}
|
|
25358
|
-
};
|
|
25359
|
-
case "ClaimAccount": {
|
|
25360
|
-
const signer = input.signer;
|
|
25361
|
-
const address = state.phase === "authenticated" && state.account.at === "claiming" ? state.account.address : null;
|
|
25362
|
-
return address === null || activeSession === null || signer === void 0 ? void 0 : {
|
|
25363
|
-
slot: IDENTITY_SLOT.account,
|
|
25364
|
-
port: "smart-account",
|
|
25365
|
-
run: (send) => {
|
|
25366
|
-
const captured = authority.snapshot();
|
|
25367
|
-
const isCurrent = () => authority.matches(captured, activeSession.authUserId);
|
|
25368
|
-
return Effect.gen(function* () {
|
|
25369
|
-
const claimed = yield* Effect.result(claimAccount(input, activeSession.authUserId, signer, isCurrent));
|
|
25370
|
-
yield* requireAccountAuthority(isCurrent, send, "deploy");
|
|
25371
|
-
yield* Result.isFailure(claimed) ? send({
|
|
25372
|
-
_tag: "AccountFailed",
|
|
25373
|
-
step: "deploy",
|
|
25374
|
-
failure: claimed.failure
|
|
25375
|
-
}).pipe(Effect.andThen(Effect.fail(claimed.failure))) : send(hasDurableClaim(claimed.success) ? {
|
|
25376
|
-
_tag: "AccountClaimed",
|
|
25377
|
-
address
|
|
25378
|
-
} : {
|
|
25379
|
-
_tag: "AccountDerived",
|
|
25380
|
-
address
|
|
25381
|
-
});
|
|
25382
|
-
});
|
|
25383
|
-
}
|
|
25384
|
-
};
|
|
25385
|
-
}
|
|
25386
|
-
case "CreateOrganization": return input.organizationSetup === void 0 || org === null || org.orgId !== null ? void 0 : {
|
|
25387
|
-
slot: IDENTITY_SLOT.org,
|
|
25388
|
-
port: "organization-setup",
|
|
25389
|
-
run: (send, controls) => beginOrganization$1(input.organizationSetup, event.draft, send, controls)
|
|
25390
|
-
};
|
|
25391
|
-
default: return absurd(event);
|
|
25392
|
-
}
|
|
25393
|
-
}
|
|
25257
|
+
get: (ref, options) => readDocument(documents.get, ref, options),
|
|
25258
|
+
verify: (ref, options) => readDocument(documents.verify, ref, options),
|
|
25259
|
+
render: (ref, options) => readDocument(documents.render, ref, options)
|
|
25394
25260
|
};
|
|
25395
|
-
}
|
|
25261
|
+
}
|
|
25396
25262
|
//#endregion
|
|
25397
|
-
//#region ../../packages/sdk/src/
|
|
25398
|
-
|
|
25399
|
-
|
|
25400
|
-
|
|
25401
|
-
|
|
25402
|
-
|
|
25403
|
-
|
|
25404
|
-
|
|
25405
|
-
|
|
25406
|
-
|
|
25407
|
-
|
|
25408
|
-
|
|
25409
|
-
|
|
25410
|
-
|
|
25411
|
-
|
|
25412
|
-
|
|
25413
|
-
|
|
25414
|
-
|
|
25415
|
-
|
|
25416
|
-
if (stopped) return;
|
|
25417
|
-
advanceGeneration();
|
|
25418
|
-
authUserId = null;
|
|
25263
|
+
//#region ../../packages/sdk/src/activity/stitching.ts
|
|
25264
|
+
/** Epoch milliseconds of the block that carried the row. */
|
|
25265
|
+
function blockMillis(row) {
|
|
25266
|
+
return Number(row.timestamp) * 1e3;
|
|
25267
|
+
}
|
|
25268
|
+
/** `0x1234…abcd` — the counterparty of an unclaimed transfer is an address. */
|
|
25269
|
+
function shortAddressLabel(address) {
|
|
25270
|
+
return address.length <= 12 ? address : `${address.slice(0, 6)}…${address.slice(-4)}`;
|
|
25271
|
+
}
|
|
25272
|
+
function directionFor(safe, transfer) {
|
|
25273
|
+
const from = transfer.from.toLowerCase();
|
|
25274
|
+
const to = transfer.to.toLowerCase();
|
|
25275
|
+
if (from === safe && to === safe) return "self";
|
|
25276
|
+
return to === safe ? "in" : "out";
|
|
25277
|
+
}
|
|
25278
|
+
function withAnnotation(item, annotation) {
|
|
25279
|
+
return annotation?.counterpartyLabel === void 0 ? item : {
|
|
25280
|
+
...item,
|
|
25281
|
+
counterpartyLabel: annotation.counterpartyLabel
|
|
25419
25282
|
};
|
|
25420
|
-
|
|
25421
|
-
|
|
25422
|
-
|
|
25423
|
-
|
|
25424
|
-
|
|
25425
|
-
|
|
25426
|
-
|
|
25427
|
-
|
|
25428
|
-
|
|
25429
|
-
|
|
25430
|
-
|
|
25431
|
-
|
|
25432
|
-
|
|
25433
|
-
|
|
25434
|
-
|
|
25435
|
-
|
|
25436
|
-
},
|
|
25437
|
-
bindState: (read) => {
|
|
25438
|
-
readState = read;
|
|
25439
|
-
},
|
|
25440
|
-
recordProviderSession: (session, previousAuthUserId) => {
|
|
25441
|
-
if (previousAuthUserId != null && String(session?.authUserId ?? "") !== previousAuthUserId) revoke();
|
|
25442
|
-
sessionStore.current = session;
|
|
25283
|
+
}
|
|
25284
|
+
/**
|
|
25285
|
+
* An indexed settlement confirms the intent. A missing row can be outside
|
|
25286
|
+
* this page or still backfilling; only the correction owner can prove rollback.
|
|
25287
|
+
*/
|
|
25288
|
+
function confirmPayment(intent, chain, expected) {
|
|
25289
|
+
if (chain !== void 0 && matchesPaymentFact(chain, expected)) return {
|
|
25290
|
+
...intent,
|
|
25291
|
+
status: "settled",
|
|
25292
|
+
...intent.execution === void 0 ? {} : { execution: {
|
|
25293
|
+
status: "settled",
|
|
25294
|
+
retryRequired: false
|
|
25295
|
+
} },
|
|
25296
|
+
receipt: {
|
|
25297
|
+
chainId: chain.chainId,
|
|
25298
|
+
transactionHash: chain.txHash
|
|
25443
25299
|
},
|
|
25444
|
-
|
|
25445
|
-
|
|
25446
|
-
|
|
25447
|
-
|
|
25448
|
-
|
|
25300
|
+
updatedAt: blockMillis(chain)
|
|
25301
|
+
};
|
|
25302
|
+
return intent;
|
|
25303
|
+
}
|
|
25304
|
+
function matchesPaymentFact(chain, expected) {
|
|
25305
|
+
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;
|
|
25306
|
+
}
|
|
25307
|
+
/**
|
|
25308
|
+
* One unclaimed Transfer as an Activity row, or `null` when this SDK cannot
|
|
25309
|
+
* denominate its asset. The indexer serves only configured assets, so a row
|
|
25310
|
+
* the registry does not know means the SDK and that deployment disagree; it is
|
|
25311
|
+
* dropped rather than shown with a guessed currency.
|
|
25312
|
+
*/
|
|
25313
|
+
function isActivityVisible(item) {
|
|
25314
|
+
return !(item.kind === "payment" && item.direction === "in" && item.execution?.notSent);
|
|
25315
|
+
}
|
|
25316
|
+
function transferItem(safe, transfer, annotation) {
|
|
25317
|
+
const asset = configuredMoneyAssetById(transfer.assetId);
|
|
25318
|
+
if (asset === null) return null;
|
|
25319
|
+
const direction = directionFor(safe.toLowerCase(), transfer);
|
|
25320
|
+
const counterparty = direction === "in" ? transfer.from : transfer.to;
|
|
25321
|
+
const at = blockMillis(transfer);
|
|
25322
|
+
return withAnnotation({
|
|
25323
|
+
kind: "movement",
|
|
25324
|
+
id: transfer.id,
|
|
25325
|
+
classification: direction === "in" ? "deposit" : "movement_only_outflow",
|
|
25326
|
+
amount: {
|
|
25327
|
+
asset: asset.assetId,
|
|
25328
|
+
value: formatUnits(transfer.amount, asset.decimals)
|
|
25449
25329
|
},
|
|
25450
|
-
|
|
25451
|
-
|
|
25452
|
-
|
|
25453
|
-
|
|
25454
|
-
|
|
25455
|
-
authUserId = null;
|
|
25330
|
+
direction,
|
|
25331
|
+
counterpartyLabel: shortAddressLabel(counterparty),
|
|
25332
|
+
receipt: {
|
|
25333
|
+
chainId: transfer.chainId,
|
|
25334
|
+
transactionHash: transfer.txHash
|
|
25456
25335
|
},
|
|
25457
|
-
|
|
25458
|
-
|
|
25459
|
-
|
|
25460
|
-
if (expectedAuthUserId === null) return state.phase !== "authenticated" && sessionStore.current === null;
|
|
25461
|
-
return state.phase === "authenticated" && state.session.authUserId === expectedAuthUserId && String(sessionStore.current?.authUserId ?? "") === expectedAuthUserId;
|
|
25462
|
-
}
|
|
25463
|
-
};
|
|
25336
|
+
createdAt: at,
|
|
25337
|
+
updatedAt: at
|
|
25338
|
+
}, annotation);
|
|
25464
25339
|
}
|
|
25465
|
-
//#endregion
|
|
25466
|
-
//#region ../../packages/sdk/src/platform/actor/transitions.ts
|
|
25467
25340
|
/**
|
|
25468
|
-
*
|
|
25469
|
-
* the defect reporter. An observer that throws is dropped and reported as a
|
|
25470
|
-
* defect; it never owns actor truth.
|
|
25341
|
+
* Money in, money out and the pending share, one bucket per exact asset.
|
|
25471
25342
|
*/
|
|
25472
|
-
|
|
25473
|
-
const
|
|
25474
|
-
const
|
|
25475
|
-
|
|
25476
|
-
|
|
25477
|
-
|
|
25478
|
-
|
|
25479
|
-
|
|
25480
|
-
|
|
25481
|
-
|
|
25482
|
-
|
|
25483
|
-
bootTransitionObserver(record);
|
|
25484
|
-
} catch (cause) {
|
|
25485
|
-
bootTransitionObserver = void 0;
|
|
25486
|
-
defect(cause);
|
|
25487
|
-
}
|
|
25488
|
-
for (const observer of transitionObservers) try {
|
|
25489
|
-
observer(record);
|
|
25490
|
-
} catch (cause) {
|
|
25491
|
-
transitionObservers.delete(observer);
|
|
25492
|
-
defect(cause);
|
|
25493
|
-
}
|
|
25494
|
-
}).pipe(Effect.andThen(logIdentityTransition(record)));
|
|
25495
|
-
const notify = (state) => {
|
|
25496
|
-
for (const observer of stateObservers) try {
|
|
25497
|
-
observer(state);
|
|
25498
|
-
} catch (cause) {
|
|
25499
|
-
stateObservers.delete(observer);
|
|
25500
|
-
defect(cause);
|
|
25501
|
-
}
|
|
25502
|
-
};
|
|
25503
|
-
return {
|
|
25504
|
-
stateObservers,
|
|
25505
|
-
transitionObservers,
|
|
25506
|
-
defect,
|
|
25507
|
-
emit,
|
|
25508
|
-
notify
|
|
25509
|
-
};
|
|
25510
|
-
};
|
|
25511
|
-
/** One machine's transition records and the failure a refused or failed reply carries. */
|
|
25512
|
-
const makeTransitionRecords = (spec, slotEpochs) => {
|
|
25513
|
-
const makeFailure = (state, event, reason, details, cause) => new ActorFailure(reason, spec.machine, spec.label(state), event._tag, details, cause);
|
|
25514
|
-
const nonApplied = (state, event, reason, outcome, env, failure) => {
|
|
25515
|
-
const slot = env.origin === null ? spec.slot(env.event) : env.origin.slot;
|
|
25516
|
-
const base = {
|
|
25517
|
-
machine: spec.machine,
|
|
25518
|
-
state: spec.label(state),
|
|
25519
|
-
event: event._tag,
|
|
25520
|
-
slot,
|
|
25521
|
-
epoch: env.origin?.epoch ?? slotEpochs.get(slot) ?? 0,
|
|
25522
|
-
duration_ms: duration(env.startedAt),
|
|
25523
|
-
...withCarriage(env.invocation)
|
|
25524
|
-
};
|
|
25525
|
-
const record = outcome === "refused" ? {
|
|
25526
|
-
...base,
|
|
25527
|
-
outcome,
|
|
25528
|
-
refusal_code: reason
|
|
25529
|
-
} : {
|
|
25530
|
-
...base,
|
|
25531
|
-
outcome,
|
|
25532
|
-
error_code: reason,
|
|
25533
|
-
...failure === void 0 ? {} : { failure }
|
|
25343
|
+
function summarizeItems(items) {
|
|
25344
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
25345
|
+
for (const item of items) {
|
|
25346
|
+
const metadata = configuredMoneyAssetById(item.amount.asset);
|
|
25347
|
+
if (metadata === null) continue;
|
|
25348
|
+
const key = item.amount.asset;
|
|
25349
|
+
const bucket = buckets.get(key) ?? {
|
|
25350
|
+
asset: item.amount.asset,
|
|
25351
|
+
in: 0n,
|
|
25352
|
+
out: 0n,
|
|
25353
|
+
pendingIn: 0n
|
|
25534
25354
|
};
|
|
25535
|
-
|
|
25536
|
-
|
|
25537
|
-
|
|
25538
|
-
|
|
25539
|
-
|
|
25540
|
-
|
|
25541
|
-
|
|
25542
|
-
|
|
25543
|
-
|
|
25544
|
-
|
|
25545
|
-
|
|
25546
|
-
|
|
25547
|
-
|
|
25548
|
-
|
|
25549
|
-
|
|
25550
|
-
|
|
25551
|
-
|
|
25552
|
-
|
|
25553
|
-
|
|
25355
|
+
const raw = rawUnits(item.amount.value, metadata.decimals);
|
|
25356
|
+
if (item.kind === "payment" && item.status !== "settled") {
|
|
25357
|
+
if (item.direction === "in" && item.status !== "cancelled" && item.status !== "failed" && !item.execution?.notSent) bucket.pendingIn += raw;
|
|
25358
|
+
} else if (item.direction === "in") bucket.in += raw;
|
|
25359
|
+
else if (item.direction === "out") bucket.out += raw;
|
|
25360
|
+
buckets.set(key, bucket);
|
|
25361
|
+
}
|
|
25362
|
+
return [...buckets.values()].map((bucket) => ({
|
|
25363
|
+
asset: bucket.asset,
|
|
25364
|
+
in: bucket.in.toString(),
|
|
25365
|
+
out: bucket.out.toString(),
|
|
25366
|
+
net: (bucket.in - bucket.out).toString(),
|
|
25367
|
+
pendingIn: bucket.pendingIn.toString()
|
|
25368
|
+
}));
|
|
25369
|
+
}
|
|
25370
|
+
/** The inverse of `formatUnits`, kept exact: `"1.25"` at 6 decimals is `1250000n`. */
|
|
25371
|
+
function rawUnits(value, decimals) {
|
|
25372
|
+
const negative = value.startsWith("-");
|
|
25373
|
+
const [whole = "0", fraction = ""] = (negative ? value.slice(1) : value).split(".");
|
|
25374
|
+
const raw = BigInt(`${whole}${fraction.padEnd(decimals, "0").slice(0, decimals)}`);
|
|
25375
|
+
return negative ? -raw : raw;
|
|
25376
|
+
}
|
|
25554
25377
|
//#endregion
|
|
25555
|
-
//#region ../../packages/sdk/src/
|
|
25556
|
-
|
|
25557
|
-
|
|
25558
|
-
|
|
25559
|
-
|
|
25560
|
-
|
|
25561
|
-
|
|
25562
|
-
|
|
25563
|
-
const
|
|
25564
|
-
|
|
25565
|
-
|
|
25566
|
-
|
|
25567
|
-
|
|
25568
|
-
|
|
25569
|
-
|
|
25570
|
-
|
|
25571
|
-
|
|
25572
|
-
|
|
25573
|
-
|
|
25574
|
-
|
|
25575
|
-
|
|
25576
|
-
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
|
|
25580
|
-
|
|
25581
|
-
|
|
25582
|
-
|
|
25583
|
-
|
|
25584
|
-
|
|
25378
|
+
//#region ../../packages/sdk/src/activity/cursor.ts
|
|
25379
|
+
function actorKey(actor) {
|
|
25380
|
+
return actor.kind === "account" ? `account:${actor.accountId}` : `organization:${actor.orgId}`;
|
|
25381
|
+
}
|
|
25382
|
+
function scopeKey(scope) {
|
|
25383
|
+
return `${scope.principalAccountId}|${actorKey(scope.actor)}|${scope.safe?.toLowerCase() ?? "none"}`;
|
|
25384
|
+
}
|
|
25385
|
+
function selectionKey(params) {
|
|
25386
|
+
const filter = params?.filter;
|
|
25387
|
+
return JSON.stringify([
|
|
25388
|
+
params?.range?.from ?? null,
|
|
25389
|
+
params?.range?.to ?? null,
|
|
25390
|
+
filter?.kind ?? null,
|
|
25391
|
+
filter?.direction ?? null,
|
|
25392
|
+
filter?.status === void 0 ? null : [...new Set(filter.status)].sort()
|
|
25393
|
+
]);
|
|
25394
|
+
}
|
|
25395
|
+
function validString(value, max) {
|
|
25396
|
+
return typeof value === "string" && value.length <= max;
|
|
25397
|
+
}
|
|
25398
|
+
function validPosition(position) {
|
|
25399
|
+
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);
|
|
25400
|
+
}
|
|
25401
|
+
function isActivityCursor(cursor, scope, selection) {
|
|
25402
|
+
return typeof cursor === "object" && cursor !== null && cursorScopeMatches(cursor, scope, selection) && cursorProgressIsValid(cursor);
|
|
25403
|
+
}
|
|
25404
|
+
/** The cursor was issued for this scope and filter selection, at a valid checkpoint. */
|
|
25405
|
+
function cursorScopeMatches(cursor, scope, selection) {
|
|
25406
|
+
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");
|
|
25407
|
+
}
|
|
25408
|
+
/** Each source's progress is well-formed and bounded. */
|
|
25409
|
+
function cursorProgressIsValid(cursor) {
|
|
25410
|
+
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);
|
|
25411
|
+
}
|
|
25412
|
+
function parseCursor(value, scope, selection) {
|
|
25413
|
+
if (value === void 0) return null;
|
|
25414
|
+
if (value.length > 4e4) throw Errors.invalidInput("cursor", "invalid Activity cursor");
|
|
25415
|
+
const cursor = JSON.parse(value);
|
|
25416
|
+
if (!isActivityCursor(cursor, scope, selection)) throw Errors.invalidInput("cursor", "Activity scope or filters changed; restart from the first page");
|
|
25417
|
+
return cursor;
|
|
25418
|
+
}
|
|
25419
|
+
function requireParams(params) {
|
|
25420
|
+
const limit = params?.limit ?? 25;
|
|
25421
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw Errors.invalidInput("limit", "must be an integer from 1 to 100");
|
|
25422
|
+
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");
|
|
25423
|
+
const f = params?.filter;
|
|
25424
|
+
if (f?.kind !== void 0 && f.kind !== "payment" && f.kind !== "movement" || f?.direction !== void 0 && ![
|
|
25425
|
+
"in",
|
|
25426
|
+
"out",
|
|
25427
|
+
"self"
|
|
25428
|
+
].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");
|
|
25429
|
+
}
|
|
25430
|
+
function matchesFilter(item, filter) {
|
|
25431
|
+
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));
|
|
25432
|
+
}
|
|
25433
|
+
//#endregion
|
|
25434
|
+
//#region ../../packages/sdk/src/activity/indexed-pages.ts
|
|
25435
|
+
function chainRead(deps, table, input) {
|
|
25436
|
+
return deps.indexer.read(table, input).pipe(Effect.catch((error) => {
|
|
25437
|
+
if (error.publicError.details?.reason === "scope-refused" || ![
|
|
25438
|
+
"NETWORK_ERROR",
|
|
25439
|
+
"RATE_LIMITED",
|
|
25440
|
+
"PROVIDER_ERROR",
|
|
25441
|
+
"CAPABILITY_UNAVAILABLE",
|
|
25442
|
+
"TIMEOUT"
|
|
25443
|
+
].includes(error.publicCode)) return Effect.fail({ publicError: error.publicError });
|
|
25444
|
+
return Effect.succeed(null);
|
|
25445
|
+
}));
|
|
25446
|
+
}
|
|
25447
|
+
function boundedRange(range, checkpoint) {
|
|
25448
|
+
return {
|
|
25449
|
+
...range?.from === void 0 ? {} : { from: range.from },
|
|
25450
|
+
to: Math.min(checkpoint, range?.to ?? checkpoint)
|
|
25585
25451
|
};
|
|
25586
|
-
|
|
25587
|
-
|
|
25588
|
-
|
|
25589
|
-
|
|
25590
|
-
|
|
25591
|
-
|
|
25592
|
-
|
|
25593
|
-
|
|
25594
|
-
|
|
25595
|
-
|
|
25596
|
-
|
|
25597
|
-
|
|
25598
|
-
|
|
25599
|
-
|
|
25600
|
-
|
|
25601
|
-
|
|
25602
|
-
|
|
25603
|
-
|
|
25604
|
-
|
|
25605
|
-
|
|
25606
|
-
|
|
25607
|
-
|
|
25608
|
-
|
|
25609
|
-
|
|
25610
|
-
|
|
25452
|
+
}
|
|
25453
|
+
function unavailablePage(deps, scope, controls, params, state, first) {
|
|
25454
|
+
if (first) return fallbackPage(deps, scope, controls, params, state, true);
|
|
25455
|
+
return Effect.annotateCurrentSpan({
|
|
25456
|
+
read_source: "indexer",
|
|
25457
|
+
read_warning: "indexer-unavailable"
|
|
25458
|
+
}).pipe(Effect.as({
|
|
25459
|
+
items: [],
|
|
25460
|
+
nextCursor: null,
|
|
25461
|
+
checkpoint: state.checkpoint,
|
|
25462
|
+
observedAt: Date.now(),
|
|
25463
|
+
readWarning: "indexer-unavailable"
|
|
25464
|
+
}));
|
|
25465
|
+
}
|
|
25466
|
+
function readTransfers(deps, safe, range, state, wanted, first) {
|
|
25467
|
+
const from = Math.max(0, Math.ceil((range.from ?? 0) / 1e3));
|
|
25468
|
+
const to = Math.floor(range.to / 1e3);
|
|
25469
|
+
if (state.transferDone || from > to || !wanted && !first) return Effect.succeed([]);
|
|
25470
|
+
return chainRead(deps, "transfer", {
|
|
25471
|
+
safe: toAddress(safe),
|
|
25472
|
+
limit: wanted ? 100 : 1,
|
|
25473
|
+
timeline: {
|
|
25474
|
+
from,
|
|
25475
|
+
to,
|
|
25476
|
+
...state.transferBefore === null ? {} : { before: state.transferBefore }
|
|
25611
25477
|
}
|
|
25612
|
-
yield* finish(origin, state);
|
|
25613
25478
|
});
|
|
25614
|
-
}
|
|
25615
|
-
|
|
25616
|
-
|
|
25617
|
-
|
|
25618
|
-
|
|
25619
|
-
|
|
25620
|
-
|
|
25621
|
-
|
|
25622
|
-
const { stateObservers, transitionObservers, defect, emit, notify } = makeActorObservers(options);
|
|
25623
|
-
const { makeFailure, nonApplied, applied } = makeTransitionRecords(spec, slotEpochs);
|
|
25624
|
-
let stopped = false;
|
|
25625
|
-
const settleLaneExit = makeLaneSettlement({
|
|
25626
|
-
spec,
|
|
25627
|
-
cell,
|
|
25628
|
-
slots,
|
|
25629
|
-
observers: {
|
|
25630
|
-
emit,
|
|
25631
|
-
notify,
|
|
25632
|
-
defect
|
|
25633
|
-
},
|
|
25634
|
-
records: {
|
|
25635
|
-
makeFailure,
|
|
25636
|
-
nonApplied,
|
|
25637
|
-
applied
|
|
25638
|
-
}
|
|
25479
|
+
}
|
|
25480
|
+
function readPayments(deps, scope, controls, range, state, wanted) {
|
|
25481
|
+
const done = state.paymentDone || !wanted;
|
|
25482
|
+
if (state.paymentPending.length > 0 || done) return Effect.succeed({
|
|
25483
|
+
items: [],
|
|
25484
|
+
pending: state.paymentPending,
|
|
25485
|
+
cursor: state.paymentCursor,
|
|
25486
|
+
done
|
|
25639
25487
|
});
|
|
25640
|
-
|
|
25641
|
-
|
|
25642
|
-
|
|
25643
|
-
|
|
25644
|
-
|
|
25645
|
-
|
|
25646
|
-
|
|
25647
|
-
|
|
25648
|
-
|
|
25649
|
-
|
|
25650
|
-
|
|
25651
|
-
|
|
25652
|
-
|
|
25653
|
-
|
|
25654
|
-
|
|
25655
|
-
|
|
25656
|
-
|
|
25657
|
-
|
|
25658
|
-
|
|
25659
|
-
|
|
25660
|
-
|
|
25661
|
-
|
|
25662
|
-
|
|
25663
|
-
const
|
|
25664
|
-
|
|
25665
|
-
|
|
25666
|
-
|
|
25667
|
-
|
|
25668
|
-
|
|
25669
|
-
|
|
25670
|
-
|
|
25671
|
-
|
|
25672
|
-
}
|
|
25673
|
-
|
|
25674
|
-
|
|
25675
|
-
|
|
25676
|
-
|
|
25677
|
-
|
|
25678
|
-
|
|
25679
|
-
yield* emitApplied(slot, slotEpochs.get(slot) ?? 0);
|
|
25680
|
-
notify(next);
|
|
25681
|
-
if (env.reply !== null) yield* Deferred.succeed(env.reply, next).pipe(Effect.asVoid);
|
|
25682
|
-
return;
|
|
25683
|
-
}
|
|
25684
|
-
const previous = slots.get(work.slot);
|
|
25685
|
-
if (previous !== void 0) {
|
|
25686
|
-
slots.delete(work.slot);
|
|
25687
|
-
yield* Fiber.interrupt(previous.fiber);
|
|
25688
|
-
const previousEvent = { _tag: previous.cursor.event };
|
|
25689
|
-
const reason = previous.cursor.epoch === (slotEpochs.get(work.slot) ?? 0) ? "SUPERSEDED" : "STALE_EPOCH";
|
|
25690
|
-
yield* emit(nonApplied(next, previousEvent, reason, "refused", {
|
|
25691
|
-
origin: previous.cursor,
|
|
25692
|
-
invocation: previous.cursor.invocation,
|
|
25693
|
-
startedAt: previous.cursor.startedAt
|
|
25694
|
-
}));
|
|
25695
|
-
yield* failReply(previous.reply, makeFailure(next, previousEvent, reason));
|
|
25696
|
-
}
|
|
25697
|
-
const epoch = (slotEpochs.get(work.slot) ?? 0) + 1;
|
|
25698
|
-
slotEpochs.set(work.slot, epoch);
|
|
25699
|
-
const cursor = {
|
|
25700
|
-
slot: work.slot,
|
|
25701
|
-
epoch,
|
|
25702
|
-
event: env.event._tag,
|
|
25703
|
-
startedAt: env.startedAt,
|
|
25704
|
-
invocation: env.invocation
|
|
25488
|
+
return convexPage(deps, scope.actor, controls, {
|
|
25489
|
+
limit: 100,
|
|
25490
|
+
range,
|
|
25491
|
+
filter: { kind: "payment" },
|
|
25492
|
+
...state.paymentCursor === null ? {} : { cursor: state.paymentCursor }
|
|
25493
|
+
}).pipe(Effect.map((page) => ({
|
|
25494
|
+
items: page.items,
|
|
25495
|
+
pending: page.items.map((item) => item.id),
|
|
25496
|
+
cursor: page.nextCursor,
|
|
25497
|
+
done: page.nextCursor === null
|
|
25498
|
+
})));
|
|
25499
|
+
}
|
|
25500
|
+
function readPaymentFacts(deps, safe, settlements) {
|
|
25501
|
+
const offsets = Array.from({ length: Math.ceil(settlements.length / 50) }, (_, i) => i * 50);
|
|
25502
|
+
return Effect.forEach(offsets, (offset) => chainRead(deps, "payment", {
|
|
25503
|
+
safe: toAddress(safe),
|
|
25504
|
+
limit: 50,
|
|
25505
|
+
settlements: settlements.slice(offset, offset + 50)
|
|
25506
|
+
}), { concurrency: 2 }).pipe(Effect.map((pages) => pages.some((page) => page === null) ? null : pages.flatMap((page) => page ?? [])));
|
|
25507
|
+
}
|
|
25508
|
+
function indexedPage(deps, scope, controls, params, state, first) {
|
|
25509
|
+
return Effect.gen(function* () {
|
|
25510
|
+
const range = boundedRange(params?.range, state.checkpoint);
|
|
25511
|
+
const wantsTransfers = params?.filter?.kind !== "payment" && params?.filter?.status === void 0;
|
|
25512
|
+
const found = yield* readTransfers(deps, scope.safe, range, state, wantsTransfers, first);
|
|
25513
|
+
if (found === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
|
|
25514
|
+
const transfers = wantsTransfers ? found : [];
|
|
25515
|
+
const page = yield* readPayments(deps, scope, controls, range, state, params?.filter?.kind !== "movement");
|
|
25516
|
+
const decoration = yield* decoratePage(deps, scope, controls, transfers, page, range);
|
|
25517
|
+
if (decoration === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
|
|
25518
|
+
const { items, next } = mergePage(scope.safe, params?.filter, params?.limit ?? 25, state, page, decoration, transfers, wantsTransfers);
|
|
25519
|
+
const cursor = next.paymentDone && next.paymentPending.length === 0 && next.transferDone ? null : JSON.stringify(next);
|
|
25520
|
+
if (cursor !== null && cursor === params?.cursor) return yield* Effect.fail({ publicError: Errors.invalidInput("cursor", "Activity source did not advance") });
|
|
25521
|
+
yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
|
|
25522
|
+
return {
|
|
25523
|
+
items,
|
|
25524
|
+
nextCursor: cursor,
|
|
25525
|
+
checkpoint: state.checkpoint,
|
|
25526
|
+
observedAt: Date.now()
|
|
25705
25527
|
};
|
|
25706
|
-
|
|
25707
|
-
|
|
25708
|
-
|
|
25709
|
-
|
|
25710
|
-
|
|
25711
|
-
|
|
25712
|
-
|
|
25713
|
-
|
|
25714
|
-
|
|
25715
|
-
|
|
25716
|
-
|
|
25717
|
-
|
|
25718
|
-
|
|
25719
|
-
|
|
25720
|
-
|
|
25721
|
-
|
|
25722
|
-
|
|
25723
|
-
|
|
25724
|
-
|
|
25725
|
-
|
|
25726
|
-
|
|
25727
|
-
|
|
25728
|
-
|
|
25729
|
-
|
|
25730
|
-
|
|
25731
|
-
|
|
25732
|
-
|
|
25733
|
-
|
|
25734
|
-
|
|
25735
|
-
|
|
25736
|
-
|
|
25737
|
-
|
|
25528
|
+
});
|
|
25529
|
+
}
|
|
25530
|
+
//#endregion
|
|
25531
|
+
//#region ../../packages/sdk/src/activity/page-overlay.ts
|
|
25532
|
+
const MAX_OVERLAY_IDS = 200;
|
|
25533
|
+
function overlay(deps, actor, controls, input) {
|
|
25534
|
+
const count = Math.max(input.references?.length ?? 0, input.settlementIds?.length ?? 0);
|
|
25535
|
+
const offsets = Array.from({ length: Math.max(1, Math.ceil(count / MAX_OVERLAY_IDS)) }, (_, i) => i * MAX_OVERLAY_IDS);
|
|
25536
|
+
return Effect.forEach(offsets, (offset) => callOperation(deps.convexCall, overlays.get, copyInvocationObservation(controls, {
|
|
25537
|
+
...actor === void 0 ? {} : { actor },
|
|
25538
|
+
...input.references === void 0 ? {} : { references: input.references.slice(offset, offset + MAX_OVERLAY_IDS) },
|
|
25539
|
+
...input.settlementIds === void 0 ? {} : { settlementIds: input.settlementIds.slice(offset, offset + MAX_OVERLAY_IDS) }
|
|
25540
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError }))), { concurrency: 2 }).pipe(Effect.flatMap((pages) => {
|
|
25541
|
+
const first = pages[0];
|
|
25542
|
+
if (pages.some((page) => scopeKey(page) !== scopeKey(first))) return Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during the read") });
|
|
25543
|
+
return Effect.succeed({
|
|
25544
|
+
...first,
|
|
25545
|
+
links: pages.flatMap((page) => page.links),
|
|
25546
|
+
annotations: pages.flatMap((page) => page.annotations),
|
|
25547
|
+
payments: pages.flatMap((page) => page.payments)
|
|
25548
|
+
});
|
|
25549
|
+
}));
|
|
25550
|
+
}
|
|
25551
|
+
function decoratePage(deps, scope, controls, transfers, page, range) {
|
|
25552
|
+
return Effect.gen(function* () {
|
|
25553
|
+
const decoration = yield* overlay(deps, scope.actor, controls, {
|
|
25554
|
+
references: [...page.pending.map((id) => ({
|
|
25555
|
+
kind: "payment",
|
|
25556
|
+
id
|
|
25557
|
+
})), ...transfers.map((row) => ({
|
|
25558
|
+
kind: "movement",
|
|
25559
|
+
id: row.id
|
|
25560
|
+
}))],
|
|
25561
|
+
settlementIds: [...new Set(transfers.flatMap((row) => row.paymentSettlementId ? [row.paymentSettlementId] : []))]
|
|
25738
25562
|
});
|
|
25563
|
+
if (scopeKey(decoration) !== scopeKey(scope)) return yield* Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during the read") });
|
|
25564
|
+
const byId = new Map([...page.items, ...decoration.payments].map((item) => [item.id, item]));
|
|
25565
|
+
const settlements = [...new Set(decoration.links.map((link) => link.settlementId))];
|
|
25566
|
+
const facts = yield* readPaymentFacts(deps, scope.safe, settlements);
|
|
25567
|
+
if (facts === null) return null;
|
|
25568
|
+
const bySettlement = new Map(facts.map((row) => [row.settlementId, row]));
|
|
25569
|
+
const byPayment = new Map(decoration.links.map((link) => [link.paymentId, {
|
|
25570
|
+
fact: bySettlement.get(link.settlementId),
|
|
25571
|
+
expected: link.expected
|
|
25572
|
+
}]));
|
|
25573
|
+
const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
|
|
25574
|
+
return {
|
|
25575
|
+
payments: page.pending.flatMap((id) => {
|
|
25576
|
+
const item = byId.get(id);
|
|
25577
|
+
if (item === void 0 || item.kind !== "payment" || item.createdAt > range.to || range.from !== void 0 && item.createdAt < range.from) return [];
|
|
25578
|
+
const confirmed = withAnnotation(confirmPayment(item, byPayment.get(id)?.fact, byPayment.get(id)?.expected), annotations.get(`payment:${id}`));
|
|
25579
|
+
return isActivityVisible(confirmed) ? [confirmed] : [];
|
|
25580
|
+
}),
|
|
25581
|
+
annotations,
|
|
25582
|
+
linked: new Set(decoration.links.filter((link) => matchesPaymentFact(bySettlement.get(link.settlementId), link.expected)).map((link) => link.settlementId))
|
|
25583
|
+
};
|
|
25739
25584
|
});
|
|
25740
|
-
|
|
25741
|
-
|
|
25742
|
-
|
|
25743
|
-
|
|
25744
|
-
|
|
25745
|
-
|
|
25746
|
-
|
|
25747
|
-
|
|
25748
|
-
|
|
25749
|
-
|
|
25750
|
-
|
|
25751
|
-
|
|
25752
|
-
|
|
25585
|
+
}
|
|
25586
|
+
function mergePage(safe, filter, limit, state, page, decoration, transfers, wantsTransfers) {
|
|
25587
|
+
const { payments, annotations, linked } = decoration;
|
|
25588
|
+
let pi = 0;
|
|
25589
|
+
let ti = 0;
|
|
25590
|
+
const items = [];
|
|
25591
|
+
const transferEnd = !wantsTransfers || transfers.length < 100;
|
|
25592
|
+
let transferBefore = state.transferBefore;
|
|
25593
|
+
while (items.length < limit) {
|
|
25594
|
+
if (pi === payments.length && !page.done || ti === transfers.length && !transferEnd) break;
|
|
25595
|
+
const payment = payments[pi];
|
|
25596
|
+
const row = transfers[ti];
|
|
25597
|
+
if (payment === void 0 && row === void 0) break;
|
|
25598
|
+
if (payment !== void 0 && (row === void 0 || payment.createdAt >= Number(row.timestamp) * 1e3)) {
|
|
25599
|
+
pi += 1;
|
|
25600
|
+
if (matchesFilter(payment, filter)) items.push(payment);
|
|
25601
|
+
} else if (row !== void 0) {
|
|
25602
|
+
ti += 1;
|
|
25603
|
+
transferBefore = {
|
|
25604
|
+
timestamp: Number(row.timestamp),
|
|
25605
|
+
id: row.id
|
|
25753
25606
|
};
|
|
25754
|
-
|
|
25755
|
-
|
|
25756
|
-
|
|
25607
|
+
if (row.paymentSettlementId && linked.has(row.paymentSettlementId)) continue;
|
|
25608
|
+
const item = transferItem(safe, row, annotations.get(`movement:${row.id}`));
|
|
25609
|
+
if (item !== null && matchesFilter(item, filter)) items.push(item);
|
|
25757
25610
|
}
|
|
25758
|
-
|
|
25759
|
-
transitionObservers.clear();
|
|
25760
|
-
yield* Queue.shutdown(mailbox);
|
|
25761
|
-
}));
|
|
25762
|
-
const offer = (event, reply, invocation) => {
|
|
25763
|
-
const state = Ref.getUnsafe(cell);
|
|
25764
|
-
if (stopped) return withInvocationParentSpan(emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", {
|
|
25765
|
-
origin: null,
|
|
25766
|
-
event,
|
|
25767
|
-
invocation,
|
|
25768
|
-
startedAt: Date.now()
|
|
25769
|
-
})).pipe(Effect.andThen(Effect.fail(makeFailure(state, event, "ACTOR_STOPPED")))), invocation);
|
|
25770
|
-
const startedAt = Date.now();
|
|
25771
|
-
return Queue.offer(mailbox, {
|
|
25772
|
-
event,
|
|
25773
|
-
reply,
|
|
25774
|
-
origin: null,
|
|
25775
|
-
invocation,
|
|
25776
|
-
startedAt
|
|
25777
|
-
}).pipe(Effect.asVoid);
|
|
25778
|
-
};
|
|
25611
|
+
}
|
|
25779
25612
|
return {
|
|
25780
|
-
|
|
25781
|
-
|
|
25782
|
-
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
stateObservers.add(observer);
|
|
25789
|
-
return () => {
|
|
25790
|
-
stateObservers.delete(observer);
|
|
25791
|
-
};
|
|
25792
|
-
},
|
|
25793
|
-
subscribeTransitions: (observer) => {
|
|
25794
|
-
transitionObservers.add(observer);
|
|
25795
|
-
return () => {
|
|
25796
|
-
transitionObservers.delete(observer);
|
|
25797
|
-
};
|
|
25613
|
+
items,
|
|
25614
|
+
next: {
|
|
25615
|
+
...state,
|
|
25616
|
+
paymentCursor: page.cursor,
|
|
25617
|
+
paymentDone: page.done,
|
|
25618
|
+
paymentPending: payments.slice(pi).map((item) => item.id),
|
|
25619
|
+
transferBefore,
|
|
25620
|
+
transferDone: ti === transfers.length && transferEnd
|
|
25798
25621
|
}
|
|
25799
25622
|
};
|
|
25800
|
-
}
|
|
25801
|
-
|
|
25802
|
-
|
|
25803
|
-
|
|
25804
|
-
|
|
25805
|
-
|
|
25806
|
-
|
|
25807
|
-
|
|
25808
|
-
|
|
25809
|
-
|
|
25810
|
-
|
|
25811
|
-
|
|
25812
|
-
|
|
25813
|
-
|
|
25814
|
-
|
|
25815
|
-
|
|
25816
|
-
|
|
25817
|
-
|
|
25818
|
-
|
|
25819
|
-
|
|
25820
|
-
|
|
25821
|
-
|
|
25822
|
-
}
|
|
25823
|
-
|
|
25824
|
-
|
|
25825
|
-
|
|
25826
|
-
|
|
25827
|
-
|
|
25828
|
-
|
|
25829
|
-
|
|
25830
|
-
|
|
25831
|
-
|
|
25832
|
-
|
|
25833
|
-
|
|
25834
|
-
|
|
25835
|
-
|
|
25836
|
-
|
|
25837
|
-
|
|
25838
|
-
|
|
25839
|
-
_tag: "ObserveOrganization",
|
|
25840
|
-
orgId: nextOrgId,
|
|
25841
|
-
snapshot
|
|
25842
|
-
})).catch(() => void 0);
|
|
25623
|
+
}
|
|
25624
|
+
function convexPage(deps, actor, controls, input) {
|
|
25625
|
+
return callOperation(deps.convexCall, items.list, copyInvocationObservation(controls, {
|
|
25626
|
+
...input,
|
|
25627
|
+
actor
|
|
25628
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })));
|
|
25629
|
+
}
|
|
25630
|
+
function fallbackPage(deps, scope, controls, params, state, degraded) {
|
|
25631
|
+
return Effect.gen(function* () {
|
|
25632
|
+
const page = yield* convexPage(deps, scope.actor, controls, {
|
|
25633
|
+
limit: params?.limit ?? 25,
|
|
25634
|
+
...state.paymentCursor === null ? {} : { cursor: state.paymentCursor },
|
|
25635
|
+
range: {
|
|
25636
|
+
...params?.range?.from === void 0 ? {} : { from: params.range.from },
|
|
25637
|
+
to: Math.min(state.checkpoint, params?.range?.to ?? state.checkpoint)
|
|
25638
|
+
},
|
|
25639
|
+
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
25640
|
+
});
|
|
25641
|
+
const decoration = yield* overlay(deps, scope.actor, controls, { references: page.items.map((item) => ({
|
|
25642
|
+
kind: item.kind,
|
|
25643
|
+
id: item.id
|
|
25644
|
+
})) });
|
|
25645
|
+
if (scopeKey(decoration) !== scopeKey(scope)) return yield* Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during fallback") });
|
|
25646
|
+
const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
|
|
25647
|
+
yield* Effect.annotateCurrentSpan({
|
|
25648
|
+
read_source: "convex",
|
|
25649
|
+
...degraded ? { read_warning: "indexer-unavailable" } : {}
|
|
25650
|
+
});
|
|
25651
|
+
return {
|
|
25652
|
+
...page,
|
|
25653
|
+
items: page.items.filter(isActivityVisible).map((item) => withAnnotation(item, annotations.get(`${item.kind}:${item.id}`))),
|
|
25654
|
+
checkpoint: state.checkpoint,
|
|
25655
|
+
nextCursor: page.nextCursor === null ? null : JSON.stringify({
|
|
25656
|
+
...state,
|
|
25657
|
+
source: "fallback",
|
|
25658
|
+
paymentCursor: page.nextCursor,
|
|
25659
|
+
degraded
|
|
25660
|
+
}),
|
|
25661
|
+
...degraded ? { readWarning: "indexer-unavailable" } : {}
|
|
25843
25662
|
};
|
|
25844
|
-
|
|
25845
|
-
|
|
25846
|
-
|
|
25847
|
-
|
|
25848
|
-
|
|
25849
|
-
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
|
|
25853
|
-
|
|
25854
|
-
|
|
25855
|
-
|
|
25856
|
-
|
|
25857
|
-
|
|
25858
|
-
|
|
25859
|
-
|
|
25860
|
-
|
|
25861
|
-
|
|
25862
|
-
|
|
25863
|
-
|
|
25864
|
-
|
|
25865
|
-
|
|
25866
|
-
|
|
25867
|
-
|
|
25868
|
-
|
|
25869
|
-
|
|
25870
|
-
authority.invalidate();
|
|
25871
|
-
unsubscribeAuthority();
|
|
25872
|
-
}));
|
|
25873
|
-
return {
|
|
25874
|
-
...actor,
|
|
25875
|
-
authLifetime: authority.snapshot,
|
|
25876
|
-
matchesAuthLifetime: authority.matches,
|
|
25877
|
-
invalidateAuthLifetime: authority.invalidate,
|
|
25878
|
-
subscribeAuthInvalidation: authority.subscribeInvalidation,
|
|
25879
|
-
authSession: () => sessionStore.current,
|
|
25880
|
-
ask: (event, controls) => actor.ask(event, controls).pipe(Effect.tap(() => event._tag === "Reset" ? Effect.sync(() => {
|
|
25881
|
-
authority.recordProviderSession(null);
|
|
25882
|
-
}) : Effect.void)),
|
|
25883
|
-
restoreAuthSession: (session, controls) => actor.ask({
|
|
25884
|
-
_tag: actor.snapshot().phase === "restoring" ? "SessionRestored" : "RestoreSession",
|
|
25885
|
-
session: modelSession(session),
|
|
25886
|
-
profileComplete: false
|
|
25887
|
-
}, controls).pipe(Effect.tap(() => Effect.sync(() => {
|
|
25888
|
-
authority.recordProviderSession(session);
|
|
25889
|
-
})))
|
|
25890
|
-
};
|
|
25891
|
-
});
|
|
25892
|
-
//#endregion
|
|
25893
|
-
//#region ../../packages/sdk/src/access/identity/restoration-observation.ts
|
|
25894
|
-
/** The correlation a transition carries onto its product event. */
|
|
25895
|
-
const linkage = (record) => ({
|
|
25896
|
-
...record.correlation_id === void 0 ? {} : { correlation_id: record.correlation_id },
|
|
25897
|
-
...record.journey_id === void 0 ? {} : { journey_id: record.journey_id }
|
|
25898
|
-
});
|
|
25899
|
-
/**
|
|
25900
|
-
* The label of the restore window every client boots into. A settled session
|
|
25901
|
-
* fact that left this label is the restoration result (#1962 R04); the same
|
|
25902
|
-
* facts from a signed-out or authenticated state are the routine session probe
|
|
25903
|
-
* that `getSession()` runs, and a probe is not a restoration attempt.
|
|
25904
|
-
*/
|
|
25905
|
-
const RESTORE_WINDOW = "restoring";
|
|
25906
|
-
/** The completions that settle the restore window by themselves, and what each says. */
|
|
25907
|
-
const SETTLING_EVENTS = {
|
|
25908
|
-
SessionRestored: "restored",
|
|
25909
|
-
SessionAbsent: "absent",
|
|
25910
|
-
SessionReadFailed: "failed"
|
|
25911
|
-
};
|
|
25912
|
-
/**
|
|
25913
|
-
* Which result the window settled on. `SessionRestored` and `SessionAbsent` are
|
|
25914
|
-
* the cache door and say it themselves. `ReadSession` is also valid inside the
|
|
25915
|
-
* window — `getSession()` drives it on a cache miss and its `SessionRead`
|
|
25916
|
-
* completion settles the window — so that one event reports both halves and the
|
|
25917
|
-
* state it produced is what separates them.
|
|
25918
|
-
*/
|
|
25919
|
-
const restorationOutcome = (event, state) => {
|
|
25920
|
-
if (event === "SessionRead") {
|
|
25921
|
-
if (state.phase === "authenticated") return "restored";
|
|
25922
|
-
return state.phase === "signed_out" ? "absent" : void 0;
|
|
25923
|
-
}
|
|
25924
|
-
return SETTLING_EVENTS[event];
|
|
25925
|
-
};
|
|
25926
|
-
/**
|
|
25927
|
-
* The restore window's one result, or nothing when this settled session fact
|
|
25928
|
-
* belongs to a probe rather than to the window.
|
|
25929
|
-
*/
|
|
25930
|
-
const restorationObservation = (record, event, state) => {
|
|
25931
|
-
if (record.slot !== "identity:session" || record.from !== RESTORE_WINDOW) return null;
|
|
25932
|
-
const outcome = restorationOutcome(event, state);
|
|
25933
|
-
if (outcome === void 0) return null;
|
|
25934
|
-
return {
|
|
25935
|
-
name: "access_recovery_completed",
|
|
25936
|
-
props: {
|
|
25937
|
-
recovery: "session_restoration",
|
|
25938
|
-
outcome,
|
|
25939
|
-
...outcome === "failed" && state.phase === "faulted" ? { reason_code: state.failure.code } : {},
|
|
25940
|
-
...linkage(record)
|
|
25941
|
-
}
|
|
25942
|
-
};
|
|
25943
|
-
};
|
|
25944
|
-
//#endregion
|
|
25945
|
-
//#region ../../packages/sdk/src/access/identity/events.ts
|
|
25946
|
-
const NONE = null;
|
|
25947
|
-
const IDENTITY_EVENT_TAG_SET = new Set(IDENTITY_EVENT_TAGS);
|
|
25948
|
-
const isIdentityEventTag = (event) => IDENTITY_EVENT_TAG_SET.has(event);
|
|
25949
|
-
const APPLIED_ACTION_BY_EVENT = {
|
|
25950
|
-
RequestOtp: "none",
|
|
25951
|
-
VerifyOtp: "none",
|
|
25952
|
-
ReadSession: "none",
|
|
25953
|
-
SignOut: "none",
|
|
25954
|
-
RestoreSession: "none",
|
|
25955
|
-
ResumeOtpEntry: "none",
|
|
25956
|
-
RestoreOtpEntry: "none",
|
|
25957
|
-
Reset: "none",
|
|
25958
|
-
SessionRestored: "restoration",
|
|
25959
|
-
SessionAbsent: "restoration",
|
|
25960
|
-
EnsureAccount: "none",
|
|
25961
|
-
ClaimAccount: "none",
|
|
25962
|
-
RetryAccount: "none",
|
|
25963
|
-
CreateOrganization: "none",
|
|
25964
|
-
AttachOrganization: "none",
|
|
25965
|
-
LeaveOrganization: "none",
|
|
25966
|
-
ObserveOrganization: "none",
|
|
25967
|
-
OtpSent: "otp-requested",
|
|
25968
|
-
OtpFailed: "failed",
|
|
25969
|
-
Verified: "verified",
|
|
25970
|
-
VerifyFailed: "failed",
|
|
25971
|
-
SessionRead: "restoration",
|
|
25972
|
-
SessionReadFailed: "restoration",
|
|
25973
|
-
SignedOut: "signed-out",
|
|
25974
|
-
SignOutFailed: "failed",
|
|
25975
|
-
AccountAt: "none",
|
|
25976
|
-
AccountDerived: "none",
|
|
25977
|
-
AccountClaiming: "none",
|
|
25978
|
-
AccountClaimed: "none",
|
|
25979
|
-
AccountFailed: "none",
|
|
25980
|
-
OrgSelected: "none",
|
|
25981
|
-
OrgSelectionFailed: "none"
|
|
25982
|
-
};
|
|
25983
|
-
const emailDomain = (email) => email.split("@")[1]?.toLowerCase() ?? "unknown";
|
|
25984
|
-
const authFailure = (record, reason, mode) => ({
|
|
25985
|
-
name: "auth_failed",
|
|
25986
|
-
props: {
|
|
25987
|
-
auth_type: "otp",
|
|
25988
|
-
reason,
|
|
25989
|
-
...mode === void 0 ? {} : { failure_mode: mode },
|
|
25990
|
-
...linkage(record)
|
|
25991
|
-
}
|
|
25992
|
-
});
|
|
25993
|
-
const REFUSAL_EVENTS = /* @__PURE__ */ new Set([
|
|
25994
|
-
"ClaimAccount",
|
|
25995
|
-
"RetryAccount",
|
|
25996
|
-
"CreateOrganization",
|
|
25997
|
-
"AttachOrganization"
|
|
25998
|
-
]);
|
|
25999
|
-
function mapIdentityProductObservation(record, state) {
|
|
26000
|
-
const event = record.event;
|
|
26001
|
-
if (record.machine !== "identity" || !isIdentityEventTag(event)) return NONE;
|
|
26002
|
-
if (record.outcome === "cancelled") return NONE;
|
|
26003
|
-
if (record.slot === "identity:auth" && record.outcome === "refused") return authFailure(record, record.refusal_code, void 0);
|
|
26004
|
-
if (record.slot === "identity:auth" && record.outcome === "failed") return authFailure(record, record.error_code, record.failure?.mode);
|
|
26005
|
-
if (record.outcome === "refused" && REFUSAL_EVENTS.has(record.event)) return {
|
|
26006
|
-
name: "identity_refused",
|
|
26007
|
-
props: {
|
|
26008
|
-
event: record.event,
|
|
26009
|
-
refusal_code: record.refusal_code,
|
|
26010
|
-
slot: record.slot,
|
|
26011
|
-
state: record.state,
|
|
26012
|
-
...linkage(record)
|
|
26013
|
-
}
|
|
26014
|
-
};
|
|
26015
|
-
if (record.outcome !== "applied") return NONE;
|
|
26016
|
-
const action = APPLIED_ACTION_BY_EVENT[event];
|
|
26017
|
-
switch (action) {
|
|
26018
|
-
case "none": return NONE;
|
|
26019
|
-
case "failed": return state.phase !== "faulted" || state.failure.code === "CANCELLED" ? NONE : authFailure(record, state.failure.code, state.failure.mode);
|
|
26020
|
-
case "otp-requested": return state.phase !== "otp_pending" ? NONE : {
|
|
26021
|
-
name: "auth_otp_requested",
|
|
26022
|
-
props: {
|
|
26023
|
-
email_domain: emailDomain(state.email),
|
|
26024
|
-
...linkage(record)
|
|
26025
|
-
}
|
|
25663
|
+
});
|
|
25664
|
+
}
|
|
25665
|
+
function readActivityPage(deps, actor, controls, params) {
|
|
25666
|
+
return Effect.gen(function* () {
|
|
25667
|
+
yield* Effect.try({
|
|
25668
|
+
try: () => requireParams(params),
|
|
25669
|
+
catch: (cause) => ({ publicError: asCapxulError(cause) })
|
|
25670
|
+
});
|
|
25671
|
+
const scope = yield* overlay(deps, actor, controls, {});
|
|
25672
|
+
const selection = selectionKey(params);
|
|
25673
|
+
const cursor = yield* Effect.try({
|
|
25674
|
+
try: () => parseCursor(params?.cursor, scopeKey(scope), selection),
|
|
25675
|
+
catch: () => ({ publicError: Errors.invalidInput("cursor", "invalid or obsolete Activity cursor; restart the read") })
|
|
25676
|
+
});
|
|
25677
|
+
const state = cursor ?? {
|
|
25678
|
+
version: 1,
|
|
25679
|
+
scope: scopeKey(scope),
|
|
25680
|
+
selection,
|
|
25681
|
+
checkpoint: Date.now(),
|
|
25682
|
+
source: "indexed",
|
|
25683
|
+
paymentCursor: null,
|
|
25684
|
+
paymentDone: false,
|
|
25685
|
+
paymentPending: [],
|
|
25686
|
+
transferBefore: null,
|
|
25687
|
+
transferDone: false,
|
|
25688
|
+
degraded: false
|
|
26026
25689
|
};
|
|
26027
|
-
|
|
26028
|
-
|
|
26029
|
-
|
|
26030
|
-
|
|
26031
|
-
|
|
26032
|
-
|
|
25690
|
+
const range = boundedRange(params?.range, state.checkpoint);
|
|
25691
|
+
if (range.from !== void 0 && range.from > range.to) return {
|
|
25692
|
+
items: [],
|
|
25693
|
+
nextCursor: null,
|
|
25694
|
+
checkpoint: state.checkpoint,
|
|
25695
|
+
observedAt: Date.now()
|
|
26033
25696
|
};
|
|
26034
|
-
|
|
26035
|
-
|
|
26036
|
-
|
|
25697
|
+
if (state.source === "fallback" || scope.safe === null) return yield* fallbackPage(deps, scope, controls, params, state, state.degraded);
|
|
25698
|
+
return yield* indexedPage(deps, scope, controls, params, state, cursor === null);
|
|
25699
|
+
});
|
|
25700
|
+
}
|
|
25701
|
+
function readActivitySummary(deps, actor, controls, params) {
|
|
25702
|
+
return Effect.gen(function* () {
|
|
25703
|
+
const totals = /* @__PURE__ */ new Map();
|
|
25704
|
+
let cursor;
|
|
25705
|
+
let count = 0;
|
|
25706
|
+
let readWarning;
|
|
25707
|
+
do {
|
|
25708
|
+
const page = yield* readActivityPage(deps, actor, controls, {
|
|
25709
|
+
limit: 100,
|
|
25710
|
+
...cursor === void 0 ? {} : { cursor },
|
|
25711
|
+
...params?.window === void 0 ? {} : { range: params.window }
|
|
25712
|
+
});
|
|
25713
|
+
count += page.items.length;
|
|
25714
|
+
readWarning ??= page.readWarning;
|
|
25715
|
+
for (const total of summarizeItems(page.items)) {
|
|
25716
|
+
const key = total.asset;
|
|
25717
|
+
const old = totals.get(key);
|
|
25718
|
+
totals.set(key, old === void 0 ? total : {
|
|
25719
|
+
...total,
|
|
25720
|
+
in: String(BigInt(old.in) + BigInt(total.in)),
|
|
25721
|
+
out: String(BigInt(old.out) + BigInt(total.out)),
|
|
25722
|
+
net: String(BigInt(old.net) + BigInt(total.net)),
|
|
25723
|
+
pendingIn: String(BigInt(old.pendingIn) + BigInt(total.pendingIn))
|
|
25724
|
+
});
|
|
25725
|
+
}
|
|
25726
|
+
cursor = page.nextCursor ?? void 0;
|
|
25727
|
+
} while (cursor !== void 0);
|
|
25728
|
+
return {
|
|
25729
|
+
window: {
|
|
25730
|
+
from: params?.window?.from ?? null,
|
|
25731
|
+
to: params?.window?.to ?? null
|
|
25732
|
+
},
|
|
25733
|
+
totals: [...totals.values()],
|
|
25734
|
+
count,
|
|
25735
|
+
observedAt: Date.now(),
|
|
25736
|
+
...readWarning === void 0 ? {} : { readWarning }
|
|
26037
25737
|
};
|
|
26038
|
-
|
|
26039
|
-
default: return absurd(action);
|
|
26040
|
-
}
|
|
25738
|
+
});
|
|
26041
25739
|
}
|
|
26042
|
-
|
|
26043
|
-
|
|
26044
|
-
|
|
26045
|
-
|
|
26046
|
-
|
|
26047
|
-
|
|
26048
|
-
|
|
26049
|
-
|
|
26050
|
-
|
|
26051
|
-
|
|
26052
|
-
|
|
26053
|
-
|
|
26054
|
-
|
|
26055
|
-
|
|
25740
|
+
function subscribeActivity(deps, actor, controls, onChange) {
|
|
25741
|
+
return Effect.gen(function* () {
|
|
25742
|
+
const scope = yield* overlay(deps, actor, controls, {});
|
|
25743
|
+
if (scope.safe === null) return () => {};
|
|
25744
|
+
let delivered = false;
|
|
25745
|
+
return yield* deps.indexer.subscribe("transfer", {
|
|
25746
|
+
safe: toAddress(scope.safe),
|
|
25747
|
+
limit: 100
|
|
25748
|
+
}, (snapshot) => {
|
|
25749
|
+
if (snapshot.status === "ok") {
|
|
25750
|
+
if (delivered) onChange();
|
|
25751
|
+
delivered = true;
|
|
25752
|
+
}
|
|
25753
|
+
if (snapshot.status === "error") {
|
|
25754
|
+
delivered = true;
|
|
25755
|
+
onChange();
|
|
25756
|
+
}
|
|
25757
|
+
});
|
|
25758
|
+
});
|
|
26056
25759
|
}
|
|
26057
25760
|
//#endregion
|
|
26058
|
-
//#region ../../packages/sdk/src/activity/
|
|
26059
|
-
/**
|
|
26060
|
-
function
|
|
26061
|
-
return
|
|
25761
|
+
//#region ../../packages/sdk/src/activity/movement-detail.ts
|
|
25762
|
+
/** An indexed Movement has no Convex materialization. Read its exact native row. */
|
|
25763
|
+
function readIndexedMovement(deps, actor, controls, eventId) {
|
|
25764
|
+
return Effect.gen(function* () {
|
|
25765
|
+
const scope = yield* overlay(deps, actor, controls, { references: [{
|
|
25766
|
+
kind: "movement",
|
|
25767
|
+
id: eventId
|
|
25768
|
+
}] });
|
|
25769
|
+
if (scope.safe === null) return null;
|
|
25770
|
+
const safe = scope.safe.toLowerCase();
|
|
25771
|
+
const row = (yield* deps.indexer.read("transfer", {
|
|
25772
|
+
safe: toAddress(safe),
|
|
25773
|
+
limit: 1,
|
|
25774
|
+
eventId
|
|
25775
|
+
}).pipe(Effect.mapError((error) => ({ publicError: error.publicError }))))[0];
|
|
25776
|
+
if (row === void 0) return null;
|
|
25777
|
+
const blockNumber = Number(row.blockNumber);
|
|
25778
|
+
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") });
|
|
25779
|
+
yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
|
|
25780
|
+
const documentSet = scope.movementDocuments.find((set) => set.movementId === row.id);
|
|
25781
|
+
return {
|
|
25782
|
+
kind: "movement",
|
|
25783
|
+
movementId: row.id,
|
|
25784
|
+
documents: documentSet?.documents ?? [],
|
|
25785
|
+
documentState: documentSet?.state ?? "pending",
|
|
25786
|
+
viewer: scope.actor,
|
|
25787
|
+
annotation: scope.annotations.find((annotation) => annotation.reference.kind === "movement" && annotation.reference.id === row.id) ?? null,
|
|
25788
|
+
evidence: {
|
|
25789
|
+
chainId: row.chainId,
|
|
25790
|
+
contractAddress: row.token,
|
|
25791
|
+
tokenAddress: row.token,
|
|
25792
|
+
transactionHash: row.txHash,
|
|
25793
|
+
blockNumber,
|
|
25794
|
+
blockHash: row.blockHash,
|
|
25795
|
+
inclusion: "included",
|
|
25796
|
+
verification: "verified",
|
|
25797
|
+
canonicalState: "finalized",
|
|
25798
|
+
transferLogIndex: row.logIndex,
|
|
25799
|
+
source: row.from,
|
|
25800
|
+
destination: row.to,
|
|
25801
|
+
rawAmount: row.amount.toString(),
|
|
25802
|
+
link: {
|
|
25803
|
+
movementId: row.id,
|
|
25804
|
+
depositPaymentId: null
|
|
25805
|
+
}
|
|
25806
|
+
}
|
|
25807
|
+
};
|
|
25808
|
+
});
|
|
26062
25809
|
}
|
|
26063
|
-
|
|
26064
|
-
|
|
26065
|
-
|
|
25810
|
+
//#endregion
|
|
25811
|
+
//#region ../../packages/sdk/src/activity/scope.ts
|
|
25812
|
+
/**
|
|
25813
|
+
* The activity backend gives its actor a different name than the other
|
|
25814
|
+
* financialOps functions: `{kind: "organization", orgId}`, and not
|
|
25815
|
+
* `{kind: "org", orgId}`. Thus it does not use `actorReferenceToBackend`.
|
|
25816
|
+
*
|
|
25817
|
+
* The `personal` case sends NO actor. This is correct, and it does not lose
|
|
25818
|
+
* data. `requireActivityActor` uses the signed-in Account when the actor is
|
|
25819
|
+
* absent, and `canReadActor` refuses all other account identifiers
|
|
25820
|
+
* (`packages/backend/convex/movement/activityAuth.ts`). Thus "personal" and
|
|
25821
|
+
* "absent" both name the one Account that an SDK caller can read. The SDK does
|
|
25822
|
+
* not hold an account identifier to send.
|
|
25823
|
+
*
|
|
25824
|
+
* The `switch` is exhaustive. A new `ActorReference` variant is a compile error
|
|
25825
|
+
* here. The conditional expression that this function replaced removed each
|
|
25826
|
+
* value that was not an organization.
|
|
25827
|
+
*/
|
|
25828
|
+
function activityActorField(actor) {
|
|
25829
|
+
if (actor === void 0 || actor.kind === "personal") return {};
|
|
25830
|
+
return { actor: {
|
|
25831
|
+
kind: "organization",
|
|
25832
|
+
orgId: actor.organizationId
|
|
25833
|
+
} };
|
|
26066
25834
|
}
|
|
26067
|
-
|
|
26068
|
-
|
|
26069
|
-
|
|
26070
|
-
|
|
26071
|
-
|
|
26072
|
-
|
|
26073
|
-
|
|
26074
|
-
return runPortEffect(Effect.gen(function* () {
|
|
26075
|
-
const pair = yield* decodeDocumentRead(PaymentDocumentReadRefSchema, ref);
|
|
26076
|
-
return yield* callOperation(deps.convexCall, row, copyInvocationObservation(controls, pair)).pipe(Effect.mapError((error) => ({ publicError: withoutDocumentBody(error) })));
|
|
26077
|
-
}), controls, row.name, deps.runPromise);
|
|
26078
|
-
};
|
|
26079
|
-
return {
|
|
26080
|
-
get: (ref, options) => readDocument(documents.get, ref, options),
|
|
26081
|
-
verify: (ref, options) => readDocument(documents.verify, ref, options),
|
|
26082
|
-
render: (ref, options) => readDocument(documents.render, ref, options)
|
|
26083
|
-
};
|
|
25835
|
+
/** Capture scope now; let the observed result boundary handle a malformed caller value. */
|
|
25836
|
+
function captureActivityInput(prepare) {
|
|
25837
|
+
try {
|
|
25838
|
+
return prepare();
|
|
25839
|
+
} catch (cause) {
|
|
25840
|
+
return Effect.die(cause);
|
|
25841
|
+
}
|
|
26084
25842
|
}
|
|
26085
|
-
|
|
26086
|
-
|
|
26087
|
-
|
|
26088
|
-
|
|
26089
|
-
|
|
25843
|
+
function observeActivityScope(actor) {
|
|
25844
|
+
const organizationId = actor?.kind === "organization" || actor?.kind === "org" ? safeEngineeringIdentifier(actor.orgId) : void 0;
|
|
25845
|
+
return Effect.annotateCurrentSpan({
|
|
25846
|
+
scope_kind: actor?.kind === "organization" || actor?.kind === "org" ? "organization" : "account",
|
|
25847
|
+
...organizationId === void 0 ? {} : { verified_organization_id: organizationId }
|
|
25848
|
+
});
|
|
26090
25849
|
}
|
|
26091
|
-
|
|
26092
|
-
|
|
26093
|
-
|
|
25850
|
+
function decodeActivityViewer(value) {
|
|
25851
|
+
const viewer = Option.getOrUndefined(Schema.decodeUnknownOption(ActorRefSchema)(value));
|
|
25852
|
+
if (viewer === void 0) return void 0;
|
|
25853
|
+
const id = viewer.kind === "organization" ? viewer.orgId : viewer.accountId;
|
|
25854
|
+
return safeEngineeringIdentifier(id) === void 0 ? void 0 : viewer;
|
|
26094
25855
|
}
|
|
26095
|
-
|
|
26096
|
-
|
|
26097
|
-
|
|
26098
|
-
|
|
26099
|
-
return
|
|
25856
|
+
/** The detail without the scope it was read for, which the SDK reports as telemetry instead. */
|
|
25857
|
+
function normalizeActivityDetail(response) {
|
|
25858
|
+
if (response === null) return null;
|
|
25859
|
+
const { viewer: _viewer, ...detail } = response;
|
|
25860
|
+
return detail;
|
|
26100
25861
|
}
|
|
26101
|
-
|
|
26102
|
-
|
|
26103
|
-
|
|
26104
|
-
|
|
25862
|
+
//#endregion
|
|
25863
|
+
//#region ../../packages/sdk/src/activity/methods.ts
|
|
25864
|
+
function makeActivityMethods(deps) {
|
|
25865
|
+
const indexerDeps = deps.indexerRead === void 0 ? void 0 : {
|
|
25866
|
+
indexer: deps.indexerRead,
|
|
25867
|
+
convexCall: deps.convexCall
|
|
26105
25868
|
};
|
|
26106
|
-
|
|
26107
|
-
|
|
26108
|
-
|
|
26109
|
-
|
|
26110
|
-
*/
|
|
26111
|
-
function confirmPayment(intent, chain, expected) {
|
|
26112
|
-
if (chain !== void 0 && matchesPaymentFact(chain, expected)) return {
|
|
26113
|
-
...intent,
|
|
26114
|
-
status: "settled",
|
|
26115
|
-
...intent.execution === void 0 ? {} : { execution: {
|
|
26116
|
-
status: "settled",
|
|
26117
|
-
retryRequired: false
|
|
26118
|
-
} },
|
|
26119
|
-
receipt: {
|
|
26120
|
-
chainId: chain.chainId,
|
|
26121
|
-
transactionHash: chain.txHash
|
|
26122
|
-
},
|
|
26123
|
-
updatedAt: blockMillis(chain)
|
|
25869
|
+
/** One public call for the caller's chosen actor, read once at call start. */
|
|
25870
|
+
const forActor = (actorRef, options, operation, program) => {
|
|
25871
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
25872
|
+
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(actorRef).actor)).pipe(Effect.flatMap((actor) => program(actor, controls))), controls, operation, deps.runPromise);
|
|
26124
25873
|
};
|
|
26125
|
-
return
|
|
26126
|
-
}
|
|
26127
|
-
|
|
26128
|
-
|
|
26129
|
-
}
|
|
26130
|
-
|
|
26131
|
-
|
|
26132
|
-
|
|
26133
|
-
|
|
26134
|
-
|
|
26135
|
-
|
|
26136
|
-
|
|
26137
|
-
|
|
26138
|
-
}
|
|
26139
|
-
|
|
26140
|
-
|
|
26141
|
-
|
|
26142
|
-
|
|
26143
|
-
|
|
26144
|
-
|
|
26145
|
-
|
|
26146
|
-
|
|
26147
|
-
|
|
26148
|
-
|
|
26149
|
-
|
|
26150
|
-
|
|
26151
|
-
|
|
26152
|
-
|
|
26153
|
-
|
|
26154
|
-
|
|
26155
|
-
|
|
26156
|
-
|
|
26157
|
-
|
|
25874
|
+
return {
|
|
25875
|
+
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)))),
|
|
25876
|
+
list: (params, options) => forActor(params?.actor, options, items.list.name, (actor, controls) => Effect.suspend(() => indexerDeps === void 0 ? callOperation(deps.convexCall, items.list, copyInvocationObservation(controls, {
|
|
25877
|
+
...actor === void 0 ? {} : { actor },
|
|
25878
|
+
...params?.cursor === void 0 ? {} : { cursor: params.cursor },
|
|
25879
|
+
...params?.limit === void 0 ? {} : { limit: params.limit },
|
|
25880
|
+
...params?.range === void 0 ? {} : { range: params.range },
|
|
25881
|
+
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
25882
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError }))) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.map((page) => ({
|
|
25883
|
+
...page,
|
|
25884
|
+
items: page.items.filter(isActivityVisible)
|
|
25885
|
+
})), Effect.tap(() => observeActivityScope(actor)))),
|
|
25886
|
+
summary: (params, options) => forActor(params?.actor, options, items.summary.name, (actor, controls) => Effect.suspend(() => indexerDeps === void 0 ? callOperation(deps.convexCall, items.summary, copyInvocationObservation(controls, {
|
|
25887
|
+
...actor === void 0 ? {} : { actor },
|
|
25888
|
+
...params?.window === void 0 ? {} : { window: params.window }
|
|
25889
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError }))) : readActivitySummary(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor)))),
|
|
25890
|
+
get: (reference, options) => {
|
|
25891
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
25892
|
+
const requestedActor = actorReferenceToBackend(options?.actor);
|
|
25893
|
+
return runPortEffect(captureActivityInput(() => {
|
|
25894
|
+
const scope = requestedActor === void 0 ? void 0 : requestedActor.kind === "org" ? {
|
|
25895
|
+
kind: "org",
|
|
25896
|
+
orgId: requestedActor.orgId
|
|
25897
|
+
} : { kind: "account" };
|
|
25898
|
+
return Effect.succeed({
|
|
25899
|
+
actor: scope,
|
|
25900
|
+
referenceActor: reference.kind === "payment" ? activityActorField(reference.actor).actor : void 0
|
|
25901
|
+
});
|
|
25902
|
+
}).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" ? {
|
|
25903
|
+
kind: "organization",
|
|
25904
|
+
orgId: actor.orgId
|
|
25905
|
+
} : void 0, controls, reference.id) : callOperation(deps.convexCall, items.get, copyInvocationObservation(controls, {
|
|
25906
|
+
reference: {
|
|
25907
|
+
kind: reference.kind,
|
|
25908
|
+
id: reference.id,
|
|
25909
|
+
...referenceActor === void 0 ? {} : { actor: referenceActor }
|
|
25910
|
+
},
|
|
25911
|
+
...actor === void 0 ? {} : { actor }
|
|
25912
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })))).pipe(Effect.flatMap((response) => {
|
|
25913
|
+
const detail = normalizeActivityDetail(response);
|
|
25914
|
+
if (response === null) return Effect.succeed(detail);
|
|
25915
|
+
const viewer = response.viewer === void 0 ? actor ?? referenceActor : decodeActivityViewer(response.viewer);
|
|
25916
|
+
return viewer === void 0 ? Effect.succeed(detail) : observeActivityScope(viewer).pipe(Effect.as(detail));
|
|
25917
|
+
})))), controls, items.get.name, deps.runPromise);
|
|
26158
25918
|
},
|
|
26159
|
-
|
|
26160
|
-
|
|
26161
|
-
|
|
26162
|
-
|
|
26163
|
-
|
|
26164
|
-
|
|
26165
|
-
|
|
26166
|
-
|
|
26167
|
-
|
|
26168
|
-
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
26175
|
-
|
|
26176
|
-
|
|
26177
|
-
|
|
26178
|
-
|
|
26179
|
-
|
|
26180
|
-
|
|
26181
|
-
|
|
26182
|
-
|
|
26183
|
-
|
|
26184
|
-
|
|
26185
|
-
|
|
26186
|
-
|
|
26187
|
-
in: bucket.in.toString(),
|
|
26188
|
-
out: bucket.out.toString(),
|
|
26189
|
-
net: (bucket.in - bucket.out).toString(),
|
|
26190
|
-
pendingIn: bucket.pendingIn.toString()
|
|
26191
|
-
}));
|
|
26192
|
-
}
|
|
26193
|
-
/** The inverse of `formatUnits`, kept exact: `"1.25"` at 6 decimals is `1250000n`. */
|
|
26194
|
-
function rawUnits(value, decimals) {
|
|
26195
|
-
const negative = value.startsWith("-");
|
|
26196
|
-
const [whole = "0", fraction = ""] = (negative ? value.slice(1) : value).split(".");
|
|
26197
|
-
const raw = BigInt(`${whole}${fraction.padEnd(decimals, "0").slice(0, decimals)}`);
|
|
26198
|
-
return negative ? -raw : raw;
|
|
26199
|
-
}
|
|
26200
|
-
//#endregion
|
|
26201
|
-
//#region ../../packages/sdk/src/activity/cursor.ts
|
|
26202
|
-
function actorKey(actor) {
|
|
26203
|
-
return actor.kind === "account" ? `account:${actor.accountId}` : `organization:${actor.orgId}`;
|
|
26204
|
-
}
|
|
26205
|
-
function scopeKey(scope) {
|
|
26206
|
-
return `${scope.principalAccountId}|${actorKey(scope.actor)}|${scope.safe?.toLowerCase() ?? "none"}`;
|
|
26207
|
-
}
|
|
26208
|
-
function selectionKey(params) {
|
|
26209
|
-
const filter = params?.filter;
|
|
26210
|
-
return JSON.stringify([
|
|
26211
|
-
params?.range?.from ?? null,
|
|
26212
|
-
params?.range?.to ?? null,
|
|
26213
|
-
filter?.kind ?? null,
|
|
26214
|
-
filter?.direction ?? null,
|
|
26215
|
-
filter?.status === void 0 ? null : [...new Set(filter.status)].sort()
|
|
26216
|
-
]);
|
|
26217
|
-
}
|
|
26218
|
-
function validString(value, max) {
|
|
26219
|
-
return typeof value === "string" && value.length <= max;
|
|
26220
|
-
}
|
|
26221
|
-
function validPosition(position) {
|
|
26222
|
-
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);
|
|
26223
|
-
}
|
|
26224
|
-
function isActivityCursor(cursor, scope, selection) {
|
|
26225
|
-
return typeof cursor === "object" && cursor !== null && cursorScopeMatches(cursor, scope, selection) && cursorProgressIsValid(cursor);
|
|
26226
|
-
}
|
|
26227
|
-
/** The cursor was issued for this scope and filter selection, at a valid checkpoint. */
|
|
26228
|
-
function cursorScopeMatches(cursor, scope, selection) {
|
|
26229
|
-
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");
|
|
26230
|
-
}
|
|
26231
|
-
/** Each source's progress is well-formed and bounded. */
|
|
26232
|
-
function cursorProgressIsValid(cursor) {
|
|
26233
|
-
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);
|
|
26234
|
-
}
|
|
26235
|
-
function parseCursor(value, scope, selection) {
|
|
26236
|
-
if (value === void 0) return null;
|
|
26237
|
-
if (value.length > 4e4) throw Errors.invalidInput("cursor", "invalid Activity cursor");
|
|
26238
|
-
const cursor = JSON.parse(value);
|
|
26239
|
-
if (!isActivityCursor(cursor, scope, selection)) throw Errors.invalidInput("cursor", "Activity scope or filters changed; restart from the first page");
|
|
26240
|
-
return cursor;
|
|
26241
|
-
}
|
|
26242
|
-
function requireParams(params) {
|
|
26243
|
-
const limit = params?.limit ?? 25;
|
|
26244
|
-
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw Errors.invalidInput("limit", "must be an integer from 1 to 100");
|
|
26245
|
-
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");
|
|
26246
|
-
const f = params?.filter;
|
|
26247
|
-
if (f?.kind !== void 0 && f.kind !== "payment" && f.kind !== "movement" || f?.direction !== void 0 && ![
|
|
26248
|
-
"in",
|
|
26249
|
-
"out",
|
|
26250
|
-
"self"
|
|
26251
|
-
].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");
|
|
26252
|
-
}
|
|
26253
|
-
function matchesFilter(item, filter) {
|
|
26254
|
-
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));
|
|
25919
|
+
annotate: (input, options) => {
|
|
25920
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
25921
|
+
return runPortEffect(captureActivityInput(() => {
|
|
25922
|
+
const actor = activityActorField(input.actor).actor;
|
|
25923
|
+
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") });
|
|
25924
|
+
return Effect.succeed({
|
|
25925
|
+
actor,
|
|
25926
|
+
referenceActor: input.reference.kind === "payment" ? activityActorField(input.reference.actor).actor : void 0
|
|
25927
|
+
});
|
|
25928
|
+
}).pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() => callOperation(deps.convexCall, annotations.save, copyInvocationObservation(controls, {
|
|
25929
|
+
...actor === void 0 ? {} : { actor },
|
|
25930
|
+
reference: input.reference.kind === "payment" && referenceActor !== void 0 ? {
|
|
25931
|
+
kind: "payment",
|
|
25932
|
+
id: input.reference.id,
|
|
25933
|
+
actor: referenceActor
|
|
25934
|
+
} : {
|
|
25935
|
+
kind: input.reference.kind,
|
|
25936
|
+
id: input.reference.id
|
|
25937
|
+
},
|
|
25938
|
+
...input.counterpartyLabel === void 0 ? {} : { counterpartyLabel: input.counterpartyLabel },
|
|
25939
|
+
...input.accountingCategory === void 0 ? {} : { accountingCategory: input.accountingCategory },
|
|
25940
|
+
...input.memo === void 0 ? {} : { memo: input.memo }
|
|
25941
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })))).pipe(Effect.tap((annotation) => {
|
|
25942
|
+
const scope = decodeActivityViewer(annotation.actor);
|
|
25943
|
+
return scope === void 0 ? Effect.void : observeActivityScope(scope);
|
|
25944
|
+
})))), controls, annotations.save.name, deps.runPromise);
|
|
25945
|
+
}
|
|
25946
|
+
};
|
|
26255
25947
|
}
|
|
25948
|
+
Object.freeze({
|
|
25949
|
+
terminal: false,
|
|
25950
|
+
outcome: null,
|
|
25951
|
+
needsAction: false
|
|
25952
|
+
});
|
|
25953
|
+
Object.freeze({
|
|
25954
|
+
terminal: false,
|
|
25955
|
+
outcome: null,
|
|
25956
|
+
needsAction: true
|
|
25957
|
+
});
|
|
25958
|
+
Object.freeze({
|
|
25959
|
+
terminal: true,
|
|
25960
|
+
outcome: "good",
|
|
25961
|
+
needsAction: false
|
|
25962
|
+
});
|
|
25963
|
+
Object.freeze({
|
|
25964
|
+
terminal: true,
|
|
25965
|
+
outcome: "bad",
|
|
25966
|
+
needsAction: false
|
|
25967
|
+
});
|
|
25968
|
+
Object.freeze({
|
|
25969
|
+
terminal: true,
|
|
25970
|
+
outcome: "neutral",
|
|
25971
|
+
needsAction: false
|
|
25972
|
+
});
|
|
26256
25973
|
//#endregion
|
|
26257
|
-
//#region ../../packages/sdk/src/
|
|
26258
|
-
function
|
|
26259
|
-
return deps.indexer.read(table, input).pipe(Effect.catch((error) => {
|
|
26260
|
-
if (error.publicError.details?.reason === "scope-refused" || ![
|
|
26261
|
-
"NETWORK_ERROR",
|
|
26262
|
-
"RATE_LIMITED",
|
|
26263
|
-
"PROVIDER_ERROR",
|
|
26264
|
-
"CAPABILITY_UNAVAILABLE",
|
|
26265
|
-
"TIMEOUT"
|
|
26266
|
-
].includes(error.publicCode)) return Effect.fail({ publicError: error.publicError });
|
|
26267
|
-
return Effect.succeed(null);
|
|
26268
|
-
}));
|
|
26269
|
-
}
|
|
26270
|
-
function boundedRange(range, checkpoint) {
|
|
25974
|
+
//#region ../../packages/sdk/src/accounts/summaries.ts
|
|
25975
|
+
function makeMeMethods(deps) {
|
|
26271
25976
|
return {
|
|
26272
|
-
|
|
26273
|
-
|
|
26274
|
-
|
|
26275
|
-
}
|
|
26276
|
-
|
|
26277
|
-
|
|
26278
|
-
|
|
26279
|
-
|
|
26280
|
-
|
|
26281
|
-
|
|
26282
|
-
|
|
26283
|
-
|
|
26284
|
-
|
|
26285
|
-
|
|
26286
|
-
|
|
26287
|
-
}));
|
|
26288
|
-
}
|
|
26289
|
-
function readTransfers(deps, safe, range, state, wanted, first) {
|
|
26290
|
-
const from = Math.max(0, Math.ceil((range.from ?? 0) / 1e3));
|
|
26291
|
-
const to = Math.floor(range.to / 1e3);
|
|
26292
|
-
if (state.transferDone || from > to || !wanted && !first) return Effect.succeed([]);
|
|
26293
|
-
return chainRead(deps, "transfer", {
|
|
26294
|
-
safe: toAddress(safe),
|
|
26295
|
-
limit: wanted ? 100 : 1,
|
|
26296
|
-
timeline: {
|
|
26297
|
-
from,
|
|
26298
|
-
to,
|
|
26299
|
-
...state.transferBefore === null ? {} : { before: state.transferBefore }
|
|
25977
|
+
get: (options) => runIfActive(options?.signal, summaries.me.name, () => callOperation(deps.convexCall, summaries.me, {}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise),
|
|
25978
|
+
home: (options) => runIfActive(options?.signal, summaries.home.name, () => (deps.chainId === void 0 ? Effect.fail(Errors.notImplemented("accounts", "me.home")) : callOperation(deps.convexCall, summaries.home, {
|
|
25979
|
+
chainId: deps.chainId,
|
|
25980
|
+
...options?.orgId === void 0 ? {} : { orgId: options.orgId }
|
|
25981
|
+
})).pipe(Effect.flatMap((wire) => Effect.try({
|
|
25982
|
+
try: () => ({
|
|
25983
|
+
...wire,
|
|
25984
|
+
balances: wire.balances === null ? null : accountPositionsFromWire(wire.balances),
|
|
25985
|
+
recentActivity: wire.recentActivity?.filter(isActivityVisible) ?? null
|
|
25986
|
+
}),
|
|
25987
|
+
catch: asCapxulError
|
|
25988
|
+
})), Effect.mapError((publicError) => ({ publicError }))), deps.runPromise),
|
|
25989
|
+
depositInstructions: (options) => {
|
|
25990
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
25991
|
+
return runPortEffect(callOperation(deps.convexCall, summaries.depositInstructions, copyInvocationObservation(controls, {})).pipe(Effect.mapError((publicError) => ({ publicError })), Effect.tap((instructions) => emitDepositInitiated(deps.telemetry, instructions, controls))), controls, summaries.depositInstructions.name, deps.runPromise);
|
|
26300
25992
|
}
|
|
26301
|
-
}
|
|
25993
|
+
};
|
|
26302
25994
|
}
|
|
26303
|
-
|
|
26304
|
-
|
|
26305
|
-
|
|
26306
|
-
|
|
26307
|
-
|
|
26308
|
-
|
|
26309
|
-
|
|
25995
|
+
//#endregion
|
|
25996
|
+
//#region ../../packages/sdk/src/access/identity/account-lane.ts
|
|
25997
|
+
const staleAccountFailure = {
|
|
25998
|
+
code: "CANCELLED",
|
|
25999
|
+
message: "Account authority changed"
|
|
26000
|
+
};
|
|
26001
|
+
const requireAccountAuthority = (isCurrent, send, step) => Effect.suspend(() => isCurrent() ? Effect.void : send({
|
|
26002
|
+
_tag: "AccountFailed",
|
|
26003
|
+
step,
|
|
26004
|
+
failure: staleAccountFailure
|
|
26005
|
+
}).pipe(Effect.andThen(Effect.fail(staleAccountFailure))));
|
|
26006
|
+
const accountCall = (name, make, isCurrent) => Effect.suspend(() => isCurrent() ? call$1(name, make()) : Effect.fail(staleAccountFailure));
|
|
26007
|
+
const modelSession = (session) => ({
|
|
26008
|
+
authUserId: String(session.authUserId),
|
|
26009
|
+
email: String(session.email)
|
|
26010
|
+
});
|
|
26011
|
+
const claimAccount = (input, authUserId, signer, isCurrent) => {
|
|
26012
|
+
const cancelled = smartAccountErrorFromCapxul("claim", Errors.cancelled({ operation: CAPXUL_OPERATIONS.smartAccount.claim }));
|
|
26013
|
+
return call$1("smart-account.claim", Effect.tryPromise({
|
|
26014
|
+
try: () => {
|
|
26015
|
+
if (!isCurrent()) throw cancelled;
|
|
26016
|
+
return signer.getAddress();
|
|
26017
|
+
},
|
|
26018
|
+
catch: (cause) => cause === cancelled ? cancelled : signerFailure(signer.source, "getAddress", cause)
|
|
26019
|
+
}).pipe(Effect.flatMap((signerAddress) => Effect.suspend(() => isCurrent() ? input.ports.smartAccount.claim({
|
|
26020
|
+
authUserId: toAuthUserId(authUserId),
|
|
26021
|
+
chainId: toChainId(input.chainId),
|
|
26022
|
+
signerAddress
|
|
26023
|
+
}) : Effect.fail(cancelled)))));
|
|
26024
|
+
};
|
|
26025
|
+
const hasDurableClaim = (account) => account.deployedAt !== null && account.claimedAt !== null && account.signerAddress !== null;
|
|
26026
|
+
const profileComplete = (profile) => profile?.onboarded === true;
|
|
26027
|
+
const sessionProfile = (input, session) => {
|
|
26028
|
+
if (session === null) return Effect.succeed({
|
|
26029
|
+
session: null,
|
|
26030
|
+
profileComplete: false
|
|
26310
26031
|
});
|
|
26311
|
-
return
|
|
26312
|
-
|
|
26313
|
-
|
|
26314
|
-
filter: { kind: "payment" },
|
|
26315
|
-
...state.paymentCursor === null ? {} : { cursor: state.paymentCursor }
|
|
26316
|
-
}).pipe(Effect.map((page) => ({
|
|
26317
|
-
items: page.items,
|
|
26318
|
-
pending: page.items.map((item) => item.id),
|
|
26319
|
-
cursor: page.nextCursor,
|
|
26320
|
-
done: page.nextCursor === null
|
|
26032
|
+
return call$1("identity.load-by-auth-user-id", input.ports.identity.loadByAuthUserId(session.authUserId)).pipe(Effect.map((profile) => ({
|
|
26033
|
+
session: modelSession(session),
|
|
26034
|
+
profileComplete: profileComplete(profile)
|
|
26321
26035
|
})));
|
|
26322
|
-
}
|
|
26323
|
-
|
|
26324
|
-
|
|
26325
|
-
|
|
26326
|
-
|
|
26327
|
-
|
|
26328
|
-
|
|
26329
|
-
})
|
|
26330
|
-
|
|
26331
|
-
|
|
26332
|
-
|
|
26333
|
-
|
|
26334
|
-
|
|
26335
|
-
const found = yield* readTransfers(deps, scope.safe, range, state, wantsTransfers, first);
|
|
26336
|
-
if (found === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
|
|
26337
|
-
const transfers = wantsTransfers ? found : [];
|
|
26338
|
-
const page = yield* readPayments(deps, scope, controls, range, state, params?.filter?.kind !== "movement");
|
|
26339
|
-
const decoration = yield* decoratePage(deps, scope, controls, transfers, page, range);
|
|
26340
|
-
if (decoration === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
|
|
26341
|
-
const { items, next } = mergePage(scope.safe, params?.filter, params?.limit ?? 25, state, page, decoration, transfers, wantsTransfers);
|
|
26342
|
-
const cursor = next.paymentDone && next.paymentPending.length === 0 && next.transferDone ? null : JSON.stringify(next);
|
|
26343
|
-
if (cursor !== null && cursor === params?.cursor) return yield* Effect.fail({ publicError: Errors.invalidInput("cursor", "Activity source did not advance") });
|
|
26344
|
-
yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
|
|
26345
|
-
return {
|
|
26346
|
-
items,
|
|
26347
|
-
nextCursor: cursor,
|
|
26348
|
-
checkpoint: state.checkpoint,
|
|
26349
|
-
observedAt: Date.now()
|
|
26350
|
-
};
|
|
26036
|
+
};
|
|
26037
|
+
const accountLane = (input, config, session, send, isCurrent) => Effect.gen(function* () {
|
|
26038
|
+
let step = "wallet";
|
|
26039
|
+
const failAt = (at, failure) => send({
|
|
26040
|
+
_tag: "AccountFailed",
|
|
26041
|
+
step: at,
|
|
26042
|
+
failure
|
|
26043
|
+
}).pipe(Effect.andThen(Effect.fail(failure)));
|
|
26044
|
+
yield* requireAccountAuthority(isCurrent, send, step);
|
|
26045
|
+
step = "identity";
|
|
26046
|
+
yield* send({
|
|
26047
|
+
_tag: "AccountAt",
|
|
26048
|
+
step: "identity"
|
|
26351
26049
|
});
|
|
26352
|
-
|
|
26353
|
-
|
|
26354
|
-
|
|
26355
|
-
|
|
26356
|
-
|
|
26357
|
-
const
|
|
26358
|
-
|
|
26359
|
-
|
|
26360
|
-
|
|
26361
|
-
|
|
26362
|
-
|
|
26363
|
-
|
|
26364
|
-
|
|
26365
|
-
|
|
26366
|
-
|
|
26367
|
-
|
|
26368
|
-
|
|
26369
|
-
|
|
26370
|
-
|
|
26371
|
-
|
|
26372
|
-
})
|
|
26373
|
-
|
|
26374
|
-
|
|
26375
|
-
|
|
26376
|
-
|
|
26377
|
-
|
|
26378
|
-
|
|
26379
|
-
|
|
26380
|
-
|
|
26381
|
-
|
|
26382
|
-
|
|
26383
|
-
|
|
26384
|
-
|
|
26385
|
-
|
|
26386
|
-
|
|
26387
|
-
|
|
26388
|
-
|
|
26389
|
-
|
|
26390
|
-
|
|
26391
|
-
const bySettlement = new Map(facts.map((row) => [row.settlementId, row]));
|
|
26392
|
-
const byPayment = new Map(decoration.links.map((link) => [link.paymentId, {
|
|
26393
|
-
fact: bySettlement.get(link.settlementId),
|
|
26394
|
-
expected: link.expected
|
|
26395
|
-
}]));
|
|
26396
|
-
const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
|
|
26397
|
-
return {
|
|
26398
|
-
payments: page.pending.flatMap((id) => {
|
|
26399
|
-
const item = byId.get(id);
|
|
26400
|
-
if (item === void 0 || item.kind !== "payment" || item.createdAt > range.to || range.from !== void 0 && item.createdAt < range.from) return [];
|
|
26401
|
-
const confirmed = withAnnotation(confirmPayment(item, byPayment.get(id)?.fact, byPayment.get(id)?.expected), annotations.get(`payment:${id}`));
|
|
26402
|
-
return isActivityVisible(confirmed) ? [confirmed] : [];
|
|
26403
|
-
}),
|
|
26404
|
-
annotations,
|
|
26405
|
-
linked: new Set(decoration.links.filter((link) => matchesPaymentFact(bySettlement.get(link.settlementId), link.expected)).map((link) => link.settlementId))
|
|
26406
|
-
};
|
|
26050
|
+
step = "provision";
|
|
26051
|
+
yield* send({
|
|
26052
|
+
_tag: "AccountAt",
|
|
26053
|
+
step: "provision"
|
|
26054
|
+
});
|
|
26055
|
+
const existing = yield* Effect.result(accountCall("smart-account.load-by-auth-user-id", () => input.ports.smartAccount.loadByAuthUserId(toAuthUserId(session.authUserId)), isCurrent));
|
|
26056
|
+
yield* requireAccountAuthority(isCurrent, send, step);
|
|
26057
|
+
if (Result.isFailure(existing)) return yield* failAt("provision", existing.failure);
|
|
26058
|
+
const provisioned = yield* Effect.result(existing.success === null ? accountCall("smart-account.provision", () => input.ports.smartAccount.provision({
|
|
26059
|
+
authUserId: toAuthUserId(session.authUserId),
|
|
26060
|
+
chainId: toChainId(input.chainId),
|
|
26061
|
+
email: toEmail(session.email)
|
|
26062
|
+
}), isCurrent) : Effect.succeed(existing.success));
|
|
26063
|
+
yield* requireAccountAuthority(isCurrent, send, step);
|
|
26064
|
+
if (Result.isFailure(provisioned)) return yield* failAt("provision", provisioned.failure);
|
|
26065
|
+
const account = provisioned.success;
|
|
26066
|
+
const address = String(account.smartAccountAddress);
|
|
26067
|
+
yield* send({
|
|
26068
|
+
_tag: "AccountDerived",
|
|
26069
|
+
address
|
|
26070
|
+
});
|
|
26071
|
+
yield* requireAccountAuthority(isCurrent, send, step);
|
|
26072
|
+
if (hasDurableClaim(account)) return yield* send({
|
|
26073
|
+
_tag: "AccountClaimed",
|
|
26074
|
+
address
|
|
26075
|
+
});
|
|
26076
|
+
if (config.requirement !== "deployed" || input.signer === void 0) return;
|
|
26077
|
+
yield* requireAccountAuthority(isCurrent, send, step);
|
|
26078
|
+
step = "deploy";
|
|
26079
|
+
yield* send({ _tag: "AccountClaiming" });
|
|
26080
|
+
const claimed = yield* Effect.result(claimAccount(input, session.authUserId, input.signer, isCurrent));
|
|
26081
|
+
yield* requireAccountAuthority(isCurrent, send, step);
|
|
26082
|
+
if (Result.isFailure(claimed)) return yield* failAt("deploy", claimed.failure);
|
|
26083
|
+
return yield* send(hasDurableClaim(claimed.success) ? {
|
|
26084
|
+
_tag: "AccountClaimed",
|
|
26085
|
+
address: String(claimed.success.smartAccountAddress)
|
|
26086
|
+
} : {
|
|
26087
|
+
_tag: "AccountDerived",
|
|
26088
|
+
address: String(claimed.success.smartAccountAddress)
|
|
26407
26089
|
});
|
|
26090
|
+
});
|
|
26091
|
+
//#endregion
|
|
26092
|
+
//#region ../../packages/sdk/src/access/identity/spec.ts
|
|
26093
|
+
const SETTLED_BY_TRANSITION_TAGS = /* @__PURE__ */ new Set([
|
|
26094
|
+
"RestoreSession",
|
|
26095
|
+
"ResumeOtpEntry",
|
|
26096
|
+
"RestoreOtpEntry",
|
|
26097
|
+
"Reset",
|
|
26098
|
+
"SessionRestored",
|
|
26099
|
+
"SessionAbsent",
|
|
26100
|
+
"AttachOrganization",
|
|
26101
|
+
"LeaveOrganization",
|
|
26102
|
+
"ObserveOrganization"
|
|
26103
|
+
]);
|
|
26104
|
+
function isSettledByTransition(event) {
|
|
26105
|
+
return SETTLED_BY_TRANSITION_TAGS.has(event._tag);
|
|
26408
26106
|
}
|
|
26409
|
-
|
|
26410
|
-
const
|
|
26411
|
-
|
|
26412
|
-
|
|
26413
|
-
|
|
26414
|
-
|
|
26415
|
-
|
|
26416
|
-
|
|
26417
|
-
|
|
26418
|
-
|
|
26419
|
-
|
|
26420
|
-
|
|
26421
|
-
|
|
26422
|
-
|
|
26423
|
-
|
|
26424
|
-
|
|
26425
|
-
|
|
26426
|
-
|
|
26427
|
-
|
|
26428
|
-
|
|
26429
|
-
|
|
26430
|
-
|
|
26431
|
-
|
|
26432
|
-
|
|
26107
|
+
const identitySpec = (input, sessionStore, authority) => {
|
|
26108
|
+
const config = {
|
|
26109
|
+
...DEFAULT_CONFIG,
|
|
26110
|
+
requirement: input.requirement,
|
|
26111
|
+
hasSigner: input.signer !== void 0,
|
|
26112
|
+
...input.otpTtlMs === void 0 ? {} : { otpTtlMs: input.otpTtlMs }
|
|
26113
|
+
};
|
|
26114
|
+
return {
|
|
26115
|
+
machine: "identity",
|
|
26116
|
+
initial: RESTORING,
|
|
26117
|
+
label,
|
|
26118
|
+
slot: identitySlot,
|
|
26119
|
+
transition: (state, event) => transition(state, event, config),
|
|
26120
|
+
recoverFailure: recoverIdentityFailure,
|
|
26121
|
+
invalidates: (event) => event._tag === "Reset" ? [
|
|
26122
|
+
IDENTITY_SLOT.auth,
|
|
26123
|
+
IDENTITY_SLOT.session,
|
|
26124
|
+
IDENTITY_SLOT.account,
|
|
26125
|
+
IDENTITY_SLOT.org
|
|
26126
|
+
] : event._tag === "SignOut" ? [
|
|
26127
|
+
IDENTITY_SLOT.session,
|
|
26128
|
+
IDENTITY_SLOT.account,
|
|
26129
|
+
IDENTITY_SLOT.org
|
|
26130
|
+
] : event._tag === "LeaveOrganization" || event._tag === "AttachOrganization" ? [IDENTITY_SLOT.org] : [],
|
|
26131
|
+
work: ({ state, event }) => {
|
|
26132
|
+
const activeSession = state.phase === "authenticated" ? state.session : null;
|
|
26133
|
+
const org = state.phase === "authenticated" && state.account.at === "claimed" ? state.account.org : null;
|
|
26134
|
+
if (isSettledByTransition(event)) return void 0;
|
|
26135
|
+
switch (event._tag) {
|
|
26136
|
+
case "RequestOtp": return {
|
|
26137
|
+
slot: IDENTITY_SLOT.auth,
|
|
26138
|
+
port: "auth-client",
|
|
26139
|
+
run: (send, controls) => call$1("auth-client.send-otp", input.ports.authClient.sendOtp(copyInvocationObservation(controls, { email: toEmail(event.email) }))).pipe(Effect.andThen(call$1("clock.now", input.ports.clock.now)), Effect.flatMap((at) => send({
|
|
26140
|
+
_tag: "OtpSent",
|
|
26141
|
+
at
|
|
26142
|
+
})), Effect.tapError((failure) => send({
|
|
26143
|
+
_tag: "OtpFailed",
|
|
26144
|
+
failure
|
|
26145
|
+
})))
|
|
26146
|
+
};
|
|
26147
|
+
case "VerifyOtp": return {
|
|
26148
|
+
slot: IDENTITY_SLOT.auth,
|
|
26149
|
+
port: "auth-client",
|
|
26150
|
+
run: (send, controls) => call$1("auth-client.verify-otp", input.ports.authClient.verifyOtp(copyInvocationObservation(controls, {
|
|
26151
|
+
email: toEmail(event.email),
|
|
26152
|
+
otp: event.otp
|
|
26153
|
+
}))).pipe(Effect.tap((session) => Effect.sync(() => {
|
|
26154
|
+
authority.recordProviderSession(session);
|
|
26155
|
+
})), Effect.flatMap((session) => sessionProfile(input, session).pipe(Effect.flatMap((loaded) => send({
|
|
26156
|
+
_tag: "Verified",
|
|
26157
|
+
session: loaded.session ?? modelSession(session),
|
|
26158
|
+
profileComplete: loaded.profileComplete
|
|
26159
|
+
})))), Effect.tapError((failure) => send({
|
|
26160
|
+
_tag: "VerifyFailed",
|
|
26161
|
+
failure
|
|
26162
|
+
})))
|
|
26163
|
+
};
|
|
26164
|
+
case "ReadSession": return {
|
|
26165
|
+
slot: IDENTITY_SLOT.session,
|
|
26166
|
+
port: "auth-client",
|
|
26167
|
+
run: (send, controls) => call$1("auth-client.get-session", input.ports.authClient.getSession(copyInvocationObservation(controls, {}))).pipe(Effect.tap((session) => Effect.sync(() => {
|
|
26168
|
+
authority.recordProviderSession(session, activeSession?.authUserId);
|
|
26169
|
+
})), Effect.flatMap((session) => sessionProfile(input, session)), Effect.flatMap((loaded) => send({
|
|
26170
|
+
_tag: "SessionRead",
|
|
26171
|
+
...loaded
|
|
26172
|
+
})), Effect.tapError((failure) => Effect.sync(() => {
|
|
26173
|
+
authority.recordProviderSession(null, failure.code === "NOT_AUTHENTICATED" ? activeSession?.authUserId : void 0);
|
|
26174
|
+
}).pipe(Effect.andThen(activeSession !== null && failure.code === "NOT_AUTHENTICATED" ? Effect.void : send({
|
|
26175
|
+
_tag: "SessionReadFailed",
|
|
26176
|
+
failure
|
|
26177
|
+
})))))
|
|
26178
|
+
};
|
|
26179
|
+
case "SignOut": return {
|
|
26180
|
+
slot: IDENTITY_SLOT.auth,
|
|
26181
|
+
port: "auth-client",
|
|
26182
|
+
run: (send, controls) => call$1("auth-client.sign-out", input.ports.authClient.signOut(copyInvocationObservation(controls, {}))).pipe(Effect.andThen(send({ _tag: "SignedOut" })), Effect.tapError((failure) => send({
|
|
26183
|
+
_tag: "SignOutFailed",
|
|
26184
|
+
failure
|
|
26185
|
+
})), Effect.ensuring(Effect.sync(() => {
|
|
26186
|
+
authority.recordProviderSession(null);
|
|
26187
|
+
})))
|
|
26188
|
+
};
|
|
26189
|
+
case "EnsureAccount":
|
|
26190
|
+
case "RetryAccount": return activeSession === null ? void 0 : {
|
|
26191
|
+
slot: IDENTITY_SLOT.account,
|
|
26192
|
+
port: "smart-account",
|
|
26193
|
+
run: (send) => {
|
|
26194
|
+
const captured = authority.snapshot();
|
|
26195
|
+
return accountLane(input, config, activeSession, send, () => authority.matches(captured, activeSession.authUserId));
|
|
26196
|
+
}
|
|
26197
|
+
};
|
|
26198
|
+
case "ClaimAccount": {
|
|
26199
|
+
const signer = input.signer;
|
|
26200
|
+
const address = state.phase === "authenticated" && state.account.at === "claiming" ? state.account.address : null;
|
|
26201
|
+
return address === null || activeSession === null || signer === void 0 ? void 0 : {
|
|
26202
|
+
slot: IDENTITY_SLOT.account,
|
|
26203
|
+
port: "smart-account",
|
|
26204
|
+
run: (send) => {
|
|
26205
|
+
const captured = authority.snapshot();
|
|
26206
|
+
const isCurrent = () => authority.matches(captured, activeSession.authUserId);
|
|
26207
|
+
return Effect.gen(function* () {
|
|
26208
|
+
const claimed = yield* Effect.result(claimAccount(input, activeSession.authUserId, signer, isCurrent));
|
|
26209
|
+
yield* requireAccountAuthority(isCurrent, send, "deploy");
|
|
26210
|
+
yield* Result.isFailure(claimed) ? send({
|
|
26211
|
+
_tag: "AccountFailed",
|
|
26212
|
+
step: "deploy",
|
|
26213
|
+
failure: claimed.failure
|
|
26214
|
+
}).pipe(Effect.andThen(Effect.fail(claimed.failure))) : send(hasDurableClaim(claimed.success) ? {
|
|
26215
|
+
_tag: "AccountClaimed",
|
|
26216
|
+
address
|
|
26217
|
+
} : {
|
|
26218
|
+
_tag: "AccountDerived",
|
|
26219
|
+
address
|
|
26220
|
+
});
|
|
26221
|
+
});
|
|
26222
|
+
}
|
|
26223
|
+
};
|
|
26224
|
+
}
|
|
26225
|
+
case "CreateOrganization": return input.organizationSetup === void 0 || org === null || org.orgId !== null ? void 0 : {
|
|
26226
|
+
slot: IDENTITY_SLOT.org,
|
|
26227
|
+
port: "organization-setup",
|
|
26228
|
+
run: (send, controls) => beginOrganization$1(input.organizationSetup, event.draft, send, controls)
|
|
26229
|
+
};
|
|
26230
|
+
default: return absurd(event);
|
|
26231
|
+
}
|
|
26433
26232
|
}
|
|
26434
|
-
}
|
|
26233
|
+
};
|
|
26234
|
+
};
|
|
26235
|
+
//#endregion
|
|
26236
|
+
//#region ../../packages/sdk/src/access/identity/authority.ts
|
|
26237
|
+
function createIdentityAuthorityTracker(sessionStore, onGenerationChange = () => void 0) {
|
|
26238
|
+
const invalidationListeners = /* @__PURE__ */ new Set();
|
|
26239
|
+
let generation = 0;
|
|
26240
|
+
let authUserId = null;
|
|
26241
|
+
let stopped = false;
|
|
26242
|
+
let readState = initialIdentityState;
|
|
26243
|
+
const advanceGeneration = () => {
|
|
26244
|
+
generation += 1;
|
|
26245
|
+
authUserId = null;
|
|
26246
|
+
const listeners = [...invalidationListeners];
|
|
26247
|
+
for (const listener of listeners) try {
|
|
26248
|
+
listener();
|
|
26249
|
+
} catch {}
|
|
26250
|
+
try {
|
|
26251
|
+
onGenerationChange();
|
|
26252
|
+
} catch {}
|
|
26253
|
+
};
|
|
26254
|
+
const revoke = () => {
|
|
26255
|
+
if (stopped) return;
|
|
26256
|
+
advanceGeneration();
|
|
26257
|
+
authUserId = null;
|
|
26258
|
+
};
|
|
26435
26259
|
return {
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
26439
|
-
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
|
|
26443
|
-
|
|
26260
|
+
snapshot: () => ({
|
|
26261
|
+
generation,
|
|
26262
|
+
authUserId
|
|
26263
|
+
}),
|
|
26264
|
+
subscribeInvalidation: (listener) => {
|
|
26265
|
+
if (stopped) {
|
|
26266
|
+
try {
|
|
26267
|
+
listener();
|
|
26268
|
+
} catch {}
|
|
26269
|
+
return () => {};
|
|
26270
|
+
}
|
|
26271
|
+
invalidationListeners.add(listener);
|
|
26272
|
+
return () => {
|
|
26273
|
+
invalidationListeners.delete(listener);
|
|
26274
|
+
};
|
|
26275
|
+
},
|
|
26276
|
+
bindState: (read) => {
|
|
26277
|
+
readState = read;
|
|
26278
|
+
},
|
|
26279
|
+
recordProviderSession: (session, previousAuthUserId) => {
|
|
26280
|
+
if (previousAuthUserId != null && String(session?.authUserId ?? "") !== previousAuthUserId) revoke();
|
|
26281
|
+
sessionStore.current = session;
|
|
26282
|
+
},
|
|
26283
|
+
observe: (state) => {
|
|
26284
|
+
if (stopped) return;
|
|
26285
|
+
const next = state.phase === "authenticated" ? state.session.authUserId : null;
|
|
26286
|
+
if (authUserId !== null && next !== authUserId) advanceGeneration();
|
|
26287
|
+
authUserId = next;
|
|
26288
|
+
},
|
|
26289
|
+
invalidate: () => {
|
|
26290
|
+
if (stopped) return;
|
|
26291
|
+
stopped = true;
|
|
26292
|
+
advanceGeneration();
|
|
26293
|
+
invalidationListeners.clear();
|
|
26294
|
+
authUserId = null;
|
|
26295
|
+
},
|
|
26296
|
+
matches: (captured, expectedAuthUserId) => {
|
|
26297
|
+
if (stopped || generation !== captured.generation || authUserId !== expectedAuthUserId) return false;
|
|
26298
|
+
const state = readState();
|
|
26299
|
+
if (expectedAuthUserId === null) return state.phase !== "authenticated" && sessionStore.current === null;
|
|
26300
|
+
return state.phase === "authenticated" && state.session.authUserId === expectedAuthUserId && String(sessionStore.current?.authUserId ?? "") === expectedAuthUserId;
|
|
26444
26301
|
}
|
|
26445
26302
|
};
|
|
26446
26303
|
}
|
|
26447
|
-
|
|
26448
|
-
|
|
26449
|
-
|
|
26450
|
-
|
|
26451
|
-
|
|
26452
|
-
|
|
26453
|
-
|
|
26454
|
-
|
|
26455
|
-
|
|
26456
|
-
|
|
26457
|
-
|
|
26458
|
-
|
|
26459
|
-
|
|
26460
|
-
|
|
26461
|
-
|
|
26462
|
-
|
|
26463
|
-
|
|
26464
|
-
|
|
26465
|
-
|
|
26466
|
-
|
|
26467
|
-
|
|
26468
|
-
|
|
26469
|
-
|
|
26470
|
-
|
|
26471
|
-
|
|
26472
|
-
|
|
26473
|
-
|
|
26474
|
-
|
|
26475
|
-
|
|
26476
|
-
|
|
26477
|
-
|
|
26478
|
-
|
|
26479
|
-
|
|
26480
|
-
|
|
26481
|
-
|
|
26482
|
-
|
|
26483
|
-
|
|
26484
|
-
|
|
26485
|
-
|
|
26486
|
-
|
|
26487
|
-
|
|
26488
|
-
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
|
|
26495
|
-
|
|
26496
|
-
|
|
26497
|
-
|
|
26498
|
-
|
|
26499
|
-
|
|
26500
|
-
|
|
26501
|
-
|
|
26502
|
-
|
|
26503
|
-
|
|
26504
|
-
|
|
26505
|
-
|
|
26506
|
-
paymentCursor: null,
|
|
26507
|
-
paymentDone: false,
|
|
26508
|
-
paymentPending: [],
|
|
26509
|
-
transferBefore: null,
|
|
26510
|
-
transferDone: false,
|
|
26511
|
-
degraded: false
|
|
26304
|
+
//#endregion
|
|
26305
|
+
//#region ../../packages/sdk/src/platform/actor/transitions.ts
|
|
26306
|
+
/**
|
|
26307
|
+
* Who hears an actor: the boot observer, transition and state subscribers, and
|
|
26308
|
+
* the defect reporter. An observer that throws is dropped and reported as a
|
|
26309
|
+
* defect; it never owns actor truth.
|
|
26310
|
+
*/
|
|
26311
|
+
const makeActorObservers = (options) => {
|
|
26312
|
+
const stateObservers = /* @__PURE__ */ new Set();
|
|
26313
|
+
const transitionObservers = /* @__PURE__ */ new Set();
|
|
26314
|
+
let bootTransitionObserver = options.onTransition;
|
|
26315
|
+
const defect = (cause) => {
|
|
26316
|
+
try {
|
|
26317
|
+
options.onDefect?.(cause);
|
|
26318
|
+
} catch {}
|
|
26319
|
+
};
|
|
26320
|
+
const emit = (record) => Effect.sync(() => {
|
|
26321
|
+
if (bootTransitionObserver !== void 0) try {
|
|
26322
|
+
bootTransitionObserver(record);
|
|
26323
|
+
} catch (cause) {
|
|
26324
|
+
bootTransitionObserver = void 0;
|
|
26325
|
+
defect(cause);
|
|
26326
|
+
}
|
|
26327
|
+
for (const observer of transitionObservers) try {
|
|
26328
|
+
observer(record);
|
|
26329
|
+
} catch (cause) {
|
|
26330
|
+
transitionObservers.delete(observer);
|
|
26331
|
+
defect(cause);
|
|
26332
|
+
}
|
|
26333
|
+
}).pipe(Effect.andThen(logIdentityTransition(record)));
|
|
26334
|
+
const notify = (state) => {
|
|
26335
|
+
for (const observer of stateObservers) try {
|
|
26336
|
+
observer(state);
|
|
26337
|
+
} catch (cause) {
|
|
26338
|
+
stateObservers.delete(observer);
|
|
26339
|
+
defect(cause);
|
|
26340
|
+
}
|
|
26341
|
+
};
|
|
26342
|
+
return {
|
|
26343
|
+
stateObservers,
|
|
26344
|
+
transitionObservers,
|
|
26345
|
+
defect,
|
|
26346
|
+
emit,
|
|
26347
|
+
notify
|
|
26348
|
+
};
|
|
26349
|
+
};
|
|
26350
|
+
/** One machine's transition records and the failure a refused or failed reply carries. */
|
|
26351
|
+
const makeTransitionRecords = (spec, slotEpochs) => {
|
|
26352
|
+
const makeFailure = (state, event, reason, details, cause) => new ActorFailure(reason, spec.machine, spec.label(state), event._tag, details, cause);
|
|
26353
|
+
const nonApplied = (state, event, reason, outcome, env, failure) => {
|
|
26354
|
+
const slot = env.origin === null ? spec.slot(env.event) : env.origin.slot;
|
|
26355
|
+
const base = {
|
|
26356
|
+
machine: spec.machine,
|
|
26357
|
+
state: spec.label(state),
|
|
26358
|
+
event: event._tag,
|
|
26359
|
+
slot,
|
|
26360
|
+
epoch: env.origin?.epoch ?? slotEpochs.get(slot) ?? 0,
|
|
26361
|
+
duration_ms: duration(env.startedAt),
|
|
26362
|
+
...withCarriage(env.invocation)
|
|
26512
26363
|
};
|
|
26513
|
-
const
|
|
26514
|
-
|
|
26515
|
-
|
|
26516
|
-
|
|
26517
|
-
|
|
26518
|
-
|
|
26364
|
+
const record = outcome === "refused" ? {
|
|
26365
|
+
...base,
|
|
26366
|
+
outcome,
|
|
26367
|
+
refusal_code: reason
|
|
26368
|
+
} : {
|
|
26369
|
+
...base,
|
|
26370
|
+
outcome,
|
|
26371
|
+
error_code: reason,
|
|
26372
|
+
...failure === void 0 ? {} : { failure }
|
|
26519
26373
|
};
|
|
26520
|
-
|
|
26521
|
-
|
|
26374
|
+
return copyInvocationObservation(env.invocation, record);
|
|
26375
|
+
};
|
|
26376
|
+
const applied = (from, to, event, slot, epoch, env) => copyInvocationObservation(env.invocation, {
|
|
26377
|
+
machine: spec.machine,
|
|
26378
|
+
from: spec.label(from),
|
|
26379
|
+
event: event._tag,
|
|
26380
|
+
to: spec.label(to),
|
|
26381
|
+
slot,
|
|
26382
|
+
epoch,
|
|
26383
|
+
outcome: "applied",
|
|
26384
|
+
duration_ms: duration(env.startedAt),
|
|
26385
|
+
...withCarriage(env.invocation)
|
|
26522
26386
|
});
|
|
26523
|
-
|
|
26524
|
-
|
|
26525
|
-
|
|
26526
|
-
|
|
26527
|
-
|
|
26528
|
-
|
|
26529
|
-
|
|
26530
|
-
|
|
26531
|
-
|
|
26532
|
-
|
|
26533
|
-
|
|
26534
|
-
|
|
26387
|
+
return {
|
|
26388
|
+
makeFailure,
|
|
26389
|
+
nonApplied,
|
|
26390
|
+
applied
|
|
26391
|
+
};
|
|
26392
|
+
};
|
|
26393
|
+
//#endregion
|
|
26394
|
+
//#region ../../packages/sdk/src/platform/actor/lane-exit.ts
|
|
26395
|
+
/**
|
|
26396
|
+
* How a work lane's exit settles its reply: finished, failed, cancelled, or
|
|
26397
|
+
* died. A timeout, cancellation or death first lets the machine recover its
|
|
26398
|
+
* own state through `spec.recoverFailure`.
|
|
26399
|
+
*/
|
|
26400
|
+
const makeLaneSettlement = (actor) => {
|
|
26401
|
+
const { spec, cell, slots } = actor;
|
|
26402
|
+
const { emit, notify, defect } = actor.observers;
|
|
26403
|
+
const { makeFailure, nonApplied, applied } = actor.records;
|
|
26404
|
+
const finish = (cursor, state) => {
|
|
26405
|
+
const held = slots.get(cursor.slot);
|
|
26406
|
+
if (held === void 0 || held.cursor !== cursor) return Effect.void;
|
|
26407
|
+
slots.delete(cursor.slot);
|
|
26408
|
+
return Effect.asVoid(Deferred.succeed(held.reply, state));
|
|
26409
|
+
};
|
|
26410
|
+
const recoverFailure = (state, cursor, failure) => {
|
|
26411
|
+
const event = spec.recoverFailure?.(state, cursor.event, failure);
|
|
26412
|
+
if (event === void 0) return Effect.succeed(state);
|
|
26413
|
+
const outcome = spec.transition(state, event);
|
|
26414
|
+
if ("refused" in outcome) return Effect.succeed(state);
|
|
26415
|
+
return Ref.set(cell, outcome.next).pipe(Effect.tap(() => {
|
|
26416
|
+
const record = applied(state, outcome.next, event, cursor.slot, cursor.epoch, {
|
|
26417
|
+
invocation: cursor.invocation,
|
|
26418
|
+
startedAt: cursor.startedAt
|
|
26535
26419
|
});
|
|
26536
|
-
|
|
26537
|
-
|
|
26538
|
-
|
|
26539
|
-
|
|
26540
|
-
|
|
26541
|
-
|
|
26542
|
-
|
|
26543
|
-
|
|
26544
|
-
|
|
26545
|
-
|
|
26546
|
-
|
|
26547
|
-
|
|
26548
|
-
|
|
26549
|
-
|
|
26550
|
-
|
|
26551
|
-
|
|
26552
|
-
|
|
26553
|
-
|
|
26554
|
-
|
|
26555
|
-
|
|
26556
|
-
|
|
26557
|
-
|
|
26558
|
-
|
|
26559
|
-
|
|
26560
|
-
|
|
26561
|
-
|
|
26562
|
-
|
|
26563
|
-
|
|
26564
|
-
|
|
26565
|
-
|
|
26566
|
-
|
|
26567
|
-
|
|
26568
|
-
return yield* deps.indexer.subscribe("transfer", {
|
|
26569
|
-
safe: toAddress(scope.safe),
|
|
26570
|
-
limit: 100
|
|
26571
|
-
}, (snapshot) => {
|
|
26572
|
-
if (snapshot.status === "ok") {
|
|
26573
|
-
if (delivered) onChange();
|
|
26574
|
-
delivered = true;
|
|
26575
|
-
}
|
|
26576
|
-
if (snapshot.status === "error") {
|
|
26577
|
-
delivered = true;
|
|
26578
|
-
onChange();
|
|
26579
|
-
}
|
|
26580
|
-
});
|
|
26420
|
+
return emit(record).pipe(Effect.andThen(Effect.sync(() => {
|
|
26421
|
+
notify(outcome.next);
|
|
26422
|
+
})));
|
|
26423
|
+
}), Effect.as(outcome.next));
|
|
26424
|
+
};
|
|
26425
|
+
return (env, origin, exit, state) => Effect.gen(function* () {
|
|
26426
|
+
const held = slots.get(origin.slot);
|
|
26427
|
+
if (held === void 0 || held.cursor !== origin) return;
|
|
26428
|
+
if (exit.defect !== void 0) {
|
|
26429
|
+
slots.delete(origin.slot);
|
|
26430
|
+
defect(exit.defect);
|
|
26431
|
+
const event = { _tag: origin.event };
|
|
26432
|
+
const died = {
|
|
26433
|
+
code: "WORK_DIED",
|
|
26434
|
+
message: exit.defect instanceof Error ? exit.defect.message : "Identity work died"
|
|
26435
|
+
};
|
|
26436
|
+
const failedState = yield* recoverFailure(state, origin, died);
|
|
26437
|
+
yield* emit(nonApplied(failedState, event, "WORK_DIED", "failed", env, died));
|
|
26438
|
+
yield* failReply(held.reply, makeFailure(failedState, event, "WORK_DIED", void 0, died));
|
|
26439
|
+
return;
|
|
26440
|
+
}
|
|
26441
|
+
if (exit.failure !== void 0) {
|
|
26442
|
+
slots.delete(origin.slot);
|
|
26443
|
+
const outcome = exit.failure.code === "CANCELLED" ? "cancelled" : "failed";
|
|
26444
|
+
const event = { _tag: origin.event };
|
|
26445
|
+
const failedState = exit.failure === timeoutFailure || exit.failure === cancelledFailure ? yield* recoverFailure(state, origin, exit.failure) : state;
|
|
26446
|
+
yield* emit(nonApplied(failedState, event, exit.failure.code, outcome, env, exit.failure));
|
|
26447
|
+
const details = exit.failure === timeoutFailure ? { reason: "timeout" } : refusedInputField(exit.failure);
|
|
26448
|
+
yield* failReply(held.reply, makeFailure(failedState, event, exit.failure.code, details, exit.failure.error ?? exit.failure));
|
|
26449
|
+
return;
|
|
26450
|
+
}
|
|
26451
|
+
yield* finish(origin, state);
|
|
26581
26452
|
});
|
|
26582
|
-
}
|
|
26453
|
+
};
|
|
26583
26454
|
//#endregion
|
|
26584
|
-
//#region ../../packages/sdk/src/
|
|
26585
|
-
|
|
26586
|
-
|
|
26587
|
-
|
|
26588
|
-
|
|
26589
|
-
|
|
26590
|
-
|
|
26591
|
-
|
|
26592
|
-
|
|
26593
|
-
|
|
26594
|
-
|
|
26595
|
-
|
|
26596
|
-
|
|
26597
|
-
|
|
26598
|
-
|
|
26599
|
-
|
|
26600
|
-
|
|
26601
|
-
|
|
26602
|
-
|
|
26603
|
-
|
|
26604
|
-
|
|
26605
|
-
|
|
26606
|
-
|
|
26607
|
-
|
|
26608
|
-
|
|
26609
|
-
|
|
26610
|
-
|
|
26611
|
-
|
|
26612
|
-
|
|
26613
|
-
|
|
26614
|
-
|
|
26615
|
-
|
|
26616
|
-
|
|
26617
|
-
|
|
26618
|
-
|
|
26619
|
-
|
|
26620
|
-
|
|
26621
|
-
|
|
26622
|
-
|
|
26623
|
-
|
|
26624
|
-
|
|
26625
|
-
|
|
26626
|
-
|
|
26627
|
-
|
|
26628
|
-
|
|
26629
|
-
|
|
26455
|
+
//#region ../../packages/sdk/src/platform/actor/shell.ts
|
|
26456
|
+
const boot = (spec, options = {}) => Effect.gen(function* () {
|
|
26457
|
+
const mailbox = yield* Queue.unbounded();
|
|
26458
|
+
const cell = yield* Ref.make(spec.initial);
|
|
26459
|
+
const slotEpochs = /* @__PURE__ */ new Map();
|
|
26460
|
+
const slots = /* @__PURE__ */ new Map();
|
|
26461
|
+
const { stateObservers, transitionObservers, defect, emit, notify } = makeActorObservers(options);
|
|
26462
|
+
const { makeFailure, nonApplied, applied } = makeTransitionRecords(spec, slotEpochs);
|
|
26463
|
+
let stopped = false;
|
|
26464
|
+
const settleLaneExit = makeLaneSettlement({
|
|
26465
|
+
spec,
|
|
26466
|
+
cell,
|
|
26467
|
+
slots,
|
|
26468
|
+
observers: {
|
|
26469
|
+
emit,
|
|
26470
|
+
notify,
|
|
26471
|
+
defect
|
|
26472
|
+
},
|
|
26473
|
+
records: {
|
|
26474
|
+
makeFailure,
|
|
26475
|
+
nonApplied,
|
|
26476
|
+
applied
|
|
26477
|
+
}
|
|
26478
|
+
});
|
|
26479
|
+
const step = (env) => Effect.gen(function* () {
|
|
26480
|
+
const state = yield* Ref.get(cell);
|
|
26481
|
+
if (env.origin !== null && (slotEpochs.get(env.origin.slot) ?? 0) !== env.origin.epoch) {
|
|
26482
|
+
const held = slots.get(env.origin.slot);
|
|
26483
|
+
if (held === void 0 || held.cursor !== env.origin) return;
|
|
26484
|
+
slots.delete(env.origin.slot);
|
|
26485
|
+
const event = { _tag: env.origin.event };
|
|
26486
|
+
yield* emit(nonApplied(state, event, "STALE_EPOCH", "refused", env));
|
|
26487
|
+
yield* failReply(held.reply, makeFailure(state, event, "STALE_EPOCH"));
|
|
26488
|
+
return;
|
|
26489
|
+
}
|
|
26490
|
+
if (isLaneExitEnvelope(env)) {
|
|
26491
|
+
yield* settleLaneExit(env, env.origin, env.event, state);
|
|
26492
|
+
return;
|
|
26493
|
+
}
|
|
26494
|
+
const outcome = spec.transition(state, env.event);
|
|
26495
|
+
if ("refused" in outcome) {
|
|
26496
|
+
yield* emit(nonApplied(state, env.event, outcome.refused, "refused", env));
|
|
26497
|
+
yield* failReply(env.reply, makeFailure(state, env.event, outcome.refused));
|
|
26498
|
+
return;
|
|
26499
|
+
}
|
|
26500
|
+
const next = outcome.next;
|
|
26501
|
+
yield* Ref.set(cell, next);
|
|
26502
|
+
const emitApplied = (slot, epoch) => emit(applied(state, next, env.event, slot, epoch, env));
|
|
26503
|
+
if (env.origin !== null) {
|
|
26504
|
+
yield* emitApplied(env.origin.slot, env.origin.epoch);
|
|
26505
|
+
notify(next);
|
|
26506
|
+
return;
|
|
26507
|
+
}
|
|
26508
|
+
for (const slot of spec.invalidates?.(env.event) ?? []) {
|
|
26509
|
+
if (!slots.has(slot)) continue;
|
|
26510
|
+
slotEpochs.set(slot, (slotEpochs.get(slot) ?? 0) + 1);
|
|
26511
|
+
}
|
|
26512
|
+
const work = env.reply === null ? void 0 : spec.work?.({
|
|
26513
|
+
state: next,
|
|
26514
|
+
event: env.event
|
|
26515
|
+
});
|
|
26516
|
+
if (work === void 0 || env.reply === null) {
|
|
26517
|
+
const slot = spec.slot(env.event);
|
|
26518
|
+
yield* emitApplied(slot, slotEpochs.get(slot) ?? 0);
|
|
26519
|
+
notify(next);
|
|
26520
|
+
if (env.reply !== null) yield* Deferred.succeed(env.reply, next).pipe(Effect.asVoid);
|
|
26521
|
+
return;
|
|
26522
|
+
}
|
|
26523
|
+
const previous = slots.get(work.slot);
|
|
26524
|
+
if (previous !== void 0) {
|
|
26525
|
+
slots.delete(work.slot);
|
|
26526
|
+
yield* Fiber.interrupt(previous.fiber);
|
|
26527
|
+
const previousEvent = { _tag: previous.cursor.event };
|
|
26528
|
+
const reason = previous.cursor.epoch === (slotEpochs.get(work.slot) ?? 0) ? "SUPERSEDED" : "STALE_EPOCH";
|
|
26529
|
+
yield* emit(nonApplied(next, previousEvent, reason, "refused", {
|
|
26530
|
+
origin: previous.cursor,
|
|
26531
|
+
invocation: previous.cursor.invocation,
|
|
26532
|
+
startedAt: previous.cursor.startedAt
|
|
26533
|
+
}));
|
|
26534
|
+
yield* failReply(previous.reply, makeFailure(next, previousEvent, reason));
|
|
26535
|
+
}
|
|
26536
|
+
const epoch = (slotEpochs.get(work.slot) ?? 0) + 1;
|
|
26537
|
+
slotEpochs.set(work.slot, epoch);
|
|
26538
|
+
const cursor = {
|
|
26539
|
+
slot: work.slot,
|
|
26540
|
+
epoch,
|
|
26541
|
+
event: env.event._tag,
|
|
26542
|
+
startedAt: env.startedAt,
|
|
26543
|
+
invocation: env.invocation
|
|
26630
26544
|
};
|
|
26545
|
+
yield* emitApplied(work.slot, epoch);
|
|
26546
|
+
notify(next);
|
|
26547
|
+
const send = (event) => Queue.offer(mailbox, {
|
|
26548
|
+
event,
|
|
26549
|
+
reply: null,
|
|
26550
|
+
origin: cursor,
|
|
26551
|
+
invocation: env.invocation,
|
|
26552
|
+
startedAt: env.startedAt
|
|
26553
|
+
}).pipe(Effect.asVoid);
|
|
26554
|
+
const complete = (event) => Queue.offer(mailbox, {
|
|
26555
|
+
event,
|
|
26556
|
+
reply: null,
|
|
26557
|
+
origin: cursor,
|
|
26558
|
+
invocation: env.invocation,
|
|
26559
|
+
startedAt: env.startedAt
|
|
26560
|
+
}).pipe(Effect.asVoid);
|
|
26561
|
+
const guarded = Effect.gen(function* () {
|
|
26562
|
+
yield* Effect.annotateCurrentSpan({
|
|
26563
|
+
slot: work.slot,
|
|
26564
|
+
epoch,
|
|
26565
|
+
port: work.port
|
|
26566
|
+
});
|
|
26567
|
+
return yield* control(work.run(send, env.invocation), env.invocation);
|
|
26568
|
+
}).pipe(Effect.withSpan(`identity.work.${work.slot}`)).pipe(Effect.matchEffect({
|
|
26569
|
+
onFailure: (failure) => complete(laneExit({ failure })),
|
|
26570
|
+
onSuccess: () => complete(laneExit({}))
|
|
26571
|
+
}), Effect.catchDefect((cause) => complete(laneExit({ defect: cause }))));
|
|
26572
|
+
const fiber = yield* Effect.forkChild(guarded, { startImmediately: true });
|
|
26573
|
+
slots.set(work.slot, {
|
|
26574
|
+
fiber,
|
|
26575
|
+
reply: env.reply,
|
|
26576
|
+
cursor
|
|
26577
|
+
});
|
|
26631
26578
|
});
|
|
26632
|
-
|
|
26633
|
-
|
|
26634
|
-
|
|
26635
|
-
|
|
26636
|
-
|
|
26637
|
-
|
|
26638
|
-
|
|
26639
|
-
|
|
26640
|
-
*
|
|
26641
|
-
|
|
26642
|
-
|
|
26643
|
-
|
|
26644
|
-
|
|
26645
|
-
|
|
26646
|
-
|
|
26647
|
-
*
|
|
26648
|
-
*
|
|
26649
|
-
|
|
26650
|
-
|
|
26651
|
-
|
|
26652
|
-
|
|
26653
|
-
|
|
26654
|
-
|
|
26655
|
-
|
|
26656
|
-
|
|
26657
|
-
|
|
26658
|
-
|
|
26659
|
-
|
|
26660
|
-
|
|
26661
|
-
|
|
26662
|
-
|
|
26663
|
-
return
|
|
26664
|
-
|
|
26665
|
-
|
|
26666
|
-
|
|
26667
|
-
|
|
26668
|
-
|
|
26669
|
-
|
|
26670
|
-
...organizationId === void 0 ? {} : { verified_organization_id: organizationId }
|
|
26671
|
-
});
|
|
26672
|
-
}
|
|
26673
|
-
function decodeActivityViewer(value) {
|
|
26674
|
-
const viewer = Option.getOrUndefined(Schema.decodeUnknownOption(ActorRefSchema)(value));
|
|
26675
|
-
if (viewer === void 0) return void 0;
|
|
26676
|
-
const id = viewer.kind === "organization" ? viewer.orgId : viewer.accountId;
|
|
26677
|
-
return safeEngineeringIdentifier(id) === void 0 ? void 0 : viewer;
|
|
26678
|
-
}
|
|
26679
|
-
/** The detail without the scope it was read for, which the SDK reports as telemetry instead. */
|
|
26680
|
-
function normalizeActivityDetail(response) {
|
|
26681
|
-
if (response === null) return null;
|
|
26682
|
-
const { viewer: _viewer, ...detail } = response;
|
|
26683
|
-
return detail;
|
|
26684
|
-
}
|
|
26685
|
-
//#endregion
|
|
26686
|
-
//#region ../../packages/sdk/src/activity/methods.ts
|
|
26687
|
-
function makeActivityMethods(deps) {
|
|
26688
|
-
const indexerDeps = deps.indexerRead === void 0 ? void 0 : {
|
|
26689
|
-
indexer: deps.indexerRead,
|
|
26690
|
-
convexCall: deps.convexCall
|
|
26691
|
-
};
|
|
26692
|
-
/** One public call for the caller's chosen actor, read once at call start. */
|
|
26693
|
-
const forActor = (actorRef, options, operation, program) => {
|
|
26694
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
26695
|
-
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(actorRef).actor)).pipe(Effect.flatMap((actor) => program(actor, controls))), controls, operation, deps.runPromise);
|
|
26579
|
+
const loop = yield* Effect.forkScoped(Effect.forever(Queue.take(mailbox).pipe(Effect.flatMap((env) => withInvocationParentSpan(step(env), env.invocation)))));
|
|
26580
|
+
yield* Effect.addFinalizer(() => Effect.gen(function* () {
|
|
26581
|
+
stopped = true;
|
|
26582
|
+
yield* Fiber.interrupt(loop);
|
|
26583
|
+
const state = yield* Ref.get(cell);
|
|
26584
|
+
const outstanding = [...slots.values()];
|
|
26585
|
+
slots.clear();
|
|
26586
|
+
for (const held of outstanding) {
|
|
26587
|
+
yield* Fiber.interrupt(held.fiber);
|
|
26588
|
+
const env = {
|
|
26589
|
+
origin: held.cursor,
|
|
26590
|
+
invocation: held.cursor.invocation,
|
|
26591
|
+
startedAt: held.cursor.startedAt
|
|
26592
|
+
};
|
|
26593
|
+
const event = { _tag: held.cursor.event };
|
|
26594
|
+
yield* emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", env));
|
|
26595
|
+
yield* failReply(held.reply, makeFailure(state, event, "ACTOR_STOPPED"));
|
|
26596
|
+
}
|
|
26597
|
+
stateObservers.clear();
|
|
26598
|
+
transitionObservers.clear();
|
|
26599
|
+
yield* Queue.shutdown(mailbox);
|
|
26600
|
+
}));
|
|
26601
|
+
const offer = (event, reply, invocation) => {
|
|
26602
|
+
const state = Ref.getUnsafe(cell);
|
|
26603
|
+
if (stopped) return withInvocationParentSpan(emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", {
|
|
26604
|
+
origin: null,
|
|
26605
|
+
event,
|
|
26606
|
+
invocation,
|
|
26607
|
+
startedAt: Date.now()
|
|
26608
|
+
})).pipe(Effect.andThen(Effect.fail(makeFailure(state, event, "ACTOR_STOPPED")))), invocation);
|
|
26609
|
+
const startedAt = Date.now();
|
|
26610
|
+
return Queue.offer(mailbox, {
|
|
26611
|
+
event,
|
|
26612
|
+
reply,
|
|
26613
|
+
origin: null,
|
|
26614
|
+
invocation,
|
|
26615
|
+
startedAt
|
|
26616
|
+
}).pipe(Effect.asVoid);
|
|
26696
26617
|
};
|
|
26697
26618
|
return {
|
|
26698
|
-
|
|
26699
|
-
|
|
26700
|
-
|
|
26701
|
-
|
|
26702
|
-
|
|
26703
|
-
|
|
26704
|
-
|
|
26705
|
-
|
|
26706
|
-
|
|
26707
|
-
|
|
26708
|
-
|
|
26709
|
-
|
|
26710
|
-
...actor === void 0 ? {} : { actor },
|
|
26711
|
-
...params?.window === void 0 ? {} : { window: params.window }
|
|
26712
|
-
})).pipe(Effect.mapError((publicError) => ({ publicError }))) : readActivitySummary(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor)))),
|
|
26713
|
-
get: (reference, options) => {
|
|
26714
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
26715
|
-
const requestedActor = actorReferenceToBackend(options?.actor);
|
|
26716
|
-
return runPortEffect(captureActivityInput(() => {
|
|
26717
|
-
const scope = requestedActor === void 0 ? void 0 : requestedActor.kind === "org" ? {
|
|
26718
|
-
kind: "org",
|
|
26719
|
-
orgId: requestedActor.orgId
|
|
26720
|
-
} : { kind: "account" };
|
|
26721
|
-
return Effect.succeed({
|
|
26722
|
-
actor: scope,
|
|
26723
|
-
referenceActor: reference.kind === "payment" ? activityActorField(reference.actor).actor : void 0
|
|
26724
|
-
});
|
|
26725
|
-
}).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" ? {
|
|
26726
|
-
kind: "organization",
|
|
26727
|
-
orgId: actor.orgId
|
|
26728
|
-
} : void 0, controls, reference.id) : callOperation(deps.convexCall, items.get, copyInvocationObservation(controls, {
|
|
26729
|
-
reference: {
|
|
26730
|
-
kind: reference.kind,
|
|
26731
|
-
id: reference.id,
|
|
26732
|
-
...referenceActor === void 0 ? {} : { actor: referenceActor }
|
|
26733
|
-
},
|
|
26734
|
-
...actor === void 0 ? {} : { actor }
|
|
26735
|
-
})).pipe(Effect.mapError((publicError) => ({ publicError })))).pipe(Effect.flatMap((response) => {
|
|
26736
|
-
const detail = normalizeActivityDetail(response);
|
|
26737
|
-
if (response === null) return Effect.succeed(detail);
|
|
26738
|
-
const viewer = response.viewer === void 0 ? actor ?? referenceActor : decodeActivityViewer(response.viewer);
|
|
26739
|
-
return viewer === void 0 ? Effect.succeed(detail) : observeActivityScope(viewer).pipe(Effect.as(detail));
|
|
26740
|
-
})))), controls, items.get.name, deps.runPromise);
|
|
26619
|
+
ask: (event, invocation = {}) => Effect.gen(function* () {
|
|
26620
|
+
const reply = yield* Deferred.make();
|
|
26621
|
+
yield* offer(event, reply, invocation);
|
|
26622
|
+
return yield* Deferred.await(reply);
|
|
26623
|
+
}),
|
|
26624
|
+
tell: (event, invocation = {}) => offer(event, null, invocation),
|
|
26625
|
+
snapshot: () => Ref.getUnsafe(cell),
|
|
26626
|
+
subscribe: (observer) => {
|
|
26627
|
+
stateObservers.add(observer);
|
|
26628
|
+
return () => {
|
|
26629
|
+
stateObservers.delete(observer);
|
|
26630
|
+
};
|
|
26741
26631
|
},
|
|
26742
|
-
|
|
26743
|
-
|
|
26744
|
-
return
|
|
26745
|
-
|
|
26746
|
-
|
|
26747
|
-
return Effect.succeed({
|
|
26748
|
-
actor,
|
|
26749
|
-
referenceActor: input.reference.kind === "payment" ? activityActorField(input.reference.actor).actor : void 0
|
|
26750
|
-
});
|
|
26751
|
-
}).pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() => callOperation(deps.convexCall, annotations.save, copyInvocationObservation(controls, {
|
|
26752
|
-
...actor === void 0 ? {} : { actor },
|
|
26753
|
-
reference: input.reference.kind === "payment" && referenceActor !== void 0 ? {
|
|
26754
|
-
kind: "payment",
|
|
26755
|
-
id: input.reference.id,
|
|
26756
|
-
actor: referenceActor
|
|
26757
|
-
} : {
|
|
26758
|
-
kind: input.reference.kind,
|
|
26759
|
-
id: input.reference.id
|
|
26760
|
-
},
|
|
26761
|
-
...input.counterpartyLabel === void 0 ? {} : { counterpartyLabel: input.counterpartyLabel },
|
|
26762
|
-
...input.accountingCategory === void 0 ? {} : { accountingCategory: input.accountingCategory },
|
|
26763
|
-
...input.memo === void 0 ? {} : { memo: input.memo }
|
|
26764
|
-
})).pipe(Effect.mapError((publicError) => ({ publicError })))).pipe(Effect.tap((annotation) => {
|
|
26765
|
-
const scope = decodeActivityViewer(annotation.actor);
|
|
26766
|
-
return scope === void 0 ? Effect.void : observeActivityScope(scope);
|
|
26767
|
-
})))), controls, annotations.save.name, deps.runPromise);
|
|
26632
|
+
subscribeTransitions: (observer) => {
|
|
26633
|
+
transitionObservers.add(observer);
|
|
26634
|
+
return () => {
|
|
26635
|
+
transitionObservers.delete(observer);
|
|
26636
|
+
};
|
|
26768
26637
|
}
|
|
26769
26638
|
};
|
|
26770
|
-
}
|
|
26771
|
-
Object.freeze({
|
|
26772
|
-
terminal: false,
|
|
26773
|
-
outcome: null,
|
|
26774
|
-
needsAction: false
|
|
26775
26639
|
});
|
|
26776
|
-
|
|
26777
|
-
|
|
26778
|
-
|
|
26779
|
-
|
|
26780
|
-
}
|
|
26781
|
-
|
|
26782
|
-
|
|
26783
|
-
|
|
26784
|
-
|
|
26640
|
+
//#endregion
|
|
26641
|
+
//#region ../../packages/sdk/src/access/identity/boot.ts
|
|
26642
|
+
const initialIdentityState = () => RESTORING;
|
|
26643
|
+
const bootIdentityFlow = (input, options = {}) => Effect.gen(function* () {
|
|
26644
|
+
const sessionStore = { current: null };
|
|
26645
|
+
let invalidateOrganizationSubscriptions = () => input.organizationSetup?.invalidateSubscriptions?.();
|
|
26646
|
+
const authority = createIdentityAuthorityTracker(sessionStore, () => invalidateOrganizationSubscriptions());
|
|
26647
|
+
const actor = yield* boot(identitySpec(input, sessionStore, authority), options);
|
|
26648
|
+
authority.bindState(actor.snapshot);
|
|
26649
|
+
const runPromise = input.runPromise ?? Effect.runPromise;
|
|
26650
|
+
let selectedOrgId = null;
|
|
26651
|
+
let stopRemote = null;
|
|
26652
|
+
let selectionEpoch = 0;
|
|
26653
|
+
invalidateOrganizationSubscriptions = () => {
|
|
26654
|
+
selectionEpoch += 1;
|
|
26655
|
+
const stop = stopRemote;
|
|
26656
|
+
stopRemote = null;
|
|
26657
|
+
try {
|
|
26658
|
+
stop?.();
|
|
26659
|
+
} finally {
|
|
26660
|
+
input.organizationSetup?.invalidateSubscriptions?.();
|
|
26661
|
+
}
|
|
26662
|
+
};
|
|
26663
|
+
const observe = (state) => {
|
|
26664
|
+
authority.observe(state);
|
|
26665
|
+
const nextOrgId = state.phase === "authenticated" && state.account.at === "claimed" ? state.account.org?.orgId ?? null : null;
|
|
26666
|
+
if (nextOrgId === selectedOrgId) return;
|
|
26667
|
+
selectedOrgId = nextOrgId;
|
|
26668
|
+
selectionEpoch += 1;
|
|
26669
|
+
const epoch = selectionEpoch;
|
|
26670
|
+
stopRemote?.();
|
|
26671
|
+
stopRemote = null;
|
|
26672
|
+
if (nextOrgId === null || input.organizationSetup === void 0) return;
|
|
26673
|
+
const capturedLifetime = authority.snapshot();
|
|
26674
|
+
const expectedAuthUserId = state.phase === "authenticated" ? state.session.authUserId : null;
|
|
26675
|
+
const emit = (snapshot) => {
|
|
26676
|
+
if (epoch !== selectionEpoch || !authority.matches(capturedLifetime, expectedAuthUserId)) return;
|
|
26677
|
+
runPromise(actor.tell({
|
|
26678
|
+
_tag: "ObserveOrganization",
|
|
26679
|
+
orgId: nextOrgId,
|
|
26680
|
+
snapshot
|
|
26681
|
+
})).catch(() => void 0);
|
|
26682
|
+
};
|
|
26683
|
+
try {
|
|
26684
|
+
const orgId = toOrgId(nextOrgId);
|
|
26685
|
+
input.organizationSetup.subscribe(orgId, emit).then((result) => {
|
|
26686
|
+
if (epoch !== selectionEpoch) {
|
|
26687
|
+
if (result.ok) result.value();
|
|
26688
|
+
return;
|
|
26689
|
+
}
|
|
26690
|
+
if (result.ok) stopRemote = result.value;
|
|
26691
|
+
else emit({
|
|
26692
|
+
status: "error",
|
|
26693
|
+
error: result.error
|
|
26694
|
+
});
|
|
26695
|
+
}).catch((cause) => emit({
|
|
26696
|
+
status: "error",
|
|
26697
|
+
error: Errors.unknown(cause)
|
|
26698
|
+
}));
|
|
26699
|
+
} catch (cause) {
|
|
26700
|
+
emit({
|
|
26701
|
+
status: "error",
|
|
26702
|
+
error: Errors.unknown(cause)
|
|
26703
|
+
});
|
|
26704
|
+
}
|
|
26705
|
+
};
|
|
26706
|
+
const unsubscribeAuthority = actor.subscribe(observe);
|
|
26707
|
+
observe(actor.snapshot());
|
|
26708
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
26709
|
+
authority.invalidate();
|
|
26710
|
+
unsubscribeAuthority();
|
|
26711
|
+
}));
|
|
26712
|
+
return {
|
|
26713
|
+
...actor,
|
|
26714
|
+
authLifetime: authority.snapshot,
|
|
26715
|
+
matchesAuthLifetime: authority.matches,
|
|
26716
|
+
invalidateAuthLifetime: authority.invalidate,
|
|
26717
|
+
subscribeAuthInvalidation: authority.subscribeInvalidation,
|
|
26718
|
+
authSession: () => sessionStore.current,
|
|
26719
|
+
ask: (event, controls) => actor.ask(event, controls).pipe(Effect.tap(() => event._tag === "Reset" ? Effect.sync(() => {
|
|
26720
|
+
authority.recordProviderSession(null);
|
|
26721
|
+
}) : Effect.void)),
|
|
26722
|
+
restoreAuthSession: (session, controls) => actor.ask({
|
|
26723
|
+
_tag: actor.snapshot().phase === "restoring" ? "SessionRestored" : "RestoreSession",
|
|
26724
|
+
session: modelSession(session),
|
|
26725
|
+
profileComplete: false
|
|
26726
|
+
}, controls).pipe(Effect.tap(() => Effect.sync(() => {
|
|
26727
|
+
authority.recordProviderSession(session);
|
|
26728
|
+
})))
|
|
26729
|
+
};
|
|
26785
26730
|
});
|
|
26786
|
-
|
|
26787
|
-
|
|
26788
|
-
|
|
26789
|
-
|
|
26731
|
+
//#endregion
|
|
26732
|
+
//#region ../../packages/sdk/src/access/identity/restoration-observation.ts
|
|
26733
|
+
/** The correlation a transition carries onto its product event. */
|
|
26734
|
+
const linkage = (record) => ({
|
|
26735
|
+
...record.correlation_id === void 0 ? {} : { correlation_id: record.correlation_id },
|
|
26736
|
+
...record.journey_id === void 0 ? {} : { journey_id: record.journey_id }
|
|
26790
26737
|
});
|
|
26791
|
-
|
|
26792
|
-
|
|
26793
|
-
|
|
26794
|
-
|
|
26738
|
+
/**
|
|
26739
|
+
* The label of the restore window every client boots into. A settled session
|
|
26740
|
+
* fact that left this label is the restoration result (#1962 R04); the same
|
|
26741
|
+
* facts from a signed-out or authenticated state are the routine session probe
|
|
26742
|
+
* that `getSession()` runs, and a probe is not a restoration attempt.
|
|
26743
|
+
*/
|
|
26744
|
+
const RESTORE_WINDOW = "restoring";
|
|
26745
|
+
/** The completions that settle the restore window by themselves, and what each says. */
|
|
26746
|
+
const SETTLING_EVENTS = {
|
|
26747
|
+
SessionRestored: "restored",
|
|
26748
|
+
SessionAbsent: "absent",
|
|
26749
|
+
SessionReadFailed: "failed"
|
|
26750
|
+
};
|
|
26751
|
+
/**
|
|
26752
|
+
* Which result the window settled on. `SessionRestored` and `SessionAbsent` are
|
|
26753
|
+
* the cache door and say it themselves. `ReadSession` is also valid inside the
|
|
26754
|
+
* window — `getSession()` drives it on a cache miss and its `SessionRead`
|
|
26755
|
+
* completion settles the window — so that one event reports both halves and the
|
|
26756
|
+
* state it produced is what separates them.
|
|
26757
|
+
*/
|
|
26758
|
+
const restorationOutcome = (event, state) => {
|
|
26759
|
+
if (event === "SessionRead") {
|
|
26760
|
+
if (state.phase === "authenticated") return "restored";
|
|
26761
|
+
return state.phase === "signed_out" ? "absent" : void 0;
|
|
26762
|
+
}
|
|
26763
|
+
return SETTLING_EVENTS[event];
|
|
26764
|
+
};
|
|
26765
|
+
/**
|
|
26766
|
+
* The restore window's one result, or nothing when this settled session fact
|
|
26767
|
+
* belongs to a probe rather than to the window.
|
|
26768
|
+
*/
|
|
26769
|
+
const restorationObservation = (record, event, state) => {
|
|
26770
|
+
if (record.slot !== "identity:session" || record.from !== RESTORE_WINDOW) return null;
|
|
26771
|
+
const outcome = restorationOutcome(event, state);
|
|
26772
|
+
if (outcome === void 0) return null;
|
|
26773
|
+
return {
|
|
26774
|
+
name: "access_recovery_completed",
|
|
26775
|
+
props: {
|
|
26776
|
+
recovery: "session_restoration",
|
|
26777
|
+
outcome,
|
|
26778
|
+
...outcome === "failed" && state.phase === "faulted" ? { reason_code: state.failure.code } : {},
|
|
26779
|
+
...linkage(record)
|
|
26780
|
+
}
|
|
26781
|
+
};
|
|
26782
|
+
};
|
|
26783
|
+
//#endregion
|
|
26784
|
+
//#region ../../packages/sdk/src/access/identity/events.ts
|
|
26785
|
+
const NONE = null;
|
|
26786
|
+
const IDENTITY_EVENT_TAG_SET = new Set(IDENTITY_EVENT_TAGS);
|
|
26787
|
+
const isIdentityEventTag = (event) => IDENTITY_EVENT_TAG_SET.has(event);
|
|
26788
|
+
const APPLIED_ACTION_BY_EVENT = {
|
|
26789
|
+
RequestOtp: "none",
|
|
26790
|
+
VerifyOtp: "none",
|
|
26791
|
+
ReadSession: "none",
|
|
26792
|
+
SignOut: "none",
|
|
26793
|
+
RestoreSession: "none",
|
|
26794
|
+
ResumeOtpEntry: "none",
|
|
26795
|
+
RestoreOtpEntry: "none",
|
|
26796
|
+
Reset: "none",
|
|
26797
|
+
SessionRestored: "restoration",
|
|
26798
|
+
SessionAbsent: "restoration",
|
|
26799
|
+
EnsureAccount: "none",
|
|
26800
|
+
ClaimAccount: "none",
|
|
26801
|
+
RetryAccount: "none",
|
|
26802
|
+
CreateOrganization: "none",
|
|
26803
|
+
AttachOrganization: "none",
|
|
26804
|
+
LeaveOrganization: "none",
|
|
26805
|
+
ObserveOrganization: "none",
|
|
26806
|
+
OtpSent: "otp-requested",
|
|
26807
|
+
OtpFailed: "failed",
|
|
26808
|
+
Verified: "verified",
|
|
26809
|
+
VerifyFailed: "failed",
|
|
26810
|
+
SessionRead: "restoration",
|
|
26811
|
+
SessionReadFailed: "restoration",
|
|
26812
|
+
SignedOut: "signed-out",
|
|
26813
|
+
SignOutFailed: "failed",
|
|
26814
|
+
AccountAt: "none",
|
|
26815
|
+
AccountDerived: "none",
|
|
26816
|
+
AccountClaiming: "none",
|
|
26817
|
+
AccountClaimed: "none",
|
|
26818
|
+
AccountFailed: "none",
|
|
26819
|
+
OrgSelected: "none",
|
|
26820
|
+
OrgSelectionFailed: "none"
|
|
26821
|
+
};
|
|
26822
|
+
const emailDomain = (email) => email.split("@")[1]?.toLowerCase() ?? "unknown";
|
|
26823
|
+
const authFailure = (record, reason, mode) => ({
|
|
26824
|
+
name: "auth_failed",
|
|
26825
|
+
props: {
|
|
26826
|
+
auth_type: "otp",
|
|
26827
|
+
reason,
|
|
26828
|
+
...mode === void 0 ? {} : { failure_mode: mode },
|
|
26829
|
+
...linkage(record)
|
|
26830
|
+
}
|
|
26795
26831
|
});
|
|
26832
|
+
const REFUSAL_EVENTS = /* @__PURE__ */ new Set([
|
|
26833
|
+
"ClaimAccount",
|
|
26834
|
+
"RetryAccount",
|
|
26835
|
+
"CreateOrganization",
|
|
26836
|
+
"AttachOrganization"
|
|
26837
|
+
]);
|
|
26838
|
+
function mapIdentityProductObservation(record, state) {
|
|
26839
|
+
const event = record.event;
|
|
26840
|
+
if (record.machine !== "identity" || !isIdentityEventTag(event)) return NONE;
|
|
26841
|
+
if (record.outcome === "cancelled") return NONE;
|
|
26842
|
+
if (record.slot === "identity:auth" && record.outcome === "refused") return authFailure(record, record.refusal_code, void 0);
|
|
26843
|
+
if (record.slot === "identity:auth" && record.outcome === "failed") return authFailure(record, record.error_code, record.failure?.mode);
|
|
26844
|
+
if (record.outcome === "refused" && REFUSAL_EVENTS.has(record.event)) return {
|
|
26845
|
+
name: "identity_refused",
|
|
26846
|
+
props: {
|
|
26847
|
+
event: record.event,
|
|
26848
|
+
refusal_code: record.refusal_code,
|
|
26849
|
+
slot: record.slot,
|
|
26850
|
+
state: record.state,
|
|
26851
|
+
...linkage(record)
|
|
26852
|
+
}
|
|
26853
|
+
};
|
|
26854
|
+
if (record.outcome !== "applied") return NONE;
|
|
26855
|
+
const action = APPLIED_ACTION_BY_EVENT[event];
|
|
26856
|
+
switch (action) {
|
|
26857
|
+
case "none": return NONE;
|
|
26858
|
+
case "failed": return state.phase !== "faulted" || state.failure.code === "CANCELLED" ? NONE : authFailure(record, state.failure.code, state.failure.mode);
|
|
26859
|
+
case "otp-requested": return state.phase !== "otp_pending" ? NONE : {
|
|
26860
|
+
name: "auth_otp_requested",
|
|
26861
|
+
props: {
|
|
26862
|
+
email_domain: emailDomain(state.email),
|
|
26863
|
+
...linkage(record)
|
|
26864
|
+
}
|
|
26865
|
+
};
|
|
26866
|
+
case "verified": return state.phase !== "authenticated" ? NONE : {
|
|
26867
|
+
name: "auth_verified",
|
|
26868
|
+
props: {
|
|
26869
|
+
auth_type: "otp",
|
|
26870
|
+
...linkage(record)
|
|
26871
|
+
}
|
|
26872
|
+
};
|
|
26873
|
+
case "signed-out": return state.phase !== "signed_out" ? NONE : {
|
|
26874
|
+
name: "auth_signed_out",
|
|
26875
|
+
props: linkage(record)
|
|
26876
|
+
};
|
|
26877
|
+
case "restoration": return restorationObservation(record, event, state);
|
|
26878
|
+
default: return absurd(action);
|
|
26879
|
+
}
|
|
26880
|
+
}
|
|
26881
|
+
const ignoreTransportFailure = (operation, operationName, eventName) => Effect.suspend(operation).pipe(Effect.catchCause(() => Effect.logWarning("identity.product.telemetry.dropped").pipe(Effect.annotateLogs({
|
|
26882
|
+
operation: operationName,
|
|
26883
|
+
product_event: eventName
|
|
26884
|
+
}), Effect.ignoreCause)));
|
|
26885
|
+
function executeIdentityProductObservation(telemetry, record, state) {
|
|
26886
|
+
const mapped = mapIdentityProductObservation(record, state);
|
|
26887
|
+
if (mapped === null) return Effect.void;
|
|
26888
|
+
copyInvocationObservation(record, mapped);
|
|
26889
|
+
const invocationTelemetry = bindProductTelemetryInvocation(telemetry, mapped);
|
|
26890
|
+
const after = () => state.phase === "authenticated" && (mapped.name === "auth_verified" || mapped.name === "access_recovery_completed") ? invocationTelemetry.identify({
|
|
26891
|
+
distinctId: state.session.authUserId,
|
|
26892
|
+
traits: { email_domain: emailDomain(state.session.email) }
|
|
26893
|
+
}) : mapped.name === "auth_signed_out" ? invocationTelemetry.reset() : Effect.void;
|
|
26894
|
+
return ignoreTransportFailure(() => invocationTelemetry.emit(mapped), "emit", mapped.name).pipe(Effect.andThen(ignoreTransportFailure(after, mapped.name === "auth_signed_out" ? "reset" : "identify", mapped.name)));
|
|
26895
|
+
}
|
|
26796
26896
|
//#endregion
|
|
26797
26897
|
//#region ../../packages/protocol/src/payroll/model.ts
|
|
26798
26898
|
function toPayrollRunId(raw) {
|
|
@@ -67840,6 +67940,11 @@ const OBSERVED_CONVEX_QUERIES = new Set(OPERATIONS.filter((row) => row.kind ===
|
|
|
67840
67940
|
const OBSERVED_CONVEX_MUTATIONS = new Set(OPERATIONS.filter((row) => row.kind === "mutation").map((row) => row.path));
|
|
67841
67941
|
//#endregion
|
|
67842
67942
|
//#region ../../packages/sdk/src/platform/transport/convex-errors.ts
|
|
67943
|
+
/**
|
|
67944
|
+
* All a redacting backend says about an error it raised itself: its request
|
|
67945
|
+
* id. The WebSocket client wraps it as `[CONVEX Q(path)] … Called by client`.
|
|
67946
|
+
*/
|
|
67947
|
+
const REDACTED_ERROR = /^(?:\[CONVEX [QMA?]\([^)]*\)\] )?\[Request ID: ([^\]\s]+)\] Server Error(?:\s+Called by client)?$/u;
|
|
67843
67948
|
function mapToCapxulError(operation, err, transport) {
|
|
67844
67949
|
const decoded = decodeConvexError(err);
|
|
67845
67950
|
if (decoded !== null) return decoded;
|
|
@@ -67864,9 +67969,26 @@ function mapToCapxulError(operation, err, transport) {
|
|
|
67864
67969
|
* anything else (an uncaught "Server Error") is a backend defect. None is a
|
|
67865
67970
|
* provider outage. The Convex request id rides in details, so the backend's
|
|
67866
67971
|
* log line can be found.
|
|
67972
|
+
*
|
|
67973
|
+
* A deployed backend redacts these errors (`REDACT_LOGS_TO_CLIENT`, #2304):
|
|
67974
|
+
* the message is only the request id, and no structured cause exists, so the
|
|
67975
|
+
* message shape is the only discriminator. Neither case can be told apart, and
|
|
67976
|
+
* the reply says where to look instead. The text matches stay for a backend
|
|
67977
|
+
* that does not redact (development, local).
|
|
67867
67978
|
*/
|
|
67868
67979
|
function backendFailure(operation, err) {
|
|
67869
67980
|
const text = err instanceof Error ? err.message : String(err);
|
|
67981
|
+
const redacted = REDACTED_ERROR.exec(text.trim())?.[1];
|
|
67982
|
+
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.`, {
|
|
67983
|
+
cause: err,
|
|
67984
|
+
layer: "convex",
|
|
67985
|
+
details: {
|
|
67986
|
+
operation,
|
|
67987
|
+
provider: "convex",
|
|
67988
|
+
requestId: redacted,
|
|
67989
|
+
reason: "backend-error-redacted"
|
|
67990
|
+
}
|
|
67991
|
+
});
|
|
67870
67992
|
const requestId = /\[Request ID: ([^\]\s]+)\]/u.exec(text)?.[1];
|
|
67871
67993
|
const where = {
|
|
67872
67994
|
operation,
|
|
@@ -70946,7 +71068,7 @@ function stdioIdentity(env = process.env, options = {}) {
|
|
|
70946
71068
|
}
|
|
70947
71069
|
//#endregion
|
|
70948
71070
|
//#region package.json
|
|
70949
|
-
var version = "4.20.0-beta.
|
|
71071
|
+
var version = "4.20.0-beta.26";
|
|
70950
71072
|
//#endregion
|
|
70951
71073
|
//#region src/deps.ts
|
|
70952
71074
|
const NO_IDENTITY = async () => void 0;
|