@capxul/mcp 4.20.0-beta.26 → 4.20.0-beta.28
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 +823 -540
- package/package.json +3 -3
package/dist/main.mjs
CHANGED
|
@@ -1227,7 +1227,7 @@ const FIELD_RULES = {
|
|
|
1227
1227
|
},
|
|
1228
1228
|
anonymousId: {
|
|
1229
1229
|
maxLength: 128,
|
|
1230
|
-
pattern: /^anon_[A-Za-z0-9-]
|
|
1230
|
+
pattern: /^(?:anon_[A-Za-z0-9-]+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/u
|
|
1231
1231
|
},
|
|
1232
1232
|
traceparent: {
|
|
1233
1233
|
maxLength: 55,
|
|
@@ -9610,10 +9610,19 @@ const ApprovalNetworkSchema = Schema.Struct({
|
|
|
9610
9610
|
name: Schema.String,
|
|
9611
9611
|
explorerUrl: Schema.String
|
|
9612
9612
|
});
|
|
9613
|
+
/**
|
|
9614
|
+
* Whose money the operation moves: the person's own, or an Organization's,
|
|
9615
|
+
* which a reader follows through that Organization's scope.
|
|
9616
|
+
*/
|
|
9617
|
+
const ApprovalActorSchema = Schema.Union([Schema.Struct({ kind: Schema.Literal("personal") }), Schema.Struct({
|
|
9618
|
+
kind: Schema.Literal("organization"),
|
|
9619
|
+
orgId: Schema.NonEmptyString
|
|
9620
|
+
})]);
|
|
9613
9621
|
/** What the person reads before they sign. */
|
|
9614
9622
|
const ApprovalSummarySchema = Schema.Struct({
|
|
9615
9623
|
title: Schema.String,
|
|
9616
9624
|
from: Schema.Struct({
|
|
9625
|
+
kind: Schema.Literals(["personal", "organization"]),
|
|
9617
9626
|
label: Schema.String,
|
|
9618
9627
|
address: AddressSchema$1
|
|
9619
9628
|
}),
|
|
@@ -9635,6 +9644,7 @@ const ApprovalSchema = Schema.Struct({
|
|
|
9635
9644
|
url: Schema.String,
|
|
9636
9645
|
kind: Schema.Literals(APPROVAL_KINDS),
|
|
9637
9646
|
state: Schema.Literals(APPROVAL_STATES),
|
|
9647
|
+
actor: ApprovalActorSchema,
|
|
9638
9648
|
summary: ApprovalSummarySchema,
|
|
9639
9649
|
operation: ApprovalOperationSchema,
|
|
9640
9650
|
/** Who must sign: the owner key of the paying Safe. */
|
|
@@ -10302,6 +10312,28 @@ const probes = {
|
|
|
10302
10312
|
})
|
|
10303
10313
|
};
|
|
10304
10314
|
//#endregion
|
|
10315
|
+
//#region ../../packages/protocol/src/evm/role-dsl.ts
|
|
10316
|
+
const ORG_BUDGET_ACTIONS = [
|
|
10317
|
+
"send",
|
|
10318
|
+
"sendBatch",
|
|
10319
|
+
"createCommitment"
|
|
10320
|
+
];
|
|
10321
|
+
/**
|
|
10322
|
+
* How a capped Budget's allowance refills (#2298). `monthly` refills the
|
|
10323
|
+
* allowance to its cap every 30 days. Roles v2 counts fixed periods from the
|
|
10324
|
+
* moment the allowance is set; it has no calendar month. Absent means `none`:
|
|
10325
|
+
* the cap is one pot that spending drains for good.
|
|
10326
|
+
*/
|
|
10327
|
+
const ORG_BUDGET_REFILLS = ["monthly", "none"];
|
|
10328
|
+
/** The fixed Roles v2 refill period of a `monthly` Budget: 30 days, in seconds. */
|
|
10329
|
+
const MONTHLY_REFILL_PERIOD_SECONDS = 30n * 24n * 60n * 60n;
|
|
10330
|
+
/** Derive the Zodiac substrate key for one durable Permission identity. */
|
|
10331
|
+
function roleKeyForPermission(permissionId) {
|
|
10332
|
+
const normalized = permissionId.trim();
|
|
10333
|
+
if (normalized.length === 0 || normalized !== permissionId) throw Errors.invalidInput("permissionId", "must be a non-empty canonical Permission ID");
|
|
10334
|
+
return keccak256$1(toBytes$1(`permission ${permissionId}`));
|
|
10335
|
+
}
|
|
10336
|
+
//#endregion
|
|
10305
10337
|
//#region ../../packages/protocol/src/permissions/permission-schema.ts
|
|
10306
10338
|
/**
|
|
10307
10339
|
* One stored `setAllowance` call inside a command's expected authority set.
|
|
@@ -10340,12 +10372,19 @@ const BudgetScopeSchema = Schema.Struct({
|
|
|
10340
10372
|
})]),
|
|
10341
10373
|
actions: Schema.NonEmptyArray(Schema.Literals(["pay", "commitments"]))
|
|
10342
10374
|
}).pipe(Schema.refine((scope) => new Set(scope.actions).size === scope.actions.length && (scope.recipients.type === "anyone" || scope.recipients.accounts.length >= 1 && scope.recipients.accounts.length <= 32 && new Set(scope.recipients.accounts).size === scope.recipients.accounts.length), { message: "Budget scope requires unique actions and one to 32 unique allowed Accounts" }));
|
|
10375
|
+
/**
|
|
10376
|
+
* How a capped Budget refills (#2298): `monthly` tops the cap back up every 30
|
|
10377
|
+
* days (a fixed Roles v2 period, not a calendar month); `none` never refills.
|
|
10378
|
+
* Absent means `none`, and writers store only `monthly`.
|
|
10379
|
+
*/
|
|
10380
|
+
const BudgetRefillSchema = Schema.Literals(ORG_BUDGET_REFILLS);
|
|
10343
10381
|
/** The accepted product policy stored with one Permission revision. */
|
|
10344
10382
|
const CurrentPolicySchema = Schema.Union([Schema.Struct({
|
|
10345
10383
|
type: Schema.Literal("budget"),
|
|
10346
10384
|
asset: AssetIdSchema,
|
|
10347
10385
|
limit: Schema.NullOr(Schema.String),
|
|
10348
|
-
scope: BudgetScopeSchema
|
|
10386
|
+
scope: BudgetScopeSchema,
|
|
10387
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
10349
10388
|
}), Schema.Struct({ type: Schema.Literal("managePeople") })]);
|
|
10350
10389
|
const BudgetIntentSchema = CurrentPolicySchema.pipe(Schema.refine((value) => {
|
|
10351
10390
|
if (value.type !== "budget") return false;
|
|
@@ -10410,7 +10449,8 @@ const ResolvedPermissionBudgetPolicySchema = Schema.Struct({
|
|
|
10410
10449
|
"send",
|
|
10411
10450
|
"sendBatch",
|
|
10412
10451
|
"createCommitment"
|
|
10413
|
-
]))
|
|
10452
|
+
])),
|
|
10453
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
10414
10454
|
});
|
|
10415
10455
|
const PermissionRecipientBindingSchema = Schema.Struct({
|
|
10416
10456
|
accountId: AccountIdSchema$1,
|
|
@@ -10422,16 +10462,22 @@ const StoredPermissionIntentSchema = Schema.Union([Schema.Struct({
|
|
|
10422
10462
|
type: Schema.Literal("budget"),
|
|
10423
10463
|
asset: AssetIdSchema,
|
|
10424
10464
|
limit: Schema.NullOr(AssetAmountSchema),
|
|
10425
|
-
scope: Schema.optional(BudgetScopeSchema)
|
|
10465
|
+
scope: Schema.optional(BudgetScopeSchema),
|
|
10466
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
10426
10467
|
}).pipe(Schema.refine((intent) => intent.limit === null || intent.limit.asset === intent.asset, { message: "Budget limit must use the Budget asset" })), Schema.Struct({ type: Schema.Literal("managePeople") })]);
|
|
10427
10468
|
/** Writes state every granted right and use a finite positive AssetAmount cap. */
|
|
10428
10469
|
const PermissionIntentSchema = Schema.Union([Schema.Struct({
|
|
10429
10470
|
type: Schema.Literal("budget"),
|
|
10430
10471
|
asset: AssetIdSchema,
|
|
10431
10472
|
limit: AssetAmountSchema,
|
|
10432
|
-
scope: BudgetScopeSchema
|
|
10473
|
+
scope: BudgetScopeSchema,
|
|
10474
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
10433
10475
|
}).pipe(Schema.refine((intent) => intent.limit.asset === intent.asset && /[1-9]/u.test(intent.limit.value), { message: "Budget cap must be positive and use the Budget asset" })), Schema.Struct({ type: Schema.Literal("managePeople") })]);
|
|
10434
|
-
/**
|
|
10476
|
+
/**
|
|
10477
|
+
* Decode once and give set-like scope fields one stable command representation.
|
|
10478
|
+
* An explicit `refill: "none"` is kept: on a change it turns a refill off,
|
|
10479
|
+
* where a missing `refill` keeps the Budget's current one (`keepRefillOnChange`).
|
|
10480
|
+
*/
|
|
10435
10481
|
function decodePermissionIntent$1(input) {
|
|
10436
10482
|
const intent = Schema.decodeUnknownSync(PermissionIntentSchema, { onExcessProperty: "error" })(input);
|
|
10437
10483
|
if (intent.type === "managePeople") return intent;
|
|
@@ -10446,6 +10492,19 @@ function decodePermissionIntent$1(input) {
|
|
|
10446
10492
|
}
|
|
10447
10493
|
});
|
|
10448
10494
|
}
|
|
10495
|
+
/**
|
|
10496
|
+
* A Budget change that leaves `refill` out keeps the Budget's current refill:
|
|
10497
|
+
* a change never turns a refill off by omission. An explicit `"none"` turns it
|
|
10498
|
+
* off. The backend plans, the SDK verifies and the CLI confirms the same
|
|
10499
|
+
* result. A create has no current refill, so a missing one means `none`.
|
|
10500
|
+
*/
|
|
10501
|
+
function keepRefillOnChange(intent, before) {
|
|
10502
|
+
if (intent.type !== "budget" || intent.refill !== void 0 || before?.type !== "budget" || before.refill !== "monthly") return intent;
|
|
10503
|
+
return {
|
|
10504
|
+
...intent,
|
|
10505
|
+
refill: before.refill
|
|
10506
|
+
};
|
|
10507
|
+
}
|
|
10449
10508
|
//#endregion
|
|
10450
10509
|
//#region ../../packages/protocol/src/permissions/replacement-schema.ts
|
|
10451
10510
|
const InvitationPhaseSchema = Schema.Literals([
|
|
@@ -10650,7 +10709,7 @@ const InviteGrantsSchema = Schema.Array(Schema.Union([
|
|
|
10650
10709
|
Schema.Struct({
|
|
10651
10710
|
type: Schema.Literal("new_budget"),
|
|
10652
10711
|
label: Schema.String.pipe(Schema.refine((value) => value.trim().length >= 1 && value.trim().length <= 128)),
|
|
10653
|
-
intent: BudgetIntentSchema
|
|
10712
|
+
intent: BudgetIntentSchema.pipe(Schema.refine((intent) => intent.refill !== "monthly", { message: "An invitation's new Budget cannot refill; change it once granted" }))
|
|
10654
10713
|
})
|
|
10655
10714
|
])).pipe(Schema.refine((grants) => {
|
|
10656
10715
|
if (grants.length < 1 || grants.length > 16) return false;
|
|
@@ -13524,7 +13583,9 @@ const PEOPLE_OPERATIONS = {
|
|
|
13524
13583
|
emitSafeConfirmed: "accounts.safes.emitSafeConfirmed",
|
|
13525
13584
|
readSafe: "accounts.safes.readSafe",
|
|
13526
13585
|
recordDeployment: "accounts.safes.recordDeployment"
|
|
13527
|
-
}
|
|
13586
|
+
},
|
|
13587
|
+
/** The home's requests to pay and Payments to retry, read in one query. */
|
|
13588
|
+
summaries: { loadNeedsYou: "accounts.summaries.loadNeedsYou" }
|
|
13528
13589
|
},
|
|
13529
13590
|
/** The host observer's names for `client.approvals.*`. */
|
|
13530
13591
|
approvals: {
|
|
@@ -13705,7 +13766,11 @@ const ORGANIZATION_OPERATIONS = {
|
|
|
13705
13766
|
seedNativeReadiness: "operator.fixtures.seedNativeReadiness",
|
|
13706
13767
|
seedPrerequisites: "operator.fixtures.seedPrerequisites"
|
|
13707
13768
|
},
|
|
13708
|
-
identities: {
|
|
13769
|
+
identities: {
|
|
13770
|
+
purgeByEmail: "operator.identities.purgeByEmail",
|
|
13771
|
+
/** One Account and every row that refers to it, unless it has Payments or an Organization tie. */
|
|
13772
|
+
purgeAccount: "operator.identities.purgeAccount"
|
|
13773
|
+
}
|
|
13709
13774
|
},
|
|
13710
13775
|
/** Organizations' internal functions; its public rows join from `OPERATIONS`. */
|
|
13711
13776
|
organizations: {
|
|
@@ -13779,7 +13844,9 @@ emitSaved: "activity.annotations.emitSaved" },
|
|
|
13779
13844
|
pay: "organizationPayments.pay",
|
|
13780
13845
|
payBatch: "organizationPayments.payBatch",
|
|
13781
13846
|
prepareExecution: "organizationPayments.prepareExecution",
|
|
13782
|
-
retry: "organizationPayments.retry"
|
|
13847
|
+
retry: "organizationPayments.retry",
|
|
13848
|
+
cancel: "organizationPayments.cancel",
|
|
13849
|
+
redirect: "organizationPayments.redirect"
|
|
13783
13850
|
},
|
|
13784
13851
|
/**
|
|
13785
13852
|
* Payments' internal functions and the client's own method names; its public
|
|
@@ -13801,7 +13868,9 @@ emitSaved: "activity.annotations.emitSaved" },
|
|
|
13801
13868
|
loadContext: "payments.commands.loadContext",
|
|
13802
13869
|
loadExecutionContext: "payments.commands.loadExecutionContext",
|
|
13803
13870
|
/** The capped Budget a prepared Organization command spends from, before its allowance is read on chain. */
|
|
13804
|
-
loadBudgetAllowance: "payments.commands.loadBudgetAllowance"
|
|
13871
|
+
loadBudgetAllowance: "payments.commands.loadBudgetAllowance",
|
|
13872
|
+
/** What a new Payment command would spend and from which Safe, read before it is born. */
|
|
13873
|
+
loadBirthFunding: "payments.commands.loadBirthFunding"
|
|
13805
13874
|
},
|
|
13806
13875
|
lifecycles: { prepareIntent: "payments.lifecycles.prepareIntent" },
|
|
13807
13876
|
/** The stored UserOperation of every money command: Payment, Permission and configuration. */
|
|
@@ -13955,7 +14024,11 @@ const PLATFORM_OPERATIONS = {
|
|
|
13955
14024
|
recordUse: "applications.bootstraps.recordUse",
|
|
13956
14025
|
resolve: "applications.bootstraps.resolve"
|
|
13957
14026
|
},
|
|
13958
|
-
keys: {
|
|
14027
|
+
keys: {
|
|
14028
|
+
issue: "applications.keys.issue",
|
|
14029
|
+
/** Operator-only: revoke one key by its id, whoever owns it. */
|
|
14030
|
+
revokeById: "applications.keys.revokeById"
|
|
14031
|
+
},
|
|
13959
14032
|
quickstarts: {
|
|
13960
14033
|
claim: "applications.quickstarts.claim",
|
|
13961
14034
|
mint: "applications.quickstarts.mint"
|
|
@@ -14234,6 +14307,42 @@ const UiStepProps = {
|
|
|
14234
14307
|
organization_id: OptionalOrgId,
|
|
14235
14308
|
reason_code: OptionalReasonCode
|
|
14236
14309
|
};
|
|
14310
|
+
/**
|
|
14311
|
+
* A journey's variants (`JOURNEYS`): the way a person came into a journey, or
|
|
14312
|
+
* the way they took through it. A variant is a property of the journey's step
|
|
14313
|
+
* events, never a journey of its own, so each journey funnel breaks down by
|
|
14314
|
+
* it. A step event takes only the variants that a journey contract names on
|
|
14315
|
+
* it, picked one by one; the analytics plan test checks both directions.
|
|
14316
|
+
* `pay_path` and `funding_source` have no carrier until their Phase 2 events.
|
|
14317
|
+
* `payment_*` events already carry `recipient_is_capxul_account`, and
|
|
14318
|
+
* `invoice_issued` carries `issuer_scope_kind`; neither is repeated here.
|
|
14319
|
+
*/
|
|
14320
|
+
const JourneyVariantProps = {
|
|
14321
|
+
/** How a person came to Get started: by themselves, an invite, money that waited, or an invoice. */
|
|
14322
|
+
entry_path: Schema.optional(Schema.Literals([
|
|
14323
|
+
"signup",
|
|
14324
|
+
"invite",
|
|
14325
|
+
"claim",
|
|
14326
|
+
"invoice"
|
|
14327
|
+
])),
|
|
14328
|
+
/** Where the person acted. `agent` is an AI agent driving the CLI. */
|
|
14329
|
+
surface: Schema.optional(Schema.Literals([
|
|
14330
|
+
"web",
|
|
14331
|
+
"cli",
|
|
14332
|
+
"agent"
|
|
14333
|
+
])),
|
|
14334
|
+
/** Who pays: a person from their own account, or an Organization from a Budget. */
|
|
14335
|
+
payer_kind: Schema.optional(Schema.Literals(["person", "organization"])),
|
|
14336
|
+
/** How a request was paid: from the payer's inbox, or by its link at checkout. */
|
|
14337
|
+
pay_path: Schema.optional(Schema.Literals(["inbox", "link"])),
|
|
14338
|
+
/** Where an account's first money came from. `bank` is the planned on-ramp. */
|
|
14339
|
+
funding_source: Schema.optional(Schema.Literals([
|
|
14340
|
+
"deposit",
|
|
14341
|
+
"payment",
|
|
14342
|
+
"claim",
|
|
14343
|
+
"bank"
|
|
14344
|
+
]))
|
|
14345
|
+
};
|
|
14237
14346
|
//#endregion
|
|
14238
14347
|
//#region ../../packages/observability/src/events/money.ts
|
|
14239
14348
|
/**
|
|
@@ -14323,6 +14432,8 @@ const PaymentInitiatedProps = Schema.Struct({
|
|
|
14323
14432
|
execution_id: OptionalString,
|
|
14324
14433
|
payment_lifecycle_id: OptionalString,
|
|
14325
14434
|
...TelemetryEnvelopeProps,
|
|
14435
|
+
payer_kind: JourneyVariantProps.payer_kind,
|
|
14436
|
+
surface: JourneyVariantProps.surface,
|
|
14326
14437
|
...RecipientTelemetryProps,
|
|
14327
14438
|
kind: Schema.String,
|
|
14328
14439
|
payment_id: Schema.String
|
|
@@ -14338,6 +14449,8 @@ const PaymentLifecycleProps = {
|
|
|
14338
14449
|
const PaymentSettledL2Props = Schema.Struct({
|
|
14339
14450
|
...PaymentLifecycleProps,
|
|
14340
14451
|
...TelemetryEnvelopeProps,
|
|
14452
|
+
payer_kind: JourneyVariantProps.payer_kind,
|
|
14453
|
+
surface: JourneyVariantProps.surface,
|
|
14341
14454
|
...RecipientTelemetryProps,
|
|
14342
14455
|
kind: Schema.String,
|
|
14343
14456
|
chain_id: Schema.Finite,
|
|
@@ -14545,6 +14658,7 @@ const OrganizationCreationStartedProps = Schema.Struct({
|
|
|
14545
14658
|
});
|
|
14546
14659
|
const OrganizationCreationReadyProps = Schema.Struct({
|
|
14547
14660
|
...TelemetryEnvelopeProps,
|
|
14661
|
+
surface: JourneyVariantProps.surface,
|
|
14548
14662
|
$insert_id: Schema.String,
|
|
14549
14663
|
organization_id: OrgIdTelemetrySchema,
|
|
14550
14664
|
attempt_number: AttemptNumberSchema,
|
|
@@ -14569,45 +14683,6 @@ const OrganizationCreationFailedProps = Schema.Struct({
|
|
|
14569
14683
|
...ORGANIZATION_SETUP_REASONS
|
|
14570
14684
|
]))
|
|
14571
14685
|
});
|
|
14572
|
-
const OrgCreateStartedProps = Schema.Struct({
|
|
14573
|
-
...TelemetryEnvelopeProps,
|
|
14574
|
-
org_id: OptionalOrgId,
|
|
14575
|
-
email_domain: OptionalString,
|
|
14576
|
-
template: OptionalString
|
|
14577
|
-
});
|
|
14578
|
-
const OrgSafeCreatedProps = Schema.Struct({
|
|
14579
|
-
...TelemetryEnvelopeProps,
|
|
14580
|
-
org_id: OptionalOrgId,
|
|
14581
|
-
chainId: OptionalChainId,
|
|
14582
|
-
safe_address: OptionalAddress
|
|
14583
|
-
});
|
|
14584
|
-
const OrgSafeConfirmedProps = Schema.Struct({
|
|
14585
|
-
...TelemetryEnvelopeProps,
|
|
14586
|
-
org_id: OptionalOrgId,
|
|
14587
|
-
chainId: OptionalChainId,
|
|
14588
|
-
safe_address: OptionalAddress,
|
|
14589
|
-
durationMs: OptionalDurationMs
|
|
14590
|
-
});
|
|
14591
|
-
const OrgRolesSeededProps = Schema.Struct({
|
|
14592
|
-
...TelemetryEnvelopeProps,
|
|
14593
|
-
org_id: OptionalOrgId,
|
|
14594
|
-
chainId: OptionalChainId,
|
|
14595
|
-
role: OptionalString,
|
|
14596
|
-
txHash: OptionalTxHash
|
|
14597
|
-
});
|
|
14598
|
-
const OrgCreatedProps = Schema.Struct({
|
|
14599
|
-
...TelemetryEnvelopeProps,
|
|
14600
|
-
org_id: OptionalOrgId,
|
|
14601
|
-
chainId: OptionalChainId,
|
|
14602
|
-
safe_address: OptionalAddress,
|
|
14603
|
-
durationMs: OptionalDurationMs
|
|
14604
|
-
});
|
|
14605
|
-
const OrgCreateFailedProps = Schema.Struct({
|
|
14606
|
-
...TelemetryEnvelopeProps,
|
|
14607
|
-
...ChainCauseTelemetryProps,
|
|
14608
|
-
org_id: OptionalOrgId,
|
|
14609
|
-
reason: OptionalString
|
|
14610
|
-
});
|
|
14611
14686
|
const OrgInviteSentProps = Schema.Struct({
|
|
14612
14687
|
...TelemetryEnvelopeProps,
|
|
14613
14688
|
org_id: OptionalOrgId,
|
|
@@ -14652,30 +14727,6 @@ Schema.Struct({
|
|
|
14652
14727
|
name: Schema.Literal("organization_creation_failed"),
|
|
14653
14728
|
props: OrganizationCreationFailedProps
|
|
14654
14729
|
});
|
|
14655
|
-
Schema.Struct({
|
|
14656
|
-
name: Schema.Literal("org_create_started"),
|
|
14657
|
-
props: OrgCreateStartedProps
|
|
14658
|
-
});
|
|
14659
|
-
Schema.Struct({
|
|
14660
|
-
name: Schema.Literal("org_safe_created"),
|
|
14661
|
-
props: OrgSafeCreatedProps
|
|
14662
|
-
});
|
|
14663
|
-
Schema.Struct({
|
|
14664
|
-
name: Schema.Literal("org_safe_confirmed"),
|
|
14665
|
-
props: OrgSafeConfirmedProps
|
|
14666
|
-
});
|
|
14667
|
-
Schema.Struct({
|
|
14668
|
-
name: Schema.Literal("org_roles_seeded"),
|
|
14669
|
-
props: OrgRolesSeededProps
|
|
14670
|
-
});
|
|
14671
|
-
Schema.Struct({
|
|
14672
|
-
name: Schema.Literal("org_created"),
|
|
14673
|
-
props: OrgCreatedProps
|
|
14674
|
-
});
|
|
14675
|
-
Schema.Struct({
|
|
14676
|
-
name: Schema.Literal("org_create_failed"),
|
|
14677
|
-
props: OrgCreateFailedProps
|
|
14678
|
-
});
|
|
14679
14730
|
Schema.Struct({
|
|
14680
14731
|
name: Schema.Literal("org_invite_sent"),
|
|
14681
14732
|
props: OrgInviteSentProps
|
|
@@ -15549,10 +15600,14 @@ const BootstrapFailedProps = Schema.Struct({
|
|
|
15549
15600
|
});
|
|
15550
15601
|
const MemberActivationStartedProps = Schema.Struct({
|
|
15551
15602
|
...TelemetryEnvelopeProps,
|
|
15603
|
+
entry_path: JourneyVariantProps.entry_path,
|
|
15604
|
+
surface: JourneyVariantProps.surface,
|
|
15552
15605
|
$insert_id: Schema.String
|
|
15553
15606
|
});
|
|
15554
15607
|
const MemberActivationReadyProps = Schema.Struct({
|
|
15555
15608
|
...TelemetryEnvelopeProps,
|
|
15609
|
+
entry_path: JourneyVariantProps.entry_path,
|
|
15610
|
+
surface: JourneyVariantProps.surface,
|
|
15556
15611
|
$insert_id: Schema.String,
|
|
15557
15612
|
duration_ms: DurationMsSchema
|
|
15558
15613
|
});
|
|
@@ -17098,7 +17153,7 @@ function mapAccountLifecycle(input) {
|
|
|
17098
17153
|
}
|
|
17099
17154
|
//#endregion
|
|
17100
17155
|
//#region ../../packages/sdk/package.json
|
|
17101
|
-
var version$2 = "4.20.0-beta.
|
|
17156
|
+
var version$2 = "4.20.0-beta.28";
|
|
17102
17157
|
//#endregion
|
|
17103
17158
|
//#region ../../packages/sdk/src/observability/telemetry/exception-projection.ts
|
|
17104
17159
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -20356,6 +20411,12 @@ function paymentsV2Signature(name) {
|
|
|
20356
20411
|
const SEND_SIGNATURE = paymentsV2Signature("send");
|
|
20357
20412
|
const SEND_BATCH_SIGNATURE = paymentsV2Signature("sendBatch");
|
|
20358
20413
|
const CREATE_COMMITMENT_SIGNATURE = paymentsV2Signature("createCommitment");
|
|
20414
|
+
/**
|
|
20415
|
+
* #2299 ruling C: only an Owner cancels or redirects an Organization-funded
|
|
20416
|
+
* commitment, so these two sit in the Owner role's scope, never a Budget's.
|
|
20417
|
+
* The contract admits only the commitment's creator, the Organization Safe.
|
|
20418
|
+
*/
|
|
20419
|
+
const OWNER_COMMITMENT_SIGNATURES = [paymentsV2Signature("cancel"), paymentsV2Signature("redirect")];
|
|
20359
20420
|
//#endregion
|
|
20360
20421
|
//#region ../../packages/protocol/src/evm/organization-payments.ts
|
|
20361
20422
|
/** Zodiac ModuleProxyFactory on Base Sepolia. */
|
|
@@ -20592,24 +20653,18 @@ function encodePreparedPaymentCallData(command) {
|
|
|
20592
20653
|
}));
|
|
20593
20654
|
}
|
|
20594
20655
|
const [item] = command.items;
|
|
20595
|
-
|
|
20596
|
-
|
|
20656
|
+
const route = command.executionRoute;
|
|
20657
|
+
if (command.items.length !== 1 || item === void 0 || isBirth(item) || route.kind !== "direct" && item.operation === "claim_commitment") throw Errors.invalidInput("items", "Commitment lifecycle requires one lone execution");
|
|
20658
|
+
const call = {
|
|
20597
20659
|
to: command.contractAddress,
|
|
20660
|
+
value: 0n,
|
|
20598
20661
|
data: lifecycleData(item)
|
|
20599
|
-
}
|
|
20600
|
-
|
|
20601
|
-
|
|
20602
|
-
|
|
20603
|
-
|
|
20604
|
-
|
|
20605
|
-
"sendBatch",
|
|
20606
|
-
"createCommitment"
|
|
20607
|
-
];
|
|
20608
|
-
/** Derive the Zodiac substrate key for one durable Permission identity. */
|
|
20609
|
-
function roleKeyForPermission(permissionId) {
|
|
20610
|
-
const normalized = permissionId.trim();
|
|
20611
|
-
if (normalized.length === 0 || normalized !== permissionId) throw Errors.invalidInput("permissionId", "must be a non-empty canonical Permission ID");
|
|
20612
|
-
return keccak256$1(toBytes$1(`permission ${permissionId}`));
|
|
20662
|
+
};
|
|
20663
|
+
return route.kind === "direct" ? safe4337ExecuteUserOpCallData(call) : buildOrgRoleSpendPersonalCallData(buildOrgRoleMultiCall({
|
|
20664
|
+
rolesModuleAddress: route.rolesModule,
|
|
20665
|
+
roleKey: route.roleKey,
|
|
20666
|
+
calls: [call]
|
|
20667
|
+
}));
|
|
20613
20668
|
}
|
|
20614
20669
|
//#endregion
|
|
20615
20670
|
//#region ../../packages/sdk/src/payments/operation-verification.ts
|
|
@@ -20638,6 +20693,16 @@ function moneySafeOpDigest(operation, chainId) {
|
|
|
20638
20693
|
userOp: safe4337UserOperation(operation)
|
|
20639
20694
|
});
|
|
20640
20695
|
}
|
|
20696
|
+
/**
|
|
20697
|
+
* An Organization-funded cancel or redirect runs through the Owner role
|
|
20698
|
+
* (#2299), which no Budget names. Its role key cannot widen what the signer
|
|
20699
|
+
* may do: the Roles module admits only a role the signer's Safe holds, and the
|
|
20700
|
+
* contract admits only the commitment's creator.
|
|
20701
|
+
*/
|
|
20702
|
+
function isOwnerLifecycle(command) {
|
|
20703
|
+
const [item] = command.items;
|
|
20704
|
+
return command.items.length === 1 && (item?.operation === "cancel_commitment" || item?.operation === "redirect_commitment");
|
|
20705
|
+
}
|
|
20641
20706
|
/** Verify every actual Money signing target against configured trust anchors. */
|
|
20642
20707
|
function verifiesMoneyOperation(evidence, expectedSafe, chainId, permissionId) {
|
|
20643
20708
|
try {
|
|
@@ -20648,7 +20713,7 @@ function verifiesMoneyOperation(evidence, expectedSafe, chainId, permissionId) {
|
|
|
20648
20713
|
const route = command.executionRoute;
|
|
20649
20714
|
if (route.kind === "direct") {
|
|
20650
20715
|
if (permissionId !== void 0 || command.senderSafe.toLowerCase() !== expectedSafe.toLowerCase()) return false;
|
|
20651
|
-
} else if (permissionId === void 0
|
|
20716
|
+
} else if ((permissionId === void 0 ? !isOwnerLifecycle(command) : route.roleKey.toLowerCase() !== roleKeyForPermission(permissionId).toLowerCase()) || command.senderSafe.toLowerCase() !== route.organizationSafe.toLowerCase()) return false;
|
|
20652
20717
|
if (operation.callData.toLowerCase() !== encodePreparedPaymentCallData(command).toLowerCase()) return false;
|
|
20653
20718
|
const digest = moneySafeOpDigest(operation, chainId);
|
|
20654
20719
|
return evidence.digest.toLowerCase() === digest.toLowerCase() && evidence.executionId === `money_execution_${digest.slice(2).toLowerCase()}`;
|
|
@@ -21365,6 +21430,15 @@ function subscribeFreshQuery(deps, args, listener, operation, projection, option
|
|
|
21365
21430
|
}
|
|
21366
21431
|
//#endregion
|
|
21367
21432
|
//#region ../../packages/sdk/src/payments/lifecycle-execution.ts
|
|
21433
|
+
/**
|
|
21434
|
+
* The prepared command is this lifecycle intent; a retry also names the
|
|
21435
|
+
* original birth command of the scope it was asked for. A claim, and an
|
|
21436
|
+
* Account's own cancel or redirect, runs directly from the signer's Safe; an
|
|
21437
|
+
* Organization's cancel or redirect runs through its Owner role (#2299).
|
|
21438
|
+
*/
|
|
21439
|
+
const lifecycleCommandCheck = (intent, expectedOrganizationId) => async (request, evidence) => await matchesIntent(intent)(request, evidence) && (intent.kind === "retry" ? expectedOrganizationId === void 0 ? evidence.sourceCommand?.kind === "birth" && evidence.sourceCommand.actor.kind === "account" : evidence.sourceCommand?.kind === "birth" && evidence.sourceCommand.actor.kind === "organization" && evidence.sourceCommand.actor.orgId === expectedOrganizationId : evidence.preparedCommand?.executionRoute.kind === (intent.kind === "claim" || expectedOrganizationId === void 0 ? "direct" : "zodiac_role"));
|
|
21440
|
+
/** The prepared command pays exactly the request the payer confirmed. */
|
|
21441
|
+
const requestPaymentCheck = (confirmation) => async (request) => "kind" in request && request.kind === "payment_request" && canonicalJson(request.confirmation) === canonicalJson(confirmation);
|
|
21368
21442
|
function executePaymentLifecycle(deps, intent, signal, controls, expectedOrganizationId, onPrepared) {
|
|
21369
21443
|
return executePrepared(deps, (signerAddress) => runIfActive(signal, CAPXUL_OPERATIONS.payments.prepareLifecycleExecution, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, lifecycles.prepareScoped, copyInvocationObservation(controls, {
|
|
21370
21444
|
signerAddress,
|
|
@@ -21373,13 +21447,13 @@ function executePaymentLifecycle(deps, intent, signal, controls, expectedOrganiz
|
|
|
21373
21447
|
kind: "organization",
|
|
21374
21448
|
orgId: expectedOrganizationId
|
|
21375
21449
|
}
|
|
21376
|
-
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise),
|
|
21450
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise), lifecycleCommandCheck(intent, expectedOrganizationId), false, void 0, signal, controls, onPrepared);
|
|
21377
21451
|
}
|
|
21378
21452
|
function executeRequestPayment(deps, confirmation, controls, onPrepared) {
|
|
21379
21453
|
return executePrepared(deps, (signerAddress) => runIfActive(controls?.signal, commands$1.prepareRequest.name, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, commands$1.prepareRequest, copyInvocationObservation(controls, {
|
|
21380
21454
|
confirmation,
|
|
21381
21455
|
signerAddress
|
|
21382
|
-
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise),
|
|
21456
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise), requestPaymentCheck(confirmation), false, void 0, controls?.signal, controls, onPrepared);
|
|
21383
21457
|
}
|
|
21384
21458
|
function executeOrganizationPayment(deps, input, signal, onPreparedClosed, controls, onPrepared) {
|
|
21385
21459
|
return executePrepared(deps, (signerAddress) => runIfActive(signal, CAPXUL_OPERATIONS.organizationPayments.prepareExecution, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, commands$1.prepareOrganization, copyInvocationObservation(controls, {
|
|
@@ -22252,17 +22326,21 @@ function paymentExecutionIntent(input) {
|
|
|
22252
22326
|
const to = normalizeTargetForBackend(input.to, "to");
|
|
22253
22327
|
if (!to.ok) return to;
|
|
22254
22328
|
try {
|
|
22255
|
-
|
|
22329
|
+
const decoded = Schema.decodeResult(PaymentExecutionIntentSchema, { onExcessProperty: "error" })({
|
|
22330
|
+
to: to.value,
|
|
22331
|
+
amount: input.amount,
|
|
22332
|
+
...input.externalAddressAcknowledged === void 0 ? {} : { externalAddressAcknowledged: input.externalAddressAcknowledged },
|
|
22333
|
+
...input.paymentType === void 0 ? {} : { paymentType: input.paymentType },
|
|
22334
|
+
...input.document === void 0 ? {} : { document: input.document },
|
|
22335
|
+
...input.supportingReceipts === void 0 ? {} : { supportingReceipts: input.supportingReceipts },
|
|
22336
|
+
...input.timing === void 0 ? {} : { timing: input.timing }
|
|
22337
|
+
});
|
|
22338
|
+
return Result.isSuccess(decoded) ? {
|
|
22256
22339
|
ok: true,
|
|
22257
|
-
value:
|
|
22258
|
-
|
|
22259
|
-
|
|
22260
|
-
|
|
22261
|
-
...input.paymentType === void 0 ? {} : { paymentType: input.paymentType },
|
|
22262
|
-
...input.document === void 0 ? {} : { document: input.document },
|
|
22263
|
-
...input.supportingReceipts === void 0 ? {} : { supportingReceipts: input.supportingReceipts },
|
|
22264
|
-
...input.timing === void 0 ? {} : { timing: input.timing }
|
|
22265
|
-
})
|
|
22340
|
+
value: decoded.success
|
|
22341
|
+
} : {
|
|
22342
|
+
ok: false,
|
|
22343
|
+
error: invalidPaymentField(decoded.failure.issue)
|
|
22266
22344
|
};
|
|
22267
22345
|
} catch {
|
|
22268
22346
|
return {
|
|
@@ -22271,6 +22349,13 @@ function paymentExecutionIntent(input) {
|
|
|
22271
22349
|
};
|
|
22272
22350
|
}
|
|
22273
22351
|
}
|
|
22352
|
+
const formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();
|
|
22353
|
+
/** The first failing field and why, e.g. `amount.value`, as the backend's row decode names it (#2381). */
|
|
22354
|
+
function invalidPaymentField(issue) {
|
|
22355
|
+
const first = formatIssue(issue).issues[0];
|
|
22356
|
+
const field = first?.path?.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
|
|
22357
|
+
return Errors.invalidInput(field === void 0 || field.length === 0 ? "payment" : field, first?.message ?? "invalid personal payment input");
|
|
22358
|
+
}
|
|
22274
22359
|
function normalizePaymentTiming(payment) {
|
|
22275
22360
|
const { release: _release, ...rest } = payment;
|
|
22276
22361
|
return {
|
|
@@ -23087,6 +23172,27 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
|
|
|
23087
23172
|
flights: /* @__PURE__ */ new Map()
|
|
23088
23173
|
};
|
|
23089
23174
|
const execute = (input, signal, operation = "organization-pay-batch", controls, onPrepared) => executeOrganizationBatch(scope, input, signal, operation, controls, onPrepared);
|
|
23175
|
+
/** One Owner-signed cancel or redirect, answering the one Payment it acted on (#2299). */
|
|
23176
|
+
const ownerLifecycle = (intent, controls, operation) => runCapxulEffect(Effect.gen(function* () {
|
|
23177
|
+
const captured = carryInvocationParentSpan(controls, yield* Effect.currentSpan.pipe(Effect.orDie));
|
|
23178
|
+
return yield* awaitCapxulResult(async () => {
|
|
23179
|
+
const execution = executionDependencies$1(deps);
|
|
23180
|
+
if (execution === null) return {
|
|
23181
|
+
ok: false,
|
|
23182
|
+
error: Errors.notImplemented("organizationPayments", "executionComposition")
|
|
23183
|
+
};
|
|
23184
|
+
const result = await executePaymentLifecycle(execution, intent, controls?.signal, captured, orgId);
|
|
23185
|
+
if (!result.ok) return result;
|
|
23186
|
+
const [payment, ...rest] = result.value.payments;
|
|
23187
|
+
return payment === void 0 || rest.length !== 0 ? {
|
|
23188
|
+
ok: false,
|
|
23189
|
+
error: Errors.unknown()
|
|
23190
|
+
} : {
|
|
23191
|
+
ok: true,
|
|
23192
|
+
value: normalizePaymentTiming(payment)
|
|
23193
|
+
};
|
|
23194
|
+
});
|
|
23195
|
+
}), observationOnlyControls(controls), operation, deps.runPromise);
|
|
23090
23196
|
return {
|
|
23091
23197
|
...makeOrganizationPaymentReadMethods(deps, orgId),
|
|
23092
23198
|
retry: (paymentId, options) => {
|
|
@@ -23145,6 +23251,19 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
|
|
|
23145
23251
|
});
|
|
23146
23252
|
}), observationOnlyControls(controls), CAPXUL_OPERATIONS.organizationPayments.pay, deps.runPromise);
|
|
23147
23253
|
},
|
|
23254
|
+
cancel: (paymentId, options) => ownerLifecycle({
|
|
23255
|
+
kind: "cancel",
|
|
23256
|
+
paymentId
|
|
23257
|
+
}, deps.invocationControls?.(options) ?? options, CAPXUL_OPERATIONS.organizationPayments.cancel),
|
|
23258
|
+
redirect: (input, options) => {
|
|
23259
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
23260
|
+
const recipient = normalizeTargetForBackend(input.to, "to");
|
|
23261
|
+
return recipient.ok ? ownerLifecycle({
|
|
23262
|
+
kind: "redirect",
|
|
23263
|
+
paymentId: input.paymentId,
|
|
23264
|
+
recipient: recipient.value
|
|
23265
|
+
}, controls, CAPXUL_OPERATIONS.organizationPayments.redirect) : runCapxulEffect(Effect.fail(recipient.error), observationOnlyControls(controls), CAPXUL_OPERATIONS.organizationPayments.redirect, deps.runPromise);
|
|
23266
|
+
},
|
|
23148
23267
|
payBatch: (input, options) => {
|
|
23149
23268
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
23150
23269
|
return runCapxulEffect(Effect.gen(function* () {
|
|
@@ -23859,6 +23978,524 @@ function sleep(ms, signal) {
|
|
|
23859
23978
|
});
|
|
23860
23979
|
}
|
|
23861
23980
|
//#endregion
|
|
23981
|
+
//#region ../../packages/protocol/src/payroll/model.ts
|
|
23982
|
+
function toPayrollRunId(raw) {
|
|
23983
|
+
return toNonEmptyStringBrand(raw, "payrollRunId", (value) => isNonEmptyString(value));
|
|
23984
|
+
}
|
|
23985
|
+
function toPayrollGroupId(raw) {
|
|
23986
|
+
return toNonEmptyStringBrand(raw, "payrollGroupId", (value) => isNonEmptyString(value));
|
|
23987
|
+
}
|
|
23988
|
+
//#endregion
|
|
23989
|
+
//#region ../../packages/sdk/src/payroll/run-checks.ts
|
|
23990
|
+
/** Match the caller's compensation to the committed V3 Payslip. */
|
|
23991
|
+
function payslipIdentityMatches(message, item, index, runId, period) {
|
|
23992
|
+
return message.runId === runId && message.itemIndex === index && message.employeeRef === item.partyId && message.periodStart === period.start && message.periodEnd === period.end && message.adjustmentsHash === hashAdjustmentsV3(item.adjustments);
|
|
23993
|
+
}
|
|
23994
|
+
function payslipAmountMatches(message, item) {
|
|
23995
|
+
const exact = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
23996
|
+
return message.gross.asset === exact.asset && message.gross.minorUnits === item.gross && message.gross.decimals === exact.decimals && message.net.asset === exact.asset && message.net.minorUnits === item.net && message.net.decimals === exact.decimals;
|
|
23997
|
+
}
|
|
23998
|
+
function isRunItem(prepared, item, evidence, index, runId, period) {
|
|
23999
|
+
if (prepared === void 0) return false;
|
|
24000
|
+
const bundle = evidence.preparedCommand?.documentBundles?.[index];
|
|
24001
|
+
const preparedItem = evidence.preparedCommand?.items[index];
|
|
24002
|
+
const payslip = bundle?.envelope;
|
|
24003
|
+
if (bundle === void 0 || payslip?.primaryType !== "Payslip") return false;
|
|
24004
|
+
return canonicalJson(prepared.to) === canonicalJson(item.to) && prepared.paymentType === "payroll" && canonicalJson(prepared.amount) === canonicalJson(item.amount) && bundle.adjustments !== void 0 && canonicalJson(bundle.adjustments) === canonicalJson(item.adjustments) && payslipIdentityMatches(payslip.message, item, index, runId, period) && payslipAmountMatches(payslip.message, item) && (preparedItem?.operation === "send" || preparedItem?.operation === "create_commitment") && preparedItem.documentHash === evidence.preparedCommand?.instructionDocuments?.[index]?.documentHash;
|
|
24005
|
+
}
|
|
24006
|
+
/**
|
|
24007
|
+
* The batch the backend derived from a payroll intent must be the batch this
|
|
24008
|
+
* caller asked for, checked before the digest is signed.
|
|
24009
|
+
*
|
|
24010
|
+
* The payslip envelope on each item is server-built and carries the run row's
|
|
24011
|
+
* own `issuedAt`, so the batch cannot be fingerprinted against a local copy
|
|
24012
|
+
* (`matchesIntent` is unavailable here). What CAN be verified is every field
|
|
24013
|
+
* the caller supplied — the org, the Budget, the request key, and per item the
|
|
24014
|
+
* recipient, the amount and the payslip figures — plus the lineage binding the
|
|
24015
|
+
* batch to the run just written. A prepared command that pays a different
|
|
24016
|
+
* person, a different amount, from a different Budget, or under a different
|
|
24017
|
+
* run never reaches the signer.
|
|
24018
|
+
*/
|
|
24019
|
+
function isRunBatch(input) {
|
|
24020
|
+
return (batch, evidence) => Promise.resolve(!("kind" in batch) && batch.orgId === input.orgId && batch.permissionId === input.permissionId && batch.requestKey === input.requestKey && batch.lineage?.kind === "payroll" && batch.lineage.runId === input.runId && batch.items.length === input.items.length && batch.items.every((item) => item.amount.asset === input.asset) && input.items.every((item, index) => isRunItem(batch.items[index], item, evidence, index, input.runId, input.period)));
|
|
24021
|
+
}
|
|
24022
|
+
/**
|
|
24023
|
+
* The batch is `isRunBatch` for the run the backend just wrote, and that run
|
|
24024
|
+
* is the period the caller asked for: the payslip's `period` is a
|
|
24025
|
+
* server-formatted label, so the stored run is where the caller's own two
|
|
24026
|
+
* numbers are checkable.
|
|
24027
|
+
*/
|
|
24028
|
+
function isAuthorizedRunBatch(input, run) {
|
|
24029
|
+
return (request, evidence) => run === void 0 || run.periodStart !== input.period.start || run.periodEnd !== input.period.end ? Promise.resolve(false) : isRunBatch({
|
|
24030
|
+
...input,
|
|
24031
|
+
runId: run.id
|
|
24032
|
+
})(request, evidence);
|
|
24033
|
+
}
|
|
24034
|
+
/** A decoded detail is the run asked for, whole: its scope, membership and quantities agree. */
|
|
24035
|
+
function checkPayrollRunDetail(value, orgId, runId) {
|
|
24036
|
+
if (value === null) return {
|
|
24037
|
+
ok: true,
|
|
24038
|
+
value: null
|
|
24039
|
+
};
|
|
24040
|
+
if (value.orgId !== orgId || value.run.id !== runId || value.items.length === 0 || value.items.length !== value.run.recipientCount || new Set(value.items.map((item) => item.payment.id)).size !== value.items.length || value.items.some((item, index) => item.itemIndex !== index || item.amount.asset !== value.run.total.asset || item.payment.amount.asset !== item.amount.asset || item.payment.paymentType !== "payroll")) return {
|
|
24041
|
+
ok: false,
|
|
24042
|
+
error: Errors.invalidInput("run", "Payroll run scope or membership does not match")
|
|
24043
|
+
};
|
|
24044
|
+
try {
|
|
24045
|
+
const total = toExactAssetAmount(value.run.total, CONFIGURED_MONEY_ASSETS);
|
|
24046
|
+
let rawTotal = 0n;
|
|
24047
|
+
for (const item of value.items) {
|
|
24048
|
+
const amount = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
24049
|
+
const payment = toExactAssetAmount(item.payment.amount, CONFIGURED_MONEY_ASSETS);
|
|
24050
|
+
if (amount.minorUnits !== payment.minorUnits) return {
|
|
24051
|
+
ok: false,
|
|
24052
|
+
error: Errors.invalidInput("run", "Payroll item quantity does not match its Payment")
|
|
24053
|
+
};
|
|
24054
|
+
rawTotal += BigInt(amount.minorUnits);
|
|
24055
|
+
}
|
|
24056
|
+
if (rawTotal !== BigInt(total.minorUnits)) return {
|
|
24057
|
+
ok: false,
|
|
24058
|
+
error: Errors.invalidInput("run", "Payroll run total does not match its items")
|
|
24059
|
+
};
|
|
24060
|
+
} catch {
|
|
24061
|
+
return {
|
|
24062
|
+
ok: false,
|
|
24063
|
+
error: Errors.invalidInput("run", "Payroll run quantities are invalid")
|
|
24064
|
+
};
|
|
24065
|
+
}
|
|
24066
|
+
return {
|
|
24067
|
+
ok: true,
|
|
24068
|
+
value
|
|
24069
|
+
};
|
|
24070
|
+
}
|
|
24071
|
+
//#endregion
|
|
24072
|
+
//#region ../../packages/sdk/src/payroll/run-decoding.ts
|
|
24073
|
+
function toPayrollRun(wire) {
|
|
24074
|
+
return {
|
|
24075
|
+
id: toPayrollRunId(wire.id),
|
|
24076
|
+
status: wire.status,
|
|
24077
|
+
periodStart: wire.periodStart,
|
|
24078
|
+
periodEnd: wire.periodEnd,
|
|
24079
|
+
total: wire.total,
|
|
24080
|
+
recipientCount: wire.recipientCount
|
|
24081
|
+
};
|
|
24082
|
+
}
|
|
24083
|
+
function toPayrollRuns(wire) {
|
|
24084
|
+
return {
|
|
24085
|
+
...mapPage(wire, toPayrollRun),
|
|
24086
|
+
settledThisMonth: wire.settledThisMonth
|
|
24087
|
+
};
|
|
24088
|
+
}
|
|
24089
|
+
function toPayrollGroup(wire) {
|
|
24090
|
+
return {
|
|
24091
|
+
id: toPayrollGroupId(wire.id),
|
|
24092
|
+
name: wire.name,
|
|
24093
|
+
tone: wire.tone,
|
|
24094
|
+
members: wire.members.map((member) => ({
|
|
24095
|
+
partyId: member.partyId,
|
|
24096
|
+
amount: member.amount,
|
|
24097
|
+
currency: member.currency
|
|
24098
|
+
}))
|
|
24099
|
+
};
|
|
24100
|
+
}
|
|
24101
|
+
function validateRunItems(input) {
|
|
24102
|
+
for (const [index, item] of input.items.entries()) {
|
|
24103
|
+
if (Schema.decodeResult(PayrollRunItemInputSchema, { onExcessProperty: "error" })(item)._tag === "Failure") return Errors.invalidInput(`items.${index}`, "invalid Payroll item");
|
|
24104
|
+
try {
|
|
24105
|
+
const exact = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
24106
|
+
const net = BigInt(item.net);
|
|
24107
|
+
const gross = BigInt(item.gross);
|
|
24108
|
+
const adjustments = item.adjustments.reduce((sum, row) => sum + BigInt(row.amount), 0n);
|
|
24109
|
+
if (item.amount.asset !== input.asset || item.net !== exact.minorUnits || gross + adjustments !== net) return Errors.invalidInput(`items.${index}`, "invalid Payroll units");
|
|
24110
|
+
} catch {
|
|
24111
|
+
return Errors.invalidInput(`items.${index}`, "invalid Payroll units");
|
|
24112
|
+
}
|
|
24113
|
+
}
|
|
24114
|
+
return null;
|
|
24115
|
+
}
|
|
24116
|
+
const RunInputSchema = Schema.Struct({
|
|
24117
|
+
permissionId: AuthorizePayrollRunExecutionInputSchema.fields.permissionId,
|
|
24118
|
+
asset: AuthorizePayrollRunExecutionInputSchema.fields.asset,
|
|
24119
|
+
period: AuthorizePayrollRunExecutionInputSchema.fields.period,
|
|
24120
|
+
items: AuthorizePayrollRunExecutionInputSchema.fields.items,
|
|
24121
|
+
requestKey: Schema.optional(AuthorizePayrollRunExecutionInputSchema.fields.requestKey)
|
|
24122
|
+
});
|
|
24123
|
+
/** Decode caller input before session or signer work. Keep routing outside the input. */
|
|
24124
|
+
function decodePayrollRunInput(value) {
|
|
24125
|
+
try {
|
|
24126
|
+
const decoded = Schema.decodeUnknownSync(RunInputSchema, { onExcessProperty: "error" })(value);
|
|
24127
|
+
const input = {
|
|
24128
|
+
permissionId: decoded.permissionId,
|
|
24129
|
+
asset: decoded.asset,
|
|
24130
|
+
period: decoded.period,
|
|
24131
|
+
...decoded.requestKey === void 0 ? {} : { requestKey: decoded.requestKey },
|
|
24132
|
+
items: decoded.items.map((item) => ({
|
|
24133
|
+
amount: item.amount,
|
|
24134
|
+
gross: item.gross,
|
|
24135
|
+
net: item.net,
|
|
24136
|
+
adjustments: item.adjustments,
|
|
24137
|
+
partyId: toPartyId(item.partyId),
|
|
24138
|
+
to: item.to.kind === "party" ? {
|
|
24139
|
+
kind: "party",
|
|
24140
|
+
partyId: toPartyId(item.to.partyId)
|
|
24141
|
+
} : item.to.kind === "address" ? {
|
|
24142
|
+
kind: "address",
|
|
24143
|
+
address: toAddress(item.to.address)
|
|
24144
|
+
} : item.to
|
|
24145
|
+
}))
|
|
24146
|
+
};
|
|
24147
|
+
if (input.items.length === 0 || input.period.start > input.period.end) throw Errors.invalidInput("input", "Payroll requires items and an ordered period");
|
|
24148
|
+
const error = validateRunItems(input);
|
|
24149
|
+
if (error !== null) throw error;
|
|
24150
|
+
return input;
|
|
24151
|
+
} catch (cause) {
|
|
24152
|
+
throw cause instanceof CapxulError ? cause : Errors.invalidInput("input", "Invalid Payroll input");
|
|
24153
|
+
}
|
|
24154
|
+
}
|
|
24155
|
+
/** Read only the exact backend prepare receipt, never an arbitrary signer failure. */
|
|
24156
|
+
function payrollResolution(error, requestKey) {
|
|
24157
|
+
const details = error.details;
|
|
24158
|
+
if (error.code !== "WRONG_STATE" || details?.method !== runs.authorize.name || details.requestKey !== requestKey || typeof details.commandId !== "string" || typeof details.runId !== "string") return void 0;
|
|
24159
|
+
return details.payrollResolution === "failed" || details.payrollResolution === "settled" ? details.payrollResolution : void 0;
|
|
24160
|
+
}
|
|
24161
|
+
function validatePayrollRunLookup(orgId, runId) {
|
|
24162
|
+
return !Schema.is(PayrollRunIdSchema)(runId) || orgId.length === 0 ? {
|
|
24163
|
+
ok: false,
|
|
24164
|
+
error: Errors.invalidInput("runId", "Organization and run ID must be valid")
|
|
24165
|
+
} : {
|
|
24166
|
+
ok: true,
|
|
24167
|
+
value: void 0
|
|
24168
|
+
};
|
|
24169
|
+
}
|
|
24170
|
+
/** Strict saved detail decoder shared by the read and fresh stream. */
|
|
24171
|
+
function decodePayrollRunDetail(raw, orgId, runId) {
|
|
24172
|
+
const decoded = Schema.decodeUnknownResult(Schema.NullOr(PayrollRunDetailSchema), { onExcessProperty: "error" })(raw);
|
|
24173
|
+
if (decoded._tag === "Failure") return {
|
|
24174
|
+
ok: false,
|
|
24175
|
+
error: Errors.invalidInput("run", "Invalid Payroll run detail")
|
|
24176
|
+
};
|
|
24177
|
+
return checkPayrollRunDetail(decoded.success, orgId, runId);
|
|
24178
|
+
}
|
|
24179
|
+
//#endregion
|
|
24180
|
+
//#region ../../packages/sdk/src/payroll/run-subscription.ts
|
|
24181
|
+
function detailValue(result) {
|
|
24182
|
+
if (!result.ok) throw result.error;
|
|
24183
|
+
return result.value;
|
|
24184
|
+
}
|
|
24185
|
+
function subscribePayrollRun(deps, orgId, runId, listener, options) {
|
|
24186
|
+
const operation = CAPXUL_OPERATIONS.org.payroll.subscribeRun;
|
|
24187
|
+
const valid = deps.validate(orgId, runId);
|
|
24188
|
+
if (!valid.ok) return runCapxulEffect(Effect.fail(valid.error), observationOnlyControls(deps.invocationControls?.(options) ?? options), operation, deps.runPromise);
|
|
24189
|
+
const decode = (raw) => detailValue(deps.decode(raw, orgId, runId));
|
|
24190
|
+
return subscribeFreshQuery(deps, {
|
|
24191
|
+
orgId,
|
|
24192
|
+
runId
|
|
24193
|
+
}, listener, operation, {
|
|
24194
|
+
decode,
|
|
24195
|
+
validateCached: (raw) => {
|
|
24196
|
+
decode(raw);
|
|
24197
|
+
},
|
|
24198
|
+
invalidValue: (cause) => isCapxulError(cause) ? cause : Errors.invalidInput("run", "Invalid Payroll run detail"),
|
|
24199
|
+
invalidScope: () => Errors.invalidInput("run", "Payroll run subscription scope does not match"),
|
|
24200
|
+
lostValue: () => Errors.invalidInput("run", "Payroll run is no longer readable"),
|
|
24201
|
+
unavailable: () => Errors.notImplemented("payroll", "subscribeRun")
|
|
24202
|
+
}, options);
|
|
24203
|
+
}
|
|
24204
|
+
//#endregion
|
|
24205
|
+
//#region ../../packages/sdk/src/payroll/run-authorization.ts
|
|
24206
|
+
/**
|
|
24207
|
+
* Authorize one payroll run: a stable request key, the prepared batch checked
|
|
24208
|
+
* against the run the caller asked for, the signature, and the submission.
|
|
24209
|
+
*/
|
|
24210
|
+
async function authorizePayrollRun(scope, input, controls, onRequestKey, onResolved) {
|
|
24211
|
+
const { deps, orgId, requestKeyScope } = scope;
|
|
24212
|
+
const signal = controls?.signal;
|
|
24213
|
+
const execution = executionDependencies(deps);
|
|
24214
|
+
if (execution === null) return {
|
|
24215
|
+
ok: false,
|
|
24216
|
+
error: Errors.notImplemented("payroll", "executionComposition")
|
|
24217
|
+
};
|
|
24218
|
+
try {
|
|
24219
|
+
input = decodePayrollRunInput(input);
|
|
24220
|
+
} catch (cause) {
|
|
24221
|
+
return {
|
|
24222
|
+
ok: false,
|
|
24223
|
+
error: asCapxulError(cause)
|
|
24224
|
+
};
|
|
24225
|
+
}
|
|
24226
|
+
const { requestKey: provided, ...canonicalInput } = input;
|
|
24227
|
+
const actorId = onRequestKey === void 0 ? void 0 : execution.actor.authSession()?.authUserId;
|
|
24228
|
+
if (onRequestKey !== void 0 && actorId === void 0) return {
|
|
24229
|
+
ok: false,
|
|
24230
|
+
error: Errors.notAuthenticated()
|
|
24231
|
+
};
|
|
24232
|
+
const requestKey = await paymentRequestKeyLifecycle(`${requestKeyScope}:${AUTHORIZE_RUN}`, onRequestKey === void 0 ? {
|
|
24233
|
+
orgId,
|
|
24234
|
+
input
|
|
24235
|
+
} : {
|
|
24236
|
+
actorId,
|
|
24237
|
+
orgId,
|
|
24238
|
+
input: canonicalInput
|
|
24239
|
+
}, provided, onRequestKey === void 0 ? void 0 : { manageProvidedKey: true });
|
|
24240
|
+
try {
|
|
24241
|
+
await onRequestKey?.(requestKey.key);
|
|
24242
|
+
if (onRequestKey !== void 0 && execution.actor.authSession()?.authUserId !== actorId) {
|
|
24243
|
+
await requestKey.finish(false).catch(() => void 0);
|
|
24244
|
+
return {
|
|
24245
|
+
ok: false,
|
|
24246
|
+
error: Errors.notAuthenticated()
|
|
24247
|
+
};
|
|
24248
|
+
}
|
|
24249
|
+
} catch (cause) {
|
|
24250
|
+
await requestKey.finish(false).catch(() => void 0);
|
|
24251
|
+
return {
|
|
24252
|
+
ok: false,
|
|
24253
|
+
error: asCapxulError(cause)
|
|
24254
|
+
};
|
|
24255
|
+
}
|
|
24256
|
+
let authorized;
|
|
24257
|
+
let acceptedReplay;
|
|
24258
|
+
let preparedClosed = false;
|
|
24259
|
+
let terminalReplay;
|
|
24260
|
+
const submitted = await executePrepared(execution, async (signerAddress) => {
|
|
24261
|
+
const prepared = await runIfActive(signal, AUTHORIZE_RUN, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, runs.authorize, copyInvocationObservation(controls, {
|
|
24262
|
+
orgId,
|
|
24263
|
+
permissionId: input.permissionId,
|
|
24264
|
+
asset: input.asset,
|
|
24265
|
+
requestKey: requestKey.key,
|
|
24266
|
+
signerAddress,
|
|
24267
|
+
period: input.period,
|
|
24268
|
+
items: input.items
|
|
24269
|
+
}))).pipe(Effect.mapError((publicError) => ({ publicError }))), controls), deps.runPromise);
|
|
24270
|
+
if (!prepared.ok) {
|
|
24271
|
+
terminalReplay = payrollResolution(prepared.error, requestKey.key);
|
|
24272
|
+
return prepared;
|
|
24273
|
+
}
|
|
24274
|
+
authorized = prepared.value.run;
|
|
24275
|
+
acceptedReplay = prepared.value.replay.kind === "accepted" ? prepared.value.replay.submission : void 0;
|
|
24276
|
+
return {
|
|
24277
|
+
ok: true,
|
|
24278
|
+
value: prepared.value.execution
|
|
24279
|
+
};
|
|
24280
|
+
}, (request, evidence) => isAuthorizedRunBatch({
|
|
24281
|
+
orgId,
|
|
24282
|
+
permissionId: input.permissionId,
|
|
24283
|
+
requestKey: requestKey.key,
|
|
24284
|
+
asset: input.asset,
|
|
24285
|
+
period: input.period,
|
|
24286
|
+
items: input.items
|
|
24287
|
+
}, authorized)(request, evidence), true, () => preparedClosed = true, signal, controls, void 0, () => acceptedReplay);
|
|
24288
|
+
const resolution = preparedClosed ? "failed" : terminalReplay;
|
|
24289
|
+
if (resolution !== void 0) try {
|
|
24290
|
+
await onResolved?.(resolution);
|
|
24291
|
+
} catch {}
|
|
24292
|
+
try {
|
|
24293
|
+
await requestKey.finish(submitted.ok || resolution !== void 0);
|
|
24294
|
+
} catch {}
|
|
24295
|
+
if (!submitted.ok) return submitted;
|
|
24296
|
+
return authorized === void 0 ? {
|
|
24297
|
+
ok: false,
|
|
24298
|
+
error: Errors.unknown()
|
|
24299
|
+
} : {
|
|
24300
|
+
ok: true,
|
|
24301
|
+
value: toPayrollRun(authorized)
|
|
24302
|
+
};
|
|
24303
|
+
}
|
|
24304
|
+
const AUTHORIZE_RUN = runs.authorize.name;
|
|
24305
|
+
function executionDependencies(deps) {
|
|
24306
|
+
return deps.actor === void 0 || deps.chainId === void 0 || deps.signer === void 0 ? null : {
|
|
24307
|
+
actor: deps.actor,
|
|
24308
|
+
chainId: deps.chainId,
|
|
24309
|
+
convexCall: deps.convexCall,
|
|
24310
|
+
runPromise: deps.runPromise,
|
|
24311
|
+
signer: deps.signer,
|
|
24312
|
+
...deps.telemetry === void 0 ? {} : { telemetry: deps.telemetry }
|
|
24313
|
+
};
|
|
24314
|
+
}
|
|
24315
|
+
//#endregion
|
|
24316
|
+
//#region ../../packages/sdk/src/payroll/runs.ts
|
|
24317
|
+
function makePayrollMethods(deps, orgId) {
|
|
24318
|
+
const requestKeyScope = deps.requestKeyScope ?? `client-${crypto.randomUUID()}`;
|
|
24319
|
+
/** One observed call of one payroll row, its reply projected inside the call, under the row's name. */
|
|
24320
|
+
const call = (row, args, options, project) => {
|
|
24321
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
24322
|
+
return runPortEffect(callOperation(deps.convexCall, row, copyInvocationObservation(controls, args)).pipe(Effect.map(project), Effect.mapError((publicError) => ({ publicError }))), controls, row.name, deps.runPromise);
|
|
24323
|
+
};
|
|
24324
|
+
return {
|
|
24325
|
+
subscribeRun: (runId, listener, options) => subscribePayrollRun({
|
|
24326
|
+
actor: deps.actor,
|
|
24327
|
+
convexCall: deps.convexCall,
|
|
24328
|
+
query: reference(runs.get),
|
|
24329
|
+
runPromise: deps.runPromise,
|
|
24330
|
+
invocationControls: deps.invocationControls,
|
|
24331
|
+
decode: decodePayrollRunDetail,
|
|
24332
|
+
validate: validatePayrollRunLookup
|
|
24333
|
+
}, orgId, runId, listener, options),
|
|
24334
|
+
getRun: (runId, options) => {
|
|
24335
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
24336
|
+
const lifetime = deps.actor?.authLifetime();
|
|
24337
|
+
const operation = runs.get.name;
|
|
24338
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
24339
|
+
const lookup = validatePayrollRunLookup(orgId, runId);
|
|
24340
|
+
if (!lookup.ok) return yield* Effect.fail(lookup.error);
|
|
24341
|
+
if (lifetime === void 0 || lifetime.authUserId === null || deps.actor === void 0) return yield* Effect.fail(Errors.notAuthenticated());
|
|
24342
|
+
const current = () => deps.actor?.matchesAuthLifetime(lifetime, lifetime.authUserId) === true;
|
|
24343
|
+
if (!current()) return yield* Effect.fail(Errors.notAuthenticated());
|
|
24344
|
+
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
24345
|
+
const captured = carryInvocationParentSpan(controls, parent);
|
|
24346
|
+
const run = yield* awaitCapxulResult(() => runIfActive(controls?.signal, operation, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, runs.get, copyInvocationObservation(captured, {
|
|
24347
|
+
orgId,
|
|
24348
|
+
runId
|
|
24349
|
+
}))).pipe(Effect.mapError((publicError) => ({ publicError }))), captured), deps.runPromise));
|
|
24350
|
+
if (!current()) return yield* Effect.fail(Errors.cancelled({ operation }));
|
|
24351
|
+
const checked = checkPayrollRunDetail(run, orgId, runId);
|
|
24352
|
+
return yield* checked.ok ? Effect.succeed(checked.value) : Effect.fail(checked.error);
|
|
24353
|
+
}), observationOnlyControls(controls), operation, deps.runPromise);
|
|
24354
|
+
},
|
|
24355
|
+
runs: (options) => call(runs.list, { orgId }, options, toPayrollRuns),
|
|
24356
|
+
authorizeRun: (input, options) => {
|
|
24357
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
24358
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
24359
|
+
if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation: AUTHORIZE_RUN }));
|
|
24360
|
+
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
24361
|
+
return yield* awaitCapxulResult(() => authorizePayrollRun({
|
|
24362
|
+
deps,
|
|
24363
|
+
orgId,
|
|
24364
|
+
requestKeyScope
|
|
24365
|
+
}, input, carryInvocationParentSpan(controls, parent), options?.onRequestKey, options?.onResolved));
|
|
24366
|
+
}), observationOnlyControls(controls), AUTHORIZE_RUN, deps.runPromise);
|
|
24367
|
+
},
|
|
24368
|
+
terms: (options) => call(terms.list, { orgId }, options, (rates) => rates),
|
|
24369
|
+
groups: {
|
|
24370
|
+
list: (options) => call(groups.list, { orgId }, options, (saved) => mapPage(saved, toPayrollGroup)),
|
|
24371
|
+
save: (input, options) => call(groups.save, {
|
|
24372
|
+
orgId,
|
|
24373
|
+
...input.id === void 0 ? {} : { id: String(input.id) },
|
|
24374
|
+
name: input.name,
|
|
24375
|
+
tone: input.tone,
|
|
24376
|
+
members: input.members
|
|
24377
|
+
}, options, toPayrollGroup),
|
|
24378
|
+
remove: (id, options) => call(groups.remove, {
|
|
24379
|
+
orgId,
|
|
24380
|
+
id
|
|
24381
|
+
}, options, () => void 0)
|
|
24382
|
+
}
|
|
24383
|
+
};
|
|
24384
|
+
}
|
|
24385
|
+
const payrollUnavailable = () => Promise.resolve({
|
|
24386
|
+
ok: false,
|
|
24387
|
+
error: Errors.notImplemented("payroll", "executionComposition")
|
|
24388
|
+
});
|
|
24389
|
+
/** The bundle an unconfigured client exposes — every verb refuses, none throws. */
|
|
24390
|
+
function makeUnavailablePayrollMethods() {
|
|
24391
|
+
return {
|
|
24392
|
+
getRun: payrollUnavailable,
|
|
24393
|
+
subscribeRun: payrollUnavailable,
|
|
24394
|
+
runs: payrollUnavailable,
|
|
24395
|
+
authorizeRun: payrollUnavailable,
|
|
24396
|
+
terms: payrollUnavailable,
|
|
24397
|
+
groups: {
|
|
24398
|
+
list: payrollUnavailable,
|
|
24399
|
+
save: payrollUnavailable,
|
|
24400
|
+
remove: payrollUnavailable
|
|
24401
|
+
}
|
|
24402
|
+
};
|
|
24403
|
+
}
|
|
24404
|
+
//#endregion
|
|
24405
|
+
//#region ../../packages/sdk/src/accounts/command-approvals.ts
|
|
24406
|
+
/**
|
|
24407
|
+
* Keep a prepared command only when it is the one asked for and its operation
|
|
24408
|
+
* is the person's own. A mismatch is closed unsent when `close`, as the signed
|
|
24409
|
+
* path closes a new Payment; a retry or a request keeps its Payment open.
|
|
24410
|
+
*/
|
|
24411
|
+
async function checked(deps, signerAddress, prepared, expected, close) {
|
|
24412
|
+
if (!prepared.ok) return prepared;
|
|
24413
|
+
const session = deps.actor?.authSession() ?? null;
|
|
24414
|
+
if (session === null || deps.chainId === void 0) return {
|
|
24415
|
+
ok: false,
|
|
24416
|
+
error: Errors.notAuthenticated()
|
|
24417
|
+
};
|
|
24418
|
+
const evidence = prepared.value;
|
|
24419
|
+
if (await expected(evidence.request, evidence) && await verifiedPreparedCommand(evidence, session.email, signerAddress, deps.chainId)) return prepared;
|
|
24420
|
+
const refused = {
|
|
24421
|
+
ok: false,
|
|
24422
|
+
error: Errors.invalidInput("execution", "prepared command mismatch")
|
|
24423
|
+
};
|
|
24424
|
+
return close ? abandonPrepared(deps, evidence.executionId, "prepared_mismatch", refused) : refused;
|
|
24425
|
+
}
|
|
24426
|
+
/** A retry, a request payment or a payroll run, rather than a new Payment. */
|
|
24427
|
+
const isCommandApproval = (input) => "retry" in input || "request" in input || "payrollRun" in input;
|
|
24428
|
+
/** Prepare a retry, a request payment or a payroll run for `signerAddress`, checked. */
|
|
24429
|
+
function prepareVerifiedCommand(deps, input, signerAddress, signal) {
|
|
24430
|
+
if ("retry" in input) return prepareVerifiedRetry(deps, input, signerAddress, signal);
|
|
24431
|
+
if ("request" in input) return prepareVerifiedRequestPayment(deps, input, signerAddress, signal);
|
|
24432
|
+
return prepareVerifiedPayrollRun(deps, input, signerAddress, signal);
|
|
24433
|
+
}
|
|
24434
|
+
/** The retry of one Payment, prepared in the scope that paid it. */
|
|
24435
|
+
async function prepareVerifiedRetry(deps, input, signerAddress, signal) {
|
|
24436
|
+
const intent = {
|
|
24437
|
+
kind: "retry",
|
|
24438
|
+
paymentId: input.retry
|
|
24439
|
+
};
|
|
24440
|
+
return checked(deps, signerAddress, await runIfActive(signal, CAPXUL_OPERATIONS.payments.prepareLifecycleExecution, () => callOperation(deps.convexCall, lifecycles.prepareScoped, {
|
|
24441
|
+
signerAddress,
|
|
24442
|
+
intent,
|
|
24443
|
+
expectedScope: input.organizationId === void 0 ? { kind: "account" } : {
|
|
24444
|
+
kind: "organization",
|
|
24445
|
+
orgId: input.organizationId
|
|
24446
|
+
}
|
|
24447
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise), lifecycleCommandCheck(intent, input.organizationId), false);
|
|
24448
|
+
}
|
|
24449
|
+
/** The Payment of one request, exactly as the request froze it. */
|
|
24450
|
+
async function prepareVerifiedRequestPayment(deps, input, signerAddress, signal) {
|
|
24451
|
+
const decoded = Schema.decodeResult(RequestPaymentConfirmationSchema, { onExcessProperty: "error" })(input.request);
|
|
24452
|
+
if (decoded._tag === "Failure") return {
|
|
24453
|
+
ok: false,
|
|
24454
|
+
error: Errors.invalidInput("confirmation", "invalid request confirmation")
|
|
24455
|
+
};
|
|
24456
|
+
const confirmation = decoded.success;
|
|
24457
|
+
return checked(deps, signerAddress, await runIfActive(signal, commands$1.prepareRequest.name, () => callOperation(deps.convexCall, commands$1.prepareRequest, {
|
|
24458
|
+
confirmation,
|
|
24459
|
+
signerAddress
|
|
24460
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise), requestPaymentCheck(confirmation), false);
|
|
24461
|
+
}
|
|
24462
|
+
/** One payroll run: the run the backend writes and the batch that pays it, checked together. */
|
|
24463
|
+
async function prepareVerifiedPayrollRun(deps, input, signerAddress, signal) {
|
|
24464
|
+
let run;
|
|
24465
|
+
try {
|
|
24466
|
+
run = decodePayrollRunInput(input.payrollRun);
|
|
24467
|
+
} catch (cause) {
|
|
24468
|
+
return {
|
|
24469
|
+
ok: false,
|
|
24470
|
+
error: asCapxulError(cause)
|
|
24471
|
+
};
|
|
24472
|
+
}
|
|
24473
|
+
const orgId = input.organizationId;
|
|
24474
|
+
const requestKey = run.requestKey ?? `payroll_${crypto.randomUUID()}`;
|
|
24475
|
+
const { permissionId, asset, period, items } = run;
|
|
24476
|
+
const authorized = await runIfActive(signal, runs.authorize.name, () => callOperation(deps.convexCall, runs.authorize, {
|
|
24477
|
+
orgId,
|
|
24478
|
+
permissionId,
|
|
24479
|
+
asset,
|
|
24480
|
+
requestKey,
|
|
24481
|
+
signerAddress,
|
|
24482
|
+
period,
|
|
24483
|
+
items
|
|
24484
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise);
|
|
24485
|
+
if (!authorized.ok) return authorized;
|
|
24486
|
+
return checked(deps, signerAddress, {
|
|
24487
|
+
ok: true,
|
|
24488
|
+
value: authorized.value.execution
|
|
24489
|
+
}, isAuthorizedRunBatch({
|
|
24490
|
+
orgId,
|
|
24491
|
+
permissionId,
|
|
24492
|
+
requestKey,
|
|
24493
|
+
asset,
|
|
24494
|
+
period,
|
|
24495
|
+
items
|
|
24496
|
+
}, authorized.value.run), authorized.value.replay.kind !== "accepted");
|
|
24497
|
+
}
|
|
24498
|
+
//#endregion
|
|
23862
24499
|
//#region ../../packages/sdk/src/accounts/organization-approvals.ts
|
|
23863
24500
|
/** The Organization Payment prepared for `signerAddress` and checked, before any approval. */
|
|
23864
24501
|
async function prepareVerifiedOrganizationPayment(deps, input, signerAddress, signal) {
|
|
@@ -23965,6 +24602,7 @@ function makeApprovalsMethods(deps) {
|
|
|
23965
24602
|
};
|
|
23966
24603
|
const prepare = async (input, options) => {
|
|
23967
24604
|
const signal = options?.signal;
|
|
24605
|
+
if (isCommandApproval(input)) return openApproval(signal, (signerAddress) => prepareVerifiedCommand(deps, input, signerAddress, signal));
|
|
23968
24606
|
if ("organizationId" in input) return openApproval(signal, (signerAddress) => prepareVerifiedOrganizationPayment(deps, input, signerAddress, signal));
|
|
23969
24607
|
const intent = paymentExecutionIntent(input);
|
|
23970
24608
|
if (!intent.ok) return intent;
|
|
@@ -24057,7 +24695,7 @@ function makeApprovalsMethods(deps) {
|
|
|
24057
24695
|
ok: true,
|
|
24058
24696
|
value: null
|
|
24059
24697
|
};
|
|
24060
|
-
const payments = await readApprovalPayments(deps, approval.operation.paymentIds, organizationId, signal);
|
|
24698
|
+
const payments = await readApprovalPayments(deps, approval.operation.paymentIds, organizationId ?? (approval.actor.kind === "organization" ? approval.actor.orgId : void 0), signal);
|
|
24061
24699
|
if (!payments.ok) return payments;
|
|
24062
24700
|
if (unsent(approval, payments.value)) {
|
|
24063
24701
|
const sent = await submit(approvalId);
|
|
@@ -26894,14 +27532,6 @@ function executeIdentityProductObservation(telemetry, record, state) {
|
|
|
26894
27532
|
return ignoreTransportFailure(() => invocationTelemetry.emit(mapped), "emit", mapped.name).pipe(Effect.andThen(ignoreTransportFailure(after, mapped.name === "auth_signed_out" ? "reset" : "identify", mapped.name)));
|
|
26895
27533
|
}
|
|
26896
27534
|
//#endregion
|
|
26897
|
-
//#region ../../packages/protocol/src/payroll/model.ts
|
|
26898
|
-
function toPayrollRunId(raw) {
|
|
26899
|
-
return toNonEmptyStringBrand(raw, "payrollRunId", (value) => isNonEmptyString(value));
|
|
26900
|
-
}
|
|
26901
|
-
function toPayrollGroupId(raw) {
|
|
26902
|
-
return toNonEmptyStringBrand(raw, "payrollGroupId", (value) => isNonEmptyString(value));
|
|
26903
|
-
}
|
|
26904
|
-
//#endregion
|
|
26905
27535
|
//#region ../../packages/sdk/src/permissions/command-decoding.ts
|
|
26906
27536
|
/** The caller's input as a command: ids branded and the intent decoded, or invalid input thrown. */
|
|
26907
27537
|
const buildCommand = {
|
|
@@ -36563,13 +37193,15 @@ function normalizeOrgBudgetPolicy(policy) {
|
|
|
36563
37193
|
if (actionSet.has(action)) throw Errors.invalidInput("policy.actions", `duplicate Budget action: ${action}`);
|
|
36564
37194
|
actionSet.add(action);
|
|
36565
37195
|
}
|
|
37196
|
+
if (policy.refill === "monthly" && limit.kind === "unlimited") throw Errors.invalidInput("policy.refill", "only a capped Budget can refill");
|
|
36566
37197
|
return {
|
|
36567
37198
|
assetId,
|
|
36568
37199
|
tokenAddress,
|
|
36569
37200
|
decimals: policy.asset.decimals,
|
|
36570
37201
|
limit,
|
|
36571
37202
|
recipients,
|
|
36572
|
-
actions: ORG_BUDGET_ACTIONS.filter((action) => actionSet.has(action))
|
|
37203
|
+
actions: ORG_BUDGET_ACTIONS.filter((action) => actionSet.has(action)),
|
|
37204
|
+
refillPeriod: policy.refill === "monthly" ? MONTHLY_REFILL_PERIOD_SECONDS : 0n
|
|
36573
37205
|
};
|
|
36574
37206
|
}
|
|
36575
37207
|
function resolvedBudgetApprovalPermission(tokenAddress) {
|
|
@@ -36644,8 +37276,8 @@ function buildOrgBudgetCallsWithEncoder(input, encodeRoles) {
|
|
|
36644
37276
|
allowanceKey,
|
|
36645
37277
|
BigInt(policy.limit.rawUnits),
|
|
36646
37278
|
BigInt(policy.limit.rawUnits),
|
|
36647
|
-
0n,
|
|
36648
|
-
|
|
37279
|
+
policy.refillPeriod === 0n ? 0n : BigInt(policy.limit.rawUnits),
|
|
37280
|
+
policy.refillPeriod,
|
|
36649
37281
|
0n
|
|
36650
37282
|
]
|
|
36651
37283
|
}),
|
|
@@ -36839,13 +37471,20 @@ function buildExplicitBootstrapRoleCalls(input, encodeRoles) {
|
|
|
36839
37471
|
roles: [{
|
|
36840
37472
|
key: managementRoleKey,
|
|
36841
37473
|
members: [input.ownerAddress],
|
|
36842
|
-
permissions: [
|
|
36843
|
-
|
|
36844
|
-
|
|
36845
|
-
|
|
36846
|
-
|
|
36847
|
-
signature
|
|
36848
|
-
|
|
37474
|
+
permissions: [
|
|
37475
|
+
{
|
|
37476
|
+
targetAddress: input.rolesModuleAddress,
|
|
37477
|
+
signature: "assignRoles(address,bytes32[],bool[])"
|
|
37478
|
+
},
|
|
37479
|
+
...ROLE_MANAGEMENT_SIGNATURES.map((signature) => ({
|
|
37480
|
+
targetAddress: input.rolesModuleAddress,
|
|
37481
|
+
signature
|
|
37482
|
+
})),
|
|
37483
|
+
...OWNER_COMMITMENT_SIGNATURES.map((signature) => ({
|
|
37484
|
+
targetAddress: CAPXUL_PAYMENTS_V2_ADDRESS,
|
|
37485
|
+
signature
|
|
37486
|
+
}))
|
|
37487
|
+
]
|
|
36849
37488
|
}]
|
|
36850
37489
|
}).map((call) => ({
|
|
36851
37490
|
to: toLowerHex(call.to, "setUpRoles.call.to"),
|
|
@@ -37310,7 +37949,7 @@ function matchesCurrentPolicy(intent, policy) {
|
|
|
37310
37949
|
asset: policy.asset,
|
|
37311
37950
|
value: policy.limit
|
|
37312
37951
|
}, CONFIGURED_MONEY_ASSETS).minorUnits;
|
|
37313
|
-
return policy.asset === intent.asset && raw === recorded && (intent.scope === void 0 || canonicalJson(intent.scope) === canonicalJson(policy.scope));
|
|
37952
|
+
return policy.asset === intent.asset && raw === recorded && intent.refill === "monthly" === (policy.refill === "monthly") && (intent.scope === void 0 || canonicalJson(intent.scope) === canonicalJson(policy.scope));
|
|
37314
37953
|
}
|
|
37315
37954
|
function previousPolicyForChange(effect, command) {
|
|
37316
37955
|
if (effect.previousIntent.type === "budget" && (effect.previousIntent.scope === void 0 || effect.previousRecipientBindings === void 0)) throw new EvidenceMismatch("Prior complete Budget proof is unavailable");
|
|
@@ -37328,8 +37967,9 @@ function policyCalls(evidence, context, command) {
|
|
|
37328
37967
|
const roleKey = effect.operation === "change" ? effect.roleKey : roleKeyForPermission(permissionId);
|
|
37329
37968
|
const revision = effect.operation === "change" ? effect.revision : 0;
|
|
37330
37969
|
if (effect.operation !== command.operation || effect.operation === "change" && effect.permissionId !== permissionId || evidence.expectedRevision !== revision) throw new EvidenceMismatch("Permission policy binding mismatch");
|
|
37970
|
+
const intent = effect.operation === "change" ? keepRefillOnChange(command.intent, effect.previousCurrentPolicy) : command.intent;
|
|
37331
37971
|
const value = evidence.value;
|
|
37332
|
-
if (Array.isArray(value) || !("type" in value) || !matchesCurrentPolicy(
|
|
37972
|
+
if (Array.isArray(value) || !("type" in value) || !matchesCurrentPolicy(intent, value.currentPolicy) || !permissionMatches(value, context.orgId, permissionId, command.operation === "create" ? 0 : revision + 1, intent, roleKey)) throw new EvidenceMismatch("Permission value mismatch");
|
|
37333
37973
|
const planned = compilePermissionPolicyCallsWithEncoder({
|
|
37334
37974
|
permissionId,
|
|
37335
37975
|
roleKey,
|
|
@@ -37337,7 +37977,7 @@ function policyCalls(evidence, context, command) {
|
|
|
37337
37977
|
actorSafe: evidence.userOpSenderSafe,
|
|
37338
37978
|
rolesModule: context.rolesModule,
|
|
37339
37979
|
managerRoleKey: context.managerRoleKey,
|
|
37340
|
-
nextIntent: encodingIntent(
|
|
37980
|
+
nextIntent: encodingIntent(intent, effect.recipientBindings),
|
|
37341
37981
|
...previous === void 0 ? {} : { previousIntent: previous },
|
|
37342
37982
|
...effect.operation !== "change" || effect.previousAllowanceKey === null ? {} : { previousAllowanceKey: effect.previousAllowanceKey }
|
|
37343
37983
|
}, encodeRoles);
|
|
@@ -37389,7 +38029,8 @@ function encodingIntent(intent, bindings) {
|
|
|
37389
38029
|
"send",
|
|
37390
38030
|
"sendBatch",
|
|
37391
38031
|
"createCommitment"
|
|
37392
|
-
].filter((action) => action === "createCommitment" ? intent.scope.actions.includes("commitments") : intent.scope.actions.includes("pay"))
|
|
38032
|
+
].filter((action) => action === "createCommitment" ? intent.scope.actions.includes("commitments") : intent.scope.actions.includes("pay")),
|
|
38033
|
+
...intent.refill === "monthly" ? { refill: intent.refill } : {}
|
|
37393
38034
|
}
|
|
37394
38035
|
};
|
|
37395
38036
|
}
|
|
@@ -38042,411 +38683,6 @@ function makePermissionMethods(deps, orgId) {
|
|
|
38042
38683
|
};
|
|
38043
38684
|
}
|
|
38044
38685
|
//#endregion
|
|
38045
|
-
//#region ../../packages/sdk/src/payroll/run-checks.ts
|
|
38046
|
-
/** Match the caller's compensation to the committed V3 Payslip. */
|
|
38047
|
-
function payslipIdentityMatches(message, item, index, runId, period) {
|
|
38048
|
-
return message.runId === runId && message.itemIndex === index && message.employeeRef === item.partyId && message.periodStart === period.start && message.periodEnd === period.end && message.adjustmentsHash === hashAdjustmentsV3(item.adjustments);
|
|
38049
|
-
}
|
|
38050
|
-
function payslipAmountMatches(message, item) {
|
|
38051
|
-
const exact = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
38052
|
-
return message.gross.asset === exact.asset && message.gross.minorUnits === item.gross && message.gross.decimals === exact.decimals && message.net.asset === exact.asset && message.net.minorUnits === item.net && message.net.decimals === exact.decimals;
|
|
38053
|
-
}
|
|
38054
|
-
function isRunItem(prepared, item, evidence, index, runId, period) {
|
|
38055
|
-
if (prepared === void 0) return false;
|
|
38056
|
-
const bundle = evidence.preparedCommand?.documentBundles?.[index];
|
|
38057
|
-
const preparedItem = evidence.preparedCommand?.items[index];
|
|
38058
|
-
const payslip = bundle?.envelope;
|
|
38059
|
-
if (bundle === void 0 || payslip?.primaryType !== "Payslip") return false;
|
|
38060
|
-
return canonicalJson(prepared.to) === canonicalJson(item.to) && prepared.paymentType === "payroll" && canonicalJson(prepared.amount) === canonicalJson(item.amount) && bundle.adjustments !== void 0 && canonicalJson(bundle.adjustments) === canonicalJson(item.adjustments) && payslipIdentityMatches(payslip.message, item, index, runId, period) && payslipAmountMatches(payslip.message, item) && (preparedItem?.operation === "send" || preparedItem?.operation === "create_commitment") && preparedItem.documentHash === evidence.preparedCommand?.instructionDocuments?.[index]?.documentHash;
|
|
38061
|
-
}
|
|
38062
|
-
/**
|
|
38063
|
-
* The batch the backend derived from a payroll intent must be the batch this
|
|
38064
|
-
* caller asked for, checked before the digest is signed.
|
|
38065
|
-
*
|
|
38066
|
-
* The payslip envelope on each item is server-built and carries the run row's
|
|
38067
|
-
* own `issuedAt`, so the batch cannot be fingerprinted against a local copy
|
|
38068
|
-
* (`matchesIntent` is unavailable here). What CAN be verified is every field
|
|
38069
|
-
* the caller supplied — the org, the Budget, the request key, and per item the
|
|
38070
|
-
* recipient, the amount and the payslip figures — plus the lineage binding the
|
|
38071
|
-
* batch to the run just written. A prepared command that pays a different
|
|
38072
|
-
* person, a different amount, from a different Budget, or under a different
|
|
38073
|
-
* run never reaches the signer.
|
|
38074
|
-
*/
|
|
38075
|
-
function isRunBatch(input) {
|
|
38076
|
-
return (batch, evidence) => Promise.resolve(!("kind" in batch) && batch.orgId === input.orgId && batch.permissionId === input.permissionId && batch.requestKey === input.requestKey && batch.lineage?.kind === "payroll" && batch.lineage.runId === input.runId && batch.items.length === input.items.length && batch.items.every((item) => item.amount.asset === input.asset) && input.items.every((item, index) => isRunItem(batch.items[index], item, evidence, index, input.runId, input.period)));
|
|
38077
|
-
}
|
|
38078
|
-
/** A decoded detail is the run asked for, whole: its scope, membership and quantities agree. */
|
|
38079
|
-
function checkPayrollRunDetail(value, orgId, runId) {
|
|
38080
|
-
if (value === null) return {
|
|
38081
|
-
ok: true,
|
|
38082
|
-
value: null
|
|
38083
|
-
};
|
|
38084
|
-
if (value.orgId !== orgId || value.run.id !== runId || value.items.length === 0 || value.items.length !== value.run.recipientCount || new Set(value.items.map((item) => item.payment.id)).size !== value.items.length || value.items.some((item, index) => item.itemIndex !== index || item.amount.asset !== value.run.total.asset || item.payment.amount.asset !== item.amount.asset || item.payment.paymentType !== "payroll")) return {
|
|
38085
|
-
ok: false,
|
|
38086
|
-
error: Errors.invalidInput("run", "Payroll run scope or membership does not match")
|
|
38087
|
-
};
|
|
38088
|
-
try {
|
|
38089
|
-
const total = toExactAssetAmount(value.run.total, CONFIGURED_MONEY_ASSETS);
|
|
38090
|
-
let rawTotal = 0n;
|
|
38091
|
-
for (const item of value.items) {
|
|
38092
|
-
const amount = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
38093
|
-
const payment = toExactAssetAmount(item.payment.amount, CONFIGURED_MONEY_ASSETS);
|
|
38094
|
-
if (amount.minorUnits !== payment.minorUnits) return {
|
|
38095
|
-
ok: false,
|
|
38096
|
-
error: Errors.invalidInput("run", "Payroll item quantity does not match its Payment")
|
|
38097
|
-
};
|
|
38098
|
-
rawTotal += BigInt(amount.minorUnits);
|
|
38099
|
-
}
|
|
38100
|
-
if (rawTotal !== BigInt(total.minorUnits)) return {
|
|
38101
|
-
ok: false,
|
|
38102
|
-
error: Errors.invalidInput("run", "Payroll run total does not match its items")
|
|
38103
|
-
};
|
|
38104
|
-
} catch {
|
|
38105
|
-
return {
|
|
38106
|
-
ok: false,
|
|
38107
|
-
error: Errors.invalidInput("run", "Payroll run quantities are invalid")
|
|
38108
|
-
};
|
|
38109
|
-
}
|
|
38110
|
-
return {
|
|
38111
|
-
ok: true,
|
|
38112
|
-
value
|
|
38113
|
-
};
|
|
38114
|
-
}
|
|
38115
|
-
//#endregion
|
|
38116
|
-
//#region ../../packages/sdk/src/payroll/run-decoding.ts
|
|
38117
|
-
function toPayrollRun(wire) {
|
|
38118
|
-
return {
|
|
38119
|
-
id: toPayrollRunId(wire.id),
|
|
38120
|
-
status: wire.status,
|
|
38121
|
-
periodStart: wire.periodStart,
|
|
38122
|
-
periodEnd: wire.periodEnd,
|
|
38123
|
-
total: wire.total,
|
|
38124
|
-
recipientCount: wire.recipientCount
|
|
38125
|
-
};
|
|
38126
|
-
}
|
|
38127
|
-
function toPayrollRuns(wire) {
|
|
38128
|
-
return {
|
|
38129
|
-
...mapPage(wire, toPayrollRun),
|
|
38130
|
-
settledThisMonth: wire.settledThisMonth
|
|
38131
|
-
};
|
|
38132
|
-
}
|
|
38133
|
-
function toPayrollGroup(wire) {
|
|
38134
|
-
return {
|
|
38135
|
-
id: toPayrollGroupId(wire.id),
|
|
38136
|
-
name: wire.name,
|
|
38137
|
-
tone: wire.tone,
|
|
38138
|
-
members: wire.members.map((member) => ({
|
|
38139
|
-
partyId: member.partyId,
|
|
38140
|
-
amount: member.amount,
|
|
38141
|
-
currency: member.currency
|
|
38142
|
-
}))
|
|
38143
|
-
};
|
|
38144
|
-
}
|
|
38145
|
-
function validateRunItems(input) {
|
|
38146
|
-
for (const [index, item] of input.items.entries()) {
|
|
38147
|
-
if (Schema.decodeResult(PayrollRunItemInputSchema, { onExcessProperty: "error" })(item)._tag === "Failure") return Errors.invalidInput(`items.${index}`, "invalid Payroll item");
|
|
38148
|
-
try {
|
|
38149
|
-
const exact = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
38150
|
-
const net = BigInt(item.net);
|
|
38151
|
-
const gross = BigInt(item.gross);
|
|
38152
|
-
const adjustments = item.adjustments.reduce((sum, row) => sum + BigInt(row.amount), 0n);
|
|
38153
|
-
if (item.amount.asset !== input.asset || item.net !== exact.minorUnits || gross + adjustments !== net) return Errors.invalidInput(`items.${index}`, "invalid Payroll units");
|
|
38154
|
-
} catch {
|
|
38155
|
-
return Errors.invalidInput(`items.${index}`, "invalid Payroll units");
|
|
38156
|
-
}
|
|
38157
|
-
}
|
|
38158
|
-
return null;
|
|
38159
|
-
}
|
|
38160
|
-
const RunInputSchema = Schema.Struct({
|
|
38161
|
-
permissionId: AuthorizePayrollRunExecutionInputSchema.fields.permissionId,
|
|
38162
|
-
asset: AuthorizePayrollRunExecutionInputSchema.fields.asset,
|
|
38163
|
-
period: AuthorizePayrollRunExecutionInputSchema.fields.period,
|
|
38164
|
-
items: AuthorizePayrollRunExecutionInputSchema.fields.items,
|
|
38165
|
-
requestKey: Schema.optional(AuthorizePayrollRunExecutionInputSchema.fields.requestKey)
|
|
38166
|
-
});
|
|
38167
|
-
/** Decode caller input before session or signer work. Keep routing outside the input. */
|
|
38168
|
-
function decodePayrollRunInput(value) {
|
|
38169
|
-
try {
|
|
38170
|
-
const decoded = Schema.decodeUnknownSync(RunInputSchema, { onExcessProperty: "error" })(value);
|
|
38171
|
-
const input = {
|
|
38172
|
-
permissionId: decoded.permissionId,
|
|
38173
|
-
asset: decoded.asset,
|
|
38174
|
-
period: decoded.period,
|
|
38175
|
-
...decoded.requestKey === void 0 ? {} : { requestKey: decoded.requestKey },
|
|
38176
|
-
items: decoded.items.map((item) => ({
|
|
38177
|
-
amount: item.amount,
|
|
38178
|
-
gross: item.gross,
|
|
38179
|
-
net: item.net,
|
|
38180
|
-
adjustments: item.adjustments,
|
|
38181
|
-
partyId: toPartyId(item.partyId),
|
|
38182
|
-
to: item.to.kind === "party" ? {
|
|
38183
|
-
kind: "party",
|
|
38184
|
-
partyId: toPartyId(item.to.partyId)
|
|
38185
|
-
} : item.to.kind === "address" ? {
|
|
38186
|
-
kind: "address",
|
|
38187
|
-
address: toAddress(item.to.address)
|
|
38188
|
-
} : item.to
|
|
38189
|
-
}))
|
|
38190
|
-
};
|
|
38191
|
-
if (input.items.length === 0 || input.period.start > input.period.end) throw Errors.invalidInput("input", "Payroll requires items and an ordered period");
|
|
38192
|
-
const error = validateRunItems(input);
|
|
38193
|
-
if (error !== null) throw error;
|
|
38194
|
-
return input;
|
|
38195
|
-
} catch (cause) {
|
|
38196
|
-
throw cause instanceof CapxulError ? cause : Errors.invalidInput("input", "Invalid Payroll input");
|
|
38197
|
-
}
|
|
38198
|
-
}
|
|
38199
|
-
/** Read only the exact backend prepare receipt, never an arbitrary signer failure. */
|
|
38200
|
-
function payrollResolution(error, requestKey) {
|
|
38201
|
-
const details = error.details;
|
|
38202
|
-
if (error.code !== "WRONG_STATE" || details?.method !== runs.authorize.name || details.requestKey !== requestKey || typeof details.commandId !== "string" || typeof details.runId !== "string") return void 0;
|
|
38203
|
-
return details.payrollResolution === "failed" || details.payrollResolution === "settled" ? details.payrollResolution : void 0;
|
|
38204
|
-
}
|
|
38205
|
-
function validatePayrollRunLookup(orgId, runId) {
|
|
38206
|
-
return !Schema.is(PayrollRunIdSchema)(runId) || orgId.length === 0 ? {
|
|
38207
|
-
ok: false,
|
|
38208
|
-
error: Errors.invalidInput("runId", "Organization and run ID must be valid")
|
|
38209
|
-
} : {
|
|
38210
|
-
ok: true,
|
|
38211
|
-
value: void 0
|
|
38212
|
-
};
|
|
38213
|
-
}
|
|
38214
|
-
/** Strict saved detail decoder shared by the read and fresh stream. */
|
|
38215
|
-
function decodePayrollRunDetail(raw, orgId, runId) {
|
|
38216
|
-
const decoded = Schema.decodeUnknownResult(Schema.NullOr(PayrollRunDetailSchema), { onExcessProperty: "error" })(raw);
|
|
38217
|
-
if (decoded._tag === "Failure") return {
|
|
38218
|
-
ok: false,
|
|
38219
|
-
error: Errors.invalidInput("run", "Invalid Payroll run detail")
|
|
38220
|
-
};
|
|
38221
|
-
return checkPayrollRunDetail(decoded.success, orgId, runId);
|
|
38222
|
-
}
|
|
38223
|
-
//#endregion
|
|
38224
|
-
//#region ../../packages/sdk/src/payroll/run-subscription.ts
|
|
38225
|
-
function detailValue(result) {
|
|
38226
|
-
if (!result.ok) throw result.error;
|
|
38227
|
-
return result.value;
|
|
38228
|
-
}
|
|
38229
|
-
function subscribePayrollRun(deps, orgId, runId, listener, options) {
|
|
38230
|
-
const operation = CAPXUL_OPERATIONS.org.payroll.subscribeRun;
|
|
38231
|
-
const valid = deps.validate(orgId, runId);
|
|
38232
|
-
if (!valid.ok) return runCapxulEffect(Effect.fail(valid.error), observationOnlyControls(deps.invocationControls?.(options) ?? options), operation, deps.runPromise);
|
|
38233
|
-
const decode = (raw) => detailValue(deps.decode(raw, orgId, runId));
|
|
38234
|
-
return subscribeFreshQuery(deps, {
|
|
38235
|
-
orgId,
|
|
38236
|
-
runId
|
|
38237
|
-
}, listener, operation, {
|
|
38238
|
-
decode,
|
|
38239
|
-
validateCached: (raw) => {
|
|
38240
|
-
decode(raw);
|
|
38241
|
-
},
|
|
38242
|
-
invalidValue: (cause) => isCapxulError(cause) ? cause : Errors.invalidInput("run", "Invalid Payroll run detail"),
|
|
38243
|
-
invalidScope: () => Errors.invalidInput("run", "Payroll run subscription scope does not match"),
|
|
38244
|
-
lostValue: () => Errors.invalidInput("run", "Payroll run is no longer readable"),
|
|
38245
|
-
unavailable: () => Errors.notImplemented("payroll", "subscribeRun")
|
|
38246
|
-
}, options);
|
|
38247
|
-
}
|
|
38248
|
-
//#endregion
|
|
38249
|
-
//#region ../../packages/sdk/src/payroll/run-authorization.ts
|
|
38250
|
-
/**
|
|
38251
|
-
* Authorize one payroll run: a stable request key, the prepared batch checked
|
|
38252
|
-
* against the run the caller asked for, the signature, and the submission.
|
|
38253
|
-
*/
|
|
38254
|
-
async function authorizePayrollRun(scope, input, controls, onRequestKey, onResolved) {
|
|
38255
|
-
const { deps, orgId, requestKeyScope } = scope;
|
|
38256
|
-
const signal = controls?.signal;
|
|
38257
|
-
const execution = executionDependencies(deps);
|
|
38258
|
-
if (execution === null) return {
|
|
38259
|
-
ok: false,
|
|
38260
|
-
error: Errors.notImplemented("payroll", "executionComposition")
|
|
38261
|
-
};
|
|
38262
|
-
try {
|
|
38263
|
-
input = decodePayrollRunInput(input);
|
|
38264
|
-
} catch (cause) {
|
|
38265
|
-
return {
|
|
38266
|
-
ok: false,
|
|
38267
|
-
error: asCapxulError(cause)
|
|
38268
|
-
};
|
|
38269
|
-
}
|
|
38270
|
-
const { requestKey: provided, ...canonicalInput } = input;
|
|
38271
|
-
const actorId = onRequestKey === void 0 ? void 0 : execution.actor.authSession()?.authUserId;
|
|
38272
|
-
if (onRequestKey !== void 0 && actorId === void 0) return {
|
|
38273
|
-
ok: false,
|
|
38274
|
-
error: Errors.notAuthenticated()
|
|
38275
|
-
};
|
|
38276
|
-
const requestKey = await paymentRequestKeyLifecycle(`${requestKeyScope}:${AUTHORIZE_RUN}`, onRequestKey === void 0 ? {
|
|
38277
|
-
orgId,
|
|
38278
|
-
input
|
|
38279
|
-
} : {
|
|
38280
|
-
actorId,
|
|
38281
|
-
orgId,
|
|
38282
|
-
input: canonicalInput
|
|
38283
|
-
}, provided, onRequestKey === void 0 ? void 0 : { manageProvidedKey: true });
|
|
38284
|
-
try {
|
|
38285
|
-
await onRequestKey?.(requestKey.key);
|
|
38286
|
-
if (onRequestKey !== void 0 && execution.actor.authSession()?.authUserId !== actorId) {
|
|
38287
|
-
await requestKey.finish(false).catch(() => void 0);
|
|
38288
|
-
return {
|
|
38289
|
-
ok: false,
|
|
38290
|
-
error: Errors.notAuthenticated()
|
|
38291
|
-
};
|
|
38292
|
-
}
|
|
38293
|
-
} catch (cause) {
|
|
38294
|
-
await requestKey.finish(false).catch(() => void 0);
|
|
38295
|
-
return {
|
|
38296
|
-
ok: false,
|
|
38297
|
-
error: asCapxulError(cause)
|
|
38298
|
-
};
|
|
38299
|
-
}
|
|
38300
|
-
let authorized;
|
|
38301
|
-
let acceptedReplay;
|
|
38302
|
-
let preparedClosed = false;
|
|
38303
|
-
let terminalReplay;
|
|
38304
|
-
const submitted = await executePrepared(execution, async (signerAddress) => {
|
|
38305
|
-
const prepared = await runIfActive(signal, AUTHORIZE_RUN, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, runs.authorize, copyInvocationObservation(controls, {
|
|
38306
|
-
orgId,
|
|
38307
|
-
permissionId: input.permissionId,
|
|
38308
|
-
asset: input.asset,
|
|
38309
|
-
requestKey: requestKey.key,
|
|
38310
|
-
signerAddress,
|
|
38311
|
-
period: input.period,
|
|
38312
|
-
items: input.items
|
|
38313
|
-
}))).pipe(Effect.mapError((publicError) => ({ publicError }))), controls), deps.runPromise);
|
|
38314
|
-
if (!prepared.ok) {
|
|
38315
|
-
terminalReplay = payrollResolution(prepared.error, requestKey.key);
|
|
38316
|
-
return prepared;
|
|
38317
|
-
}
|
|
38318
|
-
authorized = prepared.value.run;
|
|
38319
|
-
acceptedReplay = prepared.value.replay.kind === "accepted" ? prepared.value.replay.submission : void 0;
|
|
38320
|
-
return {
|
|
38321
|
-
ok: true,
|
|
38322
|
-
value: prepared.value.execution
|
|
38323
|
-
};
|
|
38324
|
-
}, (request, evidence) => authorized === void 0 || authorized.periodStart !== input.period.start || authorized.periodEnd !== input.period.end ? Promise.resolve(false) : isRunBatch({
|
|
38325
|
-
orgId,
|
|
38326
|
-
permissionId: input.permissionId,
|
|
38327
|
-
requestKey: requestKey.key,
|
|
38328
|
-
runId: authorized.id,
|
|
38329
|
-
asset: input.asset,
|
|
38330
|
-
period: input.period,
|
|
38331
|
-
items: input.items
|
|
38332
|
-
})(request, evidence), true, () => preparedClosed = true, signal, controls, void 0, () => acceptedReplay);
|
|
38333
|
-
const resolution = preparedClosed ? "failed" : terminalReplay;
|
|
38334
|
-
if (resolution !== void 0) try {
|
|
38335
|
-
await onResolved?.(resolution);
|
|
38336
|
-
} catch {}
|
|
38337
|
-
try {
|
|
38338
|
-
await requestKey.finish(submitted.ok || resolution !== void 0);
|
|
38339
|
-
} catch {}
|
|
38340
|
-
if (!submitted.ok) return submitted;
|
|
38341
|
-
return authorized === void 0 ? {
|
|
38342
|
-
ok: false,
|
|
38343
|
-
error: Errors.unknown()
|
|
38344
|
-
} : {
|
|
38345
|
-
ok: true,
|
|
38346
|
-
value: toPayrollRun(authorized)
|
|
38347
|
-
};
|
|
38348
|
-
}
|
|
38349
|
-
const AUTHORIZE_RUN = runs.authorize.name;
|
|
38350
|
-
function executionDependencies(deps) {
|
|
38351
|
-
return deps.actor === void 0 || deps.chainId === void 0 || deps.signer === void 0 ? null : {
|
|
38352
|
-
actor: deps.actor,
|
|
38353
|
-
chainId: deps.chainId,
|
|
38354
|
-
convexCall: deps.convexCall,
|
|
38355
|
-
runPromise: deps.runPromise,
|
|
38356
|
-
signer: deps.signer,
|
|
38357
|
-
...deps.telemetry === void 0 ? {} : { telemetry: deps.telemetry }
|
|
38358
|
-
};
|
|
38359
|
-
}
|
|
38360
|
-
//#endregion
|
|
38361
|
-
//#region ../../packages/sdk/src/payroll/runs.ts
|
|
38362
|
-
function makePayrollMethods(deps, orgId) {
|
|
38363
|
-
const requestKeyScope = deps.requestKeyScope ?? `client-${crypto.randomUUID()}`;
|
|
38364
|
-
/** One observed call of one payroll row, its reply projected inside the call, under the row's name. */
|
|
38365
|
-
const call = (row, args, options, project) => {
|
|
38366
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
38367
|
-
return runPortEffect(callOperation(deps.convexCall, row, copyInvocationObservation(controls, args)).pipe(Effect.map(project), Effect.mapError((publicError) => ({ publicError }))), controls, row.name, deps.runPromise);
|
|
38368
|
-
};
|
|
38369
|
-
return {
|
|
38370
|
-
subscribeRun: (runId, listener, options) => subscribePayrollRun({
|
|
38371
|
-
actor: deps.actor,
|
|
38372
|
-
convexCall: deps.convexCall,
|
|
38373
|
-
query: reference(runs.get),
|
|
38374
|
-
runPromise: deps.runPromise,
|
|
38375
|
-
invocationControls: deps.invocationControls,
|
|
38376
|
-
decode: decodePayrollRunDetail,
|
|
38377
|
-
validate: validatePayrollRunLookup
|
|
38378
|
-
}, orgId, runId, listener, options),
|
|
38379
|
-
getRun: (runId, options) => {
|
|
38380
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
38381
|
-
const lifetime = deps.actor?.authLifetime();
|
|
38382
|
-
const operation = runs.get.name;
|
|
38383
|
-
return runCapxulEffect(Effect.gen(function* () {
|
|
38384
|
-
const lookup = validatePayrollRunLookup(orgId, runId);
|
|
38385
|
-
if (!lookup.ok) return yield* Effect.fail(lookup.error);
|
|
38386
|
-
if (lifetime === void 0 || lifetime.authUserId === null || deps.actor === void 0) return yield* Effect.fail(Errors.notAuthenticated());
|
|
38387
|
-
const current = () => deps.actor?.matchesAuthLifetime(lifetime, lifetime.authUserId) === true;
|
|
38388
|
-
if (!current()) return yield* Effect.fail(Errors.notAuthenticated());
|
|
38389
|
-
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
38390
|
-
const captured = carryInvocationParentSpan(controls, parent);
|
|
38391
|
-
const run = yield* awaitCapxulResult(() => runIfActive(controls?.signal, operation, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, runs.get, copyInvocationObservation(captured, {
|
|
38392
|
-
orgId,
|
|
38393
|
-
runId
|
|
38394
|
-
}))).pipe(Effect.mapError((publicError) => ({ publicError }))), captured), deps.runPromise));
|
|
38395
|
-
if (!current()) return yield* Effect.fail(Errors.cancelled({ operation }));
|
|
38396
|
-
const checked = checkPayrollRunDetail(run, orgId, runId);
|
|
38397
|
-
return yield* checked.ok ? Effect.succeed(checked.value) : Effect.fail(checked.error);
|
|
38398
|
-
}), observationOnlyControls(controls), operation, deps.runPromise);
|
|
38399
|
-
},
|
|
38400
|
-
runs: (options) => call(runs.list, { orgId }, options, toPayrollRuns),
|
|
38401
|
-
authorizeRun: (input, options) => {
|
|
38402
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
38403
|
-
return runCapxulEffect(Effect.gen(function* () {
|
|
38404
|
-
if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation: AUTHORIZE_RUN }));
|
|
38405
|
-
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
38406
|
-
return yield* awaitCapxulResult(() => authorizePayrollRun({
|
|
38407
|
-
deps,
|
|
38408
|
-
orgId,
|
|
38409
|
-
requestKeyScope
|
|
38410
|
-
}, input, carryInvocationParentSpan(controls, parent), options?.onRequestKey, options?.onResolved));
|
|
38411
|
-
}), observationOnlyControls(controls), AUTHORIZE_RUN, deps.runPromise);
|
|
38412
|
-
},
|
|
38413
|
-
terms: (options) => call(terms.list, { orgId }, options, (rates) => rates),
|
|
38414
|
-
groups: {
|
|
38415
|
-
list: (options) => call(groups.list, { orgId }, options, (saved) => mapPage(saved, toPayrollGroup)),
|
|
38416
|
-
save: (input, options) => call(groups.save, {
|
|
38417
|
-
orgId,
|
|
38418
|
-
...input.id === void 0 ? {} : { id: String(input.id) },
|
|
38419
|
-
name: input.name,
|
|
38420
|
-
tone: input.tone,
|
|
38421
|
-
members: input.members
|
|
38422
|
-
}, options, toPayrollGroup),
|
|
38423
|
-
remove: (id, options) => call(groups.remove, {
|
|
38424
|
-
orgId,
|
|
38425
|
-
id
|
|
38426
|
-
}, options, () => void 0)
|
|
38427
|
-
}
|
|
38428
|
-
};
|
|
38429
|
-
}
|
|
38430
|
-
const payrollUnavailable = () => Promise.resolve({
|
|
38431
|
-
ok: false,
|
|
38432
|
-
error: Errors.notImplemented("payroll", "executionComposition")
|
|
38433
|
-
});
|
|
38434
|
-
/** The bundle an unconfigured client exposes — every verb refuses, none throws. */
|
|
38435
|
-
function makeUnavailablePayrollMethods() {
|
|
38436
|
-
return {
|
|
38437
|
-
getRun: payrollUnavailable,
|
|
38438
|
-
subscribeRun: payrollUnavailable,
|
|
38439
|
-
runs: payrollUnavailable,
|
|
38440
|
-
authorizeRun: payrollUnavailable,
|
|
38441
|
-
terms: payrollUnavailable,
|
|
38442
|
-
groups: {
|
|
38443
|
-
list: payrollUnavailable,
|
|
38444
|
-
save: payrollUnavailable,
|
|
38445
|
-
remove: payrollUnavailable
|
|
38446
|
-
}
|
|
38447
|
-
};
|
|
38448
|
-
}
|
|
38449
|
-
//#endregion
|
|
38450
38686
|
//#region ../../node_modules/.pnpm/axios@1.17.0/node_modules/axios/lib/helpers/bind.js
|
|
38451
38687
|
/**
|
|
38452
38688
|
* Create a bound version of a function with a specified `this` context
|
|
@@ -62221,6 +62457,22 @@ function normalizeRuntimeUrl(field, raw) {
|
|
|
62221
62457
|
}
|
|
62222
62458
|
//#endregion
|
|
62223
62459
|
//#region ../../packages/sdk/src/applications/bootstrap-adapter.ts
|
|
62460
|
+
/** A cached envelope this close to its expiry is fetched again, so it cannot lapse mid-command. */
|
|
62461
|
+
const CACHE_EXPIRY_MARGIN_MS = 3e4;
|
|
62462
|
+
/**
|
|
62463
|
+
* The cached envelope, when it is still inside its own lifetime. It is decoded
|
|
62464
|
+
* as a fresh body is; anything that does not decode, or a store that fails,
|
|
62465
|
+
* is a miss.
|
|
62466
|
+
*/
|
|
62467
|
+
async function cachedEnvelope(cache, now) {
|
|
62468
|
+
try {
|
|
62469
|
+
const cached = decodeBootstrapBody(await cache.load());
|
|
62470
|
+
const expiresAt = cached.issuedAt + cached.expiresIn - CACHE_EXPIRY_MARGIN_MS;
|
|
62471
|
+
return cached.issuedAt <= now && now < expiresAt ? cached : null;
|
|
62472
|
+
} catch {
|
|
62473
|
+
return null;
|
|
62474
|
+
}
|
|
62475
|
+
}
|
|
62224
62476
|
const DEFAULT_RETRY = {
|
|
62225
62477
|
attempts: 2,
|
|
62226
62478
|
baseDelayMs: 400
|
|
@@ -62261,19 +62513,24 @@ var HttpBootstrapAdapter = class {
|
|
|
62261
62513
|
fetchImpl;
|
|
62262
62514
|
observation;
|
|
62263
62515
|
retry;
|
|
62516
|
+
cache;
|
|
62264
62517
|
constructor(deps) {
|
|
62265
62518
|
this.bootstrapBaseUrl = deps.bootstrapBaseUrl.replace(/\/$/, "");
|
|
62266
62519
|
this.bootstrapHost = hostOf(this.bootstrapBaseUrl);
|
|
62267
62520
|
this.observation = deps.observation;
|
|
62268
62521
|
this.retry = deps.retry ?? DEFAULT_RETRY;
|
|
62522
|
+
this.cache = deps.cache;
|
|
62269
62523
|
this.fetchImpl = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
62270
62524
|
}
|
|
62271
62525
|
resolve(input) {
|
|
62272
|
-
|
|
62526
|
+
const fetched = this.attempt(input).pipe(Effect.retry({
|
|
62273
62527
|
times: this.retry.attempts,
|
|
62274
62528
|
while: isTransientBootstrapFailure,
|
|
62275
62529
|
schedule: Schedule.exponential(Duration.millis(this.retry.baseDelayMs))
|
|
62276
62530
|
}));
|
|
62531
|
+
const cache = this.cache;
|
|
62532
|
+
if (cache === void 0) return fetched;
|
|
62533
|
+
return Effect.promise(() => cachedEnvelope(cache, Date.now())).pipe(Effect.filterOrElse((cached) => cached !== null, () => fetched));
|
|
62277
62534
|
}
|
|
62278
62535
|
attempt(input) {
|
|
62279
62536
|
return Effect.tryPromise({
|
|
@@ -62298,7 +62555,13 @@ var HttpBootstrapAdapter = class {
|
|
|
62298
62555
|
}
|
|
62299
62556
|
mapResponse(res, input) {
|
|
62300
62557
|
if (res.ok) return Effect.tryPromise({
|
|
62301
|
-
try: async () =>
|
|
62558
|
+
try: async () => {
|
|
62559
|
+
const body = await res.json();
|
|
62560
|
+
const resolution = decodeBootstrapBody(body);
|
|
62561
|
+
const cache = this.cache;
|
|
62562
|
+
if (cache !== void 0) await Promise.resolve().then(() => cache.save(body)).catch(() => void 0);
|
|
62563
|
+
return resolution;
|
|
62564
|
+
},
|
|
62302
62565
|
catch: (cause) => {
|
|
62303
62566
|
if (cause instanceof CapxulError && cause.code === "INVALID_INPUT") return bootstrapErrorFromCapxul("invalidInput", cause);
|
|
62304
62567
|
return bootstrapErrorFromCapxul("malformedBody", Errors.providerError("convex", "bootstrap", cause instanceof Error ? cause : new Error(String(cause))));
|
|
@@ -63965,7 +64228,9 @@ function makeNotImplementedOrganizationPaymentsMethods() {
|
|
|
63965
64228
|
subscribe: organizationPaymentExecutionUnavailable,
|
|
63966
64229
|
pay: organizationPaymentExecutionUnavailable,
|
|
63967
64230
|
payBatch: organizationPaymentExecutionUnavailable,
|
|
63968
|
-
retry: organizationPaymentExecutionUnavailable
|
|
64231
|
+
retry: organizationPaymentExecutionUnavailable,
|
|
64232
|
+
cancel: organizationPaymentExecutionUnavailable,
|
|
64233
|
+
redirect: organizationPaymentExecutionUnavailable
|
|
63969
64234
|
};
|
|
63970
64235
|
}
|
|
63971
64236
|
const orgMeUnavailable = () => Promise.resolve({
|
|
@@ -68158,6 +68423,23 @@ function subscribeConvexQuery(client, fn, args, callback, options) {
|
|
|
68158
68423
|
* ever needs a different one.
|
|
68159
68424
|
*/
|
|
68160
68425
|
const DEFAULT_CLOSE_TIMEOUT_MS = 5e3;
|
|
68426
|
+
/**
|
|
68427
|
+
* The fetcher the Convex client's auth handshake gets. Convex first asks for
|
|
68428
|
+
* "a possibly cached token" (`forceRefreshToken: false`) and, as soon as the
|
|
68429
|
+
* server accepts it, for a fresh one (`true`): the token whose `exp` it
|
|
68430
|
+
* schedules the next refresh from. The Better Auth clients keep no token: each
|
|
68431
|
+
* ask mints one over the network, so the cached ask was a second token fetch
|
|
68432
|
+
* and a second server verification (a JWKS read) on every session restore. It
|
|
68433
|
+
* answers "none" instead, and Convex makes its one fetch forced, as a fresh
|
|
68434
|
+
* token. Every forced ask still reaches the auth client: that first fetch, the
|
|
68435
|
+
* refresh before `exp`, and the retry after the server rejects a token.
|
|
68436
|
+
* ponytail: the OAuth bearer client can hold a cached exchange; it now pays one
|
|
68437
|
+
* exchange for the first authentication instead of reusing it (never more
|
|
68438
|
+
* fetches than before). Pass the cache through if that start ever matters.
|
|
68439
|
+
*/
|
|
68440
|
+
function freshTokenHandshake(fetchToken) {
|
|
68441
|
+
return async (args) => args.forceRefreshToken ? fetchToken(args) : null;
|
|
68442
|
+
}
|
|
68161
68443
|
/** Run a Convex call under the current trace parent, when there is one. */
|
|
68162
68444
|
const withTraceparent = (run) => Effect.flatMap(Effect.serviceOption(Tracer.ParentSpan), (parent) => run(parent._tag === "Some" ? formatTraceparent(parent.value) : void 0));
|
|
68163
68445
|
var ConvexCallAdapter = class {
|
|
@@ -68182,10 +68464,10 @@ var ConvexCallAdapter = class {
|
|
|
68182
68464
|
this.#tokenProvider = deps.tokenProvider;
|
|
68183
68465
|
this.#applicationId = deps.applicationId;
|
|
68184
68466
|
this.#observation = deps.observation;
|
|
68185
|
-
|
|
68467
|
+
this.refreshAuth();
|
|
68186
68468
|
}
|
|
68187
68469
|
refreshAuth() {
|
|
68188
|
-
if (this.#tokenProvider) this.#client.setAuth(this.#tokenProvider);
|
|
68470
|
+
if (this.#tokenProvider) this.#client.setAuth(freshTokenHandshake(this.#tokenProvider));
|
|
68189
68471
|
}
|
|
68190
68472
|
observeConnection(observer) {
|
|
68191
68473
|
return this.#connectionMonitor.observe(this.#client, observer);
|
|
@@ -69623,7 +69905,8 @@ async function createProductionAdapters(input) {
|
|
|
69623
69905
|
const bootstrapLayer = HttpBootstrapLayer({
|
|
69624
69906
|
bootstrapBaseUrl: resolvedInput.value.bootstrapBaseUrl,
|
|
69625
69907
|
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
69626
|
-
...invocationObservation === void 0 ? {} : { observation: invocationObservation }
|
|
69908
|
+
...invocationObservation === void 0 ? {} : { observation: invocationObservation },
|
|
69909
|
+
...input.bootstrapCache === void 0 ? {} : { cache: input.bootstrapCache }
|
|
69627
69910
|
});
|
|
69628
69911
|
try {
|
|
69629
69912
|
const bootstrapContext = await Effect.runPromise(Layer.buildWithScope(bootstrapLayer, scope));
|
|
@@ -71068,7 +71351,7 @@ function stdioIdentity(env = process.env, options = {}) {
|
|
|
71068
71351
|
}
|
|
71069
71352
|
//#endregion
|
|
71070
71353
|
//#region package.json
|
|
71071
|
-
var version = "4.20.0-beta.
|
|
71354
|
+
var version = "4.20.0-beta.28";
|
|
71072
71355
|
//#endregion
|
|
71073
71356
|
//#region src/deps.ts
|
|
71074
71357
|
const NO_IDENTITY = async () => void 0;
|