@objectstack/plugin-approvals 17.0.0 → 17.1.0
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/CHANGELOG.md +192 -0
- package/dist/index.d.mts +2775 -624
- package/dist/index.d.ts +2775 -624
- package/dist/index.js +215 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +215 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -13
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/translations/en.objects.generated.ts","../src/translations/zh-CN.objects.generated.ts","../src/translations/ja-JP.objects.generated.ts","../src/translations/es-ES.objects.generated.ts","../src/translations/index.ts","../src/sys-approval-request.object.ts","../src/sys-approval-action.object.ts","../src/sys-approval-approver.object.ts","../src/sys-approval-delegation.object.ts","../src/approval-service.ts","../src/approver-org-scope.ts","../src/sys-approval-token.object.ts","../src/action-link-pages.ts","../src/lifecycle-hooks.ts","../src/approval-node.ts","../src/approval-revise-node.ts","../src/approvals-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'en'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const enObjects: NonNullable<TranslationData['objects']> = {\n sys_approval_request: {\n label: \"Approval Request\",\n pluralLabel: \"Approval Requests\",\n description: \"Live approval instance tracked per submission\",\n fields: {\n id: {\n label: \"Request ID\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Tenant that owns this approval request (propagated from submitter context)\"\n },\n process_name: {\n label: \"Source\",\n help: \"Origin of the request — `flow:<flowName|nodeId>` for node-driven approvals\"\n },\n object_name: {\n label: \"Object\"\n },\n record_id: {\n label: \"Record ID\"\n },\n submitter_id: {\n label: \"Submitter\"\n },\n submitter_comment: {\n label: \"Submitter Comment\"\n },\n status: {\n label: \"Status\",\n help: \"Lifecycle state of the request\",\n options: {\n pending: \"Pending\",\n approved: \"Approved\",\n rejected: \"Rejected\",\n recalled: \"Recalled\",\n returned: \"Returned\"\n }\n },\n current_step: {\n label: \"Current Step\",\n help: \"Machine name of the step awaiting approval\"\n },\n current_step_index: {\n label: \"Current Step Index\"\n },\n pending_approvers: {\n label: \"Pending Approvers\",\n help: \"Comma-separated user ids who can act on the current step\"\n },\n payload_json: {\n label: \"Snapshot\",\n help: \"Record snapshot at submission time\"\n },\n flow_run_id: {\n label: \"Flow Run\",\n help: \"Suspended automation run id this request gates (ADR-0019). The decision resumes it.\"\n },\n flow_node_id: {\n label: \"Flow Node\",\n help: \"Approval node id within the flow that opened this request (ADR-0019).\"\n },\n node_config_json: {\n label: \"Node Config\",\n help: \"Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).\"\n },\n completed_at: {\n label: \"Completed At\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n },\n _views: {\n my_pending: {\n label: \"My Pending\",\n emptyState: {\n title: \"No pending approvals\",\n message: \"You're all caught up.\"\n }\n },\n submitted_by_me: {\n label: \"I Submitted\"\n },\n completed: {\n label: \"Completed\"\n },\n all_requests: {\n label: \"All\"\n }\n },\n _actions: {\n approval_approve: {\n label: \"Approve\",\n successMessage: \"Approved.\",\n params: {\n comment: {\n label: \"Comment\"\n },\n attachments: {\n label: \"Attachments\"\n }\n }\n },\n approval_reject: {\n label: \"Reject\",\n description: \"Reject this request? A rejection is final for every approver.\",\n successMessage: \"Rejected.\",\n params: {\n comment: {\n label: \"Comment\"\n },\n attachments: {\n label: \"Attachments\"\n }\n }\n },\n approval_reassign: {\n label: \"Reassign\",\n successMessage: \"Reassigned.\",\n params: {\n to: {\n label: \"New approver\",\n helpText: \"User to hand this step to\"\n },\n comment: {\n label: \"Comment\"\n }\n }\n },\n approval_send_back: {\n label: \"Send back\",\n successMessage: \"Sent back for revision.\",\n params: {\n comment: {\n label: \"Reason\"\n }\n }\n },\n approval_request_info: {\n label: \"Request info\",\n successMessage: \"Information requested.\",\n params: {\n comment: {\n label: \"What do you need?\"\n }\n }\n },\n approval_remind: {\n label: \"Send reminder\",\n successMessage: \"Reminder sent.\",\n params: {\n comment: {\n label: \"Note\"\n }\n }\n },\n approval_recall: {\n label: \"Recall\",\n description: \"Recall this request? Approvers can no longer act on it and the record is unlocked.\",\n successMessage: \"Recalled.\",\n params: {\n comment: {\n label: \"Comment\"\n }\n }\n },\n approval_resubmit: {\n label: \"Resubmit\",\n successMessage: \"Resubmitted.\",\n params: {\n comment: {\n label: \"What changed?\"\n }\n }\n }\n }\n },\n sys_approval_action: {\n label: \"Approval Action\",\n pluralLabel: \"Approval Actions\",\n description: \"Append-only audit trail for approval actions\",\n fields: {\n id: {\n label: \"Action ID\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Tenant that owns this action (mirrors the parent request)\"\n },\n request_id: {\n label: \"Request\"\n },\n step_name: {\n label: \"Step\",\n help: \"Machine name of the step at the time of the action\"\n },\n step_index: {\n label: \"Step Index\"\n },\n action: {\n label: \"Action\",\n options: {\n submit: \"Submit\",\n approve: \"Approve\",\n reject: \"Reject\",\n recall: \"Recall\",\n escalate: \"Escalate\",\n reassign: \"Reassign\",\n remind: \"Remind\",\n request_info: \"Request Info\",\n comment: \"Comment\",\n revise: \"Revise\",\n resubmit: \"Resubmit\",\n ooo_substitute: \"Out-of-Office Substitution\"\n }\n },\n actor_id: {\n label: \"Actor\"\n },\n comment: {\n label: \"Comment\"\n },\n via_override: {\n label: \"Via Admin Override\",\n help: \"True when the actor was admitted to this action only by the privileged-override path (#3424) — they held no slot in the request’s pending-approver slate.\"\n },\n reassign_from: {\n label: \"Reassigned From\",\n help: \"User whose pending-approver slot was handed over (reassign actions only)\"\n },\n reassign_to: {\n label: \"Reassigned To\",\n help: \"User who received the pending-approver slot (reassign actions only)\"\n },\n attachments: {\n label: \"Attachments\",\n help: \"Files supporting this action — e.g. a signed contract or evidence (#3266).\"\n },\n created_at: {\n label: \"Created At\"\n }\n },\n _views: {\n recent: {\n label: \"Recent\",\n emptyState: {\n title: \"No approval actions yet\",\n message: \"Actions are logged automatically when approvals progress.\"\n }\n },\n by_actor: {\n label: \"By Actor\"\n },\n all_actions: {\n label: \"All\"\n }\n }\n },\n sys_approval_delegation: {\n label: \"Approval Delegation\",\n pluralLabel: \"Approval Delegations\",\n description: \"Self-service out-of-office rule: route this user's approver slots to a delegate within a time window (#1322 M1).\",\n fields: {\n id: {\n label: \"Delegation ID\"\n },\n delegator_id: {\n label: \"Delegator\",\n help: \"The user going out of office; their individually-routed approver slots are rerouted while active.\"\n },\n delegate_id: {\n label: \"Delegate\",\n help: \"The backup who receives the delegator's approvals while this rule is active. Acts under their own identity.\"\n },\n valid_from: {\n label: \"Valid From\",\n help: \"Rule is inactive before this instant. Null = active immediately. Enforced at resolution time via isGrantActive (ADR-0091 D2 predicate) — never by a background job.\"\n },\n valid_until: {\n label: \"Valid Until\",\n help: \"Rule is inactive AT and AFTER this instant (half-open [from, until), UTC). Null = never expires.\"\n },\n reason: {\n label: \"Reason\",\n help: \"Why the delegation exists (e.g. \\\"Annual leave 5/26–5/30\\\"). Recorded on the substitution audit row.\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Tenant that owns this rule; null = applies across tenants for this delegator.\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n },\n _views: {\n active: {\n label: \"Active\",\n emptyState: {\n title: \"No delegations\",\n message: \"Declare an out-of-office delegation so approvals route to a backup while you are away.\"\n }\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'zh-CN'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const zhCNObjects: NonNullable<TranslationData['objects']> = {\n sys_approval_request: {\n label: \"审批请求\",\n pluralLabel: \"审批请求\",\n description: \"按提交记录跟踪的实时审批实例\",\n fields: {\n id: {\n label: \"请求 ID\"\n },\n organization_id: {\n label: \"组织\",\n help: \"拥有该审批请求的租户(从提交方上下文传播)\"\n },\n process_name: {\n label: \"来源\",\n help: \"请求来源 —— 节点驱动的审批为 `flow:<flowName|nodeId>`\"\n },\n object_name: {\n label: \"对象\"\n },\n record_id: {\n label: \"记录 ID\"\n },\n submitter_id: {\n label: \"提交人\"\n },\n submitter_comment: {\n label: \"提交备注\"\n },\n status: {\n label: \"状态\",\n help: \"请求的生命周期状态\",\n options: {\n pending: \"待审批\",\n approved: \"已批准\",\n rejected: \"已拒绝\",\n recalled: \"已撤回\",\n returned: \"已退回修改\"\n }\n },\n current_step: {\n label: \"当前步骤\",\n help: \"当前等待审批的步骤机器名称\"\n },\n current_step_index: {\n label: \"当前步骤索引\"\n },\n pending_approvers: {\n label: \"待审批人\",\n help: \"可在当前步骤执行操作的用户 ID,逗号分隔\"\n },\n payload_json: {\n label: \"快照\",\n help: \"提交时的记录快照\"\n },\n flow_run_id: {\n label: \"Flow Run\",\n help: \"Suspended automation run id this request gates (ADR-0019). The decision resumes it.\"\n },\n flow_node_id: {\n label: \"Flow Node\",\n help: \"Approval node id within the flow that opened this request (ADR-0019).\"\n },\n node_config_json: {\n label: \"Node Config\",\n help: \"Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).\"\n },\n completed_at: {\n label: \"完成时间\"\n },\n created_at: {\n label: \"创建时间\"\n },\n updated_at: {\n label: \"更新时间\"\n }\n },\n _views: {\n my_pending: {\n label: \"待我审批\",\n emptyState: {\n title: \"暂无待审批的请求\",\n message: \"全部处理完毕,没有等待你审批的请求。\"\n }\n },\n submitted_by_me: {\n label: \"我提交的\"\n },\n completed: {\n label: \"已完成\"\n },\n all_requests: {\n label: \"全部\"\n }\n },\n _actions: {\n approval_approve: {\n label: \"通过\",\n successMessage: \"已通过。\",\n params: {\n comment: {\n label: \"审批意见\"\n },\n attachments: {\n label: \"附件\"\n }\n }\n },\n approval_reject: {\n label: \"拒绝\",\n description: \"拒绝该请求?拒绝对所有审批人立即生效。\",\n successMessage: \"已拒绝。\",\n params: {\n comment: {\n label: \"审批意见\"\n },\n attachments: {\n label: \"附件\"\n }\n }\n },\n approval_reassign: {\n label: \"转签\",\n successMessage: \"已转签。\",\n params: {\n to: {\n label: \"新审批人\",\n helpText: \"将此审批步骤转交给的用户\"\n },\n comment: {\n label: \"备注\"\n }\n }\n },\n approval_send_back: {\n label: \"退回修改\",\n successMessage: \"已退回修改。\",\n params: {\n comment: {\n label: \"原因\"\n }\n }\n },\n approval_request_info: {\n label: \"退回补充\",\n successMessage: \"已退回给申请人补充材料。\",\n params: {\n comment: {\n label: \"需要申请人补充什么?\"\n }\n }\n },\n approval_remind: {\n label: \"催办\",\n successMessage: \"已发送催办。\",\n params: {\n comment: {\n label: \"备注\"\n }\n }\n },\n approval_recall: {\n label: \"撤回\",\n description: \"撤回该请求?撤回后审批人将无法继续处理,记录随即解锁。\",\n successMessage: \"已撤回。\",\n params: {\n comment: {\n label: \"备注\"\n }\n }\n },\n approval_resubmit: {\n label: \"重新提交\",\n successMessage: \"已重新提交。\",\n params: {\n comment: {\n label: \"你修改了什么?\"\n }\n }\n }\n }\n },\n sys_approval_action: {\n label: \"审批动作\",\n pluralLabel: \"审批动作\",\n description: \"追加写入的审批操作审计记录\",\n fields: {\n id: {\n label: \"动作 ID\"\n },\n organization_id: {\n label: \"组织\",\n help: \"拥有该动作的租户(与父请求保持一致)\"\n },\n request_id: {\n label: \"请求\"\n },\n step_name: {\n label: \"步骤\",\n help: \"执行该动作时对应步骤的机器名称\"\n },\n step_index: {\n label: \"步骤索引\"\n },\n action: {\n label: \"操作\",\n options: {\n submit: \"提交\",\n approve: \"批准\",\n reject: \"拒绝\",\n recall: \"撤回\",\n escalate: \"升级\",\n reassign: \"转签\",\n remind: \"催办\",\n request_info: \"要求补充材料\",\n comment: \"评论\",\n revise: \"退回修改\",\n resubmit: \"重新提交\",\n ooo_substitute: \"不在岗改派\"\n }\n },\n actor_id: {\n label: \"执行人\"\n },\n comment: {\n label: \"评论\"\n },\n via_override: {\n label: \"管理员越权操作\",\n help: \"为真表示该操作者只是凭特权越权路径(#3424)被放行——他们并不在该请求的待审批人名单中。\"\n },\n reassign_from: {\n label: \"转出人\",\n help: \"被移交待审批槽位的用户(仅转签操作)\"\n },\n reassign_to: {\n label: \"转入人\",\n help: \"接收待审批槽位的用户(仅转签操作)\"\n },\n attachments: {\n label: \"附件\",\n help: \"支持该操作的文件——例如已签署的合同或证明材料(#3266)。\"\n },\n created_at: {\n label: \"创建时间\"\n }\n },\n _views: {\n recent: {\n label: \"最近\",\n emptyState: {\n title: \"暂无审批操作记录\",\n message: \"审批推进时会自动记录操作。\"\n }\n },\n by_actor: {\n label: \"按执行人\"\n },\n all_actions: {\n label: \"全部\"\n }\n }\n },\n sys_approval_delegation: {\n label: \"审批委派\",\n pluralLabel: \"审批委派\",\n description: \"自助不在岗规则:在时间窗内把该用户的审批人槽位改派给候补人(#1322 M1)。\",\n fields: {\n id: {\n label: \"委派 ID\"\n },\n delegator_id: {\n label: \"委派人\",\n help: \"即将不在岗的用户;规则生效期间,路由到其个人的审批人槽位将被改派。\"\n },\n delegate_id: {\n label: \"候补人\",\n help: \"规则生效期间接收委派人审批的候补人。以其本人身份行使。\"\n },\n valid_from: {\n label: \"生效时间\",\n help: \"此刻之前规则不生效。留空 = 立即生效。在解析时通过 isGrantActive 强制(ADR-0091 D2 判定式)——绝不依赖后台任务。\"\n },\n valid_until: {\n label: \"失效时间\",\n help: \"此刻(含)及之后规则不生效(半开区间 [from, until),UTC)。留空 = 永不过期。\"\n },\n reason: {\n label: \"原因\",\n help: \"委派存在的原因(例如“年假 5/26–5/30”)。会记入改派审计行。\"\n },\n organization_id: {\n label: \"组织\",\n help: \"拥有此规则的租户;留空 = 对该委派人跨租户生效。\"\n },\n created_at: {\n label: \"创建时间\"\n },\n updated_at: {\n label: \"更新时间\"\n }\n },\n _views: {\n active: {\n label: \"生效中\",\n emptyState: {\n title: \"暂无委派\",\n message: \"声明一条不在岗委派,休假期间审批自动改派给候补人。\"\n }\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'ja-JP'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const jaJPObjects: NonNullable<TranslationData['objects']> = {\n sys_approval_request: {\n label: \"承認リクエスト\",\n pluralLabel: \"承認リクエスト\",\n description: \"送信ごとに追跡されるライブ承認インスタンス\",\n fields: {\n id: {\n label: \"リクエスト ID\"\n },\n organization_id: {\n label: \"組織\",\n help: \"この承認リクエストを所有するテナント(送信者コンテキストから伝播)\"\n },\n process_name: {\n label: \"ソース\",\n help: \"リクエストの発生元 — ノード駆動の承認では `flow:<flowName|nodeId>`\"\n },\n object_name: {\n label: \"オブジェクト\"\n },\n record_id: {\n label: \"レコード ID\"\n },\n submitter_id: {\n label: \"送信者\"\n },\n submitter_comment: {\n label: \"送信者コメント\"\n },\n status: {\n label: \"ステータス\",\n help: \"リクエストのライフサイクル状態\",\n options: {\n pending: \"保留中\",\n approved: \"承認済み\",\n rejected: \"却下済み\",\n recalled: \"取り消し済み\",\n returned: \"差し戻し済み\"\n }\n },\n current_step: {\n label: \"現在のステップ\",\n help: \"承認待ちのステップの機械名\"\n },\n current_step_index: {\n label: \"現在のステップ番号\"\n },\n pending_approvers: {\n label: \"承認待ち承認者\",\n help: \"現在のステップを処理できるユーザー ID のカンマ区切りリスト\"\n },\n payload_json: {\n label: \"スナップショット\",\n help: \"送信時のレコードスナップショット\"\n },\n flow_run_id: {\n label: \"Flow Run\",\n help: \"Suspended automation run id this request gates (ADR-0019). The decision resumes it.\"\n },\n flow_node_id: {\n label: \"Flow Node\",\n help: \"Approval node id within the flow that opened this request (ADR-0019).\"\n },\n node_config_json: {\n label: \"Node Config\",\n help: \"Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).\"\n },\n completed_at: {\n label: \"完了日時\"\n },\n created_at: {\n label: \"作成日時\"\n },\n updated_at: {\n label: \"更新日時\"\n }\n },\n _views: {\n my_pending: {\n label: \"承認待ち\",\n emptyState: {\n title: \"承認待ちはありません\",\n message: \"すべて処理済みです。あなたの承認を待つリクエストはありません。\"\n }\n },\n submitted_by_me: {\n label: \"自分が送信\"\n },\n completed: {\n label: \"完了済み\"\n },\n all_requests: {\n label: \"すべて\"\n }\n },\n _actions: {\n approval_approve: {\n label: \"承認\",\n successMessage: \"承認しました。\",\n params: {\n comment: {\n label: \"コメント\"\n },\n attachments: {\n label: \"添付ファイル\"\n }\n }\n },\n approval_reject: {\n label: \"却下\",\n description: \"このリクエストを却下しますか?却下はすべての承認者に対して最終決定になります。\",\n successMessage: \"却下しました。\",\n params: {\n comment: {\n label: \"コメント\"\n },\n attachments: {\n label: \"添付ファイル\"\n }\n }\n },\n approval_reassign: {\n label: \"再割り当て\",\n successMessage: \"再割り当てしました。\",\n params: {\n to: {\n label: \"新しい承認者\",\n helpText: \"このステップを引き継ぐユーザー\"\n },\n comment: {\n label: \"コメント\"\n }\n }\n },\n approval_send_back: {\n label: \"差し戻し\",\n successMessage: \"修正のため差し戻しました。\",\n params: {\n comment: {\n label: \"理由\"\n }\n }\n },\n approval_request_info: {\n label: \"追加情報を依頼\",\n successMessage: \"追加情報を依頼しました。\",\n params: {\n comment: {\n label: \"何が必要ですか?\"\n }\n }\n },\n approval_remind: {\n label: \"リマインダー送信\",\n successMessage: \"リマインダーを送信しました。\",\n params: {\n comment: {\n label: \"メモ\"\n }\n }\n },\n approval_recall: {\n label: \"取り下げ\",\n description: \"このリクエストを取り下げますか?承認者は操作できなくなり、レコードのロックが解除されます。\",\n successMessage: \"取り下げました。\",\n params: {\n comment: {\n label: \"コメント\"\n }\n }\n },\n approval_resubmit: {\n label: \"再提出\",\n successMessage: \"再提出しました。\",\n params: {\n comment: {\n label: \"何を変更しましたか?\"\n }\n }\n }\n }\n },\n sys_approval_action: {\n label: \"承認アクション\",\n pluralLabel: \"承認アクション\",\n description: \"承認アクションの追記専用監査証跡\",\n fields: {\n id: {\n label: \"アクション ID\"\n },\n organization_id: {\n label: \"組織\",\n help: \"このアクションを所有するテナント(親リクエストと同じ)\"\n },\n request_id: {\n label: \"リクエスト\"\n },\n step_name: {\n label: \"ステップ\",\n help: \"アクション時点のステップの機械名\"\n },\n step_index: {\n label: \"ステップ番号\"\n },\n action: {\n label: \"アクション\",\n options: {\n submit: \"申請\",\n approve: \"承認\",\n reject: \"却下\",\n recall: \"取消\",\n escalate: \"エスカレーション\",\n reassign: \"再割り当て\",\n remind: \"リマインダー\",\n request_info: \"追加情報依頼\",\n comment: \"コメント\",\n revise: \"差し戻し\",\n resubmit: \"再提出\",\n ooo_substitute: \"不在時代理\"\n }\n },\n actor_id: {\n label: \"操作者\"\n },\n comment: {\n label: \"コメント\"\n },\n via_override: {\n label: \"管理者オーバーライド経由\",\n help: \"true の場合、実行者は特権オーバーライド経路(#3424)によってのみ許可されたことを示します — 当該リクエストの承認待ちリストには含まれていません。\"\n },\n reassign_from: {\n label: \"引き継ぎ元\",\n help: \"承認待ちスロットを引き渡したユーザー(引き継ぎ操作のみ)\"\n },\n reassign_to: {\n label: \"引き継ぎ先\",\n help: \"承認待ちスロットを受け取ったユーザー(引き継ぎ操作のみ)\"\n },\n attachments: {\n label: \"添付ファイル\",\n help: \"この操作を裏付けるファイル——署名済み契約書や証憑など(#3266)。\"\n },\n created_at: {\n label: \"作成日時\"\n }\n },\n _views: {\n recent: {\n label: \"最近\",\n emptyState: {\n title: \"承認操作はまだありません\",\n message: \"承認が進むと操作は自動的に記録されます。\"\n }\n },\n by_actor: {\n label: \"操作者別\"\n },\n all_actions: {\n label: \"すべて\"\n }\n }\n },\n sys_approval_delegation: {\n label: \"Approval Delegation\",\n pluralLabel: \"Approval Delegations\",\n description: \"Self-service out-of-office rule: route this user's approver slots to a delegate within a time window (#1322 M1).\",\n fields: {\n id: {\n label: \"Delegation ID\"\n },\n delegator_id: {\n label: \"Delegator\",\n help: \"The user going out of office; their individually-routed approver slots are rerouted while active.\"\n },\n delegate_id: {\n label: \"Delegate\",\n help: \"The backup who receives the delegator's approvals while this rule is active. Acts under their own identity.\"\n },\n valid_from: {\n label: \"Valid From\",\n help: \"Rule is inactive before this instant. Null = active immediately. Enforced at resolution time via isGrantActive (ADR-0091 D2 predicate) — never by a background job.\"\n },\n valid_until: {\n label: \"Valid Until\",\n help: \"Rule is inactive AT and AFTER this instant (half-open [from, until), UTC). Null = never expires.\"\n },\n reason: {\n label: \"Reason\",\n help: \"Why the delegation exists (e.g. \\\"Annual leave 5/26–5/30\\\"). Recorded on the substitution audit row.\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Tenant that owns this rule; null = applies across tenants for this delegator.\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n },\n _views: {\n active: {\n label: \"Active\",\n emptyState: {\n title: \"No delegations\",\n message: \"Declare an out-of-office delegation so approvals route to a backup while you are away.\"\n }\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'es-ES'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const esESObjects: NonNullable<TranslationData['objects']> = {\n sys_approval_request: {\n label: \"Solicitud de aprobación\",\n pluralLabel: \"Solicitudes de aprobación\",\n description: \"Instancia activa de aprobación registrada por envío\",\n fields: {\n id: {\n label: \"ID de solicitud\"\n },\n organization_id: {\n label: \"Organización\",\n help: \"Tenant que posee esta solicitud de aprobación (propagado desde el contexto del solicitante).\"\n },\n process_name: {\n label: \"Origen\",\n help: \"Origen de la solicitud — `flow:<flowName|nodeId>` para aprobaciones por nodo\"\n },\n object_name: {\n label: \"Objeto\"\n },\n record_id: {\n label: \"ID de registro\"\n },\n submitter_id: {\n label: \"Solicitante\"\n },\n submitter_comment: {\n label: \"Comentario del solicitante\"\n },\n status: {\n label: \"Estado\",\n help: \"Estado del ciclo de vida de la solicitud.\",\n options: {\n pending: \"Pendiente\",\n approved: \"Aprobada\",\n rejected: \"Rechazada\",\n recalled: \"Retirada\",\n returned: \"Devuelta para revisión\"\n }\n },\n current_step: {\n label: \"Paso actual\",\n help: \"Nombre técnico del paso pendiente de aprobación.\"\n },\n current_step_index: {\n label: \"Índice del paso actual\"\n },\n pending_approvers: {\n label: \"Aprobadores pendientes\",\n help: \"ID de usuario separados por comas que pueden actuar en el paso actual.\"\n },\n payload_json: {\n label: \"Instantánea\",\n help: \"Instantánea del registro en el momento del envío.\"\n },\n flow_run_id: {\n label: \"Flow Run\",\n help: \"Suspended automation run id this request gates (ADR-0019). The decision resumes it.\"\n },\n flow_node_id: {\n label: \"Flow Node\",\n help: \"Approval node id within the flow that opened this request (ADR-0019).\"\n },\n node_config_json: {\n label: \"Node Config\",\n help: \"Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).\"\n },\n completed_at: {\n label: \"Completado el\"\n },\n created_at: {\n label: \"Creado el\"\n },\n updated_at: {\n label: \"Actualizado el\"\n }\n },\n _views: {\n my_pending: {\n label: \"Aprobaciones pendientes\",\n emptyState: {\n title: \"Sin aprobaciones pendientes\",\n message: \"Estás al día: nada espera tu aprobación.\"\n }\n },\n submitted_by_me: {\n label: \"Enviadas por mí\"\n },\n completed: {\n label: \"Completadas\"\n },\n all_requests: {\n label: \"Todas\"\n }\n },\n _actions: {\n approval_approve: {\n label: \"Aprobar\",\n successMessage: \"Aprobada.\",\n params: {\n comment: {\n label: \"Comentario\"\n },\n attachments: {\n label: \"Adjuntos\"\n }\n }\n },\n approval_reject: {\n label: \"Rechazar\",\n description: \"¿Rechazar esta solicitud? Un rechazo es definitivo para todos los aprobadores.\",\n successMessage: \"Rechazada.\",\n params: {\n comment: {\n label: \"Comentario\"\n },\n attachments: {\n label: \"Adjuntos\"\n }\n }\n },\n approval_reassign: {\n label: \"Reasignar\",\n successMessage: \"Reasignada.\",\n params: {\n to: {\n label: \"Nuevo aprobador\",\n helpText: \"Usuario al que se entrega este paso\"\n },\n comment: {\n label: \"Comentario\"\n }\n }\n },\n approval_send_back: {\n label: \"Devolver\",\n successMessage: \"Devuelta para revisión.\",\n params: {\n comment: {\n label: \"Motivo\"\n }\n }\n },\n approval_request_info: {\n label: \"Pedir información\",\n successMessage: \"Información solicitada.\",\n params: {\n comment: {\n label: \"¿Qué necesitas?\"\n }\n }\n },\n approval_remind: {\n label: \"Enviar recordatorio\",\n successMessage: \"Recordatorio enviado.\",\n params: {\n comment: {\n label: \"Nota\"\n }\n }\n },\n approval_recall: {\n label: \"Retirar\",\n description: \"¿Retirar esta solicitud? Los aprobadores ya no podrán actuar sobre ella y el registro se desbloqueará.\",\n successMessage: \"Retirada.\",\n params: {\n comment: {\n label: \"Comentario\"\n }\n }\n },\n approval_resubmit: {\n label: \"Reenviar\",\n successMessage: \"Reenviada.\",\n params: {\n comment: {\n label: \"¿Qué ha cambiado?\"\n }\n }\n }\n }\n },\n sys_approval_action: {\n label: \"Acción de aprobación\",\n pluralLabel: \"Acciones de aprobación\",\n description: \"Registro de auditoría append-only para acciones de aprobación\",\n fields: {\n id: {\n label: \"ID de acción\"\n },\n organization_id: {\n label: \"Organización\",\n help: \"Tenant que posee esta acción (refleja la solicitud principal).\"\n },\n request_id: {\n label: \"Solicitud\"\n },\n step_name: {\n label: \"Paso\",\n help: \"Nombre técnico del paso en el momento de la acción.\"\n },\n step_index: {\n label: \"Índice del paso\"\n },\n action: {\n label: \"Acción\",\n options: {\n submit: \"Enviar\",\n approve: \"Aprobar\",\n reject: \"Rechazar\",\n recall: \"Retirar\",\n escalate: \"Escalar\",\n reassign: \"Reasignación\",\n remind: \"Recordatorio\",\n request_info: \"Petición de información\",\n comment: \"Comentario\",\n revise: \"Devolución\",\n resubmit: \"Reenvío\",\n ooo_substitute: \"Sustitución por ausencia\"\n }\n },\n actor_id: {\n label: \"Actor\"\n },\n comment: {\n label: \"Comentario\"\n },\n via_override: {\n label: \"Mediante anulación de administrador\",\n help: \"Verdadero cuando el actor fue admitido en esta acción únicamente por la vía de anulación privilegiada (#3424): no ocupaba ningún puesto en la lista de aprobadores pendientes de la solicitud.\"\n },\n reassign_from: {\n label: \"Reasignado de\",\n help: \"Usuario cuyo turno de aprobación pendiente fue traspasado (solo acciones de reasignación)\"\n },\n reassign_to: {\n label: \"Reasignado a\",\n help: \"Usuario que recibió el turno de aprobación pendiente (solo acciones de reasignación)\"\n },\n attachments: {\n label: \"Adjuntos\",\n help: \"Archivos que respaldan esta acción, p. ej. un contrato firmado o pruebas (#3266).\"\n },\n created_at: {\n label: \"Creado el\"\n }\n },\n _views: {\n recent: {\n label: \"Recientes\",\n emptyState: {\n title: \"Aún no hay acciones de aprobación\",\n message: \"Las acciones se registran automáticamente a medida que avanza la aprobación.\"\n }\n },\n by_actor: {\n label: \"Por actor\"\n },\n all_actions: {\n label: \"Todas\"\n }\n }\n },\n sys_approval_delegation: {\n label: \"Approval Delegation\",\n pluralLabel: \"Approval Delegations\",\n description: \"Self-service out-of-office rule: route this user's approver slots to a delegate within a time window (#1322 M1).\",\n fields: {\n id: {\n label: \"Delegation ID\"\n },\n delegator_id: {\n label: \"Delegator\",\n help: \"The user going out of office; their individually-routed approver slots are rerouted while active.\"\n },\n delegate_id: {\n label: \"Delegate\",\n help: \"The backup who receives the delegator's approvals while this rule is active. Acts under their own identity.\"\n },\n valid_from: {\n label: \"Valid From\",\n help: \"Rule is inactive before this instant. Null = active immediately. Enforced at resolution time via isGrantActive (ADR-0091 D2 predicate) — never by a background job.\"\n },\n valid_until: {\n label: \"Valid Until\",\n help: \"Rule is inactive AT and AFTER this instant (half-open [from, until), UTC). Null = never expires.\"\n },\n reason: {\n label: \"Reason\",\n help: \"Why the delegation exists (e.g. \\\"Annual leave 5/26–5/30\\\"). Recorded on the substitution audit row.\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Tenant that owns this rule; null = applies across tenants for this delegator.\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n },\n _views: {\n active: {\n label: \"Active\",\n emptyState: {\n title: \"No delegations\",\n message: \"Declare an out-of-office delegation so approvals route to a backup while you are away.\"\n }\n }\n }\n }\n};\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * ApprovalsTranslations — i18n bundle owned by this plugin (ADR-0029 D8).\n *\n * Object label/field/view/action translations for the sys_* objects this\n * plugin owns. Loaded at runtime via the plugin's `kernel:ready` hook\n * (`i18n.loadTranslations`). Regenerate with `os i18n extract` against\n * `scripts/i18n-extract.config.ts`.\n */\n\nimport type { TranslationBundle } from '@objectstack/spec/system';\nimport { enObjects } from './en.objects.generated.js';\nimport { zhCNObjects } from './zh-CN.objects.generated.js';\nimport { jaJPObjects } from './ja-JP.objects.generated.js';\nimport { esESObjects } from './es-ES.objects.generated.js';\n\nexport const ApprovalsTranslations: TranslationBundle = {\n en: { objects: enObjects },\n 'zh-CN': { objects: zhCNObjects },\n 'ja-JP': { objects: jaJPObjects },\n 'es-ES': { objects: esESObjects },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\nimport { APPROVAL_STATUSES, APPROVAL_STATUS_LABELS } from '@objectstack/spec/contracts';\n\n/**\n * sys_approval_request — Live approval instance.\n *\n * ADR-0019: opened by a flow's **Approval node** when the run reaches it; the\n * run suspends until a decision is recorded. The row's lifecycle:\n *\n * `pending` → (per-approver decisions) → `approved` | `rejected`\n * `pending` → recalled by submitter → `recalled`\n *\n * `flow_run_id` / `flow_node_id` tie the request back to the suspended run so a\n * decision can resume it; `current_step` mirrors the node id. `node_config_json`\n * snapshots the Approval node config (approvers / behaviour) the request was\n * opened with.\n *\n * `payload_json` captures a snapshot of the target record at submission\n * time — used by notifications so they can render before the record is\n * locked or changed.\n *\n * @namespace sys\n */\nexport const SysApprovalRequest = ObjectSchema.create({\n name: 'sys_approval_request',\n label: 'Approval Request',\n pluralLabel: 'Approval Requests',\n icon: 'inbox',\n isSystem: true,\n managedBy: 'engine-owned',\n description: 'Live approval instance tracked per submission',\n displayNameField: 'id',\n nameField: 'id', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)\n titleFormat: '{process_name} · {record_id}',\n highlightFields: ['process_name', 'object_name', 'record_id', 'status', 'current_step', 'submitter_id', 'updated_at'],\n\n // Curated built-in list views — render as segmented tabs in the console.\n // Filters use {current_user_id} substitution wired by the console.\n listViews: {\n my_pending: {\n type: 'grid',\n name: 'my_pending',\n label: 'My Pending',\n data: { provider: 'object', object: 'sys_approval_request' },\n columns: ['process_name', 'object_name', 'record_id', 'current_step', 'submitter_id', 'updated_at'],\n filter: [\n { field: 'status', operator: 'equals', value: 'pending' },\n { field: 'pending_approvers', operator: 'contains', value: '{current_user_id}' },\n ],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 25 },\n emptyState: { title: 'No pending approvals', message: 'You\\'re all caught up.' },\n },\n submitted_by_me: {\n type: 'grid',\n name: 'submitted_by_me',\n label: 'I Submitted',\n data: { provider: 'object', object: 'sys_approval_request' },\n columns: ['process_name', 'object_name', 'record_id', 'status', 'current_step', 'updated_at'],\n filter: [{ field: 'submitter_id', operator: 'equals', value: '{current_user_id}' }],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 25 },\n },\n completed: {\n type: 'grid',\n name: 'completed',\n label: 'Completed',\n data: { provider: 'object', object: 'sys_approval_request' },\n columns: ['process_name', 'object_name', 'record_id', 'status', 'submitter_id', 'completed_at'],\n filter: [{ field: 'status', operator: 'in', value: ['approved', 'rejected', 'recalled'] }],\n sort: [{ field: 'completed_at', order: 'desc' }],\n pagination: { pageSize: 25 },\n },\n all_requests: {\n type: 'grid',\n name: 'all_requests',\n label: 'All',\n data: { provider: 'object', object: 'sys_approval_request' },\n columns: ['process_name', 'object_name', 'record_id', 'status', 'current_step', 'submitter_id', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n },\n\n fields: {\n id: Field.text({ label: 'Request ID', required: true, readonly: true, group: 'System' }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n description: 'Tenant that owns this approval request (propagated from submitter context)',\n }),\n\n process_name: Field.text({\n label: 'Source',\n required: true,\n maxLength: 100,\n description: 'Origin of the request — `flow:<flowName|nodeId>` for node-driven approvals',\n group: 'Target',\n }),\n\n object_name: Field.text({\n label: 'Object',\n required: true,\n maxLength: 100,\n group: 'Target',\n }),\n\n record_id: Field.text({\n label: 'Record ID',\n required: true,\n maxLength: 100,\n group: 'Target',\n }),\n\n submitter_id: Field.lookup('sys_user', {\n label: 'Submitter',\n required: false,\n group: 'Target',\n }),\n\n submitter_comment: Field.textarea({\n label: 'Submitter Comment',\n required: false,\n group: 'Target',\n }),\n\n status: Field.select(\n // Derived from the contract, not re-typed (#3786). `APPROVAL_STATUSES` is\n // where the list and the reason for each entry live; `ApprovalStatus` is\n // derived from it, so this column and the contract cannot disagree. The\n // authored English label per entry lives beside it in\n // `APPROVAL_STATUS_LABELS` (#8543) — mapped here, never re-typed, so the\n // `en` bundle regenerates from the contract's own text.\n APPROVAL_STATUSES.map((value) => ({ value, label: APPROVAL_STATUS_LABELS[value] })),\n {\n label: 'Status',\n required: true,\n defaultValue: 'pending',\n description: 'Lifecycle state of the request',\n group: 'State',\n },\n ),\n\n current_step: Field.text({\n label: 'Current Step',\n required: false,\n maxLength: 100,\n description: 'Machine name of the step awaiting approval',\n group: 'State',\n }),\n\n current_step_index: Field.number({\n label: 'Current Step Index',\n required: false,\n defaultValue: 0,\n group: 'State',\n }),\n\n pending_approvers: Field.textarea({\n label: 'Pending Approvers',\n required: false,\n description: 'Comma-separated user ids who can act on the current step',\n group: 'State',\n }),\n\n payload_json: Field.textarea({\n label: 'Snapshot',\n required: false,\n description: 'Record snapshot at submission time',\n group: 'State',\n }),\n\n // ── ADR-0019: approval-as-flow-node correlation ──────────────────\n // When a request is opened by an Approval *node* (rather than a standalone\n // process), these tie it back to the suspended flow run so a decision can\n // resume it. Null for legacy process-driven requests.\n flow_run_id: Field.text({\n label: 'Flow Run',\n required: false,\n maxLength: 100,\n readonly: true,\n description: 'Suspended automation run id this request gates (ADR-0019). The decision resumes it.',\n group: 'State',\n }),\n\n flow_node_id: Field.text({\n label: 'Flow Node',\n required: false,\n maxLength: 100,\n readonly: true,\n description: 'Approval node id within the flow that opened this request (ADR-0019).',\n group: 'State',\n }),\n\n node_config_json: Field.textarea({\n label: 'Node Config',\n required: false,\n readonly: true,\n description: 'Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).',\n group: 'State',\n }),\n\n completed_at: Field.datetime({\n label: 'Completed At',\n required: false,\n group: 'State',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({ label: 'Updated At', required: false, group: 'System' }),\n },\n\n indexes: [\n // Look up \"is there a pending request for this record?\" — common\n // guard on submit and on edit-while-locked checks.\n { fields: ['object_name', 'record_id'] },\n { fields: ['status', 'object_name'] },\n // Status-windowed listings (escalation sweep, \"All\" tab ordering).\n // \"My approvals\" matching no longer scans this table: the service keeps\n // a normalized per-approver index in `sys_approval_approver` (#1745) and\n // resolves approver filters there; `pending_approvers` stays the\n // human-readable CSV source of truth only.\n { fields: ['status', 'updated_at'] },\n { fields: ['submitter_id', 'status'] },\n ],\n\n // Server-declared decision actions (objectui#2678 P2-4). The console's\n // generic action runtime renders and executes these wherever this object is\n // surfaced — the approvals inbox included — so new decision capabilities\n // (and their params) ship as metadata, not as hand-written buttons. Each\n // targets the existing approvals REST route; `{id}` resolves from the row\n // and `actorId` defaults to the caller server-side. The service remains the\n // authority on who may act; `visible` gates on the server-computed\n // per-viewer block (#3310): approver actions on `record.viewer.can_act`\n // (the caller is a current pending approver — same check the service\n // authorizes a decision with, so position/team approvers resolve correctly),\n // submitter actions on `record.viewer.is_submitter`. The core decision levers\n // (approve/reject/reassign) additionally OR in `record.viewer.can_override`\n // (#3424) so a platform/tenant admin can rescue a request routed to an\n // unstaffed position — otherwise undecidable, locking the record forever — by\n // approving, rejecting, or reassigning it to a real approver. `viewer` is\n // attached by getRequest/listRequests; where it is absent the predicate fails\n // closed.\n actions: [\n {\n name: 'approval_approve',\n label: 'Approve',\n icon: 'check-circle',\n // Primary decision — the console renders this filled/highlighted so it\n // stands out from the secondary levers in the drawer's action bar,\n // matching the mobile card hierarchy (objectui#2762 P1-5).\n variant: 'primary',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/approve',\n params: [\n { name: 'comment', label: 'Comment', type: 'textarea', required: false },\n // Decision attachments (#3266). The console renders `type:'file'` params\n // through the shared upload widget and POSTs the resolved `attachments:\n // string[]`; the decision route persists them on `sys_approval_action`.\n { name: 'attachments', label: 'Attachments', type: 'file', multiple: true, required: false },\n ],\n visible: 'record.viewer.can_act || record.viewer.can_override',\n locations: ['record_section', 'list_item'],\n successMessage: 'Approved.',\n refreshAfter: true,\n },\n {\n name: 'approval_reject',\n label: 'Reject',\n // The confirm question lives HERE, not in `confirmText`: this action\n // collects params, so the console would otherwise chain a confirm dialog\n // and then the param dialog for one decision — and the first one already\n // reads as \"the action ran\" (#7278, maintainer ruling 2026-08-10). The\n // param dialog renders this as its description, and nothing is POSTed\n // until its own Confirm: one condition, one wording, one dialog.\n // NB: the top-level action `description` (#7367), never `ai.description`\n // — that one is the LLM-facing tool contract and is not shown to anyone.\n description: 'Reject this request? A rejection is final for every approver.',\n icon: 'x-circle',\n // Destructive decision — rendered in the console's danger styling so it\n // reads as the irreversible action it is (objectui#2762 P1-5).\n variant: 'danger',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/reject',\n params: [\n { name: 'comment', label: 'Comment', type: 'textarea', required: false },\n { name: 'attachments', label: 'Attachments', type: 'file', multiple: true, required: false },\n ],\n visible: 'record.viewer.can_act || record.viewer.can_override',\n locations: ['record_section', 'list_item'],\n successMessage: 'Rejected.',\n refreshAfter: true,\n },\n {\n name: 'approval_reassign',\n label: 'Reassign',\n icon: 'arrow-right-left',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/reassign',\n params: [\n // Field-backed on `submitter_id` (the object's only `sys_user` lookup):\n // the console resolves its lookup config (`reference_to: sys_user`) so the\n // dialog renders a real user picker, while `name: 'to'` overrides the\n // request-body key to the `to` the reassign route expects. This is a\n // config-borrow, not a submitter pre-fill (`defaultFromRow` stays off).\n { field: 'submitter_id', name: 'to', label: 'New approver', required: true, helpText: 'User to hand this step to' },\n { name: 'comment', label: 'Comment', type: 'textarea', required: false },\n ],\n visible: 'record.viewer.can_act || record.viewer.can_override',\n locations: ['record_section'],\n successMessage: 'Reassigned.',\n refreshAfter: true,\n },\n\n // ── Approver secondary decisions ────────────────────────────────\n // Send back for revision / request more info (ADR-0044). Both are approver\n // actions, so `visible` gates on `record.viewer.can_act` (a current pending\n // approver) — same as approve/reject. The service stays the authority.\n {\n name: 'approval_send_back',\n label: 'Send back',\n icon: 'corner-up-left',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/revise',\n params: [\n { name: 'comment', label: 'Reason', type: 'textarea', required: false },\n ],\n visible: 'record.viewer.can_act',\n locations: ['record_section'],\n successMessage: 'Sent back for revision.',\n refreshAfter: true,\n },\n {\n name: 'approval_request_info',\n label: 'Request info',\n icon: 'help-circle',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/request-info',\n params: [\n { name: 'comment', label: 'What do you need?', type: 'textarea', required: true },\n ],\n visible: 'record.viewer.can_act',\n locations: ['record_section'],\n successMessage: 'Information requested.',\n refreshAfter: true,\n },\n\n // ── Submitter continuity actions ────────────────────────────────\n // Remind / recall (pending) and resubmit / recall (returned). These are the\n // submitter's own levers, so `visible` gates on `record.viewer.is_submitter`\n // (server-computed on the current viewer). The service re-checks ownership;\n // the predicate keeps a non-submitter from ever seeing a button they cannot\n // use.\n {\n name: 'approval_remind',\n label: 'Send reminder',\n icon: 'bell-ring',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/remind',\n params: [\n { name: 'comment', label: 'Note', type: 'textarea', required: false },\n ],\n visible: 'record.status == \"pending\" && record.viewer.is_submitter',\n locations: ['record_section'],\n successMessage: 'Reminder sent.',\n refreshAfter: true,\n },\n {\n name: 'approval_recall',\n label: 'Recall',\n // Confirm question as the param dialog's description, not `confirmText`\n // — same one-decision-one-dialog rule as `approval_reject` above (#7278).\n description: 'Recall this request? Approvers can no longer act on it and the record is unlocked.',\n icon: 'undo-2',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/recall',\n params: [\n { name: 'comment', label: 'Comment', type: 'textarea', required: false },\n ],\n // Recall applies while the request is live for the submitter — pending\n // (withdraw) or returned (abandon the revision instead of resubmitting).\n visible: '(record.status == \"pending\" || record.status == \"returned\") && record.viewer.is_submitter',\n locations: ['record_section'],\n successMessage: 'Recalled.',\n refreshAfter: true,\n },\n {\n name: 'approval_resubmit',\n label: 'Resubmit',\n icon: 'refresh-cw',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/resubmit',\n params: [\n { name: 'comment', label: 'What changed?', type: 'textarea', required: false },\n ],\n visible: 'record.status == \"returned\" && record.viewer.is_submitter',\n locations: ['record_section'],\n successMessage: 'Resubmitted.',\n refreshAfter: true,\n },\n ],\n\n enable: {\n // [ADR-0103] Engine-owned: the approval engine owns the request lifecycle\n // (SYSTEM_CTX); users act via domain actions (Submit/Approve/Recall), never\n // generic CRUD. Reads stay open.\n apiMethods: ['get', 'list'],\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\nimport { APPROVAL_ACTION_KINDS, APPROVAL_ACTION_KIND_LABELS } from '@objectstack/spec/contracts';\n\n/**\n * sys_approval_action — Audit trail row per approval action.\n *\n * Append-only: every `submit`, `approve`, `reject`, `recall`, or\n * `escalate` event lands here. The engine reads back per-step approval\n * rows to evaluate `behavior: 'unanimous'` (all approvers must approve\n * before advancing) versus `first_response` (any single approval\n * advances the step).\n *\n * @namespace sys\n */\nexport const SysApprovalAction = ObjectSchema.create({\n name: 'sys_approval_action',\n label: 'Approval Action',\n pluralLabel: 'Approval Actions',\n icon: 'check-circle',\n isSystem: true,\n managedBy: 'append-only',\n description: 'Append-only audit trail for approval actions',\n displayNameField: 'id',\n nameField: 'id', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)\n titleFormat: '{action} · {step_name}',\n highlightFields: ['request_id', 'step_name', 'action', 'actor_id', 'via_override', 'created_at'],\n\n // ADR-0104 D3 wave 2. `attachments` is a media field, so the files it holds\n // are OWNED by this row — and the storage service would otherwise authorize\n // their download by testing whether the caller can READ this row. It cannot:\n // this table is deliberately closed to ordinary approver positions, so that\n // test denies the very approver the attachment was filed for. The approvals\n // service already owns the rule for seeing a decision (visibility of the\n // parent request, exactly as `listActions` applies it), so it answers.\n fileAccessDelegate: 'approvals',\n\n listViews: {\n recent: {\n type: 'grid',\n name: 'recent',\n label: 'Recent',\n data: { provider: 'object', object: 'sys_approval_action' },\n columns: ['created_at', 'request_id', 'step_name', 'action', 'actor_id', 'via_override', 'comment'],\n sort: [{ field: 'created_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n emptyState: { title: 'No approval actions yet', message: 'Actions are logged automatically when approvals progress.' },\n },\n by_actor: {\n type: 'grid',\n name: 'by_actor',\n label: 'By Actor',\n data: { provider: 'object', object: 'sys_approval_action' },\n columns: ['actor_id', 'created_at', 'request_id', 'step_name', 'action'],\n sort: [{ field: 'actor_id', order: 'asc' }, { field: 'created_at', order: 'desc' }],\n grouping: { fields: [{ field: 'actor_id', order: 'asc', collapsed: false }] },\n pagination: { pageSize: 100 },\n },\n all_actions: {\n type: 'grid',\n name: 'all_actions',\n label: 'All',\n data: { provider: 'object', object: 'sys_approval_action' },\n columns: ['created_at', 'request_id', 'step_name', 'action', 'actor_id', 'via_override', 'comment'],\n sort: [{ field: 'created_at', order: 'desc' }],\n pagination: { pageSize: 100 },\n },\n },\n\n fields: {\n id: Field.text({ label: 'Action ID', required: true, readonly: true, group: 'System' }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n description: 'Tenant that owns this action (mirrors the parent request)',\n }),\n\n request_id: Field.lookup('sys_approval_request', {\n label: 'Request',\n required: true,\n group: 'Target',\n }),\n\n step_name: Field.text({\n label: 'Step',\n required: false,\n maxLength: 100,\n description: 'Machine name of the step at the time of the action',\n group: 'Target',\n }),\n\n step_index: Field.number({\n label: 'Step Index',\n required: false,\n group: 'Target',\n }),\n\n action: Field.select(\n // Derived from the contract, not re-typed (#3786). `APPROVAL_ACTION_KINDS`\n // is where the list and the per-kind notes live (which kinds move the flow\n // and which are thread-only); `ApprovalActionKind` is derived from it, so\n // this column and the contract cannot disagree. The authored English label\n // per kind lives beside it in `APPROVAL_ACTION_KIND_LABELS` (#8580 — the\n // #7232 humanization pass missed this field) — mapped here, never\n // re-typed, so the `en` bundle regenerates from the contract's own text.\n APPROVAL_ACTION_KINDS.map((value) => ({ value, label: APPROVAL_ACTION_KIND_LABELS[value] })),\n {\n label: 'Action',\n required: true,\n group: 'Action',\n },\n ),\n\n actor_id: Field.lookup('sys_user', {\n label: 'Actor',\n required: false,\n group: 'Action',\n }),\n\n comment: Field.textarea({ label: 'Comment', required: false, group: 'Action' }),\n\n // #4466 — the one bit of \"who really decided this\" that was still dropped.\n // A privileged admin may act on a request whose staffed approver slate they\n // hold no slot in (the #3424 override path); before this column, that\n // decision was byte-for-byte identical to the designated approver's own\n // approval. A reader of the timeline saw `approve` by the admin and could\n // not tell whether the admin WAS an approver or OVERRODE the ones who were,\n // and the bypassed approver's later `409 INVALID_STATE` was the only trace\n // — existing only if they happened to try.\n //\n // The platform KNOWS at decision time: it took the `isOverrideActor` branch\n // to admit the call at all. This is dropped information, not unavailable\n // information.\n //\n // Set on exactly the decisions that were admitted BY that branch — an admin\n // who is also a genuine slot holder is approving normally and is recorded\n // as such. Nullable and additive: rows written before this column exists\n // carry `null`, which reads as \"not recorded\", never as \"not an override\".\n via_override: Field.boolean({\n label: 'Via Admin Override',\n required: false,\n group: 'Action',\n description:\n 'True when the actor was admitted to this action only by the privileged-override path (#3424) — '\n + 'they held no slot in the request’s pending-approver slate.',\n }),\n\n // Structured hand-off parties for `action: 'reassign'` (#4365). Before\n // these existed the pair lived only inside a default free-text comment\n // (\"<from_id> → <to_id>\"), which no client could parse or render readably.\n // `comment` is pure user input again; timelines render \"from A to B\" from\n // these fields.\n reassign_from: Field.lookup('sys_user', {\n label: 'Reassigned From',\n required: false,\n group: 'Action',\n description: 'User whose pending-approver slot was handed over (reassign actions only)',\n }),\n\n reassign_to: Field.lookup('sys_user', {\n label: 'Reassigned To',\n required: false,\n group: 'Action',\n description: 'User who received the pending-approver slot (reassign actions only)',\n }),\n\n attachments: Field.file({\n label: 'Attachments',\n required: false,\n multiple: true,\n group: 'Action',\n description: 'Files supporting this action — e.g. a signed contract or evidence (#3266).',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n },\n\n indexes: [\n { fields: ['request_id', 'created_at'] },\n { fields: ['request_id', 'step_index', 'action'] },\n ],\n\n enable: {\n // [ADR-0103] Engine-owned append-only decision log: appended by the approval\n // engine (SYSTEM_CTX). Reads stay open.\n apiMethods: ['get', 'list'],\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_approval_approver — Pending-approver index (issue #1745).\n *\n * One row per (request, approver identity) while the request is **pending**.\n * `sys_approval_request.pending_approvers` stays the human-readable source of\n * truth (a CSV column), but CSV substring matching can neither be indexed nor\n * pushed into an engine query — which made \"my pending\" a post-filter in\n * memory and broke pagination beyond the scan window.\n *\n * This table is that CSV, normalized: the service mirrors every change to\n * `pending_approvers` here (open / decide / recall / send-back / reassign /\n * escalate), and clears the rows when the request leaves `pending`. So the\n * table only ever holds the live work queue — its size tracks the number of\n * open approvals, not the append-only request history.\n *\n * `approver` holds one identity literal exactly as it appears in the CSV:\n * a user id, an email, or a `role:<name>` / `team:<name>` style literal.\n * Equality (or `$in`) on this column is the indexed replacement for the old\n * per-row substring match.\n *\n * @namespace sys\n */\nexport const SysApprovalApprover = ObjectSchema.create({\n name: 'sys_approval_approver',\n label: 'Approval Approver',\n pluralLabel: 'Approval Approvers',\n icon: 'users',\n isSystem: true,\n managedBy: 'engine-owned',\n description: 'Normalized pending-approver rows for indexed inbox queries',\n displayNameField: 'id',\n nameField: 'id', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)\n titleFormat: '{approver} · {request_id}',\n highlightFields: ['request_id', 'approver', 'created_at'],\n\n fields: {\n id: Field.text({ label: 'Row ID', required: true, readonly: true, group: 'System' }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n description: 'Tenant that owns this row (mirrors the parent request)',\n }),\n\n request_id: Field.lookup('sys_approval_request', {\n label: 'Request',\n required: true,\n group: 'Target',\n }),\n\n approver: Field.text({\n label: 'Approver',\n required: true,\n maxLength: 255,\n description: 'One pending-approver identity: user id, email, or role:/team: literal',\n group: 'Target',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n },\n\n indexes: [\n // \"My pending\" inbox: equality on the identity literal, scoped by tenant.\n { fields: ['approver', 'organization_id'] },\n // Sync path: rewrite all rows of one request on each approver-set change.\n { fields: ['request_id'] },\n ],\n\n enable: {\n // [ADR-0103] Engine-owned: approver rows are rewritten by the approval\n // engine (SYSTEM_CTX) on each approver-set change, never via generic CRUD.\n apiMethods: ['get', 'list'],\n },\n});\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_approval_delegation — self-service out-of-office (OOO) delegation (#1322 M1).\n *\n * A standing, self-declared rule: \"while I (the delegator) am out between\n * `valid_from` and `valid_until`, route the approver slots that would resolve\n * to me onto my delegate instead.\" The approval service consults active rows\n * in `ApprovalService.expandApprovers` when resolving an approval node's\n * INDIVIDUALLY-routed approvers (`type: user` / `field` / `manager`) — the\n * delegate becomes a real pending approver and acts under their own identity,\n * so nothing is impersonated and the audit trail stays honest.\n *\n * Modelled as its own object (not a scalar on the better-auth-locked\n * `sys_user`), mirroring the `sys_user_position` delegation precedent\n * (ADR-0091): the validity window is enforced at RESOLUTION time via the\n * shared `isGrantActive` predicate — never by a background job (ADR-0049).\n * The window is half-open `[valid_from, valid_until)` in UTC.\n *\n * Scope note: this is the community-core OOO auto-skip only. Long-term proxy\n * \"act-as\" access (viewing/acting on another user's full queue under their\n * authority), delegation governance / segregation-of-duties, and org-wide\n * administration of others' delegations are enterprise concerns tracked\n * separately (objectstack-ai/cloud#855), not here.\n *\n * @namespace sys\n */\nexport const SysApprovalDelegation = ObjectSchema.create({\n name: 'sys_approval_delegation',\n label: 'Approval Delegation',\n pluralLabel: 'Approval Delegations',\n icon: 'user-clock',\n isSystem: true,\n // [ADR-0103, #3355] Admin/user-writable DATA on a platform-defined schema: a\n // user authors their own out-of-office delegation. The bucket default is full\n // CRUD (matching the full-CRUD apiMethods below), so no `userActions` block is\n // needed — RLS/permission sets are the authz.\n managedBy: 'system-data',\n description:\n 'Self-service out-of-office rule: route this user\\'s approver slots to a delegate within a time window (#1322 M1).',\n titleFormat: '{delegator_id} → {delegate_id}',\n highlightFields: ['delegator_id', 'delegate_id', 'valid_from', 'valid_until'],\n\n listViews: {\n active: {\n type: 'grid',\n name: 'active',\n label: 'Active',\n data: { provider: 'object', object: 'sys_approval_delegation' },\n columns: ['delegator_id', 'delegate_id', 'valid_from', 'valid_until', 'reason'],\n sort: [{ field: 'valid_until', order: 'asc' }],\n pagination: { pageSize: 50 },\n emptyState: {\n title: 'No delegations',\n message: 'Declare an out-of-office delegation so approvals route to a backup while you are away.',\n },\n },\n },\n\n fields: {\n id: Field.text({ label: 'Delegation ID', required: true, readonly: true, group: 'System' }),\n\n delegator_id: Field.lookup('sys_user', {\n label: 'Delegator',\n required: true,\n group: 'Delegation',\n description: 'The user going out of office; their individually-routed approver slots are rerouted while active.',\n }),\n\n delegate_id: Field.lookup('sys_user', {\n label: 'Delegate',\n required: true,\n group: 'Delegation',\n description: 'The backup who receives the delegator\\'s approvals while this rule is active. Acts under their own identity.',\n }),\n\n valid_from: Field.datetime({\n label: 'Valid From',\n required: false,\n group: 'Delegation',\n description:\n 'Rule is inactive before this instant. Null = active immediately. ' +\n 'Enforced at resolution time via isGrantActive (ADR-0091 D2 predicate) — never by a background job.',\n }),\n\n valid_until: Field.datetime({\n label: 'Valid Until',\n required: false,\n group: 'Delegation',\n description:\n 'Rule is inactive AT and AFTER this instant (half-open [from, until), UTC). Null = never expires.',\n }),\n\n reason: Field.text({\n label: 'Reason',\n required: false,\n maxLength: 500,\n group: 'Delegation',\n description: 'Why the delegation exists (e.g. \"Annual leave 5/26–5/30\"). Recorded on the substitution audit row.',\n }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n description: 'Tenant that owns this rule; null = applies across tenants for this delegator.',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n },\n\n indexes: [\n // Resolution-time lookup: \"active delegations for this delegator\".\n { fields: ['delegator_id', 'organization_id'] },\n { fields: ['delegate_id'] },\n { fields: ['valid_until'] },\n ],\n\n enable: {\n trackHistory: true,\n searchable: true,\n apiEnabled: true,\n // `bulk` = the batch shape of the verbs above; the gate is `bulk ∧ child`\n // (#3391 P1), so omitting it 405s /batch and the *Many routes (#3026).\n apiMethods: ['get', 'list', 'create', 'update', 'delete', 'bulk'],\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport {\n APPROVAL_BRANCH_LABELS,\n APPROVAL_REVISE_NODE_TYPE,\n approverTypeIsOrgScoped,\n canonicalApproverType,\n normalizeDecisionOutputs,\n type ApprovalNodeConfig,\n} from '@objectstack/spec/automation';\nimport { ExpressionEngine, collectCelRootIdentifiers } from '@objectstack/formula';\nimport { keysetWalk } from '@objectstack/types';\nimport {\n ADMIN_FULL_ACCESS,\n ORGANIZATION_ADMIN_GRANTS,\n BUILTIN_IDENTITY_PLATFORM_ADMIN,\n BUILTIN_IDENTITY_ORG_OWNER,\n BUILTIN_IDENTITY_ORG_ADMIN,\n} from '@objectstack/spec/identity';\nimport type {\n IApprovalService,\n ApprovalRequestRow,\n ApprovalActionRow,\n ApprovalActionAttachment,\n ApprovalDecisionInput,\n ApprovalDecisionResult,\n ApprovalRecallInput,\n ApprovalRecallResult,\n ApprovalSendBackInput,\n ApprovalSendBackResult,\n ApprovalResubmitInput,\n ApprovalResubmitResult,\n ApprovalStatus,\n} from '@objectstack/spec/contracts';\n// [#7135] The full `resolveAuthzContext` envelope — what `IApprovalService`\n// declares for every one of these context parameters since #6523 (the #6206\n// ruling: enforcement adjudicates on the whole envelope, never a per-site\n// subset). Annotating the implementation with the retired six-field shape is\n// what forced this file to cast its way out of its own contract to read\n// fields the caller had already supplied.\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\nimport { RESUME_AUTHORITY_SERVICE } from '@objectstack/spec/contracts';\nimport { isFileIdToken } from '@objectstack/spec/data';\nimport { isGrantActive } from '@objectstack/core';\nimport {\n filterApproversWhoCanRead,\n resolveApproverDirectoryOrg,\n type ApproverOrgScopeDeps,\n type ApproverOrgScopeEngine,\n} from './approver-org-scope.js';\n\n/**\n * Node-era approval runtime (ADR-0019).\n *\n * Approval is no longer a standalone engine — it is a **flow node**. A flow's\n * Approval node opens a request via {@link ApprovalService.openNodeRequest} and\n * the run suspends; a human decision via {@link ApprovalService.decide}\n * finalises the request and resumes the owning run down the matching\n * `approve` / `reject` edge.\n *\n * This service owns the durable approval *state* — `sys_approval_request` /\n * `sys_approval_action`, approver resolution (team / department / position /\n * role / manager graph), and the optional status-field mirror — plus the decision\n * API. It does not author processes, submit, or walk multi-step machinery\n * anymore; that orchestration lives on the one automation engine.\n */\nexport interface ApprovalEngine {\n find(object: string, options?: any): Promise<any[]>;\n insert(object: string, data: any, options?: any): Promise<any>;\n update(object: string, idOrData: any, dataOrOptions?: any, options?: any): Promise<any>;\n delete(object: string, options?: any): Promise<any>;\n}\n\nexport interface ApprovalClock { now(): Date }\n\n/**\n * Minimal automation surface the service uses to resume a suspended flow run\n * once a decision finalises a node-driven request. Optional — attached by the\n * plugin when an automation engine is present (see `approval-node.ts`).\n */\nexport interface ApprovalResumeSurface {\n resume?(runId: string, signal?: {\n output?: Record<string, unknown>;\n branchLabel?: string;\n /**\n * #3801: the engine refuses a resume of an `approval` suspension unless\n * the signal carries this marker — the proof that the resume is the tail\n * of a decision THIS service already authorized and recorded, not a raw\n * `POST …/runs/:runId/resume` around it. Every resume below stamps it via\n * {@link ApprovalService.serviceResume}.\n */\n [RESUME_AUTHORITY_SERVICE]?: true;\n }): Promise<unknown>;\n /** Flow definition lookup, used to derive step-progress display data. */\n getFlow?(name: string): Promise<any | null>;\n /**\n * Terminally cancel a suspended run (ADR-0044). Used when a recall lands\n * during a revision window — the run is paused at the revise-window node,\n * which has no reject edge to resume down.\n */\n cancelRun?(runId: string, reason?: string): Promise<unknown>;\n /**\n * Look up a run's recorded outcome (#3456). Used by the dead-run sweep to ask\n * \"is the run behind this pending request still alive?\".\n *\n * The contract that makes the sweep safe is the answer for a run that is\n * merely SUSPENDED (the normal state of a run waiting on an approval): the\n * engine writes no execution-log entry until a run reaches a terminal state,\n * so a suspended run resolves to `null`, never to a status. The sweep\n * therefore acts only on an explicit terminal-failure status and treats\n * `null` — unknown run, evicted log, no durable store, no automation engine —\n * as \"still alive\".\n */\n getRun?(runId: string): Promise<{ status?: string } | null>;\n /**\n * Whether a suspension still exists for `runId` — the pre-flight that keeps\n * a decision from being recorded against a run that can never advance\n * (#4420). Read-only; it never consumes the suspension.\n *\n * Still distinct from {@link getRun}, but no longer on the axis this comment\n * used to name: since #8050 `getRun` also sees a run suspended by a PREVIOUS\n * process (it resolved `null` there before, unable to tell \"waiting for a\n * human\" from \"dead\"). The difference that remains is the one a pre-flight\n * turns on — this asks the suspension store and REJECTS when it cannot be\n * read, where `getRun` degrades an outage to `null`. A caller about to WRITE\n * must not accept a degraded read.\n *\n * Rejects when the durable store cannot be read — existence is then\n * unknown, and callers must not read an outage as a dead run. Optional: an\n * engine that does not implement it simply gets no pre-flight.\n */\n hasSuspendedRun?(runId: string): Promise<boolean>;\n}\n\n/**\n * Optional messaging surface (ADR-0012 `messaging` service). When attached,\n * thread interactions (reassign / remind / request-info / comment) notify the\n * affected users; without it they degrade to audit-only.\n */\nexport interface ApprovalMessagingSurface {\n emit(input: {\n topic: string;\n audience: string[];\n payload?: Record<string, unknown>;\n severity?: string;\n dedupKey?: string;\n source?: { object: string; id: string };\n actorId?: string;\n }): Promise<unknown>;\n}\n\n/** Minimum time between submitter reminders on one request. */\nexport const REMIND_COOLDOWN_MS = 4 * 60 * 60 * 1000;\n\n/** Named job under which the SLA escalation scan is registered (ADR-0042). */\nexport const ESCALATION_JOB_NAME = 'approvals-sla-escalation';\n/** Default interval between SLA escalation scans. */\nexport const ESCALATION_SCAN_INTERVAL_MS = 5 * 60 * 1000;\n/** Reserved actor id for machine decisions made by the SLA scanner. */\nexport const SLA_ACTOR_ID = 'system:sla';\n/** Reserved actor id for requests abandoned because their run died (#3456). */\nexport const DEAD_RUN_ACTOR_ID = 'system:dead-run';\n/**\n * Run statuses that mean \"this run will never resume\", so a request still\n * pending on it is orphaned (#3456). A CLOSED set, deliberately: the dead-run\n * sweep treats every other answer — `paused` (a run waiting on its approval,\n * the normal case), `running`, an unknown status, or no answer at all — as\n * alive, so an unrecognised state can never cost someone a live approval.\n *\n * `completed` belongs here with the failure states. The approval node only\n * writes a request row on the path where it also suspends the run, and every\n * in-band transition (decide / recall / send-back / resubmit) finalises the\n * request *before* it resumes the run — so a completed run with a still-pending\n * request means the run was resumed out of band and left the request behind.\n */\nconst TERMINAL_RUN_STATUSES: ReadonlySet<string> = new Set([\n 'completed', 'failed', 'cancelled', 'timed_out',\n]);\n\n/**\n * Request statuses that can leave a ZOMBIE behind (#4469) — the terminal states\n * a decision reaches only by ALSO resuming the owning run.\n *\n * `recalled` is deliberately absent: a recall ABANDONS the request on purpose,\n * and {@link ApprovalService.recall} explicitly tolerates a run it cannot\n * resume (the withdrawal and the lock release are the point). Reporting those\n * would bury the real findings under expected ones.\n */\nconst STRANDABLE_REQUEST_STATUSES = ['approved', 'rejected', 'returned'] as const;\n\n/**\n * One terminal request whose owning flow run is unrecoverable (#4469) — the\n * decision was recorded and the flow never moved. Reporting shape only: the\n * sweep never rewrites these rows (see\n * {@link ApprovalService.inspectStrandedRequests}).\n */\nexport interface StrandedApprovalRequest {\n requestId: string;\n /** Terminal status the request reached — the decision that WAS recorded. */\n status: string;\n /** The `flow_run_id` that resolves to neither a suspension nor a run history row. */\n runId: string;\n flowName?: string;\n /** Approval node the run should have continued from. */\n nodeId?: string;\n objectName: string;\n recordId: string;\n organizationId?: string | null;\n completedAt?: string;\n /** `config.approvalStatusField`, when the node mirrors status onto the record. */\n mirrorField?: string;\n /** What that mirror field currently reads — usually the stale value an operator sees. */\n mirroredStatus?: string;\n}\n\n/** Default lifetime of an actionable-link token (ADR-0043). */\nexport const ACTION_TOKEN_TTL_MS = 72 * 60 * 60 * 1000;\n\n/** Outcome of redeeming (or peeking) an actionable-link token. */\nexport type ActionTokenOutcome =\n | { ok: true; action: 'approve' | 'reject'; request: ApprovalRequestRow; approverId: string }\n | { ok: false; reason: 'invalid' | 'expired' | 'consumed' | 'not_pending' | 'not_approver'; request?: ApprovalRequestRow };\n\n/**\n * System-elevated context for this service's own metadata reads and writes.\n *\n * [#7135] Typed as the full envelope so it is passed AS ITSELF. It used to be\n * declared `as const` and forced through `as unknown as\n * SharingExecutionContext` at the three sites that handed it to a CONTRACT\n * method — a double cast on an enforcement input, which switches checking off\n * for the whole argument rather than for the readonly-array mismatch that\n * provoked it.\n */\nconst SYSTEM_CTX: ExecutionContext = { isSystem: true, positions: [], permissions: [] };\n\n/**\n * Who is acting, for the purpose of a data write made on their behalf (#3783).\n *\n * Reads the AUTHENTICATED principal off the execution context — deliberately not\n * `input.actorId`. When this was written the two could still disagree: every\n * public entrypoint took `actorId` from the request body (`body.actorId ??\n * context.userId`, see the REST approval routes) and the service only checked\n * that it named a pending approver, never that it was the caller. That was\n * called tolerable on an audit row — but the same unchecked value was the\n * authorization key, so it was in fact impersonation, and #3800 closed it:\n * {@link ApprovalService.resolveActor} now pins the actor to an identity the\n * server can prove belongs to the caller. This helper stays the separate,\n * stricter answer for a DATA WRITE, which wants the bare human id and never a\n * `type:value` slot literal or a machine sentinel.\n *\n * A caller holding a trustworthy actor with no session behind it — the ADR-0043\n * action link, whose token cryptographically binds exactly one approver — puts\n * that actor ON the context instead of relying on this.\n *\n * `null` for a machine caller (the SLA sweep passes {@link SYSTEM_CTX}), so a\n * reserved sentinel like {@link SLA_ACTOR_ID} can never surface as a `userId`.\n */\nfunction actingUserId(context: ExecutionContext | undefined): string | null {\n const userId = context?.userId;\n return typeof userId === 'string' && userId ? userId : null;\n}\n\n/**\n * Max hops when following an OOO delegation chain (#1322 M1): A out → B, B out\n * → C, … Bounds the walk so a mis-configured chain can't loop or resolve\n * unboundedly; a cycle or self-reference also stops it early.\n */\nconst OOO_MAX_CHAIN = 8;\n\n/**\n * Approver types resolved by QUERYING a graph rather than by taking `value`\n * literally (#3807). Each can legitimately come back empty — an unstaffed\n * position, an emptied team, a mis-pointed unit — and the caller then falls\n * back to a `type:value` literal that no user can act on. They are listed here\n * so that dead end gets one warning instead of passing in silence.\n *\n * `user` / `field` are deliberately absent: they resolve to the id they were\n * given without a lookup, so there is no \"expanded to nobody\" state to report.\n * `business_unit` / `bu` are the accepted dialects of `department`.\n */\nconst GRAPH_APPROVER_TYPES: ReadonlySet<string> = new Set([\n 'team', 'department', 'business_unit', 'bu', 'position', 'org_membership_level', 'manager',\n]);\n\n/** One OOO delegation hop applied while resolving an approver (#1322 M1/M4). */\ninterface OooSubstitution {\n /** The approver who was skipped (out of office). */\n from: string;\n /** The delegate the slot was routed to. */\n to: string;\n /** The delegator's declared reason, if any. */\n reason: string | null;\n}\n\n/**\n * The CLOSED set of namespace roots an `expression` approver may reference\n * (#3447 P2). Three explicit times/sources, no `record`, no bare field names:\n * `record` means \"the record at event time\" everywhere else on the platform\n * (flow conditions: trigger snapshot; hooks: the write payload), so binding it\n * here — to either time — would silently alias one meaning to the other. The\n * runtime CEL env treats unknown roots as `dyn` (→ `null` → an empty slate),\n * so out-of-contract roots MUST be rejected before evaluation; both this\n * pre-check and the lint rule read the roots via\n * {@link collectCelRootIdentifiers} so they can never drift.\n */\nconst APPROVER_EXPRESSION_ROOTS = new Set(['current', 'trigger', 'vars']);\n\n/**\n * Evaluation context an approval node hands to `expression` approvers\n * (#3447 P2). `current` (the live record) is supplied by openNodeRequest's\n * re-read; these two carry the other roots.\n */\nexport interface ApproverExpressionContext {\n /** Submit-time snapshot (the flow's `$record`) — bound as `trigger.*`. */\n trigger?: Record<string, unknown> | null;\n /** Flow variables at node entry (nested by dotted key) — bound as `vars.*`. */\n vars?: Record<string, unknown> | null;\n}\n\n/**\n * Non-request outcome of {@link ApprovalService.openNodeRequest}: the node\n * resolved an empty approver slate and its `onEmptyApprovers: 'auto_approve'`\n * policy waved it through (#3447 P2). No `sys_approval_request` row exists —\n * nobody was ever asked — so the node must complete down its `approve` edge\n * instead of suspending.\n */\nexport interface ApprovalNodeAutoOutcome {\n autoApproved: true;\n reason: 'empty_approvers';\n}\n\nfunction uid(prefix: string): string {\n const g: any = globalThis as any;\n if (g.crypto?.randomUUID) return `${prefix}_${g.crypto.randomUUID()}`;\n return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction parseJson<T = any>(raw: unknown, fallback: T): T {\n if (raw == null || raw === '') return fallback;\n if (typeof raw === 'string') {\n try { return JSON.parse(raw) as T; } catch { return fallback; }\n }\n return raw as T;\n}\n\nfunction csvSplit(raw: unknown): string[] {\n if (!raw) return [];\n if (Array.isArray(raw)) return raw.map(String).filter(Boolean);\n return String(raw).split(',').map(s => s.trim()).filter(Boolean);\n}\n\n/**\n * Is a submitted decision output blank — i.e. does it fail a `required`\n * declaration (objectui#2955)?\n *\n * \"Present but empty\" has to count as missing: a decision UI sends whatever\n * its widget holds, and an untouched picker/text box is `''` or `[]`. Letting\n * those through would satisfy `required` with a value the downstream\n * `expression` approver then resolves to nobody — the exact stall the flag\n * exists to prevent. `false` and `0` are real values and pass.\n */\nfunction isBlankDecisionOutput(value: unknown): boolean {\n if (value === undefined || value === null) return true;\n if (typeof value === 'string') return value.trim() === '';\n if (Array.isArray(value)) return value.filter(v => v !== null && v !== undefined && String(v).trim() !== '').length === 0;\n return false;\n}\n\n/**\n * Humanize a machine name for display fallback: strips a `flow:` prefix and\n * title-cases underscore/dash segments (`flow:manager_review` → \"Manager\n * Review\"). Used only when no authored label was snapshotted on the row.\n */\nfunction prettifyMachineName(raw: string | null | undefined): string | undefined {\n if (!raw) return undefined;\n const base = String(raw).replace(/^flow:/, '').trim();\n if (!base) return undefined;\n return base\n .split(/[_\\-\\s]+/)\n .filter(Boolean)\n .map(w => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n}\n\nfunction rowFromRequest(row: any): ApprovalRequestRow {\n // Authored display labels ride the node-config snapshot (`__flowLabel` /\n // `__nodeLabel`) so they survive without a schema migration; fall back to a\n // prettified machine name for rows written before labels were captured.\n const cfg = parseJson<any>(row.node_config_json, undefined);\n return {\n id: String(row.id),\n organization_id: row.organization_id ?? undefined,\n process_name: String(row.process_name ?? ''),\n object_name: String(row.object_name ?? ''),\n record_id: String(row.record_id ?? ''),\n submitter_id: row.submitter_id ?? undefined,\n submitter_comment: row.submitter_comment ?? undefined,\n status: (row.status as ApprovalStatus) ?? 'pending',\n current_step: row.current_step ?? undefined,\n current_step_index: row.current_step_index ?? undefined,\n pending_approvers: csvSplit(row.pending_approvers),\n payload: parseJson(row.payload_json, undefined),\n flow_run_id: row.flow_run_id ?? undefined,\n flow_node_id: row.flow_node_id ?? undefined,\n completed_at: row.completed_at ?? undefined,\n created_at: row.created_at ?? undefined,\n updated_at: row.updated_at ?? undefined,\n // The row is created at submission time; expose the stable inbox-facing name.\n submitted_at: row.created_at ?? undefined,\n process_label: cfg?.__flowLabel ?? prettifyMachineName(row.process_name),\n step_label: cfg?.__nodeLabel ?? prettifyMachineName(row.current_step),\n sla_due_at: slaDueAt(row.created_at, cfg),\n // ADR-0044 revision round (rides the config snapshot; absent ⇒ round 1).\n round: typeof cfg?.__round === 'number' ? cfg.__round : undefined,\n // objectui#2902: the node's record-lock policy. The lock is enforced\n // server-side in `lifecycle-hooks.ts` off THIS SAME snapshot with the\n // same `!== false` default, so the flag a client renders and the rule the\n // server applies can never drift. Without it a console can only see\n // \"a pending request exists\" and has to assume the record is locked —\n // which mislabels every `lockRecord: false` node as locked and hides an\n // edit the server would have accepted.\n lock_record: cfg?.lockRecord !== false,\n // #3447 P2: the node's author-declared decision outputs, surfaced so a\n // decision UI can render input fields for them and POST `outputs` on\n // approve/reject. Per-request (each node declares its own), which is why\n // this rides the row instead of the static action params. Two shapes for\n // version skew: `decision_outputs` stays the bare KEY list an older\n // console renders as text inputs; `decision_output_defs` carries the\n // normalized typed declarations a picker-aware console prefers.\n ...(() => {\n const defs = normalizeDecisionOutputs(cfg?.decisionOutputs);\n return defs.length\n ? { decision_outputs: defs.map(d => d.key), decision_output_defs: defs }\n : {};\n })(),\n } as any;\n}\n\n/** `created_at + escalation.timeoutHours`, when the node declares an SLA. */\nfunction slaDueAt(createdAt: unknown, cfg: any): string | undefined {\n const hours = cfg?.escalation?.timeoutHours;\n if (typeof hours !== 'number' || hours <= 0 || !createdAt) return undefined;\n const t = Date.parse(String(createdAt));\n if (Number.isNaN(t)) return undefined;\n return new Date(t + hours * 3600_000).toISOString();\n}\n\n/**\n * Normalize one raw `attachments` entry into an {@link ApprovalActionAttachment}.\n *\n * `sys_approval_action.attachments` is a `Field.file` (multiple), so the column\n * **stores opaque `sys_file` ids** — that is the stored form of every media\n * field (ADR-0104 D3). What arrives here is whichever of three forms the read\n * path produced:\n *\n * 1. the **expanded** `{ id, name, size, mimeType, url }` the ObjectQL read\n * path resolves a stored id into — the normal case;\n * 2. a **bare id**, when there was nothing to expand it into (storage service\n * absent, file not committed);\n * 3. a **legacy inline blob** (`{ file_id, name, mime_type, url, … }`) written\n * before file-as-reference, until the backfill converts it.\n *\n * The original mapping did `String(entry)`, which turned form 1 into the\n * literal `\"[object Object]\"` — so the inbox timeline showed a nameless,\n * un-openable attachment chip (#3266 follow-up; caught by browser verification).\n *\n * Note the casing: the expanded form carries `mimeType`, the legacy blob\n * `mime_type`. Both are accepted for the duration of the migration window.\n */\nfunction normalizeActionAttachment(entry: any): ApprovalActionAttachment | undefined {\n if (entry == null) return undefined;\n // Form 2 — a bare reference. `isFileIdToken` is the platform's single arbiter\n // of \"is this string an opaque file id, or a URL?\", shared with the engine's\n // read resolver, so the two cannot disagree about what counts as an id.\n if (typeof entry === 'string') {\n const id = entry.trim();\n if (!id) return undefined;\n return isFileIdToken(id) ? { id } : { id, url: id };\n }\n if (typeof entry === 'object') {\n // Forms 1 and 3 — `file_id` is the legacy blob's key for the same thing.\n const id = entry.id ?? entry.file_id;\n if (id == null || String(id) === '') return undefined;\n const mimeType = entry.mimeType ?? entry.mime_type;\n return {\n id: String(id),\n name: typeof entry.name === 'string' ? entry.name : undefined,\n url: typeof entry.url === 'string' ? entry.url : undefined,\n mimeType: typeof mimeType === 'string' ? mimeType : undefined,\n size: typeof entry.size === 'number' ? entry.size : undefined,\n };\n }\n return undefined;\n}\n\nfunction rowFromAction(row: any): ApprovalActionRow {\n const attachments = Array.isArray(row.attachments)\n ? row.attachments.map(normalizeActionAttachment).filter((a: ApprovalActionAttachment | undefined): a is ApprovalActionAttachment => !!a)\n : [];\n return {\n id: String(row.id),\n request_id: String(row.request_id),\n step_name: row.step_name ?? undefined,\n step_index: row.step_index ?? undefined,\n action: row.action,\n actor_id: row.actor_id ?? undefined,\n comment: row.comment ?? undefined,\n // Structured reassign hand-off parties (#4365).\n reassign_from: row.reassign_from ?? undefined,\n reassign_to: row.reassign_to ?? undefined,\n // #4466 — surfaced so a timeline can SAY \"overridden the approver slate\"\n // rather than render an override identically to an ordinary approval.\n // `null` (a row written before the column existed) stays `undefined`:\n // \"not recorded\" is not the same claim as \"not an override\".\n via_override: row.via_override == null ? undefined : row.via_override === true,\n // Decision attachments (#3266): rich descriptors carrying the display name +\n // download URL, so consumers label/open them without reading `sys_file`.\n attachments: attachments.length ? attachments : undefined,\n created_at: row.created_at ?? undefined,\n };\n}\n\nexport interface ApprovalServiceOptions {\n engine: ApprovalEngine;\n clock?: ApprovalClock;\n logger?: { info?: (msg: any, ...rest: any[]) => void; warn?: (msg: any, ...rest: any[]) => void; error?: (msg: any, ...rest: any[]) => void; debug?: (msg: any, ...rest: any[]) => void };\n /**\n * Optional automation surface used to resume a suspended flow run when a\n * decision finalises a request. Usually attached after construction via\n * {@link ApprovalService.attachAutomation} once the automation engine is\n * available.\n */\n automation?: ApprovalResumeSurface;\n /** Optional messaging service for thread notifications. */\n messaging?: ApprovalMessagingSurface;\n /**\n * Absolute origin prefixed onto actionable links (ADR-0043), e.g.\n * `https://app.example.com`. Defaults to relative URLs, which work inside\n * the Console and IM webviews; outbound email needs the absolute form.\n */\n publicBaseUrl?: string;\n /**\n * [ADR-0105 D9] The tenancy posture in force. Cross-organization approver\n * targeting is a `group`-posture capability; the resolver refuses the\n * declaration under any other posture rather than silently ignoring it.\n * Absent (a stack booted with no tenancy service) reads as \"unknown\" and the\n * guard stands down.\n */\n tenancyPosture?: () => string | undefined;\n}\n\nexport class ApprovalService implements IApprovalService {\n private readonly engine: ApprovalEngine;\n private readonly clock: ApprovalClock;\n private readonly logger?: ApprovalServiceOptions['logger'];\n private automation?: ApprovalResumeSurface;\n private messaging?: ApprovalMessagingSurface;\n private publicBaseUrl: string;\n private tenancyPosture?: () => string | undefined;\n\n constructor(opts: ApprovalServiceOptions) {\n this.engine = opts.engine;\n this.clock = opts.clock ?? { now: () => new Date() };\n this.logger = opts.logger;\n this.automation = opts.automation;\n this.messaging = opts.messaging;\n this.publicBaseUrl = (opts.publicBaseUrl ?? '').replace(/\\/$/, '');\n this.tenancyPosture = opts.tenancyPosture;\n }\n\n /** Attach (or replace) the ADR-0105 D9 posture provider. */\n attachTenancyPosture(provider: () => string | undefined): void {\n this.tenancyPosture = provider;\n }\n\n /** Deps bundle for the ADR-0105 D9 org-scope helpers. */\n private get orgScopeDeps(): ApproverOrgScopeDeps {\n return {\n engine: this.engine as unknown as ApproverOrgScopeEngine,\n posture: this.tenancyPosture,\n logger: this.logger,\n };\n }\n\n /**\n * [ADR-0105 D9] Which organization's directory resolves ONE approver spec.\n * Absent declaration ⇒ the request's own organization (unchanged, no reads).\n */\n private async directoryOrgFor(a: any, requestOrgId: string | null | undefined): Promise<string | null | undefined> {\n const rawType = String(a?.type ?? '');\n return resolveApproverDirectoryOrg(\n this.orgScopeDeps,\n a?.organization,\n requestOrgId,\n rawType,\n approverTypeIsOrgScoped(rawType),\n );\n }\n\n /** Attach (or replace) the automation surface used to resume flow runs. */\n attachAutomation(automation: ApprovalResumeSurface): void {\n this.automation = automation;\n }\n\n /** Attach (or replace) the messaging surface used for thread notifications. */\n attachMessaging(messaging: ApprovalMessagingSurface): void {\n this.messaging = messaging;\n }\n\n /** Best-effort notification fan-out — failures only log. */\n private async notify(input: {\n topic: string;\n audience: string[];\n payload?: Record<string, unknown>;\n dedupKey?: string;\n source?: { object: string; id: string };\n actorId?: string;\n }): Promise<number> {\n const audience = input.audience.filter(a => a && !a.includes(':'));\n if (!this.messaging || !audience.length) return 0;\n // Deep-link the inbox (#2678 P1.5): a notification about one request should\n // land on that request, not the bare inbox. Rewritten centrally so every\n // call site — and any future one — inherits it; the query param is read by\n // the console inbox to auto-open the drawer.\n let payload = input.payload;\n if (\n payload?.actionUrl === '/system/approvals'\n && input.source?.object === 'sys_approval_request'\n && input.source.id\n ) {\n payload = { ...payload, actionUrl: `/system/approvals?request=${encodeURIComponent(input.source.id)}` };\n }\n try {\n await this.messaging.emit({ severity: 'info', ...input, payload, audience });\n return audience.length;\n } catch (err: any) {\n this.logger?.warn?.('[approvals] notification failed', {\n topic: input.topic, error: err?.message ?? String(err),\n });\n return 0;\n }\n }\n\n /** Load a request row and assert it is still pending. */\n private async loadPendingRow(requestId: string): Promise<any> {\n if (!requestId) throw new Error('VALIDATION_FAILED: requestId is required');\n const rows = await this.engine.find('sys_approval_request', {\n where: { id: requestId }, limit: 1, context: SYSTEM_CTX,\n });\n const raw: any = Array.isArray(rows) ? rows[0] : null;\n if (!raw) throw new Error(`REQUEST_NOT_FOUND: ${requestId}`);\n if (raw.status !== 'pending') throw new Error(`INVALID_STATE: request is ${raw.status}`);\n return raw;\n }\n\n /**\n * Privileged-override gate (#3424). A stuck approval — one routed to a\n * position/team with no holders (so its `pending_approvers` is only an\n * unresolvable `type:value` literal) or to approvers who have all since left —\n * is otherwise undecidable: no concrete user is in the slate, so every normal\n * `decide` / `reassign` / `recall` is `FORBIDDEN` and (with `lockRecord`) the\n * record stays locked forever with no in-product recovery. A platform or\n * tenant admin — the same posture the engine's superuser bypass already\n * trusts — may always act on a PENDING request to release it: approve, reject,\n * reassign it to a real approver, or recall it.\n *\n * A platform admin crosses the tenant wall (matching the unscoped\n * `admin_full_access` evidence); a tenant admin may override only within their\n * own org (or an org-less request). A system context always passes. Signals are\n * read defensively off the resolved exec context (`permissions` / `positions` /\n * the derived `posture`, ADR-0095) so any transport that resolves through the\n * shared authz resolver lights this up without extra wiring.\n */\n private isOverrideActor(context: ExecutionContext, requestOrg?: string | null): boolean {\n if (!context) return false;\n if (context.isSystem) return true;\n const perms = Array.isArray(context.permissions) ? context.permissions : [];\n const positions = Array.isArray(context.positions) ? context.positions : [];\n // [#7135] A DECLARED read. `posture` (ADR-0095 D2) is resolved by\n // `resolveAuthzContext` and is a field of the envelope the contract has\n // named here since #6523 — the doc block above already says it is the\n // intended signal. Until this parameter widened, reading it meant an\n // unchecked `as any` on an enforcement input: a typo (`postures`,\n // `'PLATFORM-ADMIN'`) would have compiled and silently denied every\n // override, leaving a stuck approval with no in-product recovery.\n const posture = context.posture;\n const isPlatformAdmin = posture === 'PLATFORM_ADMIN'\n || perms.includes(ADMIN_FULL_ACCESS)\n || positions.includes(BUILTIN_IDENTITY_PLATFORM_ADMIN);\n if (isPlatformAdmin) return true;\n const isTenantAdmin = posture === 'TENANT_ADMIN'\n || ORGANIZATION_ADMIN_GRANTS.some((n) => perms.includes(n))\n || positions.includes(BUILTIN_IDENTITY_ORG_OWNER)\n || positions.includes(BUILTIN_IDENTITY_ORG_ADMIN);\n if (!isTenantAdmin) return false;\n // A tenant admin's authority stops at their own org; a null-org request is\n // global and any admin may release it.\n // Only the `tenantId` half of this read lost its cast: `tenantId` is a\n // declared field of the envelope, `organizationId` is not a field of it at\n // ALL. That spelling has its own history (#5858 / `check:org-identifier`)\n // and was explicitly held out of this change (#7070) — so it stays cast,\n // and the asymmetry is now the visible marker of which of the two names\n // the contract actually knows.\n const actorTenant = context.tenantId ?? (context as any).organizationId ?? null;\n return requestOrg == null || (actorTenant != null && String(requestOrg) === String(actorTenant));\n }\n\n /**\n * Pin the acting identity to the AUTHENTICATED CALLER (#3800).\n *\n * Every public entrypoint accepts an `actorId`, and the REST routes fill it\n * from `body.actorId ?? body.actor_id ?? context.userId` — so before this\n * gate the body won. The authorization checks downstream all read that value\n * (`pending_approvers.includes(input.actorId)`, `submitter_id === actorId`),\n * which made the body-supplied string not merely the audit label but the key\n * that opens the door: any authenticated user could name a pending approver\n * and have that approver's decision recorded and the owning flow resumed.\n * #3783 drew this line for the data-write identity ({@link actingUserId});\n * this closes the authorization half.\n *\n * A caller may still name an identity OTHER than their bare user id, because\n * a slot legitimately can be keyed by one: `resolveApproverSpec` stores the\n * `type:value` literal when a graph lookup yields nothing, and an author may\n * write an email as a `user` approver. So the rule is not \"actorId must equal\n * userId\" — it is **\"actorId must be an identity the SERVER can prove belongs\n * to the caller\"**. Anything else is `FORBIDDEN`.\n *\n * A system context is exempt and keeps its explicit actor: the SLA sweep\n * passes the reserved {@link SLA_ACTOR_ID} sentinel, and the ADR-0043 action\n * link passes the approver its single-use token is cryptographically bound to\n * (having also put them on the context). Those are the only two callers that\n * hold a trustworthy actor with no session behind them.\n *\n * A caller with NO identity at all cannot act. Belt-and-suspenders: the REST\n * anonymous-deny now denies every anonymous request (#3963), but this service\n * must not rely on a caller upstream — an anonymous actor could otherwise\n * decide approvals outright by naming one.\n */\n private async resolveActor(\n actorId: string | undefined,\n context: ExecutionContext,\n ): Promise<string> {\n // The machine callers — their actor is server-minted, not caller-supplied.\n if (context?.isSystem) {\n if (!actorId) throw new Error('VALIDATION_FAILED: actorId is required');\n return actorId;\n }\n const uid = actingUserId(context);\n if (!uid) {\n throw new Error('FORBIDDEN: an approval action requires an authenticated caller');\n }\n // The common case: no actor named, or the caller named themselves.\n if (!actorId || String(actorId) === uid) return uid;\n\n // Named something else — allow it ONLY if the server can prove the caller\n // holds that identity. `positions` is resolved by the shared authz resolver\n // (never client-supplied); `role:` is the ADR-0090 D3 deprecated spelling\n // that 15.x-era slots and the Console's own identity list still carry.\n const named = String(actorId);\n for (const position of context.positions ?? []) {\n if (named === `position:${position}` || named === `role:${position}`) return named;\n }\n // Email last — it costs a read, so only when nothing cheaper matched.\n if (named.includes('@') && await this.callerHasEmail(uid, named)) return named;\n\n throw new Error(\n `FORBIDDEN: cannot act as '${named}' — an approval action is recorded against the authenticated caller`,\n );\n }\n\n /** Does `userId`'s own account carry `email`? (Slots keyed by email, #3800.) */\n private async callerHasEmail(userId: string, email: string): Promise<boolean> {\n try {\n const rows = await this.engine.find('sys_user', {\n where: { id: userId }, limit: 1, context: SYSTEM_CTX,\n });\n const row: any = Array.isArray(rows) ? rows[0] : null;\n return !!row?.email && String(row.email).toLowerCase() === email.toLowerCase();\n } catch {\n return false;\n }\n }\n\n /**\n * Expand the approvers on an Approval node into user IDs by querying the\n * graph tables for `team:` / `department:` / `position:` /\n * `org_membership_level:` / `manager:` approver types. Falls back to a\n * prefixed literal (`type:value`) when graph lookups produce nothing — so\n * existing fixtures and flows that rely on substring matching keep working.\n *\n * **Graph semantics:**\n * - `team` → flat members of `sys_team` (better-auth; no BFS)\n * - `department` → recursive BFS of `sys_business_unit.parent_business_unit_id`\n * → members of every descendant via `sys_business_unit_member`\n * - `position` → holders via `sys_user_position` ∪ `sys_member.role`\n * transition source (ADR-0090 D3 / ADR-0057 D4)\n * - `org_membership_level`\n * → users with `sys_member.role = value` in tenant — the\n * better-auth MEMBERSHIP TIER (owner/admin/member), not a\n * position; author `position` for org positions\n * - `manager` → `sys_user.manager_id` of `record[value] ?? record.owner_id`\n * - `field` → literal user id stored in `record[value]`\n * - `user` → literal value\n *\n * `role` is accepted as the deprecated spelling of `org_membership_level`\n * (ADR-0090 D3) for one window: it resolves identically and logs a warning.\n *\n * **Out-of-office (#1322 M1):** individually-routed approvers — the ones that\n * resolve to a specific person (`user` / `field` / `manager`) — are passed\n * through {@link ApprovalService.applyOooDelegation}, which reroutes them onto\n * an active delegate when the resolved user has declared OOO. Group/graph\n * approvers (`team` / `department` / `position` / `org_membership_level`) are\n * left untouched: a group still has its other members, and position-routed\n * leave is already covered by ADR-0091 job delegation. Pass an `opts.now` /\n * `opts.substitutions` collector to record the hops for audit + notification.\n */\n private async expandApprovers(\n step: any,\n record?: any,\n organizationId?: string | null,\n opts?: {\n now?: number;\n substitutions?: OooSubstitution[];\n groups?: Record<string, string[]>;\n /** #3447 P2: `trigger`/`vars` roots for `expression` approvers. */\n exprCtx?: ApproverExpressionContext;\n /**\n * #3447 P2 audit collector: what each dynamic spec resolved FROM (the\n * live field value / the expression's intermediate values), snapshotted\n * as `__resolvedFrom` so \"why these people\" stays answerable later.\n */\n resolvedFrom?: Record<string, unknown>;\n },\n ): Promise<string[]> {\n if (!step || !Array.isArray(step.approvers)) return [];\n const now = opts?.now ?? this.clock.now().getTime();\n const out: string[] = [];\n const specs: any[] = step.approvers;\n for (let idx = 0; idx < specs.length; idx++) {\n const a = specs[idx];\n if (!a) continue;\n // Approvers without an explicit `group` each form their own group keyed\n // by position (#3266), so a plain per-approver list behaves predictably.\n const groupKey = a.group != null && String(a.group) !== '' ? String(a.group) : `#${idx}`;\n\n // #3447 P2: `expression` approvers resolve OUTSIDE resolveApproverSpec —\n // a graph-expanded expression (resolveAs: department/…) must key each\n // intermediate value as its own per_group group, which the flat string[]\n // contract of resolveApproverSpec cannot carry.\n if (canonicalApproverType(String(a.type)) === 'expression') {\n const resolved = await this.resolveExpressionApprovers(\n a, record, organizationId, now, opts?.substitutions, opts?.exprCtx,\n );\n if (opts?.resolvedFrom) opts.resolvedFrom[`expression#${idx}`] = resolved.raw;\n for (const entry of resolved.slots) {\n if (!entry.id) continue;\n out.push(entry.id);\n if (opts?.groups) {\n (opts.groups[entry.id] ??= []).push(entry.subGroup ? `${groupKey}:${entry.subGroup}` : groupKey);\n }\n }\n continue;\n }\n\n if (opts?.resolvedFrom && canonicalApproverType(String(a.type)) === 'field' && a.value != null) {\n opts.resolvedFrom[`field:${a.value}`] = (record as any)?.[a.value] ?? null;\n }\n const ids = await this.resolveApproverSpec(a, record, organizationId, now, opts?.substitutions);\n // per_group (#3266): tag each resolved id with this spec's group.\n for (const u of ids) {\n if (!u) continue;\n out.push(u);\n if (opts?.groups) (opts.groups[u] ??= []).push(groupKey);\n }\n }\n return out.filter(Boolean);\n }\n\n /**\n * Resolve ONE approver spec to concrete approver identities, applying OOO\n * substitution (#1322) to individually-routed types. Extracted from\n * {@link ApprovalService.expandApprovers} so the caller can tag each spec's\n * resolved ids with a group (#3266) without duplicating the resolution logic.\n * Returns the `type:value` literal as a single-element fallback when a graph\n * lookup yields nothing — same behaviour as before the extraction.\n */\n private async resolveApproverSpec(\n a: any,\n record: any,\n organizationId: string | null | undefined,\n now: number,\n substitutions?: OooSubstitution[],\n ): Promise<string[]> {\n // ADR-0090 D3: `role` is the deprecated spelling of `org_membership_level`.\n // Resolve on the canonical type, but keep the AUTHORED spelling in the\n // `type:value` fallback below — stored `sys_approval_approver` rows and\n // `pending_approvers` slots from 15.x carry the old literal.\n const type = canonicalApproverType(String(a.type));\n if (type !== a.type) {\n this.logger?.warn?.(\n `[approvals] approver type '${a.type}' is deprecated (ADR-0090 D3) — author '${type}' instead`,\n { deprecated: a.type, canonical: type },\n );\n }\n // [ADR-0105 D9] WHERE this approver is looked up — the request's own\n // organization unless the spec targets another one in the same group.\n //\n // Resolved HERE, above the `user` / `field` / `manager` early returns,\n // because refusing a declaration on a directory-less type is one of the\n // things this resolution DOES (those types name a person outright, so\n // `organization` on them cannot narrow anything and an author who wrote it\n // misunderstood the field). Resolving it after those returns made the\n // refusal unreachable and the declaration silently inert — exactly the\n // \"ignored, not refused\" behaviour ADR-0105 D9 rules out, and what the\n // cloud group-posture dogfood caught.\n //\n // Costs nothing on the overwhelmingly common path: with no `organization`\n // declared, the resolver returns the request org without reading anything.\n const directoryOrg = await this.directoryOrgFor(a, organizationId);\n const crossOrg = directoryOrg !== organizationId;\n\n if (type === 'user') {\n return this.applyOooDelegation(String(a.value), now, organizationId, substitutions);\n }\n if (type === 'field' && record) {\n // #3447: a record field can name MANY approvers — a multi-select user\n // field arrives as an array (or a legacy CSV string). Fan each out into\n // its own slot and OOO-substitute per person; collapsing to `String(...)`\n // (→ `'u1,u2'`) would mint one bogus approver id and skip every delegate.\n const out: string[] = [];\n for (const id of csvSplit((record as any)[a.value])) {\n out.push(...await this.applyOooDelegation(id, now, organizationId, substitutions));\n }\n return out;\n }\n // `directoryOrg` / `crossOrg` were resolved at the TOP of this method, so\n // the refusal reaches directory-less types too. Resolution failures\n // propagate: they are routing bugs, and that call sits OUTSIDE the\n // swallowing try below on purpose (see the catch's comment).\n //\n // A cross-org slate is filtered to the people who can actually READ the\n // request (D2 union); same-org routing is untouched and does no extra read.\n const bounded = async (users: string[]): Promise<string[]> => (\n crossOrg\n ? filterApproversWhoCanRead(this.orgScopeDeps, users, organizationId, {\n approverType: type, value: a.value != null ? String(a.value) : undefined,\n directoryOrgId: directoryOrg,\n })\n : users\n );\n\n try {\n if (type === 'team') {\n const users = await this.expandTeamUsers(String(a.value));\n if (users.length) return users;\n } else if (type === 'department' || type === 'business_unit' || type === 'bu') {\n const users = await bounded(await this.expandBusinessUnitUsers(String(a.value), directoryOrg));\n if (users.length) return users;\n } else if (type === 'position') {\n const users = await bounded(await this.expandPositionUsers(String(a.value), directoryOrg));\n if (users.length) return users;\n } else if (type === 'org_membership_level') {\n const users = await bounded(await this.expandMembershipTierUsers(String(a.value), directoryOrg));\n if (users.length) return users;\n } else if (type === 'manager' && record) {\n const subject = (record as any)[a.value] ?? (record as any).owner_id;\n if (subject) {\n const mgr = await this.lookupManager(String(subject));\n if (mgr) return this.applyOooDelegation(mgr, now, organizationId, substitutions);\n }\n }\n } catch { /* a directory lookup failed → fall through to the literal slot */ }\n // #3508: `queue` is declared-but-unenforced — there is no queue branch\n // above, so a queue approver always lands here and the `queue:<id>` slot\n // routes to nobody. The spec marks it non-authorable\n // (NON_AUTHORABLE_APPROVER_TYPES) so designers stop offering it; warn for\n // the stored flows that still carry one, so the silent dead slot is at\n // least visible to operators.\n if (type === 'queue') {\n this.logger?.warn?.(\n `[approvals] approver type 'queue' is not implemented — the slot resolves to nobody (#3508)`,\n { value: a.value },\n );\n } else if (GRAPH_APPROVER_TYPES.has(type)) {\n // #3807 follow-up — every OTHER way to land here is a graph type whose\n // lookup produced nobody, and the literal below is a slot no user can\n // ever act on. That silence is what let #3807 hide: a `department`\n // approver pointing at a seeded (env-wide) unit resolved to\n // `department:<id>` on every request, the request opened with an empty\n // slate, and nothing in the logs said so — the first symptom was a\n // permanently stuck approval (#3424). The fallback itself stays (a\n // literal keeps 15.x slots and substring fixtures working); it just\n // stops being invisible.\n this.logger?.warn?.(\n `[approvals] approver '${type}:${a.value}' expanded to nobody — the slot routes to no one `\n + `and the request cannot advance until someone is added or the approver is re-pointed (#3807)`,\n { type, value: a.value, organizationId: organizationId ?? null },\n );\n }\n return [`${a.type}:${a.value}`];\n }\n\n /**\n * Resolve an `expression` approver (#3447 P2): evaluate its CEL source at\n * node entry against the three explicit roots — `current` (live record),\n * `trigger` (submit snapshot), `vars` (flow variables) — then expand the\n * result into people per `resolveAs`.\n *\n * Every failure here THROWS (config/parse errors as `VALIDATION_FAILED`,\n * evaluation faults as `EXPRESSION_FAILED`) so the approval node fails\n * loudly instead of opening a request routed to nobody — an approver\n * expression that cannot run is a routing bug, never \"condition not met\".\n * Error messages carry the correct spelling because their primary reader is\n * the AI author fixing the flow on the next validate pass.\n *\n * Returns `slots` (approver id + optional per_group sub-key) and `raw` (the\n * expression's own values, pre-expansion) for the `__resolvedFrom` audit.\n */\n private async resolveExpressionApprovers(\n a: any,\n liveRecord: any,\n organizationId: string | null | undefined,\n now: number,\n substitutions?: OooSubstitution[],\n exprCtx?: ApproverExpressionContext,\n ): Promise<{ slots: Array<{ id: string; subGroup?: string }>; raw: string[] }> {\n const source = String(a.value ?? '').trim();\n if (!source) {\n throw new Error('VALIDATION_FAILED: expression approver has an empty expression');\n }\n\n // Closed-root pre-check. The runtime env resolves ANY unknown root as dyn →\n // null, so `record.x` / a bare field would silently yield an empty slate;\n // reject it here with the correct spelling instead.\n const parsed = collectCelRootIdentifiers(source);\n if (!parsed.ok) {\n throw new Error(`VALIDATION_FAILED: expression approver does not parse: ${parsed.error} — source: \\`${source}\\``);\n }\n const illegal = parsed.roots.filter(r => !APPROVER_EXPRESSION_ROOTS.has(r));\n if (illegal.length) {\n const hint = illegal.includes('record') || illegal.includes('previous')\n ? `\\`record\\`/\\`previous\\` are not bound here — write \\`current.<field>\\` for the record's live state `\n + `at node entry, or \\`trigger.<field>\\` for the submit-time snapshot (\\`vars.previous\\` carries the pre-update row)`\n : `did you mean \\`current.<field>\\` (live record), \\`trigger.<field>\\` (submit snapshot), or \\`vars.<name>\\` (flow variable)?`;\n throw new Error(\n `VALIDATION_FAILED: expression approver references \\`${illegal.join('`, `')}\\` — `\n + `only \\`current.*\\`, \\`trigger.*\\` and \\`vars.*\\` are available; ${hint}. Source: \\`${source}\\``,\n );\n }\n\n const result = ExpressionEngine.evaluate(\n { dialect: 'cel', source },\n { extra: { current: liveRecord ?? {}, trigger: exprCtx?.trigger ?? {}, vars: exprCtx?.vars ?? {} } },\n );\n if (!result.ok) {\n throw new Error(\n `EXPRESSION_FAILED: expression approver failed to evaluate (${result.error.kind}): `\n + `${result.error.message} — source: \\`${source}\\``,\n );\n }\n\n // Normalize to a string list: a user-id/CSV string, an array of ids, or\n // null/empty (an EMPTY slate — legal, handled by onEmptyApprovers). Any\n // other shape is a config bug, rejected loudly.\n const value = result.value as unknown;\n let raw: string[];\n if (value == null || value === '') {\n raw = [];\n } else if (typeof value === 'string') {\n raw = csvSplit(value);\n } else if (Array.isArray(value)) {\n const bad = value.find(v => v != null && typeof v !== 'string' && typeof v !== 'number');\n if (bad !== undefined) {\n throw new Error(\n `EXPRESSION_FAILED: expression approver must yield ids (string / CSV / string array), `\n + `got an array containing ${typeof bad} — source: \\`${source}\\``,\n );\n }\n raw = value.map(v => String(v ?? '').trim()).filter(Boolean);\n } else {\n throw new Error(\n `EXPRESSION_FAILED: expression approver must yield ids (string / CSV / string array), `\n + `got ${typeof value} — source: \\`${source}\\``,\n );\n }\n\n // `resolveAs` expansion. `user` (default): each value IS a person —\n // individually routed, so OOO delegation applies (#1322). Graph kinds\n // re-expand each value through the same lookups the static types use; a\n // group still has its other members, so like the static graph types they\n // are NOT OOO-substituted, and with per_group each intermediate value\n // forms its own sub-group (one sign-off per returned department). A value\n // whose expansion is empty keeps a `<kind>:<value>` literal slot — same\n // unstaffed-target behaviour (and #3424 admin rescue) as the static types.\n const resolveAs = String(a.resolveAs ?? 'user');\n if (resolveAs === 'user') {\n const slots: Array<{ id: string }> = [];\n for (const id of raw) {\n for (const routed of await this.applyOooDelegation(id, now, organizationId, substitutions)) {\n slots.push({ id: routed });\n }\n }\n return { slots, raw };\n }\n // [ADR-0105 D9] An expression that re-expands into a graph kind consults the\n // same org-scoped directories the static types do, so it honours the same\n // targeting. Resolved once for the whole slate, before the per-value loop —\n // the declaration is a property of the spec, not of what the CEL returned.\n const directoryOrg = await this.directoryOrgFor(a, organizationId);\n const crossOrg = directoryOrg !== organizationId;\n const slots: Array<{ id: string; subGroup: string }> = [];\n for (const key of raw) {\n let users: string[] = [];\n try {\n if (resolveAs === 'department') users = await this.expandBusinessUnitUsers(key, directoryOrg);\n else if (resolveAs === 'position') users = await this.expandPositionUsers(key, directoryOrg);\n else if (resolveAs === 'team') users = await this.expandTeamUsers(key);\n else {\n throw new Error(\n `VALIDATION_FAILED: expression approver has unknown resolveAs '${resolveAs}' — `\n + `use 'user', 'department', 'position', or 'team'`,\n );\n }\n } catch (err: any) {\n if (String(err?.message ?? '').startsWith('VALIDATION_FAILED')) throw err;\n users = [];\n }\n if (crossOrg && users.length) {\n users = await filterApproversWhoCanRead(this.orgScopeDeps, users, organizationId, {\n approverType: resolveAs, value: key, directoryOrgId: directoryOrg,\n });\n }\n if (!users.length) {\n slots.push({ id: `${resolveAs}:${key}`, subGroup: key });\n continue;\n }\n for (const u of users) slots.push({ id: u, subGroup: key });\n }\n return { slots, raw };\n }\n\n /** Flat team — `sys_team` is better-auth's collaboration grouping (no hierarchy). */\n private async expandTeamUsers(teamId: string): Promise<string[]> {\n if (!teamId) return [];\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_team_member', {\n where: { team_id: teamId },\n fields: ['user_id'],\n limit: 10000,\n context: SYSTEM_CTX,\n } as any);\n } catch { rows = []; }\n return Array.from(new Set((rows ?? []).map((r: any) => String(r.user_id ?? '')).filter(Boolean)));\n }\n\n /**\n * Tenant scope for a `sys_business_unit` read that may legitimately be\n * env-wide (#3807).\n *\n * `organization_id = null` on a platform object means \"owned by no\n * organization\" — a row written by a seed, the file layer, or bootstrap,\n * i.e. before (or outside) any org exists. A strict\n * `organization_id = <request org>` equality made every such row invisible:\n * the seed check below found nothing, the whole expansion returned `[]`, and\n * the approver fell back to the dead `department:<id>` literal that routes to\n * nobody. That is not an edge case — an app's org tree is normally seeded\n * (a seed cannot know the org id the runtime mints at boot) while the\n * approval request always carries one, so EVERY department approver a\n * designer could pick resolved to nobody.\n *\n * Widen to \"this org ∪ env-wide\", the same predicate `sys_metadata`'s\n * pending-draft listing settled on for the identical reason. Another org's\n * unit still fails the match, so the wall between two organizations is\n * unchanged — only rows belonging to no org at all become visible.\n */\n private businessUnitOrgScope(\n filter: Record<string, unknown>,\n organizationId?: string | null,\n ): Record<string, unknown> {\n if (!organizationId) return filter;\n return { ...filter, $or: [{ organization_id: organizationId }, { organization_id: null }] };\n }\n\n /** Recursive department — walks `sys_business_unit.parent_business_unit_id`. */\n private async expandBusinessUnitUsers(businessUnitId: string, organizationId?: string | null): Promise<string[]> {\n if (!businessUnitId) return [];\n // Seed sanity check: skip if dept doesn't exist or is inactive within tenant.\n try {\n const seed = await this.engine.find('sys_business_unit', {\n where: this.businessUnitOrgScope({ id: businessUnitId }, organizationId),\n fields: ['id', 'active'],\n limit: 1,\n context: SYSTEM_CTX,\n } as any);\n const seedRow: any = Array.isArray(seed) ? seed[0] : null;\n if (!seedRow || seedRow.active === false) return [];\n } catch { return []; }\n\n const seen = new Set<string>([businessUnitId]);\n const queue: string[] = [businessUnitId];\n while (queue.length) {\n const parent = queue.shift()!;\n let kids: any[] = [];\n try {\n const filter = this.businessUnitOrgScope(\n { parent_business_unit_id: parent, active: { $ne: false } },\n organizationId,\n );\n kids = await this.engine.find('sys_business_unit', { filter, fields: ['id'], limit: 1000, context: SYSTEM_CTX } as any);\n } catch { kids = []; }\n for (const k of kids ?? []) {\n const kid = String((k as any).id ?? '');\n if (kid && !seen.has(kid)) { seen.add(kid); queue.push(kid); }\n }\n }\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_business_unit_member', {\n where: { business_unit_id: { $in: Array.from(seen) } },\n fields: ['user_id'],\n limit: 10000,\n context: SYSTEM_CTX,\n } as any);\n } catch { rows = []; }\n return Array.from(new Set((rows ?? []).map((r: any) => String(r.user_id ?? '')).filter(Boolean)));\n }\n\n /**\n * Position holders (ADR-0090 D3): `sys_user_position` is the platform-owned\n * assignment table, keyed by the position's machine name (ADR-0057 D4),\n * unioned with the better-auth membership string (`sys_member.role`) as a\n * transition source — the same semantics as `PositionGraphService` in\n * `plugin-sharing`, so an approval routes to exactly the users the sharing\n * engine would expand for the same position.\n */\n private async expandPositionUsers(positionName: string, organizationId?: string | null): Promise<string[]> {\n if (!positionName) return [];\n const users = new Set<string>();\n const filter: any = { position: positionName };\n if (organizationId) filter.organization_id = organizationId;\n try {\n const rows = await this.engine.find('sys_user_position', {\n filter, fields: ['user_id'], limit: 10000, context: SYSTEM_CTX,\n } as any);\n for (const r of (rows ?? []) as any[]) {\n const uid = String(r.user_id ?? '');\n if (uid) users.add(uid);\n }\n } catch { /* table may not exist on minimal stacks — union source below still applies */ }\n // ADR-0057 D4 transition source: pre-migration stacks still carry the\n // position name in better-auth's `sys_member.role` column, so the same\n // lookup serves a position name here and a membership tier for\n // `org_membership_level` — the column is one, the two concepts are not.\n for (const uid of await this.expandMembershipTierUsers(positionName, organizationId)) users.add(uid);\n return Array.from(users);\n }\n\n /**\n * better-auth org-membership tier (`sys_member.role`: owner/admin/member) —\n * NOT positions. Named for the projection (`org_membership_level`, ADR-0057\n * D7 / ADR-0090 D3), not for better-auth's column: the column name is theirs\n * and stays, the platform-facing word does not.\n */\n private async expandMembershipTierUsers(tier: string, organizationId?: string | null): Promise<string[]> {\n if (!tier) return [];\n const filter: any = { role: tier };\n if (organizationId) filter.organization_id = organizationId;\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_member', { filter, fields: ['user_id'], limit: 10000, context: SYSTEM_CTX } as any);\n } catch { rows = []; }\n return Array.from(new Set((rows ?? []).map((r: any) => String(r.user_id ?? '')).filter(Boolean)));\n }\n\n private async lookupManager(userId: string): Promise<string | null> {\n try {\n const rows = await this.engine.find('sys_user', {\n where: { id: userId }, fields: ['id', 'manager_id'], limit: 1, context: SYSTEM_CTX,\n } as any);\n const row: any = Array.isArray(rows) ? rows[0] : null;\n return row?.manager_id ? String(row.manager_id) : null;\n } catch { return null; }\n }\n\n /**\n * Out-of-office auto-skip (#1322 M1). Given an individually-routed approver\n * id, follow any active `sys_approval_delegation` chain and return the id the\n * slot should actually go to — the delegate acts under their own identity, so\n * no impersonation is involved. Returns `[userId]` unchanged when there is no\n * active delegation. Each hop is appended to `collector` (when supplied) so\n * the caller can audit + notify (M4).\n *\n * The chain (A out → B, B out → C, …) is bounded by {@link OOO_MAX_CHAIN} and\n * stops on a self-reference or a cycle, so a mis-declared loop degrades to the\n * last reachable delegate rather than hanging.\n */\n private async applyOooDelegation(\n userId: string,\n now: number,\n organizationId?: string | null,\n collector?: OooSubstitution[],\n ): Promise<string[]> {\n const start = String(userId ?? '').trim();\n if (!start) return [];\n let current = start;\n const visited = new Set<string>([current]);\n for (let hop = 0; hop < OOO_MAX_CHAIN; hop++) {\n const del = await this.lookupActiveDelegation(current, now, organizationId);\n if (!del) break;\n const to = String(del.delegate_id ?? '').trim();\n if (!to || to === current || visited.has(to)) break; // no-op / self / cycle\n collector?.push({ from: current, to, reason: del.reason != null ? String(del.reason) : null });\n visited.add(to);\n current = to;\n }\n return [current];\n }\n\n /**\n * The active OOO delegation for a delegator at `now`, or null. Validity is the\n * shared `isGrantActive` half-open window (ADR-0091 D2), enforced here at\n * resolution time — never by a background job. When several rows are active,\n * the one expiring soonest wins (the most specific coverage window).\n */\n private async lookupActiveDelegation(\n delegatorId: string,\n now: number,\n organizationId?: string | null,\n ): Promise<any | null> {\n if (!delegatorId) return null;\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_approval_delegation', {\n where: { delegator_id: delegatorId },\n fields: ['id', 'delegator_id', 'delegate_id', 'valid_from', 'valid_until', 'reason', 'organization_id'],\n limit: 50,\n context: SYSTEM_CTX,\n } as any);\n } catch { return null; } // table absent on minimal stacks — no OOO, resolve as-is\n const active = (rows ?? []).filter((r: any) =>\n isGrantActive(r, now)\n // A null-org rule applies across tenants; a scoped rule only within its tenant.\n && (organizationId == null || r.organization_id == null || String(r.organization_id) === String(organizationId)));\n if (!active.length) return null;\n active.sort((a: any, b: any) => {\n const au = a.valid_until ? Date.parse(String(a.valid_until)) : Number.POSITIVE_INFINITY;\n const bu = b.valid_until ? Date.parse(String(b.valid_until)) : Number.POSITIVE_INFINITY;\n return au - bu;\n });\n return active[0];\n }\n\n /**\n * Mirror a request status onto a business-object field, if configured.\n *\n * **Elevated, but not anonymous (#3783).** The write stays `isSystem`: the\n * record is normally LOCKED while its approval is live and the submitter\n * cannot edit it, so only a platform write can land the status — that is what\n * the lock hook's system exemption (`lifecycle-hooks.ts`) is for. What it must\n * NOT do is throw away *who* caused the transition. Every status below is\n * something a specific human just did — a submitter submitting or recalling,\n * an approver deciding or sending back — and this write is what fires the\n * target object's record-change flows. With no `userId` on it those cascades\n * inherit no trigger user, and since #3760 a `runAs:'user'` run with no trigger\n * user has its data ops REFUSED — so \"when the invoice is approved, do X\", the\n * most natural approvals automation there is, had to declare `runAs:'system'`\n * and take blanket elevation for a case where a perfectly good scoped identity\n * existed. Re-attaching the actor lets those cascades run as the deciding user\n * with RLS enforced. Same shape the approval node already uses when it calls\n * into this service (`approval-node.ts`).\n *\n * `actorId` is `null` for the genuinely machine-driven transitions (the SLA\n * escalation's auto-decision, the dead-run sweep). There is no human to name\n * there, and naming a sentinel would put a non-user in `updated_by` and in\n * every downstream flow's identity. Those cascades stay user-less — a flow\n * that wants to react to them still has to declare `runAs:'system'`, which is\n * the honest answer rather than an oversight.\n *\n * Deliberately carries `userId` ONLY, not the request's org. On an\n * ExecutionContext `tenantId` is a driver-scoping knob, not attribution\n * (`buildDriverOptions` turns it into a tenant predicate on the update), so\n * passing it would newly org-scope this write and silently no-op the mirror on\n * a record whose org differs from the request's — while buying nothing: the\n * automation engine back-fills the run's `tenantId` from the resolved user's\n * own grants.\n */\n private async mirrorStatusField(\n object: string,\n recordId: string,\n field: string,\n status: string,\n actorId: string | null,\n ): Promise<void> {\n try {\n const context = actorId ? { ...SYSTEM_CTX, userId: actorId } : SYSTEM_CTX;\n await this.engine.update(object, { id: recordId, [field]: status }, { context });\n } catch (err: any) {\n this.logger?.warn?.(`[approvals] mirrorStatusField failed: ${err?.message ?? err}`);\n }\n }\n\n /**\n * Re-read a business record's CURRENT state by id so approver resolution binds\n * to live data at node entry, not the trigger snapshot the flow froze into\n * `$record` at submit time (#3447). A `field` / `manager` approver names *who*\n * decides, and an earlier node — or the approver of an earlier step — may have\n * written that routing field after submit (e.g. a lead reviewer picking which\n * departments co-review). Graph approvers (team / position / …) already query\n * live; this brings the in-record types into line.\n *\n * Read under system identity: approver routing is a platform concern and the\n * record is the flow's own subject, so the submitter's RLS/FLS must not narrow\n * it. Degrades to `fallback` (the snapshot) when the record can't be re-read —\n * hard-deleted between submit and node entry, or an object whose backend can't\n * serve a point read — warning rather than throwing so a transient miss can't\n * wedge an approval. That \"warn but proceed\" stance matches the\n * no-concrete-approver guard (#3424) and the \"record is gone\" enrichment path\n * that already falls back to the payload snapshot.\n */\n private async loadLiveRecord(object: string, recordId: string, fallback?: any): Promise<any> {\n try {\n const rows = await this.engine.find(object, {\n where: { id: recordId }, limit: 1, context: SYSTEM_CTX,\n } as any);\n const live = Array.isArray(rows) ? rows[0] : rows;\n if (live) return live;\n this.logger?.warn?.(\n `[approvals] live record ${object}/${recordId} not found at node entry — `\n + 'resolving approvers against the trigger snapshot (#3447 fallback).',\n { object, recordId },\n );\n } catch (err: any) {\n this.logger?.warn?.(\n `[approvals] live record re-read failed for ${object}/${recordId}: ${err?.message ?? err} — `\n + 'resolving approvers against the trigger snapshot (#3447 fallback).',\n );\n }\n return fallback ?? {};\n }\n\n // ── ADR-0019: Approval-as-flow-node ──────────────────────────\n //\n // A flow's Approval node opens a request via `openNodeRequest` (carrying its\n // own approvers/behavior config and the suspended run id), then suspends. A\n // later `decide` finalizes it and resumes the flow run down the matching\n // `approve`/`reject` edge. The record lock is enforced by a beforeUpdate hook\n // keyed on a *pending* request, so finalizing auto-releases it.\n\n /**\n * Open a pending approval request on behalf of a flow's Approval node. The\n * node config (approvers / behavior / status field) is snapshotted on the row\n * so a decision can be made without any process to resolve against.\n *\n * #3447 P2: may instead return an {@link ApprovalNodeAutoOutcome} — no\n * request opened — when the slate resolves empty and the node's\n * `onEmptyApprovers` policy is `auto_approve`.\n */\n async openNodeRequest(\n input: {\n object: string;\n recordId: string;\n runId: string;\n nodeId: string;\n config: ApprovalNodeConfig;\n flowName?: string;\n /** Authored flow label, snapshotted for inbox display. */\n flowLabel?: string;\n /** Authored node label, snapshotted for inbox display. */\n nodeLabel?: string;\n submitterId?: string | null;\n record?: any;\n organizationId?: string | null;\n /**\n * #3447 P2: flow variables at node entry (nested by dotted key, as the\n * engine's CEL conditions see them) — the `vars.*` root for `expression`\n * approvers. `input.record` doubles as their `trigger.*` root.\n */\n variables?: Record<string, unknown> | null;\n },\n context: ExecutionContext,\n ): Promise<ApprovalRequestRow | ApprovalNodeAutoOutcome> {\n if (!input.object) throw new Error('VALIDATION_FAILED: object is required');\n if (!input.recordId) throw new Error('VALIDATION_FAILED: recordId is required');\n if (!input.runId) throw new Error('VALIDATION_FAILED: runId is required');\n\n // One pending request per (object, record).\n const existing = await this.engine.find('sys_approval_request', {\n where: { object_name: input.object, record_id: input.recordId, status: 'pending' },\n limit: 1, context: SYSTEM_CTX,\n });\n if (Array.isArray(existing) && existing[0]) {\n throw new Error(`DUPLICATE_REQUEST: a pending approval already exists for ${input.object}/${input.recordId}`);\n }\n\n // `organizationId` is not on the envelope — see isOverrideActor().\n const ctxOrg = (context as any)?.organizationId ?? context?.tenantId ?? input.organizationId ?? null;\n const nowDate = this.clock.now();\n // OOO auto-skip (#1322 M1): reroute individually-routed approvers who are\n // out of office. Collected hops drive the audit + notification below (M4).\n const substitutions: OooSubstitution[] = [];\n // Group membership per resolved approver (#3266) — snapshotted so quorum /\n // per_group finalization is decided against the slate resolved at OPEN time\n // (OOO-substituted), not re-resolved live at each decision.\n const groups: Record<string, string[]> = {};\n // #3447: resolve approvers against the record's LIVE state at node entry, not\n // the trigger snapshot carried in `input.record`. This is the whole fix — an\n // earlier step may have written the field this node routes on.\n const liveRecord = await this.loadLiveRecord(input.object, input.recordId, input.record);\n const resolvedFrom: Record<string, unknown> = {};\n const approvers = await this.expandApprovers(\n { approvers: input.config.approvers }, liveRecord, ctxOrg, {\n now: nowDate.getTime(), substitutions, groups,\n exprCtx: { trigger: input.record ?? null, vars: input.variables ?? null },\n resolvedFrom,\n },\n );\n\n // Empty-slate policy (#3447 P2). \"Empty\" = no CONCRETE person — an\n // unstaffed position / empty expression result leaves only `type:value`\n // literal slots, decidable by nobody.\n if (!approvers.some(a => a && !a.includes(':'))) {\n const emptyPolicy = (input.config as any).onEmptyApprovers ?? 'admin_rescue';\n if (emptyPolicy === 'fail') {\n throw new Error(\n `NO_APPROVERS: approval node '${input.nodeId}' on ${input.object}/${input.recordId} resolved to no `\n + `concrete approver and its onEmptyApprovers policy is 'fail'. Check that the approver target(s) `\n + `are staffed / the routing field or expression yields user ids at node entry.`,\n );\n }\n if (emptyPolicy === 'auto_approve') {\n this.logger?.warn?.(\n `[approvals] approval node '${input.nodeId}' on ${input.object}/${input.recordId} resolved to no `\n + `concrete approver — auto-approving per onEmptyApprovers: 'auto_approve' (no request opened).`,\n { object: input.object, recordId: input.recordId, node: input.nodeId, resolved: approvers },\n );\n return { autoApproved: true, reason: 'empty_approvers' };\n }\n // #3424 admin_rescue (default): the request is still opened (a privileged\n // admin can override it, and legacy 15.x literal slots stay queryable) —\n // the only option that neither waves the record through nor kills the\n // run — but warn loudly so the misconfiguration surfaces instead of\n // silently locking the record with no obvious cause.\n this.logger?.warn?.(\n `[approvals] approval node '${input.nodeId}' on ${input.object}/${input.recordId} resolved to no concrete approver`\n + ' — the request is decidable only by a privileged admin. Check that the approver target(s) are staffed.',\n { object: input.object, recordId: input.recordId, node: input.nodeId, resolved: approvers },\n );\n }\n\n const now = nowDate.toISOString();\n const id = uid('areq');\n const processName = `flow:${input.flowName ?? input.nodeId}`;\n // Display labels ride the config snapshot (no schema migration needed);\n // `rowFromRequest` surfaces them as `process_label` / `step_label`.\n const configSnapshot: any = { ...input.config };\n if (input.flowLabel) configSnapshot.__flowLabel = input.flowLabel;\n if (input.nodeLabel) configSnapshot.__nodeLabel = input.nodeLabel;\n // Snapshot the resolved approver→group map for EVERY multi-approver\n // behavior (was quorum/per_group only). #3447 P2 makes this load-bearing\n // for unanimous too: an `expression` approver can only resolve at OPEN\n // time (decide has no flow variables to evaluate against), so the tally\n // must read the open-time slate — which also pins unanimous+field to the\n // slate the approvers actually saw, instead of re-reading a field that may\n // have changed again since.\n if (input.config.behavior && input.config.behavior !== 'first_response') {\n configSnapshot.__approverGroups = groups;\n }\n // #3447 P2: snapshot what the dynamic approver sources resolved FROM (the\n // live routing-field value / the expression's intermediate values) so the\n // audit trail answers \"why these people\" — the resolution INPUT, pairing\n // the resolution RESULT already persisted as `pending_approvers`.\n if (Object.keys(resolvedFrom).length) {\n configSnapshot.__resolvedFrom = resolvedFrom;\n }\n // ADR-0044 round numbering: rounds of a revise loop share the run — count\n // this (run, node)'s prior requests; the new one is round N+1. Stamped on\n // the snapshot (precedent: __flowLabel), so no schema migration.\n try {\n const prior = await this.engine.find('sys_approval_request', {\n where: { flow_run_id: input.runId, flow_node_id: input.nodeId }, limit: 500, context: SYSTEM_CTX,\n });\n const n = Array.isArray(prior) ? prior.length : 0;\n if (n > 0) configSnapshot.__round = n + 1;\n } catch { /* round display is best-effort */ }\n const row: any = {\n id,\n process_name: processName,\n object_name: input.object,\n record_id: input.recordId,\n submitter_id: input.submitterId ?? context.userId ?? null,\n status: 'pending',\n current_step: input.nodeId,\n current_step_index: 0,\n pending_approvers: approvers.join(','),\n payload_json: input.record != null ? JSON.stringify(input.record) : null,\n flow_run_id: input.runId,\n flow_node_id: input.nodeId,\n node_config_json: JSON.stringify(configSnapshot),\n organization_id: ctxOrg,\n created_at: now,\n updated_at: now,\n };\n await this.engine.insert('sys_approval_request', row, { context: SYSTEM_CTX });\n await this.syncApproverIndex(id, approvers, ctxOrg, now);\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: id, organization_id: ctxOrg,\n step_name: input.nodeId, step_index: 0, action: 'submit',\n actor_id: input.submitterId ?? context.userId ?? null, comment: null, created_at: now,\n }, { context: SYSTEM_CTX });\n\n // OOO substitution audit + notification (#1322 M4). Each hop that rerouted\n // an approver away from an out-of-office user is recorded on the request's\n // audit trail (a system action, no human actor) and notified to both the\n // delegate — who now owns the slot — and the skipped approver.\n for (const sub of substitutions) {\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: id, organization_id: ctxOrg,\n step_name: input.nodeId, step_index: 0, action: 'ooo_substitute',\n actor_id: null,\n comment: `${sub.from} → ${sub.to}${sub.reason ? ` — ${sub.reason}` : ''}`,\n created_at: now,\n }, { context: SYSTEM_CTX });\n await this.notify({\n topic: 'approval.ooo_substituted',\n audience: [sub.to],\n source: { object: 'sys_approval_request', id },\n dedupKey: `approval-ooo-${id}-${sub.to}`,\n payload: {\n title: 'Approval routed to you (out-of-office cover)',\n message: `You are covering an approval on ${input.object}/${input.recordId} while ${sub.from} is out of office.`,\n actionUrl: '/system/approvals',\n },\n });\n await this.notify({\n topic: 'approval.ooo_skipped',\n audience: [sub.from],\n source: { object: 'sys_approval_request', id },\n dedupKey: `approval-ooo-skip-${id}-${sub.from}`,\n payload: {\n title: 'Approval routed to your delegate',\n message: `An approval on ${input.object}/${input.recordId} was routed to ${sub.to} while you are out of office.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n\n // Record lock (when `lockRecord !== false`) is enforced by the beforeUpdate\n // hook keyed on the now-pending request; no extra write needed here.\n if (input.config.approvalStatusField) {\n // Attributed to whoever the row itself calls the submitter (#3783), so\n // there is exactly one answer to \"who submitted this\". Not the\n // {@link actingUserId} route: `submitterId` is server-supplied here (the\n // approval node passes the run's own trigger user) and unreachable from a\n // request body, so it carries none of the caller-controlled risk that rule\n // exists for — and it already resolves to `context.userId` in every\n // first-party path.\n await this.mirrorStatusField(\n input.object, input.recordId, input.config.approvalStatusField, 'pending',\n row.submitter_id ?? null,\n );\n }\n\n return rowFromRequest(row);\n }\n\n /**\n * True when the approve tally satisfies the node's `behavior` (#3266):\n * - `unanimous` — every resolved approver approved.\n * - `quorum` — at least `minApprovals` distinct approvals (default = all).\n * - `per_group` — every group reached `minApprovals` approvals (default 1).\n * Thresholds are clamped to the resolvable count / group size, so a mis-set\n * value can never deadlock a request.\n */\n private isApprovalSatisfied(\n behavior: string,\n config: ApprovalNodeConfig,\n original: string[],\n groupMap: Record<string, string[]>,\n approved: Set<string>,\n ): boolean {\n if (behavior === 'unanimous') {\n return original.length > 0 && original.every(a => approved.has(a));\n }\n if (behavior === 'quorum') {\n const n = original.length || 1;\n const need = Math.min(Math.max(1, config.minApprovals ?? n), n);\n // Count distinct approvals (robust to OOO/reassign changing who holds a slot).\n return approved.size >= need;\n }\n if (behavior === 'per_group') {\n const perGroupNeed = Math.max(1, config.minApprovals ?? 1);\n const size: Record<string, number> = {};\n for (const gs of Object.values(groupMap)) for (const g of gs) size[g] = (size[g] ?? 0) + 1;\n const groups = Object.keys(size);\n if (!groups.length) return true; // nothing to gate\n const got: Record<string, number> = {};\n for (const a of approved) for (const g of (groupMap[a] ?? [])) got[g] = (got[g] ?? 0) + 1;\n return groups.every(g => (got[g] ?? 0) >= Math.min(perGroupNeed, size[g]));\n }\n return true; // first_response and unknown → first approval finalizes\n }\n\n /**\n * Record a decision on a node-driven request. Honours the node's `behavior`\n * (#3266): `first_response` finalizes on the first approval; `unanimous`,\n * `quorum`, and `per_group` hold the request open until their tally is met\n * (see {@link ApprovalService.isApprovalSatisfied}). A rejection always\n * finalizes the node (one veto). When the request finalizes, returns the\n * suspended run id + node id so the caller (or {@link ApprovalService.decide})\n * can resume the flow down the matching branch.\n */\n async decideNode(\n requestId: string,\n input: { decision: 'approve' | 'reject'; actorId: string; comment?: string; attachments?: string[]; outputs?: Record<string, unknown> },\n context: ExecutionContext,\n ): Promise<{ request: ApprovalRequestRow; runId: string | null; nodeId: string | null; finalized: boolean; decision: 'approve' | 'reject'; outputs?: Record<string, unknown> }> {\n if (!requestId) throw new Error('VALIDATION_FAILED: requestId is required');\n const actorId = await this.resolveActor(input?.actorId, context);\n if (input.decision !== 'approve' && input.decision !== 'reject') {\n throw new Error('VALIDATION_FAILED: decision must be approve|reject');\n }\n\n // Read the raw row to reach flow_* correlation + the node config snapshot.\n const rawRows = await this.engine.find('sys_approval_request', {\n where: { id: requestId }, limit: 1, context: SYSTEM_CTX,\n });\n const raw: any = Array.isArray(rawRows) ? rawRows[0] : null;\n if (!raw) throw new Error(`REQUEST_NOT_FOUND: ${requestId}`);\n if (raw.status !== 'pending') throw new Error(`INVALID_STATE: request is ${raw.status}`);\n\n const pendingApprovers = csvSplit(raw.pending_approvers);\n // A privileged admin may override a stuck request (#3424) even when they\n // hold no slot — the escape hatch for an approval routed to an unstaffed\n // position or to approvers who have all left.\n const isOverride = this.isOverrideActor(context, raw.organization_id ?? null);\n const isSlotHolder = pendingApprovers.includes(actorId);\n if (!isSlotHolder && !isOverride) {\n throw new Error(`FORBIDDEN: actor '${actorId}' is not a pending approver`);\n }\n // #4466 — the audit fact this decision would otherwise drop: the actor was\n // admitted ONLY by the override branch, holding no slot in the staffed\n // slate. An admin who IS a slot holder is approving normally, so the two\n // conditions are recorded apart rather than collapsed into \"actor is admin\".\n const viaOverride = isOverride && !isSlotHolder;\n\n const config = parseJson<ApprovalNodeConfig>(raw.node_config_json, { approvers: [], behavior: 'first_response' } as any);\n const org = raw.organization_id ?? null;\n const nodeId: string | null = raw.flow_node_id ?? raw.current_step ?? null;\n const runId: string | null = raw.flow_run_id ?? null;\n const now = this.clock.now().toISOString();\n\n // #3447 P2: decision outputs — validated BEFORE any write (audit included)\n // so an out-of-contract payload rejects atomically. The trust model is a\n // `screen` node's: the AUTHOR declares the keys (`config.decisionOutputs`),\n // the approver only fills values. A decision carrying undeclared keys is a\n // caller bug; `decision`/`requestId` are reserved by the resume envelope.\n const outputKeys = input.outputs ? Object.keys(input.outputs) : [];\n let acceptedOutputs: Record<string, unknown> | undefined;\n // Typed declarations and bare keys whitelist identically — one normalizer\n // (spec) is the single reader of the union shape.\n const declaredDefs = normalizeDecisionOutputs((config as any).decisionOutputs);\n if (outputKeys.length) {\n const declared = declaredDefs.map(d => d.key);\n if (!declared.length) {\n throw new Error(\n `VALIDATION_FAILED: this approval node declares no decisionOutputs — outputs are not accepted. `\n + `Declare the keys on the node config (decisionOutputs: [${outputKeys.map(k => `'${k}'`).join(', ')}]) `\n + `to let approvers hand them to the flow.`,\n );\n }\n const reserved = outputKeys.filter(k => k === 'decision' || k === 'requestId');\n if (reserved.length) {\n throw new Error(\n `VALIDATION_FAILED: decision output key(s) \\`${reserved.join('`, `')}\\` are reserved by the resume `\n + `envelope — pick different names.`,\n );\n }\n const undeclared = outputKeys.filter(k => !declared.includes(k));\n if (undeclared.length) {\n throw new Error(\n `VALIDATION_FAILED: decision output key(s) \\`${undeclared.join('`, `')}\\` are not declared on this `\n + `node — declared keys: ${declared.map(k => `'${k}'`).join(', ') || '(none)'}.`,\n );\n }\n acceptedOutputs = { ...input.outputs };\n }\n\n // objectui#2955: `required` outputs. Unlike `type`/`multiple` — which only\n // shape the input widget — this one is a runtime contract: the flow must\n // never resume past this node with a required key missing, because that is\n // precisely what a downstream `expression` approver reads. Before this,\n // an author's only backstop was `onEmptyApprovers` (the next node opens,\n // resolves nobody, and stalls for an admin rescue).\n //\n // APPROVE only. A reject leaves down the reject edge, where the outputs\n // are not read — demanding routing data to say \"no\" would block the\n // rejection. Outputs still ride a reject when the approver filled them.\n //\n // Enforced for EVERY approve path, with no elevation bypass: a one-click\n // email action link cannot fill a form, and an `auto_approve` SLA\n // escalation has nobody to ask — both must fail rather than resume the run\n // with the key missing. The escalation sweep already isolates a throwing\n // request (it catches per-request), so that decision simply stays pending\n // and visibly overdue instead of advancing into a broken node.\n if (input.decision === 'approve') {\n const missing = declaredDefs\n .filter(d => d.required === true && isBlankDecisionOutput(input.outputs?.[d.key]))\n .map(d => d.key);\n if (missing.length) {\n throw new Error(\n `VALIDATION_FAILED: decision output(s) \\`${missing.join('`, `')}\\` are required to approve this `\n + `request — open the approval and fill them in before approving.`,\n );\n }\n }\n\n // The run behind this request must still be resumable before ANY of it is\n // written down (#4420). Last of the refusals, first before the writes: a\n // decision recorded against a dead run is a zombie nothing later can undo,\n // and the approver is told it succeeded.\n //\n // Non-finalizing votes are checked too — a co-sign that can never reach a\n // resume is just as stuck, and catching it here keeps the tally honest.\n await this.assertRunResumable(runId, requestId);\n\n // Audit the decision first so the quorum/per_group tally below sees it.\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: org,\n step_name: nodeId, step_index: 0, action: input.decision,\n actor_id: actorId, comment: input.comment ?? null,\n // #4466: the override is recorded on the DECISION, not inferred later.\n // Written as an explicit `false` for an ordinary decision so a reader can\n // tell \"checked, and it was not an override\" from a legacy row's `null`.\n via_override: viaOverride,\n attachments: input.attachments?.length ? input.attachments : null,\n created_at: now,\n }, { context: SYSTEM_CTX });\n\n // Multi-approver aggregation on approve (#3266). A rejection always\n // finalizes the node (one veto), so only the approve path can hold it open.\n // `first_response` finalizes on the first approval (falls straight through).\n const behavior = config.behavior ?? 'first_response';\n // A privileged override (an admin rescuing a stuck request, #3424) is an\n // authoritative decision, not one vote among the resolved slate — it\n // finalizes the node immediately, regardless of `unanimous`/`quorum`/\n // `per_group`. Only a real slot holder's approval feeds the multi-approver\n // tally below.\n if (input.decision === 'approve' && behavior !== 'first_response' && isSlotHolder) {\n const acts = await this.engine.find('sys_approval_action', {\n where: { request_id: requestId, step_index: 0, action: 'approve' }, limit: 1000, context: SYSTEM_CTX,\n });\n const approved = new Set<string>((acts ?? []).map((a: any) => String(a.actor_id ?? '')).filter(Boolean));\n\n // Tally against the OPEN-time snapshot (already OOO-substituted) for\n // every behavior that carries one. Re-resolution survives ONLY as the\n // back-compat path for requests opened before the snapshot existed —\n // it cannot ever run for an `expression` approver (#3447 P2: decide has\n // no flow variables to evaluate against; open time is the only\n // resolution point), and those always have a snapshot.\n const snapshotGroups = (config as any).__approverGroups as Record<string, string[]> | undefined;\n let original: string[];\n let groupMap: Record<string, string[]>;\n if (snapshotGroups) {\n groupMap = snapshotGroups;\n original = Object.keys(snapshotGroups);\n } else {\n original = await this.expandApprovers(\n { approvers: config.approvers }, parseJson(raw.payload_json, undefined), org,\n );\n groupMap = {};\n }\n\n if (!this.isApprovalSatisfied(behavior, config, original, groupMap, approved)) {\n const stillPending = original.filter(a => !approved.has(a));\n await this.engine.update('sys_approval_request', {\n id: requestId, pending_approvers: stillPending.join(','), updated_at: now,\n // #3447 P2: a mid-tally approval may carry outputs too (unanimous /\n // per_group co-sign, each approver contributing their declared keys)\n // — accumulate them on the snapshot so the FINALIZING decision hands\n // the merged set to the flow.\n ...(acceptedOutputs ? {\n node_config_json: JSON.stringify({\n ...config,\n __decisionOutputs: { ...((config as any).__decisionOutputs ?? {}), ...acceptedOutputs },\n }),\n } : {}),\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, stillPending, org, now);\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, nodeId, finalized: false, decision: input.decision };\n }\n }\n\n const finalStatus = input.decision === 'approve' ? 'approved' : 'rejected';\n // #3447 P2: the full accumulated output set — earlier co-sign votes' plus\n // this finalizing decision's — resumes the run and stays snapshotted for\n // the audit trail (\"what did the approvers hand the flow\").\n const mergedOutputs: Record<string, unknown> | undefined =\n acceptedOutputs || (config as any).__decisionOutputs\n ? { ...((config as any).__decisionOutputs ?? {}), ...(acceptedOutputs ?? {}) }\n : undefined;\n await this.engine.update('sys_approval_request', {\n id: requestId, status: finalStatus, pending_approvers: null, completed_at: now, updated_at: now,\n ...(mergedOutputs ? {\n node_config_json: JSON.stringify({ ...config, __decisionOutputs: mergedOutputs }),\n } : {}),\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, [], org, now);\n if (config.approvalStatusField) {\n await this.mirrorStatusField(\n raw.object_name, raw.record_id, config.approvalStatusField, finalStatus,\n actingUserId(context),\n );\n }\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, nodeId, finalized: true, decision: input.decision, outputs: mergedOutputs };\n }\n\n /**\n * Continue the owning flow run after an outcome this service has already\n * authorized and written down (#3801).\n *\n * The `approval` node declares `resumeAuthority: 'service'`, so the engine\n * refuses any resume of an approval suspension that does not carry\n * {@link RESUME_AUTHORITY_SERVICE}. Every approvals-side resume goes through\n * here so the marker is stamped in ONE place — a new outcome path cannot\n * quietly ship a resume that the gate then rejects at runtime, and nothing\n * in this file hands the marker to a caller-supplied signal.\n *\n * Callers still guard on `typeof this.automation?.resume === 'function'`\n * (approvals runs fine with no automation attached) and keep their own\n * try/catch, because what a failed resume means differs per path.\n *\n * Throws when the engine REPORTS failure, not only when it throws one. The\n * engine answers a lost run with `{ success: false, code: 'RUN_NOT_FOUND' }`\n * — a plain return value that every caller here used to discard, which is\n * how an approval could be recorded, reported as resumed, and leave its flow\n * stranded forever (#4420). The thrown error carries {@link resumeCodeOf}'s\n * `resumeCode` so callers can tell a benign duplicate from a dead run.\n */\n private async serviceResume(\n runId: string,\n signal: { output?: Record<string, unknown>; branchLabel?: string },\n ): Promise<void> {\n const result = await this.automation!.resume!(runId, { ...signal, [RESUME_AUTHORITY_SERVICE]: true });\n const reported = result as { success?: boolean; code?: string; error?: string } | undefined;\n // Only an explicit `success: false` is a failure. An engine (or a test\n // double) that returns nothing is reporting nothing, and has always meant\n // \"it ran\".\n if (reported && typeof reported === 'object' && reported.success === false) {\n const err = new Error(\n `resume of run '${runId}' failed${reported.code ? ` [${reported.code}]` : ''}: ${reported.error ?? 'unknown error'}`,\n ) as Error & { resumeCode?: string };\n err.resumeCode = reported.code;\n throw err;\n }\n }\n\n /** The engine failure code behind a {@link serviceResume} rejection, if any. */\n private static resumeCodeOf(err: unknown): string | undefined {\n return (err as { resumeCode?: string } | undefined)?.resumeCode;\n }\n\n /**\n * Refuse an operation whose whole point is to advance a flow run when that\n * run no longer exists — BEFORE anything is written down (#4420).\n *\n * The half-state this prevents is the one the issue reported: a request\n * flipped to `approved`, a success toast, and a flow that never moves. Once\n * the decision row is written there is nothing left to fail cleanly.\n *\n * Deliberately permissive at the edges:\n * - no automation attached, or an engine without `hasSuspendedRun` → no\n * pre-flight at all (standalone approvals compositions are unaffected);\n * - the store cannot be READ → fail OPEN. A transient outage must not block\n * every decision in the tenant; the post-resume check still catches a real\n * failure and reports it loudly.\n */\n private async assertRunResumable(runId: string | null | undefined, requestId: string): Promise<void> {\n if (!runId) return;\n if (typeof this.automation?.resume !== 'function') return;\n if (typeof this.automation?.hasSuspendedRun !== 'function') return;\n let alive: boolean;\n try {\n alive = await this.automation.hasSuspendedRun(runId);\n } catch (err: any) {\n this.logger?.warn?.('[approvals] could not verify the flow run is resumable — proceeding', {\n request: requestId, run: runId, error: err?.message ?? String(err),\n });\n return;\n }\n if (!alive) {\n throw new Error(\n `RESUME_TARGET_LOST: the flow run '${runId}' behind request ${requestId} no longer exists ` +\n `(it was cancelled, or it paused in a process that did not persist suspended runs). ` +\n `Nothing was recorded. An administrator can recall the request to release the record.`,\n );\n }\n }\n\n /**\n * The run named by a recorded outcome cannot be advanced at all, because no\n * automation engine in THIS process implements the capability it needs\n * (#4420). Returns the reason, or `undefined` when the capability is there\n * and the caller should proceed.\n *\n * **Why this is not simply \"not our problem\".** Approvals is legitimately\n * usable with no engine attached, and {@link assertRunResumable} deliberately\n * stays out of the way for that reason. But \"no engine is attached\" and \"no\n * run is waiting\" are different facts, and only the second is benign: a\n * `flow_run_id` on the row is the request's OWN declaration that a run is\n * parked on this decision. Deciding it in a process that cannot resume it\n * reproduces #4420's reported half-state exactly — a durable decision, a\n * mirrored status field frozen mid-workflow, a flow parked forever — while\n * the caller is answered HTTP 200. The engine-less composition is the one\n * path the #4420 fix left silent, because every guard it added hangs off an\n * engine that is not there.\n *\n * **So the outcome stands, but it is never silent.** Rolling the decision\n * back is not on the table (a human really did decide, and the row is\n * already durable), and refusing every such call would break the standalone\n * compositions the pre-flight protects. What is owed is the report: `error`\n * level per AGENTS.md's durability rule — persisted state and runtime state\n * disagree and nothing looks broken from the outside — plus a `resumeError`\n * on the response, so `resumed: false` carries its reason instead of leaving\n * the caller to guess whether a resume was even attempted.\n *\n * Reuses the registered `RESUME_FAILED` code (ADR-0112 ledger) and\n * {@link serviceResume}'s message shape: the fact being reported — an\n * outcome recorded whose run did not advance — is the same one, and this\n * needs no new vocabulary of its own.\n */\n private missingRunCapability(\n runId: string,\n requestId: string,\n what: string,\n capability: 'resume' | 'cancelRun',\n ): string | undefined {\n const fn = capability === 'resume' ? this.automation?.resume : this.automation?.cancelRun;\n if (typeof fn === 'function') return undefined;\n this.logger?.error?.(\n '[approvals] no automation engine to advance the recorded outcome — the run is stranded',\n { request: requestId, run: runId, outcome: what, capability },\n );\n return (\n `${capability} of run '${runId}' failed [RESUME_FAILED]: ${what} was recorded on request ${requestId}, ` +\n `but no automation engine in this process can ${capability} its flow run — the run stays parked and the ` +\n `record's mirrored status will not advance. Compose the automation service in this host, or recall the ` +\n `request to release the record.`\n );\n }\n\n /**\n * Resume the run behind an outcome that has ALREADY been written down, and\n * fail loudly when it cannot be (#4420).\n *\n * For the operations whose product is the resume — a finalised decision, a\n * send-back, a resubmit. Their rows are durable by the time this runs, so a\n * failure here cannot be undone; the one thing left worth doing is refusing\n * to call it success. {@link assertRunResumable} is what keeps this rare:\n * everything it catches never reaches a write.\n *\n * `RESUME_IN_PROGRESS` is the exception — a concurrent resume is already\n * advancing the run, so the outcome stands and only `resumed` is false. So\n * is a composition with no engine at all ({@link missingRunCapability}),\n * which cannot throw without breaking every standalone deployment — it\n * reports through `resumeError` instead.\n *\n * @param what - how the recorded outcome reads in the error, e.g.\n * `\"the approve decision\"`.\n */\n private async resumeRecordedOutcome(\n runId: string,\n requestId: string,\n what: string,\n signal: { output?: Record<string, unknown>; branchLabel?: string },\n ): Promise<{ resumed: boolean; resumeError?: string }> {\n const missing = this.missingRunCapability(runId, requestId, what, 'resume');\n if (missing) return { resumed: false, resumeError: missing };\n try {\n await this.serviceResume(runId, signal);\n return { resumed: true };\n } catch (err: any) {\n const reason = err?.message ?? String(err);\n if (ApprovalService.resumeCodeOf(err) === 'RESUME_IN_PROGRESS') {\n this.logger?.warn?.('[approvals] resume skipped — already in progress', {\n request: requestId, run: runId, outcome: what,\n });\n return { resumed: false, resumeError: reason };\n }\n this.logger?.error?.('[approvals] resume failed — the run is stranded', {\n request: requestId, run: runId, outcome: what, error: reason,\n });\n throw new Error(\n `RESUME_FAILED: ${what} was recorded on request ${requestId}, but its flow run '${runId}' ` +\n `could not be resumed and is now stranded: ${reason}`,\n );\n }\n }\n\n /**\n * Public contract entrypoint (ADR-0019). Records a decision on a node-driven\n * request via {@link ApprovalService.decideNode} and, when it finalizes,\n * resumes the owning flow run down the matching `approve` / `reject` edge.\n *\n * A finalising decision whose run cannot be resumed FAILS (#4420). The\n * decision is already durable by then, so the failure cannot be rolled back\n * — but it must not be reported as success either: this used to answer HTTP\n * 200 with `resumed: true` while the flow stayed parked forever, which left\n * the approver with no signal and the record mirroring a stage it never\n * reached. `decideNode`'s pre-flight means the common case (the run died\n * before the decision) never gets this far; what survives here is a genuine\n * race, and it names the stranded run.\n */\n async decide(\n requestId: string,\n input: ApprovalDecisionInput,\n context: ExecutionContext,\n ): Promise<ApprovalDecisionResult> {\n const result = await this.decideNode(requestId, input, context);\n\n let resumed = false;\n let resumeError: string | undefined;\n // No `typeof this.automation?.resume === 'function'` guard here (#4420):\n // skipping the call when no engine is attached is precisely how a decision\n // against a parked run returned 200 / `resumed: false` with nothing logged.\n // `resumeRecordedOutcome` reports that composition gap instead of hiding it.\n if (result.finalized && result.runId) {\n const branchLabel = result.decision === 'approve'\n ? APPROVAL_BRANCH_LABELS.approve\n : APPROVAL_BRANCH_LABELS.reject;\n const outcome = await this.resumeRecordedOutcome(\n result.runId, requestId, `the ${result.decision} decision`,\n {\n branchLabel,\n // #3447 P2: accepted decision outputs ride the resume envelope and\n // land as `<nodeId>.<key>` flow variables — a later approval node's\n // `expression` approver reads them as `vars.<nodeId>.<key>`.\n // Reserved keys are spread LAST so no output can shadow them (the\n // whitelist already rejects them; this is defense in depth).\n output: { ...(result.outputs ?? {}), decision: result.decision, requestId },\n },\n );\n resumed = outcome.resumed;\n resumeError = outcome.resumeError;\n }\n\n return {\n request: result.request,\n finalized: result.finalized,\n decision: result.decision,\n runId: result.runId,\n resumed,\n ...(resumeError ? { resumeError } : {}),\n };\n }\n\n /**\n * Withdraw a pending request (submitter only). Finalises the row as\n * `recalled`, releases the record lock (keyed on pending status), mirrors\n * the status field when configured, and resumes the owning flow run down\n * the `reject` branch with `output.decision = 'recall'` — leaving the run\n * suspended forever would leak it.\n *\n * ADR-0044: also valid on the LATEST `returned` request of its run — the\n * submitter abandons the revision window instead of resubmitting. The run\n * is then paused at the revise-window node (no reject edge), so it is\n * terminally cancelled via {@link ApprovalResumeSurface.cancelRun} rather\n * than resumed.\n */\n async recall(\n requestId: string,\n input: ApprovalRecallInput,\n context: ExecutionContext,\n ): Promise<ApprovalRecallResult> {\n if (!requestId) throw new Error('VALIDATION_FAILED: requestId is required');\n const actorId = await this.resolveActor(input?.actorId, context);\n\n const rawRows = await this.engine.find('sys_approval_request', {\n where: { id: requestId }, limit: 1, context: SYSTEM_CTX,\n });\n const raw: any = Array.isArray(rawRows) ? rawRows[0] : null;\n if (!raw) throw new Error(`REQUEST_NOT_FOUND: ${requestId}`);\n const inReviseWindow = raw.status === 'returned';\n if (raw.status !== 'pending' && !inReviseWindow) {\n throw new Error(`INVALID_STATE: request is ${raw.status}`);\n }\n // The submitter withdraws their own request; a privileged admin may recall\n // any pending request to release a stuck record (#3424).\n if (!this.isOverrideActor(context, raw.organization_id ?? null)\n && raw.submitter_id && String(raw.submitter_id) !== String(actorId)) {\n throw new Error(`FORBIDDEN: only the submitter may recall this request`);\n }\n // A returned request is only recallable while it is still the run's live\n // frontier — a resubmitted (or later-node) request supersedes it.\n if (inReviseWindow) await this.assertLatestForRun(raw);\n\n const config = parseJson<ApprovalNodeConfig>(raw.node_config_json, { approvers: [], behavior: 'first_response' } as any);\n const org = raw.organization_id ?? null;\n const nodeId: string | null = raw.flow_node_id ?? raw.current_step ?? null;\n const runId: string | null = raw.flow_run_id ?? null;\n const now = this.clock.now().toISOString();\n\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: org,\n step_name: nodeId, step_index: 0, action: 'recall',\n actor_id: actorId, comment: input.comment ?? null, created_at: now,\n }, { context: SYSTEM_CTX });\n\n await this.engine.update('sys_approval_request', {\n id: requestId, status: 'recalled', pending_approvers: null, completed_at: now, updated_at: now,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, [], org, now);\n if (config.approvalStatusField) {\n await this.mirrorStatusField(\n raw.object_name, raw.record_id, config.approvalStatusField, 'recalled',\n actingUserId(context),\n );\n }\n\n // A recall ABANDONS the request, so a run that cannot be resumed must not\n // fail the call — the withdrawal and the record-lock release are the point,\n // and they have already happened. It is still reported rather than\n // swallowed: `resumed: false` plus a reason, logged at error (#4420).\n let resumed = false;\n let resumeError: string | undefined;\n if (inReviseWindow) {\n // ADR-0044: the run is paused at the revise-window node, which has no\n // reject out-edge to resume down — terminally cancel it instead.\n if (runId) {\n resumeError = this.missingRunCapability(runId, requestId, 'the recall', 'cancelRun');\n if (!resumeError) {\n try {\n await this.automation!.cancelRun!(runId, `approval request ${requestId} recalled during revision`);\n } catch (err: any) {\n resumeError = err?.message ?? String(err);\n this.logger?.error?.('[approvals] cancelRun after revise-window recall failed — the run may be stranded', {\n request: requestId, run: runId, error: resumeError,\n });\n }\n }\n }\n } else if (runId) {\n resumeError = this.missingRunCapability(runId, requestId, 'the recall', 'resume');\n if (!resumeError) {\n try {\n await this.serviceResume(runId, {\n branchLabel: APPROVAL_BRANCH_LABELS.reject,\n output: { decision: 'recall', requestId },\n });\n resumed = true;\n } catch (err: any) {\n resumeError = err?.message ?? String(err);\n this.logger?.error?.('[approvals] resume after recall failed — the run may be stranded', {\n request: requestId, run: runId, error: resumeError,\n });\n }\n }\n }\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, resumed, ...(resumeError ? { resumeError } : {}) };\n }\n\n // ── Send back for revision / resubmit (ADR-0044) ─────────────\n\n /**\n * ADR-0044 send back for revision. Finalises the pending request as\n * `returned` (a third terminal state — approver-initiated rework, distinct\n * from submitter-initiated `recalled`) and resumes the owning flow run down\n * its `revise` edge to the revise window: the record lock (keyed on `pending`)\n * releases, the submitter reworks the data, then {@link resubmit}s.\n *\n * Requires the approval node to declare a `revise` out-edge — validated\n * BEFORE any mutation, because resuming with an unmatched `branchLabel`\n * falls back to *all* out-edges. Past the node's `maxRevisions` budget the\n * request auto-rejects instead (resumes down `reject` with\n * `output.autoRejected = true`) so instances cannot orbit forever.\n */\n async sendBack(\n requestId: string,\n input: ApprovalSendBackInput,\n context: ExecutionContext,\n ): Promise<ApprovalSendBackResult> {\n const actorId = await this.resolveActor(input?.actorId, context);\n const raw = await this.loadPendingRow(requestId);\n const pending = csvSplit(raw.pending_approvers);\n if (!context.isSystem && !pending.includes(actorId)) {\n throw new Error(`FORBIDDEN: actor '${actorId}' is not a pending approver`);\n }\n\n const config = parseJson<ApprovalNodeConfig>(raw.node_config_json, { approvers: [], behavior: 'first_response' } as any);\n const org = raw.organization_id ?? null;\n const nodeId: string | null = raw.flow_node_id ?? raw.current_step ?? null;\n const runId: string | null = raw.flow_run_id ?? null;\n\n await this.assertReviseEdge(raw, nodeId);\n // A send-back exists to move the run to its revise window. If the run\n // is gone there is nothing to send back TO, so refuse before writing —\n // same reasoning as decideNode's pre-flight (#4420).\n await this.assertRunResumable(runId, requestId);\n\n const now = this.clock.now().toISOString();\n const maxRevisions = typeof (config as any).maxRevisions === 'number' ? (config as any).maxRevisions : 3;\n let priorSendBacks = 0;\n if (runId && nodeId) {\n const siblings = await this.engine.find('sys_approval_request', {\n where: { flow_run_id: runId, flow_node_id: nodeId, status: 'returned' }, limit: 500, context: SYSTEM_CTX,\n });\n priorSendBacks = Array.isArray(siblings) ? siblings.length : 0;\n }\n\n // Audit the revise intent first (audit-first, like decideNode) — on the\n // auto-reject path the trail then reads `revise → reject`, preserving\n // what the approver actually asked for.\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: org,\n step_name: nodeId, step_index: 0, action: 'revise',\n actor_id: actorId, comment: input.comment ?? null, created_at: now,\n }, { context: SYSTEM_CTX });\n\n if (priorSendBacks >= maxRevisions) {\n // Revision budget exhausted — auto-reject (ADR-0044 loop guard).\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: org,\n step_name: nodeId, step_index: 0, action: 'reject',\n actor_id: actorId,\n comment: `Auto-rejected: revision limit (${maxRevisions}) exceeded`, created_at: now,\n }, { context: SYSTEM_CTX });\n await this.engine.update('sys_approval_request', {\n id: requestId, status: 'rejected', pending_approvers: null, completed_at: now, updated_at: now,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, [], org, now);\n if (config.approvalStatusField) {\n await this.mirrorStatusField(\n raw.object_name, raw.record_id, config.approvalStatusField, 'rejected',\n actingUserId(context),\n );\n }\n let resumed = false;\n let resumeError: string | undefined;\n if (runId) {\n const outcome = await this.resumeRecordedOutcome(\n runId, requestId, 'the auto-rejection',\n {\n branchLabel: APPROVAL_BRANCH_LABELS.reject,\n output: { decision: 'reject', autoRejected: true, requestId },\n },\n );\n resumed = outcome.resumed;\n resumeError = outcome.resumeError;\n }\n if (raw.submitter_id) {\n await this.notify({\n topic: 'approval.returned',\n audience: [String(raw.submitter_id)],\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n payload: {\n title: 'Approval auto-rejected',\n message: `Your ${raw.object_name}/${raw.record_id} exceeded the revision limit (${maxRevisions}) and was rejected.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, resumed, autoRejected: true, ...(resumeError ? { resumeError } : {}) };\n }\n\n await this.engine.update('sys_approval_request', {\n id: requestId, status: 'returned', pending_approvers: null, completed_at: now, updated_at: now,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, [], org, now);\n if (config.approvalStatusField) {\n await this.mirrorStatusField(\n raw.object_name, raw.record_id, config.approvalStatusField, 'returned',\n actingUserId(context),\n );\n }\n\n let resumed = false;\n let resumeError: string | undefined;\n if (runId) {\n const outcome = await this.resumeRecordedOutcome(\n runId, requestId, 'the send-back',\n {\n branchLabel: APPROVAL_BRANCH_LABELS.revise,\n output: { decision: 'revise', requestId },\n },\n );\n resumed = outcome.resumed;\n resumeError = outcome.resumeError;\n }\n\n if (raw.submitter_id) {\n await this.notify({\n topic: 'approval.returned',\n audience: [String(raw.submitter_id)],\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n payload: {\n title: 'Sent back for revision',\n message: input.comment?.trim() || `Your ${raw.object_name}/${raw.record_id} needs rework before it can be approved.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, resumed, ...(resumeError ? { resumeError } : {}) };\n }\n\n /**\n * ADR-0044 resubmit after rework. Valid on the LATEST `returned` request of\n * its run, submitter-only. Audits `resubmit` on the returned (round-N)\n * request and resumes the run from the revise-window node; traversal walks\n * the declared back-edge into the approval node, whose executor opens the\n * round-N+1 request — fresh approver slate, record re-locks.\n */\n async resubmit(\n requestId: string,\n input: ApprovalResubmitInput,\n context: ExecutionContext,\n ): Promise<ApprovalResubmitResult> {\n const actorId = await this.resolveActor(input?.actorId, context);\n const rawRows = await this.engine.find('sys_approval_request', {\n where: { id: requestId }, limit: 1, context: SYSTEM_CTX,\n });\n const raw: any = Array.isArray(rawRows) ? rawRows[0] : null;\n if (!raw) throw new Error(`REQUEST_NOT_FOUND: ${requestId}`);\n if (raw.status !== 'returned') {\n throw new Error(`INVALID_STATE: request is ${raw.status} (resubmit applies to returned requests)`);\n }\n if (!context.isSystem && raw.submitter_id && String(raw.submitter_id) !== String(actorId)) {\n throw new Error('FORBIDDEN: only the submitter may resubmit');\n }\n await this.assertLatestForRun(raw);\n\n // A colliding pending request on the same record (e.g. a record-change\n // trigger re-fired off an edit made inside the revise window) would make\n // the approval node's re-entry fail AFTER the engine consumed the\n // suspension — permanently killing the run. Refuse up front instead; the\n // submitter resolves the collision (recall the other request) first.\n const colliding = await this.engine.find('sys_approval_request', {\n where: { object_name: raw.object_name, record_id: raw.record_id, status: 'pending' },\n limit: 1, context: SYSTEM_CTX,\n });\n if (Array.isArray(colliding) && colliding[0]) {\n throw new Error(\n `DUPLICATE_REQUEST: another approval request is already pending on ${raw.object_name}/${raw.record_id} — resolve it before resubmitting`,\n );\n }\n\n const org = raw.organization_id ?? null;\n const nodeId: string | null = raw.flow_node_id ?? raw.current_step ?? null;\n const runId: string | null = raw.flow_run_id ?? null;\n const now = this.clock.now().toISOString();\n\n // The next round only exists if the resume lands, so a run that is already\n // gone fails the resubmit outright rather than recording a round that can\n // never open (#4420).\n await this.assertRunResumable(runId, requestId);\n\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: org,\n step_name: nodeId, step_index: 0, action: 'resubmit',\n actor_id: actorId, comment: input.comment ?? null, created_at: now,\n }, { context: SYSTEM_CTX });\n\n let resumed = false;\n let resumeError: string | undefined;\n if (runId) {\n const outcome = await this.resumeRecordedOutcome(\n runId, requestId, 'the resubmit',\n {\n branchLabel: APPROVAL_BRANCH_LABELS.resubmit,\n output: { resubmitted: true, requestId },\n },\n );\n resumed = outcome.resumed;\n resumeError = outcome.resumeError;\n }\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, resumed, ...(resumeError ? { resumeError } : {}) };\n }\n\n /**\n * ADR-0044 guard: the flow's approval node must declare a `revise`\n * out-edge before send-back is allowed — the engine's branch-label fallback\n * (no matching label ⇒ ALL out-edges) must never be reachable from a user\n * action.\n *\n * Since #3823 it also checks WHAT that edge targets: the revise window must\n * be an `approval_revise` node, the pause this service owns. ADR-0044 D3\n * pointed the edge at an ordinary `wait`, which is `resumeAuthority: 'any'`,\n * so a raw engine resume walked the resubmit back-edge with no submitter\n * check, no `resubmit` audit row, and — with a pending request colliding on\n * the record — destroyed the run by consuming the suspension before the\n * re-entry failed. Refused HERE, before any mutation, for the same reason the\n * missing-edge check is: a run must never be parked in a revise window that\n * something other than {@link resubmit} can advance.\n *\n * Also refused at authoring time — `flow-approval-revise-target-not-service-owned`\n * in `@objectstack/lint` gates `os build` / `os validate` / `os lint` and the\n * runtime metadata publish path — so a flow reaching this check at all is one\n * published before that gate existed.\n */\n private async assertReviseEdge(raw: any, nodeId: string | null): Promise<void> {\n const processName = String(raw.process_name ?? '');\n const flowName = processName.startsWith('flow:') ? processName.slice('flow:'.length) : undefined;\n if (!flowName || !nodeId || typeof this.automation?.getFlow !== 'function') {\n throw new Error('VALIDATION_FAILED: send-back requires the owning flow definition (automation engine unavailable)');\n }\n const flow: any = await this.automation.getFlow(flowName);\n const reviseEdges = Array.isArray(flow?.edges)\n ? flow.edges.filter((e: any) => e?.source === nodeId && e?.label === APPROVAL_BRANCH_LABELS.revise)\n : [];\n if (reviseEdges.length === 0) {\n throw new Error(\n `VALIDATION_FAILED: approval node '${nodeId}' has no '${APPROVAL_BRANCH_LABELS.revise}' out-edge — ` +\n 'the flow does not support send-back for revision',\n );\n }\n const nodeTypeById = new Map<string, string>(\n (Array.isArray(flow?.nodes) ? flow.nodes : [])\n .filter((n: any) => typeof n?.id === 'string')\n .map((n: any) => [n.id as string, typeof n.type === 'string' ? n.type : '']),\n );\n for (const edge of reviseEdges) {\n const target = typeof edge?.target === 'string' ? edge.target : '';\n const targetType = nodeTypeById.get(target);\n if (targetType === APPROVAL_REVISE_NODE_TYPE) continue;\n throw new Error(\n `VALIDATION_FAILED: approval node '${nodeId}' has a '${APPROVAL_BRANCH_LABELS.revise}' out-edge into ` +\n `node '${target || '(unknown)'}'` +\n (targetType === undefined ? ' which the flow does not declare' : ` of type '${targetType || '(untyped)'}'`) +\n `, but the revise window must be an '${APPROVAL_REVISE_NODE_TYPE}' node — that pause continues only ` +\n 'through this service (submitter-only, audited, and refusing a colliding pending request), and any ' +\n `other node type there is resumable by anyone with the run id (amended ADR-0044, #3823). Fix the flow: ` +\n `set node '${target || '<revise target>'}' to type '${APPROVAL_REVISE_NODE_TYPE}'.`,\n );\n }\n }\n\n /**\n * ADR-0044 guard: a `returned` request is only actionable (resubmit /\n * recall) while it is still the newest request on its run — a later round\n * or a later node's request supersedes it.\n */\n private async assertLatestForRun(raw: any): Promise<void> {\n const runId = raw.flow_run_id;\n if (!runId) return;\n // SortNode's key is `order` (spec/data/query.zod.ts) — `direction` would\n // silently default to ascending and return the OLDEST row.\n const rows = await this.engine.find('sys_approval_request', {\n where: { flow_run_id: runId },\n orderBy: [{ field: 'created_at', order: 'desc' }], limit: 1, context: SYSTEM_CTX,\n });\n const latest: any = Array.isArray(rows) ? rows[0] : null;\n if (latest && String(latest.id) !== String(raw.id)) {\n throw new Error('INVALID_STATE: a newer approval request supersedes this one');\n }\n }\n\n // ── Thread interactions (no flow movement) ───────────────────\n\n /**\n * Hand a pending-approver slot to someone else. `from` defaults to the\n * actor itself; the actor must hold the slot being handed over (or be a\n * system caller). A privileged admin (#3424) may reassign a request whose\n * slate holds no real user — an unstaffed-position literal — by handing the\n * whole request to a real approver, rescuing it from the locked dead-end.\n * Audits `reassign` and notifies the new approver.\n */\n async reassign(\n requestId: string,\n input: { actorId: string; to: string; from?: string; comment?: string },\n context: ExecutionContext,\n ): Promise<{ request: ApprovalRequestRow }> {\n const actorId = await this.resolveActor(input?.actorId, context);\n const to = String(input?.to ?? '').trim();\n if (!to) throw new Error('VALIDATION_FAILED: `to` (new approver) is required');\n const raw = await this.loadPendingRow(requestId);\n\n const pending = csvSplit(raw.pending_approvers);\n if (pending.includes(to)) {\n throw new Error(`VALIDATION_FAILED: '${to}' is already a pending approver`);\n }\n const isOverride = this.isOverrideActor(context, raw.organization_id ?? null);\n const from = String(input.from ?? actorId).trim();\n // #4466 — same rule as `decideNode`: the marker records that the actor was\n // admitted only by the privileged branch, holding no slot themselves. A\n // reassign is the other action #3424 lets an admin take over a slate they\n // are not on, so it carries the same fact.\n const viaOverride = isOverride && !pending.includes(actorId);\n let next: string[];\n if (pending.includes(from)) {\n // Normal hand-off: the actor holds the slot being moved (or is a\n // system/admin caller acting on a real holder's slot).\n if (!context.isSystem && !isOverride && actorId !== from && !pending.includes(actorId)) {\n throw new Error(`FORBIDDEN: actor '${actorId}' is not a pending approver`);\n }\n next = pending.map(a => (a === from ? to : a));\n } else if (isOverride) {\n // Admin rescue (#3424): the caller holds no slot — the slate is an\n // unstaffed-position literal or a set of departed approvers. Reassign the\n // whole request to a real approver so the normal decision flow can resume.\n next = [to];\n } else {\n throw new Error(`FORBIDDEN: '${from}' is not a pending approver on this request`);\n }\n const now = this.clock.now().toISOString();\n // Audit first, then mutate — mirrors decideNode(), so a failed audit\n // write can never leave a moved slot without a trail.\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: raw.organization_id ?? null,\n step_name: raw.flow_node_id ?? raw.current_step ?? null, step_index: 0, action: 'reassign',\n // The hand-off parties are STRUCTURED fields (#4365) — the old default\n // comment (`\"<from> → <to>\"`) baked raw user ids into user-facing text.\n // `comment` is pure user input: absent unless the actor wrote one.\n actor_id: actorId, reassign_from: from, reassign_to: to,\n via_override: viaOverride,\n comment: input.comment ?? null, created_at: now,\n }, { context: SYSTEM_CTX });\n // per_group / quorum (#3266): carry the delegated slot's group membership to\n // the new approver in the snapshot, so their approval still counts for the\n // original group.\n let configPatch: Record<string, unknown> = {};\n try {\n const cfg = parseJson<any>(raw.node_config_json, null);\n const groups = cfg?.__approverGroups as Record<string, string[]> | undefined;\n if (groups && groups[from] && !groups[to]) {\n groups[to] = groups[from];\n delete groups[from];\n configPatch = { node_config_json: JSON.stringify(cfg) };\n }\n } catch { /* snapshot left untouched on parse failure */ }\n await this.engine.update('sys_approval_request', {\n id: requestId, pending_approvers: next.join(','), updated_at: now, ...configPatch,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, next, raw.organization_id ?? null, now);\n\n await this.notify({\n topic: 'approval.reassigned',\n audience: [to],\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n dedupKey: `approval-reassign-${requestId}-${to}`,\n payload: {\n title: 'Approval handed to you',\n message: `You are now an approver on ${raw.object_name}/${raw.record_id}.`,\n actionUrl: '/system/approvals',\n },\n });\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh! };\n }\n\n /**\n * Submitter nudge — notify every pending approver. Throttled to one\n * reminder per {@link REMIND_COOLDOWN_MS} per request.\n */\n async remind(\n requestId: string,\n input: { actorId: string; comment?: string },\n context: ExecutionContext,\n ): Promise<{ request: ApprovalRequestRow; notified: number }> {\n const actorId = await this.resolveActor(input?.actorId, context);\n const raw = await this.loadPendingRow(requestId);\n if (!context.isSystem && raw.submitter_id && String(raw.submitter_id) !== String(actorId)) {\n throw new Error('FORBIDDEN: only the submitter may send reminders');\n }\n\n const acts = await this.engine.find('sys_approval_action', {\n where: { request_id: requestId, action: 'remind' },\n orderBy: [{ field: 'created_at', order: 'desc' }], limit: 1, context: SYSTEM_CTX,\n });\n const last: any = Array.isArray(acts) ? acts[0] : null;\n const now = this.clock.now();\n if (last?.created_at && now.getTime() - Date.parse(last.created_at) < REMIND_COOLDOWN_MS) {\n throw new Error('THROTTLED: a reminder was already sent recently');\n }\n\n const pending = csvSplit(raw.pending_approvers);\n const nowIso = now.toISOString();\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: raw.organization_id ?? null,\n step_name: raw.flow_node_id ?? raw.current_step ?? null, step_index: 0, action: 'remind',\n actor_id: actorId, comment: input.comment ?? null, created_at: nowIso,\n }, { context: SYSTEM_CTX });\n\n // Per-approver fan-out: concrete identities (user ids / emails) each get\n // their OWN one-tap approve/reject links (ADR-0043); `role:*`-style\n // literals can't carry a personal token and fall back to a plain nudge.\n let notified = 0;\n const concrete = pending.filter(a => a && !a.includes(':'));\n const literals = pending.filter(a => a && a.includes(':'));\n for (const approver of concrete) {\n try {\n const tokens = await this.issueActionTokens(requestId, approver);\n notified += await this.notify({\n topic: 'approval.reminder',\n audience: [approver],\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n dedupKey: `approval-remind-${requestId}-${nowIso}-${approver}`,\n payload: {\n title: 'Approval reminder',\n message: `A decision on ${raw.object_name}/${raw.record_id} is still waiting on you.`,\n actionUrl: '/system/approvals',\n actions: [\n { label: 'Approve', url: this.actionLinkUrl(tokens.approve) },\n { label: 'Reject', url: this.actionLinkUrl(tokens.reject) },\n ],\n },\n });\n } catch (err: any) {\n this.logger?.warn?.('[approvals] reminder with action links failed', {\n request: requestId, approver, error: err?.message ?? String(err),\n });\n }\n }\n if (literals.length) {\n notified += await this.notify({\n topic: 'approval.reminder',\n audience: literals,\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n dedupKey: `approval-remind-${requestId}-${nowIso}`,\n payload: {\n title: 'Approval reminder',\n message: `A decision on ${raw.object_name}/${raw.record_id} is still waiting on you.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, notified };\n }\n\n // ── Actionable links (ADR-0043) ──────────────────────────────\n\n /** Build the session-less confirm-page URL for a raw token. */\n actionLinkUrl(rawToken: string): string {\n return `${this.publicBaseUrl}/api/v1/approvals/act?token=${encodeURIComponent(rawToken)}`;\n }\n\n /**\n * Issue one-tap approve/reject tokens for one approver on one pending\n * request. Raw tokens are returned ONCE; only SHA-256 hashes are stored\n * (`sys_approval_token`), so a DB leak yields no usable links.\n */\n async issueActionTokens(\n requestId: string,\n approverId: string,\n opts?: { ttlMs?: number },\n ): Promise<{ approve: string; reject: string }> {\n if (!approverId?.trim()) throw new Error('VALIDATION_FAILED: approverId is required');\n const raw = await this.loadPendingRow(requestId);\n const pending = csvSplit(raw.pending_approvers);\n if (!pending.includes(approverId)) {\n throw new Error(`FORBIDDEN: '${approverId}' is not a pending approver on this request`);\n }\n const now = this.clock.now();\n const expires = new Date(now.getTime() + (opts?.ttlMs ?? ACTION_TOKEN_TTL_MS)).toISOString();\n const out = { approve: '', reject: '' };\n for (const action of ['approve', 'reject'] as const) {\n const rawToken = randomBytes(32).toString('base64url');\n await this.engine.insert('sys_approval_token', {\n id: uid('atok'),\n organization_id: raw.organization_id ?? null,\n token_hash: createHash('sha256').update(rawToken).digest('hex'),\n request_id: requestId,\n action,\n approver_id: approverId,\n expires_at: expires,\n consumed_at: null,\n created_at: now.toISOString(),\n }, { context: SYSTEM_CTX });\n out[action] = rawToken;\n }\n return out;\n }\n\n /** Shared validation chain for peek/redeem. Returns the token row when live. */\n private async resolveActionToken(rawToken: string): Promise<\n { ok: true; token: any; request: ApprovalRequestRow } | Extract<ActionTokenOutcome, { ok: false }>\n > {\n const trimmed = rawToken?.trim();\n if (!trimmed) return { ok: false, reason: 'invalid' };\n const hash = createHash('sha256').update(trimmed).digest('hex');\n const rows = await this.engine.find('sys_approval_token', {\n where: { token_hash: hash }, limit: 1, context: SYSTEM_CTX,\n });\n const token: any = Array.isArray(rows) ? rows[0] : null;\n if (!token) return { ok: false, reason: 'invalid' };\n if (token.consumed_at) return { ok: false, reason: 'consumed' };\n if (Date.parse(token.expires_at) < this.clock.now().getTime()) {\n return { ok: false, reason: 'expired' };\n }\n const request = await this.getRequest(token.request_id, SYSTEM_CTX);\n if (!request || request.status !== 'pending') {\n return { ok: false, reason: 'not_pending', request: request ?? undefined };\n }\n if (!(request.pending_approvers ?? []).includes(token.approver_id)) {\n // Reassigned away / slot consumed by a unanimous round — the link died\n // with the slot (ADR-0043 invalidation row).\n return { ok: false, reason: 'not_approver', request };\n }\n return { ok: true, token, request };\n }\n\n /** GET confirm page: validate WITHOUT consuming — never mutates. */\n async peekActionToken(rawToken: string): Promise<ActionTokenOutcome> {\n const res = await this.resolveActionToken(rawToken);\n if (!res.ok) return res;\n return { ok: true, action: res.token.action, request: res.request, approverId: res.token.approver_id };\n }\n\n /**\n * POST redemption: consume the token FIRST (a failed decide still burns\n * it — replay-safe), then decide as the bound approver.\n */\n async redeemActionToken(rawToken: string): Promise<ActionTokenOutcome> {\n const res = await this.resolveActionToken(rawToken);\n if (!res.ok) return res;\n await this.engine.update('sys_approval_token', {\n id: res.token.id, consumed_at: this.clock.now().toISOString(),\n }, { context: SYSTEM_CTX });\n const out = await this.decide(res.token.request_id, {\n decision: res.token.action,\n actorId: res.token.approver_id,\n comment: 'Via action link',\n // The token IS the authentication (#3783): it is single-use, hashed at\n // rest and bound to one approver, who `resolveActionToken` has just\n // re-checked still holds a pending slot. So this decision has a real\n // acting user even though no session carried it — name them on the\n // context, so the status mirror and every flow it cascades into are\n // attributed exactly like a decision made through the UI. Elevation is\n // unchanged: `isSystem` still stands in for the missing session.\n }, { ...SYSTEM_CTX, userId: res.token.approver_id });\n return { ok: true, action: res.token.action, request: out.request, approverId: res.token.approver_id };\n }\n\n /**\n * Approver asks the submitter for more information. The request stays\n * pending — a thread interaction, not a flow decision.\n */\n async requestInfo(\n requestId: string,\n input: { actorId: string; comment: string },\n context: ExecutionContext,\n ): Promise<{ request: ApprovalRequestRow }> {\n const actorId = await this.resolveActor(input?.actorId, context);\n if (!input?.comment?.trim()) throw new Error('VALIDATION_FAILED: comment is required');\n const raw = await this.loadPendingRow(requestId);\n const pending = csvSplit(raw.pending_approvers);\n if (!context.isSystem && !pending.includes(actorId)) {\n throw new Error(`FORBIDDEN: actor '${actorId}' is not a pending approver`);\n }\n\n const now = this.clock.now().toISOString();\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: raw.organization_id ?? null,\n step_name: raw.flow_node_id ?? raw.current_step ?? null, step_index: 0, action: 'request_info',\n actor_id: actorId, comment: input.comment.trim(), created_at: now,\n }, { context: SYSTEM_CTX });\n\n if (raw.submitter_id) {\n await this.notify({\n topic: 'approval.request_info',\n audience: [String(raw.submitter_id)],\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n payload: {\n title: 'More information requested',\n message: input.comment.trim(),\n actionUrl: '/system/approvals',\n },\n });\n }\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh! };\n }\n\n /** Free-form reply on the thread (submitter or any pending approver). */\n async comment(\n requestId: string,\n input: { actorId: string; comment: string; attachments?: string[] },\n context: ExecutionContext,\n ): Promise<{ request: ApprovalRequestRow }> {\n const actorId = await this.resolveActor(input?.actorId, context);\n if (!input?.comment?.trim()) throw new Error('VALIDATION_FAILED: comment is required');\n const raw = await this.loadPendingRow(requestId);\n const pending = csvSplit(raw.pending_approvers);\n const isSubmitter = raw.submitter_id && String(raw.submitter_id) === String(actorId);\n if (!context.isSystem && !isSubmitter && !pending.includes(actorId)) {\n throw new Error(`FORBIDDEN: actor '${actorId}' is not on this request`);\n }\n\n const now = this.clock.now().toISOString();\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: raw.organization_id ?? null,\n step_name: raw.flow_node_id ?? raw.current_step ?? null, step_index: 0, action: 'comment',\n actor_id: actorId, comment: input.comment.trim(),\n attachments: input.attachments?.length ? input.attachments : null,\n created_at: now,\n }, { context: SYSTEM_CTX });\n\n // Notify the other side of the thread.\n const audience = isSubmitter ? pending : [String(raw.submitter_id ?? '')].filter(Boolean);\n await this.notify({\n topic: 'approval.comment',\n audience,\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n payload: {\n title: 'New comment on an approval',\n message: input.comment.trim(),\n actionUrl: '/system/approvals',\n },\n });\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh! };\n }\n\n // ── SLA escalation (ADR-0042) ─────────────────────────────────\n\n /**\n * One escalation sweep: every *pending* request whose node config declares\n * `escalation.timeoutHours` and whose deadline has passed is escalated\n * **at most once, ever** — the `escalate` audit row is the idempotency\n * marker, written before any mutation (audit-first, like reassign). One\n * bad row never stops the sweep.\n */\n async runEscalations(): Promise<{ scanned: number; escalated: number }> {\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_approval_request', {\n where: { status: 'pending' }, limit: 500, context: SYSTEM_CTX,\n }) ?? [];\n } catch (err: any) {\n this.logger?.warn?.('[approvals] escalation scan failed to list requests', {\n error: err?.message ?? String(err),\n });\n return { scanned: 0, escalated: 0 };\n }\n\n let escalated = 0;\n for (const raw of rows) {\n try {\n const cfg = parseJson<any>(raw.node_config_json, undefined);\n const esc = cfg?.escalation;\n if (!esc || typeof esc.timeoutHours !== 'number' || esc.timeoutHours <= 0) continue;\n const due = slaDueAt(raw.created_at, cfg);\n if (!due || Date.parse(due) > this.clock.now().getTime()) continue;\n\n // Single-shot: a prior 'escalate' action means this request is done.\n const prior = await this.engine.find('sys_approval_action', {\n where: { request_id: raw.id, action: 'escalate' }, limit: 1, context: SYSTEM_CTX,\n });\n if (Array.isArray(prior) && prior[0]) continue;\n\n await this.escalateRequest(raw, esc);\n escalated++;\n } catch (err: any) {\n this.logger?.warn?.('[approvals] escalation failed for request', {\n request: raw?.id, error: err?.message ?? String(err),\n });\n }\n }\n if (escalated > 0) {\n this.logger?.info?.('[approvals] SLA escalation sweep', { scanned: rows.length, escalated });\n }\n return { scanned: rows.length, escalated };\n }\n\n // ── Dead-run release (#3456) ──────────────────────────────────\n\n /**\n * One dead-run sweep: a pending request whose owning flow run has reached a\n * TERMINAL state can never be decided — nothing is left to resume — so the\n * request is finalised as `recalled` and, with `lockRecord`, the record it was\n * holding is released.\n *\n * This is the recovery half of #3456. The prevention half is the record lock's\n * owning-run exemption (`lifecycle-hooks.ts`), which stops a run from killing\n * itself on its own lock in the first place; this sweep cleans up the runs that\n * still die — for any reason, including a process crash, which no in-band\n * handler can catch because the process that would have run it is gone.\n *\n * **Fail-safe by construction.** It acts only on an explicit terminal status\n * from a closed set. Every other answer — `paused` (the normal state of a run\n * waiting on its approval), `running`, an unrecognised status, `null` (unknown\n * run, evicted log, no durable store), a `getRun` that throws, or no automation\n * engine at all — is read as \"still alive\" and left strictly alone. The failure\n * mode is therefore \"a dead run's lock survives until an admin recalls it\"\n * (today's behaviour, #3424), never \"a live approval is destroyed\".\n *\n * `recalled` is the finalisation because it is the platform's existing terminal\n * state for *a live request that ended without a decision*; the audit row names\n * the real cause and {@link DEAD_RUN_ACTOR_ID} the real actor, so a dead-run\n * release is never mistaken for a submitter's withdrawal.\n */\n /**\n * Read-only inspection for the OTHER dead-run shape: a request that is\n * already TERMINAL while its `flow_run_id` points at nothing (#4469).\n *\n * #4460 stopped new ones being produced; nothing found the ones already\n * stuck. The failure mode (#4420) is a request row flipped to `approved` /\n * `rejected` / `returned` whose owning run no longer exists — the decision\n * landed, the flow never moved. Any deployment on 17.0.0-rc.1 that hit the\n * wiring hole and crossed a restart mid-approval can be carrying these rows.\n *\n * {@link releaseDeadRunRequests} cannot see them, for a reason worth naming:\n * it scans `status: 'pending'`, and the very step that zombified the request\n * is the one that took it OUT of `pending`. The act of breaking it removed it\n * from the only sweeper's field of view — which is a large part of why this\n * class of failure stayed silent.\n *\n * It also could not have answered the question even if it looked: its\n * liveness oracle is `getRun`, which treats both `null` and `paused` as alive\n * (conservative, correct) — so it has no way to say \"this run is really\n * gone\". (Until #8050 that oracle was weaker still: after a restart it\n * returned `null` for a perfectly ALIVE suspended run, so \"alive\" and\n * \"unknown\" were the same answer. It now reports such a run as `paused`,\n * which does not change any branch here — both already meant \"leave alone\" —\n * but it is why the sweep below needs `hasSuspendedRun` as a second oracle\n * rather than a sharper reading of the first.)\n *\n * So this uses BOTH oracles, and a row must fail both to be reported:\n *\n * - `hasSuspendedRun(runId) === false` — the suspension store itself says no\n * live pause exists. It THROWS when the store cannot be read, and that\n * case is SKIPPED, never counted as dead: an unreadable store means\n * \"unknown\", and a storage outage must not be published as a lost run.\n * - `getRun(runId) == null` — no terminal history row either (the `run_`\n * prefixed rows in `sys_automation_run`). A run that merely finished is\n * not stranded; a request whose run neither waits nor ever completed is.\n *\n * **Reports; never rewrites.** No status is changed and no run is cancelled.\n * The decision genuinely happened — a human approved or rejected — and\n * silently rolling it back would make the audit trail disagree with the\n * facts. What an operator needs first is visibility: which requests are stuck\n * at which step, and what the mirrored status field on the business record\n * still says. Whether to re-run the downstream actions or re-open the\n * approval is a judgement call this cannot make.\n */\n async inspectStrandedRequests(options?: { limit?: number }): Promise<{\n scanned: number;\n stranded: StrandedApprovalRequest[];\n /** Rows skipped because the suspension store could not be read — NOT healthy, just unknown. */\n undetermined: number;\n }> {\n const empty = { scanned: 0, stranded: [] as StrandedApprovalRequest[], undetermined: 0 };\n // Both oracles are required. Without `hasSuspendedRun` there is no way to\n // tell a live cross-restart pause from a dead run, and reporting on\n // `getRun` alone would name every healthy paused approval as stranded.\n if (typeof this.automation?.hasSuspendedRun !== 'function') return empty;\n if (typeof this.automation?.getRun !== 'function') return empty;\n\n const limit = options?.limit ?? 500;\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_approval_request', {\n where: { status: { $in: [...STRANDABLE_REQUEST_STATUSES] } }, limit, context: SYSTEM_CTX,\n }) ?? [];\n } catch (err: any) {\n this.logger?.warn?.('[approvals] stranded-request scan failed to list requests', {\n error: err?.message ?? String(err),\n });\n return empty;\n }\n\n const stranded: StrandedApprovalRequest[] = [];\n let undetermined = 0;\n for (const raw of rows) {\n const runId = raw?.flow_run_id ? String(raw.flow_run_id) : '';\n if (!runId) continue; // not node-driven — no run was ever supposed to move\n\n let suspended: boolean;\n try {\n suspended = await this.automation.hasSuspendedRun!(runId);\n } catch (err: any) {\n // Store unreadable ⇒ existence unknown. Skipping is the only safe\n // answer; counted so \"0 stranded\" can never be read as \"all clear\"\n // when nothing could actually be checked.\n undetermined++;\n this.logger?.warn?.('[approvals] stranded-request scan could not read the suspension store', {\n request: raw?.id, run: runId, error: err?.message ?? String(err),\n });\n continue;\n }\n if (suspended) continue; // still parked — the run is alive and resumable\n\n let terminal: { status?: string } | null = null;\n try {\n terminal = await this.automation.getRun!(runId);\n } catch (err: any) {\n undetermined++;\n this.logger?.warn?.('[approvals] stranded-request scan could not read the run history', {\n request: raw?.id, run: runId, error: err?.message ?? String(err),\n });\n continue;\n }\n if (terminal) continue; // the run ran to a terminal state — it is not dangling\n\n // Neither suspended nor ever finished: the run this decision was supposed\n // to advance is genuinely gone.\n const config = parseJson<ApprovalNodeConfig>(\n raw.node_config_json, { approvers: [], behavior: 'first_response' } as any,\n );\n const mirrorField = config.approvalStatusField;\n let mirroredStatus: string | undefined;\n if (mirrorField) {\n try {\n const recs = await this.engine.find(raw.object_name, {\n where: { id: raw.record_id }, limit: 1, context: SYSTEM_CTX,\n });\n const rec: any = Array.isArray(recs) ? recs[0] : null;\n if (rec) mirroredStatus = rec[mirrorField] ?? undefined;\n } catch { /* display-only — a mirror read must never fail the scan */ }\n }\n stranded.push({\n requestId: String(raw.id),\n status: raw.status,\n runId,\n flowName: typeof raw.process_name === 'string' ? raw.process_name.replace(/^flow:/, '') : undefined,\n nodeId: raw.flow_node_id ?? raw.current_step ?? undefined,\n objectName: raw.object_name,\n recordId: raw.record_id,\n organizationId: raw.organization_id ?? null,\n completedAt: raw.completed_at ?? undefined,\n mirrorField,\n mirroredStatus,\n });\n }\n\n if (stranded.length || undetermined) {\n this.logger?.warn?.('[approvals] stranded terminal requests (decision recorded, flow run gone)', {\n scanned: rows.length, stranded: stranded.length, undetermined,\n requests: stranded.map(s => `${s.requestId}@${s.nodeId ?? '?'} → run ${s.runId}`),\n });\n }\n return { scanned: rows.length, stranded, undetermined };\n }\n\n async releaseDeadRunRequests(): Promise<{ scanned: number; released: number }> {\n // No liveness oracle → no basis to declare anything dead.\n if (typeof this.automation?.getRun !== 'function') return { scanned: 0, released: 0 };\n\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_approval_request', {\n where: { status: 'pending' }, limit: 500, context: SYSTEM_CTX,\n }) ?? [];\n } catch (err: any) {\n this.logger?.warn?.('[approvals] dead-run sweep failed to list requests', {\n error: err?.message ?? String(err),\n });\n return { scanned: 0, released: 0 };\n }\n\n let released = 0;\n for (const raw of rows) {\n try {\n const runId = raw?.flow_run_id ? String(raw.flow_run_id) : '';\n if (!runId) continue; // not node-driven — no run owns it, nothing to check\n\n let status: string | undefined;\n try {\n const run = await this.automation.getRun!(runId);\n status = typeof run?.status === 'string' ? run.status : undefined;\n } catch (err: any) {\n // Unknown liveness is NOT death — leave the request pending.\n this.logger?.warn?.('[approvals] dead-run sweep could not read run status', {\n request: raw?.id, run: runId, error: err?.message ?? String(err),\n });\n continue;\n }\n if (!status || !TERMINAL_RUN_STATUSES.has(status)) continue;\n\n await this.abandonForDeadRun(raw, runId, status);\n released++;\n } catch (err: any) {\n // One bad row never stops the sweep (mirrors runEscalations).\n this.logger?.warn?.('[approvals] dead-run release failed for request', {\n request: raw?.id, error: err?.message ?? String(err),\n });\n }\n }\n if (released > 0) {\n this.logger?.info?.('[approvals] dead-run sweep', { scanned: rows.length, released });\n }\n return { scanned: rows.length, released };\n }\n\n /**\n * Finalise one pending request whose owning run is terminal. Mirrors the\n * shape of {@link recall} — audit row first (so a crash mid-release leaves a\n * trace of the intent), then the status transition, approver-index sync and\n * the optional status-field mirror. No resume/cancel of the run: it is already\n * terminal, which is precisely why we are here.\n */\n private async abandonForDeadRun(raw: any, runId: string, runStatus: string): Promise<void> {\n const org = raw.organization_id ?? null;\n const nodeId: string | null = raw.flow_node_id ?? raw.current_step ?? null;\n const now = this.clock.now().toISOString();\n\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: raw.id, organization_id: org,\n step_name: nodeId, step_index: 0, action: 'recall',\n actor_id: DEAD_RUN_ACTOR_ID,\n comment: `owning flow run ${runId} is ${runStatus} — request abandoned and record lock released`,\n created_at: now,\n }, { context: SYSTEM_CTX });\n\n await this.engine.update('sys_approval_request', {\n id: raw.id, status: 'recalled', pending_approvers: null, completed_at: now, updated_at: now,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(raw.id, [], org, now);\n\n const config = parseJson<ApprovalNodeConfig>(\n raw.node_config_json, { approvers: [], behavior: 'first_response' } as any,\n );\n if (config.approvalStatusField) {\n // No human did this — a sweep did. Left user-less on purpose (#3783): a\n // flow that wants to react to a dead-run release declares runAs:'system'.\n await this.mirrorStatusField(\n raw.object_name, raw.record_id, config.approvalStatusField, 'recalled', null,\n );\n }\n\n this.logger?.warn?.('[approvals] released a record held by a dead approval run', {\n request: raw.id, run: runId, runStatus, object: raw.object_name, record: raw.record_id,\n });\n }\n\n /** Execute the configured escalation action for one overdue request. */\n private async escalateRequest(raw: any, esc: any): Promise<void> {\n const action: string = esc.action ?? 'notify';\n const escalateTo: string | undefined =\n typeof esc.escalateTo === 'string' && esc.escalateTo.trim() ? esc.escalateTo.trim() : undefined;\n const now = this.clock.now().toISOString();\n const pending = csvSplit(raw.pending_approvers);\n\n // `escalateTo` is a position machine name or a user id (same contract as\n // the `position` ApproverType, ADR-0090 D3). Position holders win; an\n // empty expansion falls back to the literal, so a config naming a\n // specific user id keeps working unchanged.\n let escalatees: string[] = [];\n if (escalateTo) {\n try {\n escalatees = await this.expandPositionUsers(escalateTo, raw.organization_id ?? null);\n } catch { escalatees = []; }\n if (!escalatees.length) escalatees = [escalateTo];\n }\n\n // Audit first — this row IS the idempotency marker (ADR-0042 §1).\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: raw.id, organization_id: raw.organization_id ?? null,\n step_name: raw.flow_node_id ?? raw.current_step ?? null, step_index: 0, action: 'escalate',\n actor_id: SLA_ACTOR_ID,\n comment: `${action}${escalateTo ? ` → ${escalateTo}` : ''}`,\n created_at: now,\n }, { context: SYSTEM_CTX });\n\n if (action === 'reassign' && escalatees.length) {\n await this.engine.update('sys_approval_request', {\n id: raw.id, pending_approvers: escalatees.join(','), updated_at: now,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(raw.id, escalatees, raw.organization_id ?? null, now);\n await this.notify({\n topic: 'approval.escalated',\n audience: escalatees,\n actorId: SLA_ACTOR_ID,\n source: { object: 'sys_approval_request', id: raw.id },\n payload: {\n title: 'Approval escalated to you',\n message: `An overdue approval on ${raw.object_name}/${raw.record_id} was escalated to you.`,\n actionUrl: '/system/approvals',\n },\n });\n } else if (action === 'auto_approve' || action === 'auto_reject') {\n await this.decide(raw.id, {\n decision: action === 'auto_approve' ? 'approve' : 'reject',\n actorId: SLA_ACTOR_ID,\n comment: 'SLA escalation',\n }, SYSTEM_CTX);\n } else {\n // 'notify' (and the reassign-without-target fallback)\n await this.notify({\n topic: 'approval.sla_breached',\n audience: [...pending, ...escalatees],\n actorId: SLA_ACTOR_ID,\n source: { object: 'sys_approval_request', id: raw.id },\n payload: {\n title: 'Approval SLA breached',\n message: `A decision on ${raw.object_name}/${raw.record_id} is overdue.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n\n if (esc.notifySubmitter !== false && raw.submitter_id) {\n await this.notify({\n topic: 'approval.sla_breached',\n audience: [String(raw.submitter_id)],\n actorId: SLA_ACTOR_ID,\n source: { object: 'sys_approval_request', id: raw.id },\n payload: {\n title: 'Your approval request breached its SLA',\n message: `${raw.object_name}/${raw.record_id}: escalation action '${action}' was taken.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n }\n\n // ── Display enrichment ───────────────────────────────────────\n\n /**\n * Resolve the schema-declared display field for an object, when the engine\n * exposes schema metadata (`getSchema`). Falls back to common title-ish\n * field names so plain `ApprovalEngine` fakes still enrich sensibly.\n */\n private resolveDisplayField(object: string): string | undefined {\n try {\n const schema: any = (this.engine as any).getSchema?.(object);\n const fields = schema?.fields ?? {};\n // [ADR-0079] `nameField` is the canonical primary-title pointer;\n // `displayNameField` is the deprecated alias (still honored).\n const declared = schema?.nameField ?? schema?.displayNameField;\n if (declared && declared !== 'id' && fields[declared]) return declared;\n for (const cand of ['name', 'title', 'subject', 'label']) {\n if (fields[cand]) return cand;\n }\n } catch { /* schema unavailable — heuristics below still apply */ }\n return undefined;\n }\n\n private static pickTitle(rec: any, displayField?: string): string | undefined {\n const candidates = displayField\n ? [displayField, 'name', 'title', 'subject', 'label']\n : ['name', 'title', 'subject', 'label'];\n for (const f of candidates) {\n const v = rec?.[f];\n if (v != null && String(v).trim() && f !== 'id') return String(v);\n }\n return undefined;\n }\n\n /**\n * Batch-resolve `sys_user` display names for identifiers that may be user\n * ids or emails. Best-effort — failures leave entries unresolved.\n */\n private async resolveUserNames(identifiers: Array<string | null | undefined>): Promise<Map<string, string>> {\n const names = new Map<string, string>();\n const targets = Array.from(new Set(identifiers.filter(Boolean))) as string[];\n if (!targets.length) return names;\n try {\n const users = await this.engine.find('sys_user', {\n where: { id: { $in: targets } }, fields: ['id', 'name', 'email'],\n limit: targets.length, context: SYSTEM_CTX,\n });\n for (const u of (users ?? []) as any[]) {\n if (u?.id && (u.name || u.email)) names.set(String(u.id), String(u.name ?? u.email));\n }\n } catch { /* best-effort */ }\n const unresolvedEmails = targets.filter(t => !names.has(t) && t.includes('@'));\n if (unresolvedEmails.length) {\n try {\n const users = await this.engine.find('sys_user', {\n where: { email: { $in: unresolvedEmails } }, fields: ['email', 'name'],\n limit: unresolvedEmails.length, context: SYSTEM_CTX,\n });\n for (const u of (users ?? []) as any[]) {\n if (u?.email && u.name) names.set(String(u.email), String(u.name));\n }\n } catch { /* best-effort */ }\n }\n return names;\n }\n\n /** Lookup-typed fields (key + referenced object) of an object's schema. */\n private resolveLookupFields(object: string): Array<{ key: string; reference: string }> {\n try {\n const schema: any = (this.engine as any).getSchema?.(object);\n const fields = schema?.fields ?? {};\n const out: Array<{ key: string; reference: string }> = [];\n for (const [key, f] of Object.entries<any>(fields)) {\n if ((f?.type === 'lookup' || f?.type === 'master_detail' || f?.type === 'user') && f?.reference) {\n out.push({ key, reference: String(f.reference) });\n }\n }\n return out;\n } catch { return []; }\n }\n\n /**\n * Field key → display label for an object's schema. Lets the inbox summary\n * show a human field name (\"考核状态\") instead of a title-cased machine key\n * (\"Assessment Status\"). For a single-locale project the schema label already\n * IS the localized string; symmetric with `resolveDisplayField`/lookup\n * resolution that power `payload_display`.\n */\n private resolveFieldLabels(object: string): Record<string, string> {\n try {\n const schema: any = (this.engine as any).getSchema?.(object);\n const fields = schema?.fields ?? {};\n const out: Record<string, string> = {};\n for (const [key, f] of Object.entries<any>(fields)) {\n if (f?.label) out[key] = String(f.label);\n }\n return out;\n } catch { return {}; }\n }\n\n /**\n * Attach inbox display fields to rows so clients never render a raw\n * identifier: `record_title`, `submitter_name`, `object_label`,\n * `pending_approver_names` (user-id approvers), `payload_display`\n * (lookup foreign keys in the snapshot → referenced record titles), and\n * `payload_labels` (snapshot field keys → the target object's field labels).\n * Batched: one query per distinct object (target + referenced) plus one\n * `sys_user` lookup. Best-effort — a deleted record falls back to the\n * payload snapshot, and any failure leaves the field unset rather than\n * failing the list.\n */\n private async enrichRows(rows: ApprovalRequestRow[]): Promise<void> {\n if (!rows.length) return;\n\n // Record titles + object labels, batched per object.\n const byObject = new Map<string, Set<string>>();\n for (const r of rows) {\n if (!r.object_name || !r.record_id) continue;\n let set = byObject.get(r.object_name);\n if (!set) { set = new Set(); byObject.set(r.object_name, set); }\n set.add(r.record_id);\n }\n const titles = new Map<string, string>();\n const objectLabels = new Map<string, string>();\n for (const [object, idSet] of byObject) {\n try {\n const schema: any = (this.engine as any).getSchema?.(object);\n if (schema?.label) objectLabels.set(object, String(schema.label));\n } catch { /* label optional */ }\n const ids = Array.from(idSet);\n const displayField = this.resolveDisplayField(object);\n try {\n const recs = await this.engine.find(object, {\n where: { id: { $in: ids } }, limit: ids.length, context: SYSTEM_CTX,\n });\n for (const rec of (recs ?? []) as any[]) {\n const title = ApprovalService.pickTitle(rec, displayField);\n if (rec?.id && title) titles.set(`${object} ${rec.id}`, title);\n }\n } catch { /* object may be unregistered — payload fallback below */ }\n }\n\n // Lookup foreign keys inside payload snapshots → referenced record titles.\n const lookupFieldsByObject = new Map<string, Array<{ key: string; reference: string }>>();\n // Field key → label per object, for the snapshot summary's field names.\n const fieldLabelsByObject = new Map<string, Record<string, string>>();\n for (const object of byObject.keys()) {\n const lookups = this.resolveLookupFields(object);\n if (lookups.length) lookupFieldsByObject.set(object, lookups);\n const labels = this.resolveFieldLabels(object);\n if (Object.keys(labels).length) fieldLabelsByObject.set(object, labels);\n }\n const refIds = new Map<string, Set<string>>();\n for (const r of rows) {\n const lookups = lookupFieldsByObject.get(r.object_name);\n const payload: any = r.payload;\n if (!lookups || !payload || typeof payload !== 'object') continue;\n for (const { key, reference } of lookups) {\n const v = payload[key];\n if (v == null || typeof v === 'object' || !String(v).trim()) continue;\n let set = refIds.get(reference);\n if (!set) { set = new Set(); refIds.set(reference, set); }\n set.add(String(v));\n }\n }\n const refTitles = new Map<string, string>();\n for (const [object, idSet] of refIds) {\n const ids = Array.from(idSet);\n const displayField = this.resolveDisplayField(object);\n try {\n const recs = await this.engine.find(object, {\n where: { id: { $in: ids } }, limit: ids.length, context: SYSTEM_CTX,\n });\n for (const rec of (recs ?? []) as any[]) {\n const title = ApprovalService.pickTitle(rec, displayField);\n if (rec?.id && title) refTitles.set(`${object} ${rec.id}`, title);\n }\n } catch { /* referenced object unreadable — leave unresolved */ }\n }\n\n // Display names for submitters AND user-id approvers in one lookup.\n // `role:<r>` (and other `type:value` literals) are already readable.\n const userIdentifiers: Array<string | null | undefined> = [];\n for (const r of rows) {\n userIdentifiers.push(r.submitter_id);\n for (const a of r.pending_approvers ?? []) {\n if (a && !a.includes(':')) userIdentifiers.push(a);\n }\n }\n const names = await this.resolveUserNames(userIdentifiers);\n\n for (const r of rows as any[]) {\n const title = titles.get(`${r.object_name} ${r.record_id}`)\n ?? ApprovalService.pickTitle(r.payload, undefined);\n if (title) r.record_title = title;\n const name = r.submitter_id ? names.get(String(r.submitter_id)) : undefined;\n if (name) r.submitter_name = name;\n const label = objectLabels.get(r.object_name);\n if (label) r.object_label = label;\n\n const approverNames: Record<string, string> = {};\n for (const a of r.pending_approvers ?? []) {\n const n = names.get(String(a));\n if (n) approverNames[a] = n;\n }\n if (Object.keys(approverNames).length) r.pending_approver_names = approverNames;\n\n const lookups = lookupFieldsByObject.get(r.object_name);\n if (lookups && r.payload && typeof r.payload === 'object') {\n const display: Record<string, string> = {};\n for (const { key, reference } of lookups) {\n const v = (r.payload as any)[key];\n if (v == null) continue;\n const t = refTitles.get(`${reference} ${String(v)}`);\n if (t) display[key] = t;\n }\n if (Object.keys(display).length) r.payload_display = display;\n }\n\n // Field labels for the snapshot keys the summary renders (only keys\n // actually present in the payload — a deleted field's label is noise).\n const fieldLabels = fieldLabelsByObject.get(r.object_name);\n if (fieldLabels && r.payload && typeof r.payload === 'object') {\n const labels: Record<string, string> = {};\n for (const key of Object.keys(r.payload as Record<string, unknown>)) {\n const l = fieldLabels[key];\n if (l) labels[key] = l;\n }\n if (Object.keys(labels).length) r.payload_labels = labels;\n }\n }\n }\n\n // ── Pending-approver index (issue #1745) ─────────────────────\n\n /**\n * Mirror one request's `pending_approvers` CSV into the normalized\n * `sys_approval_approver` index. Called by every write path that changes\n * the approver set; an empty `approvers` clears the request's rows (the\n * request left `pending`). Diff-based so reassign/unanimous churn doesn't\n * rewrite untouched rows.\n */\n private async syncApproverIndex(\n requestId: string,\n approvers: string[],\n org: string | null,\n now: string,\n ): Promise<void> {\n const desired = new Set(approvers.map(a => String(a).trim()).filter(Boolean));\n const existing = await this.engine.find('sys_approval_approver', {\n where: { request_id: requestId }, limit: 500, context: SYSTEM_CTX,\n });\n const rows: any[] = Array.isArray(existing) ? existing : [];\n for (const row of rows) {\n if (desired.has(String(row.approver))) desired.delete(String(row.approver));\n else await this.engine.delete('sys_approval_approver', { where: { id: row.id }, context: SYSTEM_CTX });\n }\n for (const approver of desired) {\n await this.engine.insert('sys_approval_approver', {\n id: uid('aapr'), request_id: requestId, approver,\n organization_id: org, created_at: now,\n }, { context: SYSTEM_CTX });\n }\n }\n\n /**\n * Rebuild the whole `sys_approval_approver` index from the CSV source of\n * truth. Idempotent; run at plugin start so rows written before the index\n * existed (or drifted past a crashed sync) become queryable. Cost tracks\n * the number of *pending* requests, not the request history.\n */\n async rebuildApproverIndex(): Promise<{ requests: number; inserted: number; deleted: number }> {\n // Both walks seek by `id` rather than counting from the start (#4363).\n // The desired-state walk fed the deletion pass below, so a row it skipped\n // was not a slow page — it was an approver silently dropped from someone's\n // queue. An offset walk over `status = 'pending'` has no such guarantee:\n // it slices an arrangement nothing holds steady, and this method is itself\n // a writer against the same tables.\n const desired = new Map<string, { approvers: Set<string>; org: string | null }>();\n const PAGE = 500;\n const requests = keysetWalk<Record<string, any>>(\n (q) => this.engine.find('sys_approval_request', {\n ...q,\n fields: ['id', 'pending_approvers', 'organization_id'],\n context: SYSTEM_CTX,\n }),\n { where: { status: 'pending' }, pageSize: PAGE },\n );\n for await (const rows of requests.pages()) {\n for (const r of rows) {\n desired.set(String(r.id), {\n approvers: new Set(csvSplit(r.pending_approvers)),\n org: r.organization_id ?? null,\n });\n }\n }\n\n // Current state: read the whole index first, THEN mutate. The seek makes\n // that separation a belt rather than the only brace — `created_at` is not\n // unique, so the previous ORDER BY could not make these pages a partition\n // even before the deletes below shifted the rows underneath them.\n const indexRows: any[] = [];\n const index = keysetWalk<Record<string, any>>(\n (q) => this.engine.find('sys_approval_approver', { ...q, context: SYSTEM_CTX }),\n { pageSize: PAGE },\n );\n for await (const rows of index.pages()) indexRows.push(...rows);\n let inserted = 0; let deleted = 0;\n const seen = new Map<string, Set<string>>();\n for (const row of indexRows) {\n const reqId = String(row.request_id);\n const want = desired.get(reqId);\n const have = seen.get(reqId) ?? seen.set(reqId, new Set()).get(reqId)!;\n // Orphan (request no longer pending), stale entry, or duplicate → drop.\n if (!want || !want.approvers.has(String(row.approver)) || have.has(String(row.approver))) {\n await this.engine.delete('sys_approval_approver', { where: { id: row.id }, context: SYSTEM_CTX });\n deleted++;\n continue;\n }\n have.add(String(row.approver));\n }\n\n const now = this.clock.now().toISOString();\n for (const [reqId, want] of desired) {\n const have = seen.get(reqId);\n for (const approver of want.approvers) {\n if (have?.has(approver)) continue;\n await this.engine.insert('sys_approval_approver', {\n id: uid('aapr'), request_id: reqId, approver,\n organization_id: want.org, created_at: now,\n }, { context: SYSTEM_CTX });\n inserted++;\n }\n }\n return { requests: desired.size, inserted, deleted };\n }\n\n // ── Read API ─────────────────────────────────────────────────\n\n /** Filter type accepted by {@link listRequests} / {@link countRequests}. */\n private buildRequestWhere(\n filter: {\n object?: string;\n recordId?: string;\n status?: ApprovalStatus | ApprovalStatus[];\n submitterId?: string;\n q?: string;\n } | undefined,\n context: ExecutionContext,\n ): { where: any; tenantOrg: string | null } {\n const f: any = {};\n if (filter?.object) f.object_name = filter.object;\n if (filter?.recordId) f.record_id = filter.recordId;\n if (filter?.submitterId) f.submitter_id = filter.submitterId;\n // Tenant isolation: when a caller context carries a tenant identifier\n // (organizationId / tenantId), scope the query to that tenant. SYSTEM\n // callers (no tenant) see all rows. This prevents the bespoke endpoint\n // from leaking other-tenant rows since we deliberately query with\n // SYSTEM_CTX to bypass RLS on the engine (the approver-visibility rule\n // spans three identity forms, which RLS can't model cleanly).\n // `organizationId` is not on the envelope — see isOverrideActor().\n const tenantOrg = (context as any)?.organizationId ?? context?.tenantId ?? null;\n if (tenantOrg) f.organization_id = tenantOrg;\n // Free-text search, pushed down: `payload_json` carries the record\n // snapshot, so record titles match without any join. `$contains` is the\n // driver's escaped-LIKE operator.\n const q = filter?.q?.trim();\n if (q) {\n f.$or = [\n { process_name: { $contains: q } },\n { object_name: { $contains: q } },\n { record_id: { $contains: q } },\n { submitter_id: { $contains: q } },\n { payload_json: { $contains: q } },\n ];\n }\n // Status pushes down whole: `$in` for arrays (all bundled drivers\n // support it), equality for a single value.\n if (Array.isArray(filter?.status)) {\n const statuses = (filter!.status as ApprovalStatus[]).filter(Boolean);\n if (statuses.length === 1) f.status = statuses[0];\n else if (statuses.length > 1) f.status = { $in: statuses };\n } else if (filter?.status) {\n f.status = filter.status;\n }\n return { where: f, tenantOrg };\n }\n\n /** Window the approver-index probe — pending queues live far below this. */\n private static readonly APPROVER_INDEX_CAP = 10_000;\n\n /**\n * Resolve an approver filter to matching request ids via the normalized\n * `sys_approval_approver` index — the indexed replacement for the old\n * in-memory CSV scan, and what makes approver-filtered pagination correct\n * past any scan window (issue #1745). A request matches when ANY of the\n * caller's identities (user id / email / role:<r>) holds a pending slot.\n * Returns null when the filter is absent (callers skip the id constraint).\n */\n private async approverRequestIds(\n targets: string[],\n tenantOrg: string | null,\n ): Promise<string[] | null> {\n if (!targets.length) return null;\n const where: any = targets.length === 1\n ? { approver: targets[0] }\n : { approver: { $in: targets } };\n if (tenantOrg) where.organization_id = tenantOrg;\n const rows = await this.engine.find('sys_approval_approver', {\n where, fields: ['request_id'],\n limit: ApprovalService.APPROVER_INDEX_CAP, context: SYSTEM_CTX,\n });\n const list: any[] = Array.isArray(rows) ? rows : [];\n if (list.length >= ApprovalService.APPROVER_INDEX_CAP) {\n this.logger?.warn?.('[approvals] approver index probe hit its window — results may be truncated', {\n cap: ApprovalService.APPROVER_INDEX_CAP, targets: targets.length,\n });\n }\n return [...new Set<string>(list.map(r => String(r.request_id)))];\n }\n\n /**\n * The request ids this caller is a PARTICIPANT of, or `null` for a caller\n * who may see everything in scope (#3590).\n *\n * These reads deliberately run with `SYSTEM_CTX` to bypass RLS — the\n * approver-visibility rule spans several identity forms that RLS cannot model\n * cleanly, which is why it has to be expressed here. Until now only the\n * TENANT half of that rule was applied, so any authenticated user could read\n * any request in their tenant (and, once attachments derived their access\n * from the request, its files too). This adds the participant half.\n *\n * A participant is the submitter, a current approver, or someone who has\n * already acted on the request (a past approver whose slot has moved on, a\n * commenter). Admins with override authority keep the unrestricted view the\n * \"all requests\" console surface depends on.\n *\n * Keying on the concrete user id is sufficient rather than an approximation:\n * position/team/manager/field approvers are resolved to concrete user ids at\n * open time, and the `type:value` literal is only the fallback for a spec\n * that resolved to NOBODY — a slot no one can act on either way (`can_act`\n * is a plain membership test over the resolved ids). So this cannot hide a\n * request from someone who could actually act on it.\n */\n private async visibleRequestIds(\n context: ExecutionContext,\n tenantOrg: string | null,\n ): Promise<Set<string> | null> {\n if (this.isOverrideActor(context, tenantOrg)) return null;\n const uid = context?.userId != null ? String(context.userId) : '';\n // A tokenless/anonymous caller participates in nothing. Fail closed.\n if (!uid) return new Set<string>();\n\n const ids = new Set<string>();\n const cap = ApprovalService.APPROVER_INDEX_CAP;\n const add = (rows: unknown, key: string) => {\n const list: any[] = Array.isArray(rows) ? rows : [];\n for (const r of list) if (r?.[key] != null) ids.add(String(r[key]));\n if (list.length >= cap) {\n this.logger?.warn?.(\n '[approvals] participant-visibility probe hit its window — some requests may be hidden from a legitimate participant',\n { cap, key },\n );\n }\n };\n\n try {\n // Current approver — via the normalized index, so every identity form\n // the write path recorded is covered.\n for (const id of (await this.approverRequestIds([uid], tenantOrg)) ?? []) ids.add(id);\n\n const orgWhere = tenantOrg ? { organization_id: tenantOrg } : {};\n add(\n await this.engine.find('sys_approval_request', {\n where: { submitter_id: uid, ...orgWhere },\n fields: ['id'], limit: cap, context: SYSTEM_CTX,\n }),\n 'id',\n );\n // Already acted on it: a past approver whose slot has moved on, or a\n // commenter. They saw it legitimately; keep it that way.\n add(\n await this.engine.find('sys_approval_action', {\n where: { actor_id: uid },\n fields: ['request_id'], limit: cap, context: SYSTEM_CTX,\n }),\n 'request_id',\n );\n } catch (err) {\n // Never widen on error: a failed probe yields whatever was collected.\n this.logger?.warn?.('[approvals] participant-visibility probe failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n return ids;\n }\n\n /** Intersect an existing `where.id` constraint with the participant set. */\n private applyVisibility(where: any, visible: Set<string> | null): boolean {\n if (!visible) return true;\n if (visible.size === 0) return false;\n let allowed = [...visible];\n const current = where.id;\n if (typeof current === 'string') allowed = allowed.filter((x) => x === current);\n else if (current && typeof current === 'object' && Array.isArray(current.$in)) {\n const set = new Set(current.$in.map((v: unknown) => String(v)));\n allowed = allowed.filter((x) => set.has(x));\n }\n if (allowed.length === 0) return false;\n where.id = allowed.length === 1 ? allowed[0] : { $in: allowed };\n return true;\n }\n\n async listRequests(\n filter: {\n object?: string;\n recordId?: string;\n status?: ApprovalStatus | ApprovalStatus[];\n approverId?: string | string[];\n submitterId?: string;\n q?: string;\n limit?: number;\n offset?: number;\n } | undefined,\n context: ExecutionContext,\n ): Promise<ApprovalRequestRow[]> {\n const { where, tenantOrg } = this.buildRequestWhere(filter, context);\n const approverTargets = (Array.isArray(filter?.approverId) ? filter!.approverId : filter?.approverId ? [filter.approverId] : [])\n .map(t => String(t).trim())\n .filter(Boolean);\n\n // Every filter now pushes into the engine (issue #1745): approver via\n // the normalized index, status arrays via $in — so the page window is\n // always engine-side and correct at any table size.\n const ids = await this.approverRequestIds(approverTargets, tenantOrg);\n if (ids) {\n if (ids.length === 0) return [];\n where.id = ids.length === 1 ? ids[0] : { $in: ids };\n }\n\n // #3590: the caller-supplied `approverId` is a FILTER, not authorization —\n // omitting it used to return every request in the tenant. Intersect with\n // what this caller actually participates in.\n if (!this.applyVisibility(where, await this.visibleRequestIds(context, tenantOrg))) return [];\n\n const findOpts: any = {\n where,\n orderBy: [{ field: 'created_at', order: 'desc' }],\n context: SYSTEM_CTX,\n };\n if (filter?.limit != null || filter?.offset != null) {\n findOpts.limit = Math.min(Math.max(filter?.limit ?? 50, 1), 200);\n if (filter?.offset) findOpts.offset = Math.max(filter.offset, 0);\n } else {\n // Unpaginated callers keep the legacy bounded window.\n findOpts.limit = 500;\n }\n\n const rows = await this.engine.find('sys_approval_request', findOpts);\n const list = Array.isArray(rows) ? rows.map(rowFromRequest) : [];\n await this.enrichRows(list);\n this.attachViewers(list, context);\n return list;\n }\n\n async countRequests(\n filter: Parameters<IApprovalService['listRequests']>[0],\n context: ExecutionContext,\n ): Promise<number> {\n const { where, tenantOrg } = this.buildRequestWhere(filter, context);\n const approverTargets = (Array.isArray(filter?.approverId) ? filter!.approverId : filter?.approverId ? [filter.approverId] : [])\n .map(t => String(t).trim())\n .filter(Boolean);\n\n const ids = await this.approverRequestIds(approverTargets, tenantOrg);\n if (ids) {\n if (ids.length === 0) return 0;\n where.id = ids.length === 1 ? ids[0] : { $in: ids };\n }\n\n // #3590 — the count must agree with the list it paginates.\n if (!this.applyVisibility(where, await this.visibleRequestIds(context, tenantOrg))) return 0;\n\n const countFn = (this.engine as any).count;\n if (typeof countFn === 'function') {\n try {\n const n = await countFn.call(this.engine, 'sys_approval_request', { where, context: SYSTEM_CTX });\n if (typeof n === 'number') return n;\n } catch { /* fall through to scan */ }\n }\n // Engine without count(): bounded scan. The approver-filtered case is\n // exact (the id set bounds it); the unfiltered case keeps the legacy\n // 500 window.\n const rows = await this.engine.find('sys_approval_request', {\n where, fields: ['id'], limit: ids ? Math.max(500, ids.length) : 500, context: SYSTEM_CTX,\n });\n return Array.isArray(rows) ? rows.length : 0;\n }\n\n /**\n * Read the request a write path just changed, to echo back as its result.\n *\n * NOT participant-gated (#3590), deliberately: the operation authorized\n * itself by its own rule before writing, so re-asking \"may you see this?\"\n * for the echo answers a question that has already been settled — and would\n * answer it WRONG for a caller context that carries no `userId` (a\n * flow-driven resume, a service-to-service call), turning a successful write\n * into a `null` result. Gating belongs on the read API, not on an\n * operation's own return value.\n */\n private async readBackRequest(\n requestId: string,\n context: ExecutionContext,\n ): Promise<ApprovalRequestRow | null> {\n return this.loadRequest(requestId, context, false);\n }\n\n async getRequest(requestId: string, context: ExecutionContext): Promise<ApprovalRequestRow | null> {\n return this.loadRequest(requestId, context, true);\n }\n\n private async loadRequest(\n requestId: string,\n context: ExecutionContext,\n enforceVisibility: boolean,\n ): Promise<ApprovalRequestRow | null> {\n if (!requestId) return null;\n const where: any = { id: requestId };\n // `organizationId` is not on the envelope — see isOverrideActor().\n const tenantOrg = (context as any)?.organizationId ?? context?.tenantId;\n if (tenantOrg) where.organization_id = tenantOrg;\n const rows = await this.engine.find('sys_approval_request', {\n where, limit: 1, context: SYSTEM_CTX,\n });\n if (!Array.isArray(rows) || !rows[0]) return null;\n // #3590: tenant scoping alone let any authenticated user read any request\n // — and, once decision attachments derived their access from the request\n // (#3580), its files too. Participation is the rest of the rule.\n if (enforceVisibility) {\n const visible = await this.visibleRequestIds(context, tenantOrg ?? null);\n if (visible && !visible.has(String(rows[0].id))) return null;\n }\n const row = rowFromRequest(rows[0]);\n await this.enrichRows([row]);\n await this.attachFlowSteps(row);\n await this.attachDecisionProgress(row, rows[0]);\n this.attachViewers([row], context);\n return row;\n }\n\n /**\n * Server-computed decision aggregation progress (#3266 / objectui#2678 P1.5).\n * Single-read enrichment only (like {@link ApprovalService.attachFlowSteps}):\n * for a PENDING request whose behavior aggregates multiple approvals\n * (`unanimous` / `quorum` / `per_group`), expose\n * `decision_progress: { behavior, got, need, groups? }` so any client renders\n * \"2 of 3\" or per-group ticks without re-deriving the engine's tally rules.\n * `first_response` requests carry no progress (one approval finalizes).\n * Display-only and best-effort — errors leave the row untouched.\n */\n private async attachDecisionProgress(row: ApprovalRequestRow, raw: any): Promise<void> {\n try {\n if (row.status !== 'pending') return;\n const cfg = parseJson<any>(raw.node_config_json, undefined);\n const behavior = cfg?.behavior ?? 'first_response';\n if (behavior !== 'unanimous' && behavior !== 'quorum' && behavior !== 'per_group') return;\n\n const acts = await this.engine.find('sys_approval_action', {\n where: { request_id: row.id, step_index: 0, action: 'approve' }, limit: 1000, context: SYSTEM_CTX,\n });\n const approved = new Set<string>((acts ?? []).map((a: any) => String(a.actor_id ?? '')).filter(Boolean));\n\n const snapshot = cfg?.__approverGroups as Record<string, string[]> | undefined;\n const slate = snapshot ? Object.keys(snapshot) : [...approved, ...(row.pending_approvers ?? [])];\n const total = slate.length || 1;\n\n const progress: any = { behavior, got: approved.size, need: total };\n if (behavior === 'quorum') {\n progress.need = Math.min(Math.max(1, cfg?.minApprovals ?? total), total);\n } else if (behavior === 'per_group' && snapshot) {\n const perGroupNeed = Math.max(1, cfg?.minApprovals ?? 1);\n const size: Record<string, number> = {};\n for (const gs of Object.values(snapshot)) for (const g of gs) size[g] = (size[g] ?? 0) + 1;\n const got: Record<string, number> = {};\n for (const a of approved) for (const g of (snapshot[a] ?? [])) got[g] = (got[g] ?? 0) + 1;\n progress.groups = Object.keys(size).sort().map(g => {\n const need = Math.min(perGroupNeed, size[g]);\n return { group: g, got: Math.min(got[g] ?? 0, need), need, satisfied: (got[g] ?? 0) >= need };\n });\n progress.got = progress.groups.filter((g: any) => g.satisfied).length;\n progress.need = progress.groups.length;\n\n // Approver→group(s) for the STILL-PENDING slots (objectui#2807), so the\n // console can label each \"waiting on\" chip with the group it represents\n // rather than showing duplicate, context-free names. Only pending slots\n // matter — a resolved approver has dropped out of `pending_approvers`.\n // Synthetic (unnamed, `#N`) group keys are dropped: a `· #0` sub-tag is\n // noise, and the client would have to filter it anyway.\n const pendingGroups: Record<string, string[]> = {};\n for (const a of (row.pending_approvers ?? [])) {\n const named = (snapshot[a] ?? []).filter((g) => !/^#\\d+$/.test(g));\n if (named.length) pendingGroups[a] = named;\n }\n if (Object.keys(pendingGroups).length) {\n (row as any).pending_approver_groups = pendingGroups;\n }\n }\n (row as any).decision_progress = progress;\n } catch { /* display-only enrichment */ }\n }\n\n /**\n * Attach the per-viewer capability block (#3310) from the caller's context.\n * `can_act` mirrors the exact authorization the decision methods enforce — the\n * caller's user id is in the resolved `pending_approvers` while the request is\n * still `pending` (position/team/manager approvers are already resolved to\n * concrete user ids at open time, so a plain membership test is faithful).\n * `is_submitter` is a straight owner check. `can_override` (#3424) is true for\n * a platform/tenant admin on a PENDING request — the recovery path for an\n * approval routed to an unstaffed position or to approvers who have all left;\n * clients OR it into the decision actions' `visible` gate so an admin can act\n * even when they hold no slot. System/tokenless contexts get a both-false\n * `can_act`/`is_submitter` block (system gets `can_override` too — it may act\n * on anything). Cheap + synchronous — safe on list reads.\n */\n private attachViewers(rows: ApprovalRequestRow[], context: ExecutionContext): void {\n const uid = context?.userId != null ? String(context.userId) : null;\n for (const row of rows) {\n const pending = row.pending_approvers ?? [];\n (row as any).viewer = {\n can_act: row.status === 'pending' && !!uid && pending.includes(uid),\n is_submitter: !!uid && row.submitter_id != null && String(row.submitter_id) === uid,\n can_override: row.status === 'pending'\n && this.isOverrideActor(context, (row as any).organization_id ?? null),\n };\n }\n }\n\n /**\n * Derive approval-step progress from the owning flow's graph (single-read\n * enrichment only — list reads skip it). Walks from the start node\n * preferring `approve`/`true` edges, so the result is the flow's main\n * approval trunk; conditional side-steps show as part of the potential\n * path. Display-only and best-effort.\n */\n private async attachFlowSteps(row: ApprovalRequestRow): Promise<void> {\n try {\n const flowName = row.process_name?.startsWith('flow:') ? row.process_name.slice(5) : undefined;\n if (!flowName || typeof this.automation?.getFlow !== 'function') return;\n const flow: any = await this.automation.getFlow(flowName);\n if (!flow?.nodes?.length) return;\n const nodesById = new Map<string, any>(flow.nodes.map((n: any) => [n.id, n]));\n const steps: Array<{ id: string; label: string }> = [];\n const seen = new Set<string>();\n let cur: any = flow.nodes.find((n: any) => n.type === 'start');\n while (cur && !seen.has(cur.id)) {\n seen.add(cur.id);\n if (cur.type === 'approval') steps.push({ id: cur.id, label: cur.label || cur.id });\n const out = (flow.edges ?? []).filter((e: any) => e.source === cur.id);\n if (!out.length) break;\n const pick = out.find((e: any) => e.label === 'approve')\n ?? out.find((e: any) => e.label === 'true')\n ?? out[0];\n cur = nodesById.get(pick.target);\n }\n if (steps.length === 0) return;\n const currentId = row.flow_node_id ?? row.current_step;\n const currentIdx = steps.findIndex(s => s.id === currentId);\n (row as any).flow_steps = steps.map((s, i) => ({\n ...s,\n state: currentIdx < 0 ? 'upcoming'\n : i < currentIdx ? 'done'\n : i === currentIdx ? (row.status === 'approved' ? 'done' : 'current')\n : 'upcoming',\n }));\n } catch { /* display-only — never fail the read */ }\n }\n\n async listActions(requestId: string, context: ExecutionContext): Promise<ApprovalActionRow[]> {\n if (!requestId) return [];\n // Tenant gate: ensure the caller can see the parent request before\n // returning its action history. Skipping this would leak history rows\n // across tenants the same way the unscoped list-requests path did.\n const req = await this.getRequest(requestId, context);\n if (!req) return [];\n const rows = await this.engine.find('sys_approval_action', {\n where: { request_id: requestId },\n limit: 500,\n orderBy: [{ field: 'created_at', order: 'asc' }],\n context: SYSTEM_CTX,\n });\n const actions = Array.isArray(rows) ? rows.map(rowFromAction) : [];\n // Timeline display: resolve actor ids to names so the audit trail never\n // shows a raw identifier. Role/team literals are already readable. The\n // reassign hand-off parties (#4365) resolve through the same batch.\n const names = await this.resolveUserNames(\n actions\n .flatMap(a => [a.actor_id, a.reassign_from, a.reassign_to])\n .filter(id => id && !id.includes(':')),\n );\n for (const a of actions as any[]) {\n const n = a.actor_id ? names.get(String(a.actor_id)) : undefined;\n if (n) a.actor_name = n;\n const fromName = a.reassign_from ? names.get(String(a.reassign_from)) : undefined;\n if (fromName) a.reassign_from_name = fromName;\n const toName = a.reassign_to ? names.get(String(a.reassign_to)) : undefined;\n if (toName) a.reassign_to_name = toName;\n }\n return actions;\n }\n\n /**\n * `IFileAccessDelegate` — may this caller download a decision attachment?\n * (ADR-0104 D3 wave 2; declared by `sys_approval_action.fileAccessDelegate`.)\n *\n * A file referenced by `sys_approval_action.attachments` is owned by that\n * audit row, so the storage service would otherwise authorize the download by\n * testing whether the caller can READ the row. It cannot: `sys_approval_action`\n * is deliberately closed to ordinary approver positions, so that test denies\n * the very approver the attachment was filed for.\n *\n * The rule that actually governs seeing a decision is the one `listActions`\n * applies — can the caller see the PARENT REQUEST? — so this reuses it\n * exactly, rather than inventing a second, looser rule for the bytes. Fails\n * closed on any error.\n */\n async authorizeFileRead(actionId: string, context: ExecutionContext): Promise<boolean> {\n if (!actionId) return false;\n try {\n const rows = await this.engine.find('sys_approval_action', {\n where: { id: actionId },\n limit: 1,\n context: SYSTEM_CTX,\n });\n const requestId = (Array.isArray(rows) ? rows[0] : undefined)?.request_id;\n if (!requestId) return false;\n // Same gate as listActions: visibility of the decision's parent request.\n return !!(await this.getRequest(String(requestId), context));\n } catch {\n return false;\n }\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0105 D9] Cross-organization approver targeting — resolving WHICH\n * organization's directory an approver is looked up in.\n *\n * One organization id used to do three jobs at once in `openNodeRequest`:\n * where the request row lives, where its inbox index rows live, and where its\n * approvers are looked up. The first two are the request's own organization by\n * definition. The third is not: a group CFO holds her `cfo` position in the\n * GROUP organization while the purchase order she signs off lives in the PLANT\n * organization. Binding all three together meant\n * `expandPositionUsers('cfo', <plant>)` matched nobody and the slot fell into\n * `onEmptyApprovers` — a group escalation could not be expressed at all.\n *\n * This module resolves only the third job. The request keeps living in its own\n * organization, and D2's membership union is what lets a group-side approver\n * READ it — see `assertApproversCanRead` below for why that is a precondition\n * worth checking rather than assuming.\n */\n\n/** Cycle guard for the `parent_organization_id` walk (mirrors the BU subtree walk). */\nconst MAX_ORG_DEPTH = 32;\n\nconst SYSTEM_CTX = { isSystem: true } as const;\n\nexport interface ApproverOrgScopeEngine {\n find(object: string, options: any): Promise<any[]>;\n}\n\nexport interface ApproverOrgScopeDeps {\n engine: ApproverOrgScopeEngine;\n /**\n * The tenancy posture in force, when the host could resolve one. `undefined`\n * means \"unknown\" — a stack booted without the tenancy service — and is\n * treated as permissive so a minimal test/embedded stack is not broken by a\n * guard it cannot answer.\n */\n posture?: () => string | undefined;\n logger?: { warn?: (msg: any, ...rest: any[]) => void; debug?: (msg: any, ...rest: any[]) => void };\n}\n\n/**\n * Every failure here THROWS `VALIDATION_FAILED`, matching how `expression`\n * approvers already fail (#3447 P2): an approver declaration that cannot be\n * resolved is a ROUTING BUG, never \"condition not met\". Silently falling back\n * to the request's own organization would be the worst option available — a\n * flow that reads as \"escalate to group\" would quietly approve inside the\n * plant, and the deployment that changed posture would reroute its approvals\n * with no signal at all.\n *\n * Messages name the offending value because their primary reader is the AI\n * author fixing the flow on the next validate pass.\n */\nfunction fail(message: string): never {\n throw new Error(`VALIDATION_FAILED: ${message}`);\n}\n\nasync function findOrg(\n engine: ApproverOrgScopeEngine,\n where: Record<string, unknown>,\n): Promise<any | null> {\n try {\n const rows = await engine.find('sys_organization', {\n where,\n fields: ['id', 'slug', 'parent_organization_id'],\n limit: 1,\n context: SYSTEM_CTX,\n } as any);\n return Array.isArray(rows) && rows[0] ? rows[0] : null;\n } catch {\n return null;\n }\n}\n\n/** The id chain from `orgId` up to its root, inclusive. Fail-closed on cycles. */\nasync function ancestorChain(\n engine: ApproverOrgScopeEngine,\n orgId: string,\n): Promise<string[]> {\n const chain: string[] = [];\n const seen = new Set<string>();\n let cursor: string | null = orgId;\n for (let depth = 0; cursor && depth < MAX_ORG_DEPTH; depth++) {\n if (seen.has(cursor)) break; // cycle in the grouping metadata — stop, do not loop\n seen.add(cursor);\n chain.push(cursor);\n const row = await findOrg(engine, { id: cursor });\n const parent = row?.parent_organization_id;\n cursor = parent ? String(parent) : null;\n }\n return chain;\n}\n\n/**\n * Resolve an approver's `organization` declaration to a concrete organization\n * id for directory lookups. Returns the request's own organization when the\n * declaration is absent — the unchanged default path, which does no reads.\n */\nexport async function resolveApproverDirectoryOrg(\n deps: ApproverOrgScopeDeps,\n declaration: string | null | undefined,\n requestOrgId: string | null | undefined,\n approverType: string,\n isOrgScopedType: boolean,\n): Promise<string | null | undefined> {\n const declared = typeof declaration === 'string' ? declaration.trim() : '';\n if (!declared) return requestOrgId;\n\n // An `organization` on `user` / `field` / `manager` / `team` is not a\n // narrower routing — those types never consult an org-scoped directory, so\n // the declaration would have no effect. Refuse rather than ignore: a flow\n // author who wrote it believed it did something.\n if (!isOrgScopedType) {\n fail(\n `approver type '${approverType}' resolves people without an organization directory, `\n + `so 'organization: ${declared}' would have no effect — remove it `\n + `(ADR-0105 D9 applies to position / org_membership_level / department / expression)`,\n );\n }\n\n // Posture guard. ADR-0105 D9 applies to `group` only: in `isolated`, cross-org\n // approval remains system-context mirroring (cloud #2937 contract), and in\n // `single` there is no second organization to target. Refusing here — rather\n // than in a lint — is deliberate: posture is ENVIRONMENT configuration, so the\n // same portable flow metadata may be deployed into any posture and no static\n // check can see which. A deployment migrating group → isolated must fail\n // loudly instead of silently rerouting its approvals.\n const posture = deps.posture?.();\n if (posture && posture !== 'group') {\n fail(\n `cross-organization approver targeting ('organization: ${declared}') requires the `\n + `'group' tenancy posture; this deployment resolves '${posture}' (ADR-0105 D9)`,\n );\n }\n\n const requestOrg = requestOrgId ? String(requestOrgId) : '';\n if (!requestOrg) {\n fail(\n `'organization: ${declared}' cannot be resolved for a request that carries no `\n + `organization — cross-organization targeting needs an organization to resolve from`,\n );\n }\n\n const chain = await ancestorChain(deps.engine, requestOrg);\n\n if (declared === '$root') {\n const root = chain[chain.length - 1];\n if (root === requestOrg && chain.length === 1) {\n fail(\n `'organization: $root' resolved to the request's own organization — this organization `\n + `has no 'parent_organization_id' lineage. Declare the group hierarchy (ADR-0105 D6) `\n + `or drop the targeting`,\n );\n }\n return root;\n }\n\n if (declared === '$parent') {\n const parent = chain[1];\n if (!parent) {\n fail(\n `'organization: $parent' has no parent to resolve to — the request's organization is `\n + `already the root of its group (ADR-0105 D6 'parent_organization_id')`,\n );\n }\n return parent;\n }\n\n // A slug names one specific organization — the shape the symbols cannot\n // express (a SIBLING, e.g. a shared-services centre approving payables for\n // every plant). Slugs rather than ids because flow metadata is portable\n // across environments and organization ids are minted per deployment.\n const target = await findOrg(deps.engine, { slug: declared });\n if (!target?.id) {\n fail(\n `no organization with slug '${declared}' — 'organization' takes an organization SLUG `\n + `or a symbol ($root / $parent), never an id (ids are per-deployment, flows are portable)`,\n );\n }\n const targetId = String(target.id);\n if (targetId === requestOrg) return targetId;\n\n // Legality: the target must share a grouping root with the request's\n // organization. \"Shares a root\" rather than \"is an ancestor\" because the\n // sibling case is a first-class use. The rule depends only on the\n // organization tree — never on who submitted — so one flow routes identically\n // for every submitter, which is what makes a routing bug reproducible.\n const targetChain = await ancestorChain(deps.engine, targetId);\n const targetRoot = targetChain[targetChain.length - 1];\n const requestRoot = chain[chain.length - 1];\n if (!targetRoot || !requestRoot || targetRoot !== requestRoot) {\n fail(\n `organization '${declared}' is not in the same group as the request's organization `\n + `— cross-organization approval routes WITHIN one group (they must share a `\n + `'parent_organization_id' root, ADR-0105 D6/D9)`,\n );\n }\n return targetId;\n}\n\n/**\n * Drop approvers who could not READ the request they were routed to, and say\n * so.\n *\n * ADR-0105 D9 notes that reads by cross-org approvers \"are covered by D2\n * (membership union)\". True — but D2's union is `organization_id IN\n * accessible_org_ids`, and the request row is stamped with the REQUEST's\n * organization. So a group-side approver reaches it only if she also holds a\n * membership there. That is the intended group shape (group staff are members\n * of every plant while holding their positions in the group org — the shape the\n * ADR-0105 acceptance dogfood already models), but nothing enforces it.\n *\n * Left unchecked the failure is SILENT and expensive: routing succeeds, the\n * request and inbox rows are written, and the approver then opens a task she\n * cannot open — the wall hides the record. Filtering here converts that into\n * the empty-slate path the node already has a policy for\n * (`onEmptyApprovers`), and logs exactly who was dropped and why, so the fix\n * (\"grant the membership\" or \"retarget\") is legible without a debugger.\n */\nexport async function filterApproversWhoCanRead(\n deps: ApproverOrgScopeDeps,\n userIds: string[],\n requestOrgId: string | null | undefined,\n context: { approverType: string; value?: string; directoryOrgId?: string | null },\n): Promise<string[]> {\n const requestOrg = requestOrgId ? String(requestOrgId) : '';\n if (!requestOrg || userIds.length === 0) return userIds;\n\n let members: any[] = [];\n try {\n members = await deps.engine.find('sys_member', {\n where: { organization_id: requestOrg, user_id: { $in: userIds } },\n fields: ['user_id'],\n limit: 10000,\n context: SYSTEM_CTX,\n } as any);\n } catch {\n // Membership unreadable — do NOT drop everyone on an infrastructure\n // hiccup. Routing to someone who may not see the request is recoverable\n // (an admin can grant the membership); silently emptying a live approval\n // slate is not.\n return userIds;\n }\n\n const canRead = new Set(\n (members ?? []).map((m: any) => String(m?.user_id ?? '')).filter(Boolean),\n );\n const dropped = userIds.filter((u) => !canRead.has(u));\n if (dropped.length === 0) return userIds;\n\n deps.logger?.warn?.(\n `[approvals] ADR-0105 D9: ${dropped.length} cross-organization approver(s) dropped — `\n + `they hold '${context.value ?? context.approverType}' in organization `\n + `'${context.directoryOrgId}' but no membership in the request's organization `\n + `'${requestOrg}', so the D2 union wall would hide the request from them. `\n + `Grant those users a membership in the request's organization, or retarget the approver.`,\n { dropped, requestOrganizationId: requestOrg, directoryOrganizationId: context.directoryOrgId },\n );\n return userIds.filter((u) => canRead.has(u));\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_approval_token — single-use actionable-link tokens (ADR-0043).\n *\n * One row per issued approve/reject link. Only the SHA-256 **hash** of the\n * raw token is stored — a database leak yields no usable links. A token is\n * dead once any of these holds: `consumed_at` set, `expires_at` passed, the\n * request left `pending`, or the bound approver no longer holds a slot\n * (the last two are re-checked at redemption, not materialized here).\n *\n * @namespace sys\n */\nexport const SysApprovalToken = ObjectSchema.create({\n name: 'sys_approval_token',\n label: 'Approval Action Token',\n pluralLabel: 'Approval Action Tokens',\n icon: 'key',\n isSystem: true,\n managedBy: 'engine-owned',\n description: 'Single-use tokens behind actionable approval links',\n displayNameField: 'id',\n nameField: 'id', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)\n\n fields: {\n id: Field.text({ label: 'Token ID', required: true, readonly: true, group: 'System' }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n }),\n\n token_hash: Field.text({\n label: 'Token Hash',\n required: true,\n maxLength: 100,\n readonly: true,\n description: 'SHA-256 hex of the raw token — the raw value is never stored',\n group: 'Token',\n }),\n\n request_id: Field.text({\n label: 'Request',\n required: true,\n maxLength: 100,\n readonly: true,\n group: 'Token',\n }),\n\n action: Field.select(['approve', 'reject'], {\n label: 'Action',\n required: true,\n readonly: true,\n group: 'Token',\n }),\n\n approver_id: Field.text({\n label: 'Approver',\n required: true,\n maxLength: 200,\n readonly: true,\n description: 'Identity the token is bound to; the decision is audited as this approver',\n group: 'Token',\n }),\n\n expires_at: Field.datetime({\n label: 'Expires At',\n required: true,\n readonly: true,\n group: 'Lifecycle',\n }),\n\n consumed_at: Field.datetime({\n label: 'Consumed At',\n required: false,\n group: 'Lifecycle',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n },\n\n indexes: [\n { fields: ['token_hash'] },\n { fields: ['request_id'] },\n ],\n\n enable: {\n // [ADR-0103] Engine-owned: one-time email-approval tokens are minted and\n // consumed by the approval engine (SYSTEM_CTX), never via the data API.\n apiMethods: ['get', 'list'],\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Session-less HTML for the actionable-link confirm/result pages (ADR-0043).\n *\n * Deliberately tiny and dependency-free: these pages are reached from an\n * email or IM message by a bearer with no session, so they must not assume\n * the Console bundle, auth state, or client-side i18n. Static bilingual\n * (EN / 中文) copy keeps them readable for the demo audience without a\n * locale negotiation step.\n *\n * The GET page NEVER mutates — the decision happens only on the POST form\n * submit (mail-gateway link prefetchers must not approve requests).\n */\n\nimport type { ApprovalRequestRow, ApprovalActionKind } from '@objectstack/spec/contracts';\n\nfunction esc(s: unknown): string {\n return String(s ?? '')\n .replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>')\n .replaceAll('\"', '"').replaceAll(\"'\", ''');\n}\n\nfunction shell(title: string, body: string): string {\n return `<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta name=\"robots\" content=\"noindex\">\n<title>${esc(title)}</title>\n<style>\n body{font:15px/1.6 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n background:#f6f7f9;color:#1a202c;margin:0;display:flex;min-height:100vh;align-items:center;justify-content:center}\n .card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;max-width:440px;width:calc(100% - 32px);\n padding:28px 32px;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n h1{font-size:18px;margin:0 0 4px}\n .sub{color:#64748b;font-size:13px;margin:0 0 20px}\n .row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px solid #f1f5f9;font-size:14px}\n .row b{font-weight:600;text-align:right}\n .k{color:#64748b}\n .actions{margin-top:22px;display:flex;gap:10px}\n button{flex:1;padding:10px 16px;border-radius:8px;border:1px solid transparent;font-size:15px;font-weight:600;cursor:pointer}\n .approve{background:#059669;color:#fff}\n .reject{background:#fff;color:#dc2626;border-color:#fca5a5}\n .badge{display:inline-block;padding:2px 10px;border-radius:999px;font-size:12px;font-weight:600;margin-bottom:14px}\n .ok{background:#ecfdf5;color:#047857}.warn{background:#fffbeb;color:#b45309}.err{background:#fef2f2;color:#b91c1c}\n a{color:#2563eb;text-decoration:none}\n .foot{margin-top:18px;font-size:12px;color:#94a3b8}\n</style></head><body><div class=\"card\">${body}</div></body></html>`;\n}\n\nfunction summaryRows(req: ApprovalRequestRow): string {\n const rows: Array<[string, string]> = [\n ['Process · 流程', req.process_label || req.process_name],\n ['Step · 步骤', req.step_label || req.current_step || '—'],\n ['Record · 记录', req.record_title || req.record_id],\n ['Object · 对象', req.object_label || req.object_name],\n ['Requester · 申请人', req.submitter_name || req.submitter_id || '—'],\n ];\n return rows.map(([k, v]) => `<div class=\"row\"><span class=\"k\">${esc(k)}</span><b>${esc(v)}</b></div>`).join('');\n}\n\n/** GET page: summary + a POST form. Rendering only — no mutation. */\nexport function renderConfirmPage(input: {\n request: ApprovalRequestRow;\n action: Extract<ApprovalActionKind, 'approve' | 'reject'>;\n approverId: string;\n token: string;\n actPath: string;\n}): string {\n const approving = input.action === 'approve';\n const verb = approving ? 'Approve · 通过' : 'Reject · 拒绝';\n return shell(`${verb} — Approval`, `\n <h1>${approving ? '✅ Approve this request?' : '⛔ Reject this request?'}</h1>\n <p class=\"sub\">${approving ? '确认通过该审批请求?' : '确认拒绝该审批请求?'}\n Acting as · 操作身份:<b>${esc(input.approverId)}</b></p>\n ${summaryRows(input.request)}\n <form method=\"post\" action=\"${esc(input.actPath)}\" class=\"actions\">\n <input type=\"hidden\" name=\"token\" value=\"${esc(input.token)}\">\n <button type=\"submit\" class=\"${approving ? 'approve' : 'reject'}\">${verb}</button>\n </form>\n <p class=\"foot\">This link is single-use and expires automatically. · 此链接一次有效,过期自动失效。</p>`);\n}\n\nconst RESULT_COPY: Record<string, { cls: string; title: string; body: string }> = {\n approved: { cls: 'ok', title: '✅ Approved · 已通过', body: 'The decision was recorded. · 审批结果已记录。' },\n rejected: { cls: 'ok', title: '⛔ Rejected · 已拒绝', body: 'The decision was recorded. · 审批结果已记录。' },\n invalid: { cls: 'err', title: 'Invalid link · 链接无效', body: 'This link is not recognized. · 无法识别该链接。' },\n expired: { cls: 'warn', title: 'Link expired · 链接已过期', body: 'Ask the requester to send a new reminder. · 请让申请人重新发送催办。' },\n consumed: { cls: 'warn', title: 'Already used · 链接已使用', body: 'This link was already used once. · 该链接已被使用过。' },\n not_pending: { cls: 'warn', title: 'Already decided · 请求已处理', body: 'This request is no longer pending. · 该请求已不在待审批状态。' },\n not_approver: { cls: 'warn', title: 'No longer your approval · 已不在你名下', body: 'This approval was handed to someone else. · 该审批已转由他人处理。' },\n};\n\n/** Terminal page for every redemption outcome (and stale GETs). */\nexport function renderResultPage(kind: keyof typeof RESULT_COPY, request?: ApprovalRequestRow): string {\n const copy = RESULT_COPY[kind] ?? RESULT_COPY.invalid;\n return shell(copy.title, `\n <span class=\"badge ${copy.cls}\">${esc(copy.title)}</span>\n ${request ? summaryRows(request) : ''}\n <p>${esc(copy.body)}</p>\n <p class=\"foot\"><a href=\"/system/approvals\">Open the Approvals Inbox · 打开审批中心</a></p>`);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Lifecycle Hooks — node-era record lock (ADR-0019).\n *\n * Approval is now a flow node, so there is no per-object process registry to\n * bind auto-trigger hooks against — a flow decides *when* to open an approval.\n * What remains worth enforcing at the data layer is the **record lock**: while\n * a record has a pending `sys_approval_request`, block edits to it.\n *\n * A single global `beforeUpdate` hook handles every object (the target object\n * of an approval node is only known at flow-run time). For each update it:\n *\n * 1. Skips engine self-writes (status mirror) and `sys_approval_*` bookkeeping.\n * 2. Resolves **which records the write would touch** and looks up the pending\n * requests gating them.\n * 3. Reads the lock policy from each request's `node_config_json` snapshot:\n * - `lockRecord === false` → allow.\n * - otherwise block, EXCEPT when the only changed field is the configured\n * `approvalStatusField` (so the status mirror is never blocked) or the\n * writer is the run that opened the request (`flowRunId`, #3456 / #3712).\n *\n * ## There is no admin exemption — by design (#4839)\n *\n * The lock applies to a platform/tenant admin exactly as it applies to anyone\n * else. An admin who must release a locked record does it through the\n * **sanctioned rescue path** (#3424): `ApprovalService.recall` / `decideNode`\n * (reject) / `reassign`, all gated by `isOverrideActor` and all audited\n * (`via_override`, #4466). Finalising the request is what releases the lock —\n * the record is never edited *under* a live approval, which is precisely the\n * guarantee a compliance-minded tenant buys the lock for.\n *\n * This is also not a behaviour change. The hook used to open with\n * `if (session.roles?.includes('admin')) return`, but `roles` has no producer:\n * ObjectQL's `buildSession()` never writes it, so the branch was dead on every\n * real engine path and the lock has always applied to admins. Deleting it makes\n * the code say what the runtime does — and closes a second, forbidden admin\n * dialect: privilege in this codebase is judged by the ADR-0095 vocabulary\n * (`permissions` / `positions` / derived posture), never by a string comparison\n * against `'admin'` (ADR-0090 D3 bans the `role` spelling outright). See\n * {@link file://./approval-service.ts} `isOverrideActor` for the one predicate.\n *\n * ## Step 2 is per-row, and it covers PREDICATE writes (#4778)\n *\n * The engine extracts `input.id` only from a **scalar** `where.id`; an operator\n * object (`{ $in: [...] }`) or any other predicate is a multi-row write that\n * routes to `updateMany`, and reaches this hook with **no** `input.id`. The\n * hook used to open with `if (!id) return`, i.e. it read *\"no row was\n * resolved\"* as *\"there is nothing to authorize\"* when the truth was *\"nothing\n * was ever queried\"* — the same fail-open reasoning as #4757 (`sys_attachment`)\n * and #4630 (`sys_comment`). Rewriting the very same edit as `multi: true` then\n * bypassed the lock with **no privilege at all**: no `isSystem`, no\n * `lockRecord: false`, no whitelisted field.\n *\n * So the hook now resolves the row set the way the attachment/comment guards\n * do, with one difference the record lock forces: it is a **per-row** guard\n * (\"does THIS record have a pending approval\"), not a \"should this whole-table\n * write be refused\" guard — so an unscoped bulk update is *not* refused\n * outright. Instead the resolution is inverted, which is what keeps it cheap:\n * the bound is on the **pending requests for the object** (of which there are\n * normally none, and the hook returns after one query), never on the update's\n * match set. Only when some record of the object *is* locked does the hook ask\n * the engine which of those locked rows the caller's predicate actually\n * matches. Past {@link PENDING_LOCK_LIMIT} locked records — or if that\n * intersection query fails — the write fails **CLOSED**.\n *\n * Every exemption above is evaluated on the multi-row path exactly as on the\n * by-id path: extending a guard to more rows must move the *allow* rules with\n * the *deny* rules, or fail-open merely becomes false-positive.\n *\n * Registered under `packageId: 'plugin-approvals:lock'` so it can be cleanly\n * unbound on plugin stop.\n */\n\nexport const APPROVALS_HOOK_PACKAGE = 'plugin-approvals:lock';\n\ninterface MinimalEngine {\n registerHook(event: string, handler: (ctx: any) => any | Promise<any>, options?: {\n object?: string | string[];\n priority?: number;\n packageId?: string;\n }): void;\n unregisterHooksByPackage(packageId: string): number;\n find<T = any>(object: string, args: any, opts?: any): Promise<T[]>;\n}\n\ninterface MinimalLogger {\n debug?: (msg: any, ...rest: any[]) => void;\n info?: (msg: any, ...rest: any[]) => void;\n warn?: (msg: any, ...rest: any[]) => void;\n error?: (msg: any, ...rest: any[]) => void;\n}\n\nfunction parseJson<T = any>(raw: unknown, fallback: T): T {\n if (raw == null || raw === '') return fallback;\n if (typeof raw === 'string') {\n try { return JSON.parse(raw) as T; } catch { return fallback; }\n }\n return raw as T;\n}\n\n/**\n * Bound on the records one write may be authorized against — the sibling of\n * `sys_attachment`'s `MULTI_DELETE_AUTH_LIMIT` and `sys_comment`'s\n * `MULTI_WRITE_AUTH_LIMIT` (#4757 / #4630).\n *\n * It bounds LOCKED records (pending requests on the object) and ids the caller\n * names, never the update's match set: a mass update of 50 000 unlocked rows\n * costs one query and is allowed, while an object carrying more pending\n * approvals than this cannot be decided row by row and fails CLOSED.\n */\nconst PENDING_LOCK_LIMIT = 1_000;\n\n/**\n * The approvals bookkeeping — and the row set a predicate write would touch —\n * are read as SYSTEM. A guard's own input must never be narrowed by the\n * caller's visibility: a locked row the caller cannot READ is still a locked\n * row they must not WRITE (the #4630 rule).\n */\nconst SYSTEM_CTX = { isSystem: true, positions: [], permissions: [] } as const;\n\nfunction lockedError(message: string): never {\n const err: any = new Error(`RECORD_LOCKED: ${message}`);\n err.code = 'RECORD_LOCKED';\n err.statusCode = 409;\n throw err;\n}\n\n/**\n * The record ids a write names outright — a scalar id or an `{ $in: [...] }` —\n * or `null` when the ids cannot be read off it (any other predicate, or an\n * `$in` carrying a non-scalar, which we refuse to guess at).\n *\n * Mirrors `asIdList` in the attachment/comment kits. An empty `$in` legitimately\n * names *no* record, so it returns `[]` (nothing to gate), not `null`.\n */\nfunction asIdList(id: unknown): Array<string | number> | null {\n if (typeof id === 'number') return [id];\n if (typeof id === 'string') return id === '' ? null : [id];\n if (id && typeof id === 'object' && Array.isArray((id as any).$in)) {\n const raw = (id as any).$in as unknown[];\n const scalars = raw.filter((v): v is string | number => typeof v === 'string' || typeof v === 'number');\n return scalars.length === raw.length ? scalars : null;\n }\n return null;\n}\n\n/** The pending request gating a record, plus its snapshotted node config. */\nasync function pendingRequestFor(\n engine: MinimalEngine,\n objectName: string,\n recordId: string,\n): Promise<any | null> {\n try {\n const rows = await engine.find('sys_approval_request', {\n where: { object_name: objectName, record_id: String(recordId), status: 'pending' },\n limit: 1,\n context: { ...SYSTEM_CTX },\n } as any);\n return Array.isArray(rows) && rows[0] ? rows[0] : null;\n } catch {\n return null;\n }\n}\n\n/** Pending requests gating any of the records a write names by id. */\nasync function pendingRequestsForRecords(\n engine: MinimalEngine,\n objectName: string,\n recordIds: ReadonlyArray<string | number>,\n): Promise<any[]> {\n if (recordIds.length === 0) return [];\n if (recordIds.length > PENDING_LOCK_LIMIT) {\n lockedError(\n `refusing to authorize an update naming more than ${PENDING_LOCK_LIMIT} records of '${objectName}' — ` +\n 'the approval lock cannot check them row by row; scope the write',\n );\n }\n if (recordIds.length === 1) {\n const one = await pendingRequestFor(engine, objectName, String(recordIds[0]));\n return one ? [one] : [];\n }\n try {\n const rows = await engine.find('sys_approval_request', {\n where: { object_name: objectName, record_id: { $in: recordIds.map(String) }, status: 'pending' },\n limit: PENDING_LOCK_LIMIT + 1,\n context: { ...SYSTEM_CTX },\n } as any);\n return Array.isArray(rows) ? rows : [];\n } catch {\n return [];\n }\n}\n\n/**\n * Every record of `objectName` currently held by a pending approval, capped one\n * past the bound so the caller can tell \"at the cap\" from \"over it\".\n *\n * `null` means the bookkeeping could not be read at all. That reads as \"no\n * lock\" — deliberately the ONE fail-open left, and the pre-existing behaviour\n * of {@link pendingRequestFor}: this hook is GLOBAL over every object, so a\n * kernel where `sys_approval_request` is absent or momentarily unreadable would\n * otherwise refuse every update in the deployment. The fail-closed decisions\n * below are the ones an attacker can actually steer (a predicate they choose);\n * \"is the approvals table readable\" is not one of them.\n */\nasync function pendingRequestsForObject(\n engine: MinimalEngine,\n objectName: string,\n): Promise<any[] | null> {\n try {\n const rows = await engine.find('sys_approval_request', {\n where: { object_name: objectName, status: 'pending' },\n limit: PENDING_LOCK_LIMIT + 1,\n context: { ...SYSTEM_CTX },\n } as any);\n return Array.isArray(rows) ? rows : [];\n } catch {\n return null;\n }\n}\n\n/**\n * Narrow `candidates` (pending requests) to the ones whose record the caller's\n * predicate actually matches — asked of the engine as an intersection, so the\n * query is bounded by the number of LOCKED rows, not by the update's match set.\n *\n * A failure here fails CLOSED: we know some record of this object is locked and\n * we could not prove the write misses it.\n */\nasync function narrowToMatchedRecords(\n engine: MinimalEngine,\n objectName: string,\n where: unknown,\n candidates: any[],\n): Promise<any[]> {\n const lockedIds = candidates.map((c) => String(c?.record_id ?? ''));\n let rows: any[];\n try {\n rows = await engine.find(objectName, {\n where: { $and: [where, { id: { $in: lockedIds } }] },\n fields: ['id'],\n limit: lockedIds.length,\n context: { ...SYSTEM_CTX },\n } as any);\n } catch (err) {\n lockedError(\n `cannot determine which rows a predicate update on '${objectName}' would touch ` +\n `(${(err as Error)?.message ?? String(err)}); ${candidates.length} record(s) of it carry a pending ` +\n 'approval, so the write is refused',\n );\n }\n const matched = new Set((Array.isArray(rows) ? rows : []).map((r: any) => String(r?.id)));\n return candidates.filter((c) => matched.has(String(c?.record_id ?? '')));\n}\n\n/**\n * The pending requests gating the rows THIS write would touch.\n *\n * Four shapes, cheapest first:\n * - `input.id` (engine's scalar by-id fast path) → that record only. The\n * driver updates by primary key, so the rest of `where` never narrows it —\n * narrowing here would be a fail-open.\n * - predicate naming ids only (`{ id: { $in: [...] } }`) → those records.\n * - predicate with other keys → the object's locked records, intersected with\n * the predicate.\n * - no predicate at all (`updateMany` over the whole table) → every locked\n * record of the object.\n */\nasync function gatingRequests(\n engine: MinimalEngine,\n ctx: any,\n objectName: string,\n): Promise<any[]> {\n const byId = asIdList(ctx?.input?.id);\n if (byId) return pendingRequestsForRecords(engine, objectName, byId);\n\n // Predicate write. `where` is canonical here: the engine folds the `filter`\n // alias into it before hooks run (PD #12 — no consumer-side `?? filter`).\n const rawWhere = (ctx?.input?.options as any)?.where;\n const hasWhere = rawWhere !== undefined && rawWhere !== null;\n const whereObj = hasWhere && typeof rawWhere === 'object' && !Array.isArray(rawWhere)\n ? rawWhere as Record<string, unknown>\n : null;\n\n const namedIds = whereObj ? asIdList(whereObj.id) : null;\n const candidates = namedIds\n ? await pendingRequestsForRecords(engine, objectName, namedIds)\n : await pendingRequestsForObject(engine, objectName);\n if (candidates === null) return []; // bookkeeping unreadable — see the note above\n if (candidates.length === 0) return []; // nothing locked in reach\n if (candidates.length > PENDING_LOCK_LIMIT) {\n lockedError(\n `refusing a predicate update on '${objectName}': more than ${PENDING_LOCK_LIMIT} of its records carry a ` +\n 'pending approval, so the lock cannot decide row by row; scope the write to the rows you mean',\n );\n }\n // No predicate → the write touches every row, so every locked row is in reach.\n if (!hasWhere) return candidates;\n // The predicate was exactly the id list we already resolved against.\n if (namedIds && whereObj && Object.keys(whereObj).every((k) => k === 'id')) return candidates;\n return narrowToMatchedRecords(engine, objectName, rawWhere, candidates);\n}\n\n/**\n * Bind the global record-lock hook. Caller is responsible for calling\n * {@link unbindAllHooks} first if re-binding.\n */\nexport function bindApprovalLockHook(engine: MinimalEngine, logger?: MinimalLogger): void {\n engine.registerHook('beforeUpdate', async (ctx: any) => {\n const object = (ctx?.object ?? ctx?.objectName) as string | undefined;\n // No object name (shouldn't happen) or our own bookkeeping objects → skip.\n if (!object || String(object).startsWith('sys_approval')) return;\n\n const data = (ctx?.input?.data ?? {}) as Record<string, unknown>;\n const changedFields = Object.keys(data).filter((k) => k !== 'id' && k !== 'updated_at');\n if (changedFields.length === 0) return;\n\n // ── Caller-level exemptions. Row-independent, so they are decided once,\n // before any row is resolved — and they hold identically for a by-id and a\n // predicate write (#4778: an exemption that only survives on one path turns\n // a fail-open into a false-positive).\n\n // Allow engine self-writes (status mirror from the approvals service, etc).\n if ((ctx?.session as any)?.isSystem) return;\n\n // NOTE (#4839): there is deliberately NO admin exemption here. A privileged\n // admin releases a locked record through the audited #3424 rescue path\n // (recall / reject / reassign, gated by `isOverrideActor`), not by editing\n // the record while its approval is live. See the module docstring.\n\n // ── Which rows does this write touch, and which of them are locked?\n const gating = await gatingRequests(engine, ctx, object);\n if (gating.length === 0) return;\n\n // The run that OPENED this approval may still write its own target record\n // (#3456). Without this the lock cannot tell \"the run that owns this pending\n // request\" from \"an unrelated user edit\", so a flow that touches the record\n // between opening the approval and the decision — or a manual `resume` with\n // no decision — dies on its own `RECORD_LOCKED` and leaves the record locked\n // behind it.\n //\n // Keyed on run identity, NOT on elevation: a `runAs:'user'` run must stay\n // RLS-scoped, so widening it to `isSystem` would be the wrong tool. The\n // automation engine stamps `flowRunId` into the server-built ExecutionContext\n // (never client-supplied, like `isSystem`) and it grants nothing by itself —\n // the only write it permits is to the one record this very run already holds\n // a pending request against.\n //\n // Read off `provenance`, not `session`: provenance says WHAT produced the\n // write, and a run can own its writes while resolving no principal at all.\n // A schedule-triggered run is exactly that — it reaches here with NO\n // session, and that shape is the one that used to die on its own lock\n // (#3712).\n const writerRun = (ctx?.provenance as any)?.flowRunId;\n\n // Per-row verdict: the write is refused as soon as ONE touched record is\n // locked against it. Each request carries its own snapshotted policy, so a\n // multi-row write spanning two approvals is judged by each of them.\n for (const pending of gating) {\n if (writerRun && pending?.flow_run_id && String(writerRun) === String(pending.flow_run_id)) continue;\n\n const config = parseJson<any>(pending?.node_config_json, {});\n if (config?.lockRecord === false) continue;\n\n // Allow when every changed field is the approval status mirror.\n const mirror = config?.approvalStatusField;\n if (typeof mirror === 'string' && mirror && changedFields.every((f) => f === mirror)) continue;\n\n lockedError(\n `record '${String(pending?.record_id ?? '')}' of '${object}' is locked while an approval is in progress`,\n );\n }\n }, { packageId: APPROVALS_HOOK_PACKAGE, priority: 50 });\n\n logger?.info?.('[approvals] record-lock hook bound');\n}\n\n/** The self-service out-of-office delegation object (#1322). */\nexport const DELEGATION_OBJECT = 'sys_approval_delegation';\n\n/**\n * Self-service write guard for `sys_approval_delegation` (#1322 follow-up).\n *\n * The object is `apiEnabled` CRUD so a user can declare their own out-of-office\n * delegation. But it is a system object: it gets no auto `owner_id` anchor and\n * (with no `sharingModel`) defaults to a `public` sharing model, so an\n * unguarded member could **forge a delegation for someone else**\n * (`delegator_id = victim`) and reroute the victim's individually-routed\n * approvals to themselves. This guard forces a normal user's writes to name\n * themselves as the delegator:\n *\n * - **system** context (service / seed / import) → bypass;\n * - otherwise `delegator_id` must equal the acting user — an absent delegator\n * on insert is stamped to the caller, a foreign delegator is rejected.\n *\n * ## Delegation is SELF-MANAGED — there is no admin exemption (#4839)\n *\n * The guard used to bypass for `session.roles?.includes('admin')`, so an admin\n * could declare an out-of-office delegation *on behalf of* another user. That\n * branch never ran (`roles` has no producer — ObjectQL's `buildSession()` never\n * writes it), and it is not being restored, for two reasons:\n *\n * 1. **It could not do the job it was written for.** A delegation is consulted\n * at *slate-resolution* time only: `applyOooDelegation` runs inside\n * `resolveApproverSpec` while a request is being OPENED. Declaring a\n * delegation for an approver who has *already* gone unavailable does not\n * touch the approvals pending against them — it only redirects future ones.\n * 2. **The in-flight case already has a sanctioned path.** For the approvals\n * an unavailable approver is *currently* holding, a privileged admin uses\n * `ApprovalService.reassign` (hand that approver's slot to a substitute —\n * it even carries the slot's per_group membership over), `recall`, or\n * `decideNode` with `reject`; all three are gated by `isOverrideActor`'s\n * ADR-0095 criteria and audited via `via_override` (#4466). That is the\n * operation an admin actually needs, and it exists.\n *\n * So the semantic is final: **a delegation names its own author as delegator**;\n * acting for someone else is done through the approval-level override, in the\n * one privilege vocabulary this codebase has (ADR-0090 D3 / ADR-0095 D3) — never\n * by comparing a session field against the string `'admin'`.\n *\n * Row-level ownership on update/delete (you can only touch a delegation you\n * created) is already enforced by `member_default`'s wildcard\n * `created_by == current_user.id` RLS; this guard adds the delegator-identity\n * check that RLS alone can't express. Mirrors the ADR-0092 identity write-guard\n * shape and the security plugin's `owner_id` anchor guard, scoped to this one\n * object.\n */\nexport function bindDelegationWriteGuard(engine: MinimalEngine, logger?: MinimalLogger): void {\n const makeGuard = (isInsert: boolean) => async (ctx: any) => {\n const session = (ctx?.session ?? {}) as any;\n if (session.isSystem) return; // service / seed / import\n // NOTE (#4839): no admin exemption — a delegation names its own author as\n // delegator. Acting on another user's in-flight approvals is the approval\n // service's audited override (reassign / recall / reject), not a forged\n // delegation row. See the docstring above.\n const userId = session.userId != null ? String(session.userId) : '';\n const data = ctx?.input?.data;\n const rows = Array.isArray(data) ? data : (data && typeof data === 'object' ? [data] : []);\n const deny = (): never => {\n const err: any = new Error(\n 'FORBIDDEN: you may only manage out-of-office delegations where you are the delegator'\n + (userId ? ` ('${userId}')` : ''),\n );\n err.code = 'FORBIDDEN';\n err.statusCode = 403;\n throw err;\n };\n for (const row of rows) {\n if (!row || typeof row !== 'object' || Array.isArray(row)) continue;\n const has = Object.prototype.hasOwnProperty.call(row, 'delegator_id');\n const supplied = has ? String((row as any).delegator_id ?? '') : '';\n if (isInsert && (!has || supplied === '')) {\n // Self-service: stamp the caller as delegator when omitted (the schema's\n // `required` is the fallback if the engine doesn't persist the stamp).\n if (!userId) deny();\n (row as any).delegator_id = userId;\n continue;\n }\n // A foreign delegator on insert (forge) or update (relabel/hijack) → deny.\n if (has && supplied !== userId) deny();\n }\n };\n engine.registerHook('beforeInsert', makeGuard(true), { object: DELEGATION_OBJECT, packageId: APPROVALS_HOOK_PACKAGE, priority: 50 });\n engine.registerHook('beforeUpdate', makeGuard(false), { object: DELEGATION_OBJECT, packageId: APPROVALS_HOOK_PACKAGE, priority: 50 });\n logger?.info?.('[approvals] delegation write-guard bound');\n}\n\n/** Unregister every hook the lock module registered. */\nexport function unbindAllHooks(engine: MinimalEngine): number {\n return engine.unregisterHooksByPackage(APPROVALS_HOOK_PACKAGE);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Approval-as-flow-node provider (ADR-0019).\n *\n * Registers an `approval` node executor on the automation engine so an approval\n * rides the one flow engine as a durable-pause node:\n *\n * 1. On entry the node opens a `sys_approval_request` (reusing the mature\n * approver-resolution / audit / lock / status-mirror machinery) and returns\n * `{ suspend: true }` — the engine persists the run and stops traversal.\n * 2. A decision (`ApprovalService.decide`) finalizes the request and resumes\n * the run down the matching `approve` / `reject` out-edge.\n *\n * The approval *state* (request/action rows) stays first-class and owned by this\n * plugin — a flow-run log can't drive an inbox / recall / audit. Only the\n * orchestration (when to pause, which branch to take) moves onto the engine.\n */\n\nimport {\n defineActionDescriptor,\n ApprovalNodeConfigSchema,\n getApprovalNodeConfigJsonSchema,\n APPROVAL_NODE_TYPE,\n type ApprovalNodeConfig,\n} from '@objectstack/spec/automation';\n// [#7135] The full `resolveAuthzContext` envelope — what\n// `IApprovalService.openNodeRequest` declares for its context parameter since\n// #6523 (the #6206 ruling: no per-site subset contracts).\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\nimport type { ApprovalService } from './approval-service.js';\nimport { registerApprovalReviseNode } from './approval-revise-node.js';\n\n/** Minimal surface of the automation engine this provider depends on. */\nexport interface ApprovalAutomationSurface {\n registerNodeExecutor(executor: {\n type: string;\n descriptor?: unknown;\n execute(node: any, variables: Map<string, unknown>, context: any): Promise<{\n success: boolean;\n output?: Record<string, unknown>;\n error?: string;\n suspend?: boolean;\n correlation?: string;\n /**\n * #3447 P2: walk this labelled out-edge on normal (non-suspend)\n * completion — how an `onEmptyApprovers: 'auto_approve'` node continues\n * down `approve` without a decision. Mirrors NodeExecutionResult.\n */\n branchLabel?: string;\n }>;\n }): void;\n resume?(runId: string, signal?: { output?: Record<string, unknown>; branchLabel?: string }): Promise<unknown>;\n}\n\ninterface MinimalLogger {\n info?: (msg: any, ...rest: any[]) => void;\n warn?: (msg: any, ...rest: any[]) => void;\n}\n\nconst SYSTEM_CTX: ExecutionContext = { isSystem: true, positions: [], permissions: [] };\n\n/**\n * Rebuild the nested object the engine's CEL conditions see from the flow's\n * flat variable Map — dotted keys (`get_rec.record`) become nested paths, so an\n * `expression` approver's `vars.get_rec.record.owner_id` reads exactly like a\n * condition's. Mirrors the engine's own evaluateCondition rebuild; kept local\n * because this plugin deliberately does not depend on service-automation.\n */\nfunction nestVariables(variables: Map<string, unknown>): Record<string, unknown> {\n const vars: Record<string, unknown> = {};\n for (const [key, value] of variables) {\n const segs = key.split('.');\n let cursor = vars;\n for (let i = 0; i < segs.length - 1; i++) {\n if (typeof cursor[segs[i]] !== 'object' || cursor[segs[i]] === null) {\n cursor[segs[i]] = {};\n }\n cursor = cursor[segs[i]] as Record<string, unknown>;\n }\n cursor[segs[segs.length - 1]] = value;\n }\n return vars;\n}\n\n/**\n * Register the `approval` node executor on the automation engine, plus the\n * `approval_revise` window the ADR-0044 send-back parks on. Idempotent at the\n * engine level (re-registering replaces). Safe to skip when no automation\n * service is present.\n *\n * The two register together deliberately (#3823): send-back resumes the run\n * down the `revise` edge onto the revise-window node, and `sendBack` refuses a\n * flow whose revise edge targets anything else — so an engine holding the\n * `approval` node without the revise window would accept approvals whose\n * send-back can never run.\n */\nexport function registerApprovalNode(\n automation: ApprovalAutomationSurface,\n service: ApprovalService,\n logger?: MinimalLogger,\n): void {\n registerApprovalReviseNode(automation, logger);\n automation.registerNodeExecutor({\n type: APPROVAL_NODE_TYPE,\n descriptor: defineActionDescriptor({\n type: APPROVAL_NODE_TYPE,\n version: '1.0.0',\n name: 'Approval',\n description: 'Route a record for human approval; suspends the flow until a decision, '\n + 'then continues down the approve / reject branch.',\n icon: 'check-circle',\n category: 'human',\n paradigms: ['flow'],\n source: 'plugin',\n // Human decision: the run suspends here awaiting an external reply.\n supportsPause: true, // (`isAsync` stood here — retired in #6748, ADR-0049: nothing read it)\n // #3801: this pause is NOT resumable through the generic run-resume\n // route. Continuing an approval is a side effect of a DECISION, and the\n // decision is the thing that must be authorized (the approver slate),\n // recorded (`sys_approval_action`) and mirrored (the status field) —\n // all of which lives in `ApprovalService.decide`. The engine now refuses\n // any resume of an approval suspension that does not carry the service's\n // in-process marker, so \"decide via the approvals API, never a raw engine\n // resume\" is enforced rather than merely documented.\n resumeAuthority: 'service',\n // Publish the node's config contract (ADR-0018 §configSchema) so the\n // Studio flow designer renders the Approval property form from the engine\n // rather than a hardcoded client form — the engine owns the shape.\n configSchema: getApprovalNodeConfigJsonSchema(),\n }),\n async execute(node, variables, context) {\n const parsed = ApprovalNodeConfigSchema.safeParse(node.config ?? {});\n if (!parsed.success) {\n const msg = parsed.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; ');\n return { success: false, error: `Approval node '${node.id}' has invalid config: ${msg}` };\n }\n const config = parsed.data as ApprovalNodeConfig;\n\n const runId = variables.get('$runId');\n const record = (variables.get('$record') ?? context?.record ?? {}) as Record<string, unknown>;\n const object = (context?.object ?? (record as any)?.object_name) as string | undefined;\n const recordId = (record as any)?.id as string | undefined;\n\n if (!runId) return { success: false, error: `Approval node '${node.id}': missing $runId` };\n if (!object) return { success: false, error: `Approval node '${node.id}': no target object in context` };\n if (!recordId) return { success: false, error: `Approval node '${node.id}': no record id in $record` };\n\n // Flow identity comes from engine-seeded variables (`$flowName` /\n // `$flowLabel`) so the request row can carry a human-readable origin;\n // `context.flowName` is a legacy fallback for direct callers.\n const flowName = (variables.get('$flowName') as string | undefined) ?? context?.flowName;\n const flowLabel = variables.get('$flowLabel') as string | undefined;\n\n try {\n const request = await service.openNodeRequest({\n object,\n recordId: String(recordId),\n runId: String(runId),\n nodeId: node.id,\n config,\n flowName,\n flowLabel,\n nodeLabel: typeof node.label === 'string' ? node.label : undefined,\n submitterId: context?.userId ?? null,\n record,\n organizationId: context?.organizationId ?? context?.tenantId ?? null,\n // #3447 P2: flow variables (nested, as CEL conditions see them) — the\n // `vars.*` root for `expression` approvers; `record` above doubles as\n // their `trigger.*` snapshot root.\n variables: nestVariables(variables),\n }, {\n ...SYSTEM_CTX,\n userId: context?.userId,\n // [#7135] ⚠️ This assertion SURVIVES the annotation widening, and it\n // is `as ExecutionContext` rather than the `as unknown as\n // SharingExecutionContext` double cast it replaces. The sole reason\n // a cast is still needed is `organizationId`, which is not a field\n // of the envelope at ALL — that spelling has its own history (#5858\n // / `check:org-identifier`) and was explicitly held out of this\n // change (#7070), so removing the key here would be a RUNTIME change\n // belonging to that card. Dropping the second hop matters: `as\n // unknown as` erases the value entirely, while a single assertion\n // still requires the literal to be comparable to the envelope, so a\n // `userId: 42` here is once again a compile error.\n organizationId: context?.organizationId,\n tenantId: context?.tenantId,\n } as ExecutionContext);\n\n // #3447 P2: empty slate + onEmptyApprovers: 'auto_approve' — nobody to\n // ask, no request row. Complete (don't suspend) straight down the\n // `approve` edge; `autoApproved` on the output keeps the waved-through\n // hop distinguishable from a human decision in the run trace.\n if ('autoApproved' in request) {\n logger?.info?.('[approvals] approval node auto-approved (empty approver slate)', {\n node: node.id, run: String(runId),\n });\n return {\n success: true,\n branchLabel: 'approve',\n output: { decision: 'approve', autoApproved: true },\n };\n }\n\n logger?.info?.('[approvals] approval node suspended run', {\n node: node.id, request: request.id, run: String(runId),\n });\n // Suspend the run; the request id is the correlation key surfaced on\n // the suspended-run record for lookup.\n return { success: true, suspend: true, correlation: request.id };\n } catch (err: any) {\n return { success: false, error: `Approval node '${node.id}': ${err?.message ?? String(err)}` };\n }\n },\n });\n\n logger?.info?.('[approvals] approval node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The ADR-0044 **revise window** as its own flow node (amended ADR-0044, #3823).\n *\n * A send-back resumes the run down the approval node's `revise` edge; the run\n * parks on the node that edge targets while the record is unlocked and the\n * submitter reworks it, and `ApprovalService.resubmit` drives it back into the\n * approval node over the declared back-edge (round N+1).\n *\n * That pause is **service-owned**: `resubmit` is the thing that authorizes\n * (submitter-only), orders (latest request for the run) and records (a\n * `resubmit` audit row) the continuation, and refuses up front when another\n * request is pending on the record — the guard that keeps the run alive,\n * because the approval node's re-entry would otherwise fail *after* the engine\n * consumed the suspension and leave the run terminally dead.\n *\n * ADR-0044 D3 originally parked it on an ordinary `wait` node, which is\n * `resumeAuthority: 'any'` — correctly so for a signal wait, and exactly wrong\n * here: a raw `POST /automation/:name/runs/:runId/resume` (empty body suffices)\n * walked the back-edge around every one of those checks. The #3801 resume gate\n * keys on the node type that produced the suspension, so the fix is a node type\n * that declares itself service-owned. Nothing about the gate changes.\n *\n * Why a dedicated type rather than re-suspending inside the approval node: the\n * revise window stays *visible flow state* — a box on the canvas, a step in the\n * run log, a row in the suspended-run store — which is the property ADR-0044\n * chose the generic `wait` for in the first place. Only the reuse was wrong.\n */\n\nimport {\n defineActionDescriptor,\n APPROVAL_REVISE_NODE_TYPE,\n} from '@objectstack/spec/automation';\nimport type { ApprovalAutomationSurface } from './approval-node.js';\n\ninterface MinimalLogger {\n info?: (msg: any, ...rest: any[]) => void;\n warn?: (msg: any, ...rest: any[]) => void;\n}\n\n/**\n * Correlation prefix for a revise-window suspension — what the suspended-run\n * record carries so an operator (or `listSuspendedRuns`) can tell a revise\n * window from a timer or signal wait at a glance.\n *\n * Deliberately NOT one of the engine's linked-run prefixes (`subflow:` /\n * `map:`): those mean \"this run is waiting on another run\", and the resume gate\n * follows them to judge the child's node. A revise window waits on a person.\n */\nexport const APPROVAL_REVISE_CORRELATION_PREFIX = 'approval_revise:';\n\n/**\n * Register the `approval_revise` node executor. Called from\n * {@link registerApprovalNode} rather than wired separately on purpose: a\n * deployment that has the `approval` node must have this one too, or every\n * send-back in it is refused at runtime — there is no half of this feature\n * worth installing.\n */\nexport function registerApprovalReviseNode(\n automation: ApprovalAutomationSurface,\n logger?: MinimalLogger,\n): void {\n automation.registerNodeExecutor({\n type: APPROVAL_REVISE_NODE_TYPE,\n descriptor: defineActionDescriptor({\n type: APPROVAL_REVISE_NODE_TYPE,\n version: '1.0.0',\n name: 'Revise Window',\n description:\n 'Durable pause an approval send-back parks the run on while the submitter reworks the '\n + 'record. Continues only through the approvals service (resubmit), which re-enters the '\n + 'approval node over the declared back-edge.',\n icon: 'pencil',\n // Waits on a human — the same category as `approval` and `screen`.\n category: 'human',\n paradigms: ['flow'],\n source: 'plugin',\n supportsPause: true, // (`isAsync` stood here — retired in #6748, ADR-0049: nothing read it)\n // #3823 / amended ADR-0044: THE point of this node type. The revise\n // window is a service-owned continuation, so the #3801 gate must refuse\n // a raw resume of it — which it does for any node type declaring this.\n resumeAuthority: 'service',\n // No config: the window is pure position in the graph. Left schemaless\n // (like `wait` / `subflow` / `decision`) rather than declaring an empty\n // object, so nothing invents an authorable surface that has no reader.\n }),\n async execute(node) {\n // Arms nothing on entry — no timer, no job, no external subscription — so\n // there is deliberately no `onSuspensionReleased` hook to pair with it\n // (contrast the wait node's timer one-shot, #5529). The only thing that\n // ends this pause is `ApprovalService.resubmit` (or a `recall`, which\n // cancels the run).\n return {\n success: true,\n suspend: true,\n correlation: `${APPROVAL_REVISE_CORRELATION_PREFIX}${node.id}`,\n };\n },\n });\n\n logger?.info?.('[approvals] approval revise-window node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type {\n IHttpServer,\n II18nService,\n IJobService,\n IObjectQLEngine,\n} from '@objectstack/spec/contracts';\nimport { SysApprovalRequest } from './sys-approval-request.object.js';\nimport { SysApprovalAction } from './sys-approval-action.object.js';\nimport { SysApprovalApprover } from './sys-approval-approver.object.js';\nimport { SysApprovalToken } from './sys-approval-token.object.js';\nimport { SysApprovalDelegation } from './sys-approval-delegation.object.js';\nimport { renderConfirmPage, renderResultPage } from './action-link-pages.js';\nimport {\n ApprovalService,\n ESCALATION_JOB_NAME,\n ESCALATION_SCAN_INTERVAL_MS,\n type ApprovalEngine,\n type ApprovalMessagingSurface,\n} from './approval-service.js';\nimport { bindApprovalLockHook, bindDelegationWriteGuard, unbindAllHooks } from './lifecycle-hooks.js';\nimport { registerApprovalNode, type ApprovalAutomationSurface } from './approval-node.js';\n\nexport interface ApprovalsPluginOptions {\n /** Disable runtime registration (schemas still register). */\n disableService?: boolean;\n /**\n * Interval between SLA escalation scans (ADR-0042). Defaults to\n * {@link ESCALATION_SCAN_INTERVAL_MS} (5 min). Only takes effect when a\n * `job` service is installed; without one, SLA stays display-only.\n */\n escalationScanIntervalMs?: number;\n /**\n * Absolute origin for actionable links in outbound notifications\n * (ADR-0043), e.g. `https://app.example.com`. Relative by default.\n */\n publicBaseUrl?: string;\n /**\n * Disable the record-lock hook. Schema + service stay intact; only the\n * engine-level lock wiring is suppressed. Useful when a caller wants the\n * manual API only (e.g. tests).\n */\n disableAutoHooks?: boolean;\n}\n\n/**\n * ApprovalsServicePlugin — registers sys_approval_{request,action}, the\n * `approvals` service, the `approval` flow node executor (ADR-0019), and the\n * record-lock hook.\n *\n * ADR-0019: approval is no longer a standalone process engine. A flow's\n * Approval node opens a request and suspends the run; a decision via the\n * service resumes it down the matching branch.\n */\nexport class ApprovalsServicePlugin implements Plugin {\n name = 'com.objectstack.service.approvals';\n version = '1.0.0';\n type = 'standard';\n dependencies = ['com.objectstack.engine.objectql'];\n\n private readonly options: ApprovalsPluginOptions;\n private service?: ApprovalService;\n private engine?: IObjectQLEngine;\n private escalationJobScheduled = false;\n\n constructor(options: ApprovalsPluginOptions = {}) {\n this.options = options;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n ctx.getService<{ register(m: any): void }>('manifest').register({\n id: 'com.objectstack.service.approvals',\n name: 'Approvals Service',\n version: '1.0.0',\n type: 'plugin',\n scope: 'system',\n defaultDatasource: 'cloud',\n namespace: 'sys',\n objects: [SysApprovalRequest, SysApprovalAction, SysApprovalApprover, SysApprovalToken, SysApprovalDelegation],\n // ADR-0029 D7 — contribute the Approvals entries into the Setup app's\n // `group_approvals` slot. This plugin owns these objects (K2.b), so it\n // ships their menu too; when the plugin isn't installed the slot is empty.\n //\n // #7234 — order inside the slot is ARRAY ORDER: `applyNavContributions`\n // does `group.children.push(...c.items)` per contribution (sorted by\n // `priority`), so the inbox being first in this array is what puts it\n // above the raw tables. Do not reorder without meaning to.\n //\n // The inbox entry is the working surface (decision actions, node\n // progress, drawer); the three object entries below stay as the\n // admin/diagnostic view of the engine's own tables — reachable only here,\n // behind `group_approvals`' `manage_platform_settings` gate.\n navigationContributions: [\n {\n app: 'setup',\n group: 'group_approvals',\n priority: 100,\n items: [\n { id: 'nav_approvals_inbox', type: 'component', label: 'Approvals Inbox', componentRef: 'approvals:inbox', icon: 'list-checks' },\n { id: 'nav_approval_requests', type: 'object', label: 'Requests', objectName: 'sys_approval_request', icon: 'inbox', requiresObject: 'sys_approval_request' },\n { id: 'nav_approval_actions', type: 'object', label: 'Action History', objectName: 'sys_approval_action', icon: 'history', requiresObject: 'sys_approval_action' },\n { id: 'nav_approval_delegations', type: 'object', label: 'Delegations (OOO)', objectName: 'sys_approval_delegation', icon: 'user-clock', requiresObject: 'sys_approval_delegation' },\n ],\n },\n ],\n });\n // ADR-0029 D8 — contribute this plugin's object translations to the i18n\n // service on kernel:ready (the i18n plugin may register after this one).\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', async () => {\n try {\n const i18n = ctx.getService<II18nService>('i18n');\n if (i18n && typeof i18n.loadTranslations === 'function') {\n const { ApprovalsTranslations } = await import('./translations/index.js');\n for (const [locale, data] of Object.entries(ApprovalsTranslations)) {\n i18n.loadTranslations(locale, data as Record<string, unknown>);\n }\n }\n } catch { /* i18n optional */ }\n });\n }\n ctx.logger.info('ApprovalsServicePlugin: schemas registered');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (this.options.disableService) return;\n // This plugin needs the engine SEEN WHOLE, not the data plane: it binds\n // `registerHook` / `unregisterHooksByPackage` below. That is `objectql`,\n // whose ledger entry (`CoreServiceContracts`) records it as \"the SAME\n // instance as `data`, seen whole\" — so the alias fallback resolves the same\n // object and is typed as the same contract.\n let engine: IObjectQLEngine | null = null;\n try { engine = ctx.getService<IObjectQLEngine>('objectql'); }\n catch { try { engine = ctx.getService<IObjectQLEngine>('data'); } catch { /* ignore */ } }\n if (!engine) {\n ctx.logger.warn('ApprovalsServicePlugin: no ObjectQL engine — service NOT registered');\n return;\n }\n this.engine = engine;\n\n this.service = new ApprovalService({\n engine: engine as ApprovalEngine,\n logger: ctx.logger,\n publicBaseUrl: this.options.publicBaseUrl,\n // [ADR-0105 D9] Cross-organization approver targeting is a `group`-posture\n // capability. Read LAZILY (not captured at start) because the tenancy\n // service resolves its posture during its own start, which may not have\n // run yet; an unresolvable posture reads as \"unknown\" and the guard\n // stands down rather than refusing a legitimate flow on a minimal stack.\n tenancyPosture: () => {\n try {\n const tenancy = ctx.getService<{ posture?: string }>('tenancy');\n const posture = tenancy?.posture;\n return typeof posture === 'string' && posture ? posture : undefined;\n } catch {\n return undefined;\n }\n },\n });\n\n // Record lock: block edits to a record while it has a pending request.\n // Delegation write-guard: a self-service OOO delegation may only name the\n // acting user as delegator (#1322 follow-up). Both bind under the same\n // package id, so unbindAllHooks clears them together.\n if (!this.options.disableAutoHooks) {\n try {\n unbindAllHooks(engine);\n bindApprovalLockHook(engine, ctx.logger);\n bindDelegationWriteGuard(engine, ctx.logger);\n } catch (err: any) {\n ctx.logger.warn?.('[approvals] failed to bind approval hooks', { error: err?.message });\n }\n }\n\n ctx.registerService('approvals', this.service);\n ctx.logger.info('ApprovalsServicePlugin: service registered');\n\n // Optional messaging service (ADR-0012): thread interactions (reassign /\n // remind / request-info / comment) notify users when present; without it\n // they degrade to audit-only.\n try {\n // `messaging` has no contract in the ledger, so this is the named local\n // surface the service itself already declares — not `any`. It omits\n // members on purpose; omitting one it USES would be a compile error at\n // the `attachMessaging` call, which is the whole point.\n const messaging = ctx.getService<ApprovalMessagingSurface>('messaging');\n if (messaging && typeof messaging.emit === 'function') {\n this.service.attachMessaging(messaging);\n }\n } catch { /* messaging not installed */ }\n\n // SLA escalation clock (ADR-0042): a plugin-internal job, deliberately\n // NOT a flow trigger (ADR-0041 §1). Interval sweep + one catch-up scan at\n // boot so a restart doesn't extend a breach by a scan period. Wired on\n // kernel:ready — the job service may start after this plugin. No `job`\n // service → SLA stays display-only.\n const wireEscalationClock = async () => {\n try {\n const jobs = ctx.getService<IJobService>('job');\n if (!jobs || typeof jobs.schedule !== 'function' || !this.service) return;\n const svc = this.service;\n const intervalMs = this.options.escalationScanIntervalMs ?? ESCALATION_SCAN_INTERVAL_MS;\n // Both sweeps ride this one clock: they walk the same `pending` set, and\n // the dead-run release (#3456) is reconciliation with the same \"catch up\n // after a restart\" requirement — a run killed BY the restart is exactly\n // the shape no in-band handler can clean up.\n // Genuinely independent — an escalation failure must not strand locked\n // records, and vice versa, so neither can short-circuit the other.\n const sweep = async () => {\n const results = await Promise.allSettled([\n svc.runEscalations(),\n svc.releaseDeadRunRequests(),\n // #4469 — the other half of the dead-run picture, and the one no\n // sweeper could see: a request already TERMINAL whose run is gone.\n // Read-only by design (it reports; it never rewrites a decision\n // that really happened), so it rides the same clock purely to make\n // the finding surface without an operator knowing to go looking.\n svc.inspectStrandedRequests(),\n ]);\n for (const r of results) {\n if (r.status === 'rejected') {\n ctx.logger.warn?.('[approvals] periodic sweep leg failed', {\n error: (r.reason as any)?.message ?? String(r.reason),\n });\n }\n }\n };\n await jobs.schedule(ESCALATION_JOB_NAME, { type: 'interval', intervalMs }, sweep);\n this.escalationJobScheduled = true;\n void sweep().catch((err: any) => {\n ctx.logger.warn?.('[approvals] boot sweep failed', { error: err?.message });\n });\n ctx.logger.info('ApprovalsServicePlugin: SLA escalation scan scheduled', { intervalMs });\n } catch { /* job service not installed */ }\n };\n // Actionable-link pages (ADR-0043): session-less confirm + redemption,\n // mounted straight on the host Hono app. GET only renders; the decision\n // happens exclusively on the POST (mail-gateway prefetch safe).\n const mountActionPages = async () => {\n try {\n // [#4251 B5] Canonical name FIRST. This read was `http-server`-only,\n // and `http-server` is the deprecated alias: the ledger records\n // `http.server` as canonical and as \"the ONLY name present on all\n // provider paths\" — `runtime.ts`'s `config.server` path registers no\n // alias at all. On that path this lookup threw, the catch below\n // swallowed it, and the ADR-0043 action pages silently never mounted,\n // so every approval e-mail link 404'd. Same latent alias-only miss\n // #4393 fixed in metadata/cloud-connection; per-name `try` because\n // `getService` THROWS on an empty slot, so `a() ?? b()` in one `try`\n // never reaches `b`.\n const readServer = (name: string): IHttpServer | undefined => {\n try { return ctx.getService<IHttpServer>(name); } catch { return undefined; }\n };\n const http = readServer('http.server') ?? readServer('http-server');\n const rawApp = http && typeof http.getRawApp === 'function' ? http.getRawApp() : null;\n if (!rawApp || !this.service) return;\n const svc = this.service;\n const ACT_PATH = '/api/v1/approvals/act';\n const html = (c: any, body: string, status = 200) =>\n c.body(body, status, { 'Content-Type': 'text/html; charset=utf-8' });\n rawApp.get(ACT_PATH, async (c: any) => {\n const token = String(c.req.query('token') ?? '');\n const peek = await svc.peekActionToken(token);\n if (!peek.ok) return html(c, renderResultPage(peek.reason, peek.request), 200);\n return html(c, renderConfirmPage({\n request: peek.request, action: peek.action, approverId: peek.approverId,\n token, actPath: ACT_PATH,\n }));\n });\n rawApp.post(ACT_PATH, async (c: any) => {\n let token = '';\n try {\n const body = await c.req.parseBody();\n token = String(body?.token ?? '');\n } catch { /* fall through to invalid */ }\n const out = await svc.redeemActionToken(token);\n if (!out.ok) return html(c, renderResultPage(out.reason, out.request), 200);\n return html(c, renderResultPage(out.action === 'approve' ? 'approved' : 'rejected', out.request));\n });\n ctx.logger.info(`ApprovalsServicePlugin: actionable-link pages mounted at ${ACT_PATH}`);\n } catch { /* http server not installed */ }\n };\n\n // Pending-approver index backfill (issue #1745): rebuild the normalized\n // sys_approval_approver rows from the pending_approvers CSV so requests\n // written before the index existed (or drifted past a crashed sync) are\n // queryable. Idempotent; cost tracks the live pending queue.\n const backfillApproverIndex = async () => {\n try {\n const svc = this.service;\n if (!svc) return;\n const out = await svc.rebuildApproverIndex();\n if (out.inserted > 0 || out.deleted > 0) {\n ctx.logger.info('ApprovalsServicePlugin: approver index rebuilt', out);\n }\n } catch (err: any) {\n ctx.logger.warn?.('[approvals] approver index backfill failed', { error: err?.message });\n }\n };\n\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', wireEscalationClock);\n (ctx as any).hook('kernel:ready', mountActionPages);\n (ctx as any).hook('kernel:ready', backfillApproverIndex);\n } else {\n await wireEscalationClock();\n await mountActionPages();\n await backfillApproverIndex();\n }\n\n // ADR-0019: contribute the `approval` node to the flow engine when one is\n // present. The node lets a flow suspend on an approval and resume on\n // decision; the service is wired to the same engine so `decide()` can\n // resume the suspended run.\n //\n // #4771 — the degradation must be LOUD (#4632). This used to be one\n // try/catch logging at `info`, and dev's default log level is `warn`: the\n // one line that says \"every `approval` node in this deployment is dead\"\n // was invisible in exactly the deployment where it is true, while the flow\n // registration was warning about `approval` in the deployments where it is\n // false. The catch is also narrowed to the service *lookup*, so a genuine\n // failure inside registerApprovalNode surfaces as itself instead of being\n // relabelled \"no automation engine\".\n let automation: ApprovalAutomationSurface | undefined;\n try {\n automation = ctx.getService<ApprovalAutomationSurface>('automation');\n } catch {\n automation = undefined; // no automation service registered in this stack\n }\n if (automation && typeof automation.registerNodeExecutor === 'function') {\n this.service.attachAutomation(automation);\n registerApprovalNode(automation, this.service, ctx.logger);\n } else {\n ctx.logger.warn(\n 'ApprovalsServicePlugin: no automation engine — the `approval` flow node is NOT registered. '\n + 'Every ADR-0019 approval flow in this deployment fails at execution time with NO_EXECUTOR. '\n + 'Add @objectstack/service-automation to the stack to enable them.',\n );\n }\n }\n\n async stop(ctx: PluginContext): Promise<void> {\n if (this.escalationJobScheduled) {\n try {\n const jobs = ctx.getService<IJobService>('job');\n await jobs?.cancel?.(ESCALATION_JOB_NAME);\n } catch { /* ignore */ }\n this.escalationJobScheduled = false;\n }\n if (this.engine) {\n try { unbindAllHooks(this.engine); } catch { /* ignore */ }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,IAUa;AAVb;AAAA;AAAA;AAUO,IAAM,YAAqD;AAAA,MAChE,sBAAsB;AAAA,QACpB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,MAAM;AAAA,YACN,SAAS;AAAA,cACP,SAAS;AAAA,cACT,UAAU;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,YACZ;AAAA,UACF;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,oBAAoB;AAAA,YAClB,OAAO;AAAA,UACT;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,kBAAkB;AAAA,YAChB,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,YAAY;AAAA,YACV,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,UAAU;AAAA,UACR,kBAAkB;AAAA,YAChB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,cACA,aAAa;AAAA,gBACX,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,aAAa;AAAA,YACb,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,cACA,aAAa;AAAA,gBACX,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,IAAI;AAAA,gBACF,OAAO;AAAA,gBACP,UAAU;AAAA,cACZ;AAAA,cACA,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,oBAAoB;AAAA,YAClB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,uBAAuB;AAAA,YACrB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,aAAa;AAAA,YACb,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,qBAAqB;AAAA,QACnB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,UAAU;AAAA,cACV,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,cAAc;AAAA,cACd,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,UAAU;AAAA,cACV,gBAAgB;AAAA,YAClB;AAAA,UACF;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,UACT;AAAA,UACA,SAAS;AAAA,YACP,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,eAAe;AAAA,YACb,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,UACT;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,MACA,yBAAyB;AAAA,QACvB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;AClUA,IAUa;AAVb;AAAA;AAAA;AAUO,IAAM,cAAuD;AAAA,MAClE,sBAAsB;AAAA,QACpB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,MAAM;AAAA,YACN,SAAS;AAAA,cACP,SAAS;AAAA,cACT,UAAU;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,YACZ;AAAA,UACF;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,oBAAoB;AAAA,YAClB,OAAO;AAAA,UACT;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,kBAAkB;AAAA,YAChB,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,YAAY;AAAA,YACV,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,UAAU;AAAA,UACR,kBAAkB;AAAA,YAChB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,cACA,aAAa;AAAA,gBACX,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,aAAa;AAAA,YACb,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,cACA,aAAa;AAAA,gBACX,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,IAAI;AAAA,gBACF,OAAO;AAAA,gBACP,UAAU;AAAA,cACZ;AAAA,cACA,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,oBAAoB;AAAA,YAClB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,uBAAuB;AAAA,YACrB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,aAAa;AAAA,YACb,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,qBAAqB;AAAA,QACnB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,UAAU;AAAA,cACV,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,cAAc;AAAA,cACd,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,UAAU;AAAA,cACV,gBAAgB;AAAA,YAClB;AAAA,UACF;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,UACT;AAAA,UACA,SAAS;AAAA,YACP,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,eAAe;AAAA,YACb,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,UACT;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,MACA,yBAAyB;AAAA,QACvB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;AClUA,IAUa;AAVb;AAAA;AAAA;AAUO,IAAM,cAAuD;AAAA,MAClE,sBAAsB;AAAA,QACpB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,MAAM;AAAA,YACN,SAAS;AAAA,cACP,SAAS;AAAA,cACT,UAAU;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,YACZ;AAAA,UACF;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,oBAAoB;AAAA,YAClB,OAAO;AAAA,UACT;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,kBAAkB;AAAA,YAChB,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,YAAY;AAAA,YACV,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,UAAU;AAAA,UACR,kBAAkB;AAAA,YAChB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,cACA,aAAa;AAAA,gBACX,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,aAAa;AAAA,YACb,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,cACA,aAAa;AAAA,gBACX,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,IAAI;AAAA,gBACF,OAAO;AAAA,gBACP,UAAU;AAAA,cACZ;AAAA,cACA,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,oBAAoB;AAAA,YAClB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,uBAAuB;AAAA,YACrB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,aAAa;AAAA,YACb,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,qBAAqB;AAAA,QACnB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,UAAU;AAAA,cACV,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,cAAc;AAAA,cACd,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,UAAU;AAAA,cACV,gBAAgB;AAAA,YAClB;AAAA,UACF;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,UACT;AAAA,UACA,SAAS;AAAA,YACP,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,eAAe;AAAA,YACb,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,UACT;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,MACA,yBAAyB;AAAA,QACvB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;AClUA,IAUa;AAVb;AAAA;AAAA;AAUO,IAAM,cAAuD;AAAA,MAClE,sBAAsB;AAAA,QACpB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,MAAM;AAAA,YACN,SAAS;AAAA,cACP,SAAS;AAAA,cACT,UAAU;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,YACZ;AAAA,UACF;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,oBAAoB;AAAA,YAClB,OAAO;AAAA,UACT;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,kBAAkB;AAAA,YAChB,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,YAAY;AAAA,YACV,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,UAAU;AAAA,UACR,kBAAkB;AAAA,YAChB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,cACA,aAAa;AAAA,gBACX,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,aAAa;AAAA,YACb,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,cACA,aAAa;AAAA,gBACX,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,IAAI;AAAA,gBACF,OAAO;AAAA,gBACP,UAAU;AAAA,cACZ;AAAA,cACA,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,oBAAoB;AAAA,YAClB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,uBAAuB;AAAA,YACrB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,aAAa;AAAA,YACb,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,UACA,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,QAAQ;AAAA,cACN,SAAS;AAAA,gBACP,OAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,qBAAqB;AAAA,QACnB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,UAAU;AAAA,cACV,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,cAAc;AAAA,cACd,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,UAAU;AAAA,cACV,gBAAgB;AAAA,YAClB;AAAA,UACF;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,UACT;AAAA,UACA,SAAS;AAAA,YACP,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,eAAe;AAAA,YACb,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,UACT;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,MACA,yBAAyB;AAAA,QACvB,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,cAAc;AAAA,YACZ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,cACV,OAAO;AAAA,cACP,SAAS;AAAA,YACX;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;AClUA;AAAA;AAAA;AAAA;AAAA,IAiBa;AAjBb;AAAA;AAAA;AAYA;AACA;AACA;AACA;AAEO,IAAM,wBAA2C;AAAA,MACtD,IAAI,EAAE,SAAS,UAAU;AAAA,MACzB,SAAS,EAAE,SAAS,YAAY;AAAA,MAChC,SAAS,EAAE,SAAS,YAAY;AAAA,MAChC,SAAS,EAAE,SAAS,YAAY;AAAA,IAClC;AAAA;AAAA;;;ACpBA,SAAS,cAAc,aAAa;AACpC,SAAS,mBAAmB,8BAA8B;AAsBnD,IAAM,qBAAqB,aAAa,OAAO;AAAA,EACpD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAM;AAAA,EACN,UAAU;AAAA,EACV,WAAW;AAAA,EACX,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,WAAW;AAAA;AAAA,EACX,aAAa;AAAA,EACb,iBAAiB,CAAC,gBAAgB,eAAe,aAAa,UAAU,gBAAgB,gBAAgB,YAAY;AAAA;AAAA;AAAA,EAIpH,WAAW;AAAA,IACT,YAAY;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,EAAE,UAAU,UAAU,QAAQ,uBAAuB;AAAA,MAC3D,SAAS,CAAC,gBAAgB,eAAe,aAAa,gBAAgB,gBAAgB,YAAY;AAAA,MAClG,QAAQ;AAAA,QACN,EAAE,OAAO,UAAU,UAAU,UAAU,OAAO,UAAU;AAAA,QACxD,EAAE,OAAO,qBAAqB,UAAU,YAAY,OAAO,oBAAoB;AAAA,MACjF;AAAA,MACA,MAAM,CAAC,EAAE,OAAO,cAAc,OAAO,OAAO,CAAC;AAAA,MAC7C,YAAY,EAAE,UAAU,GAAG;AAAA,MAC3B,YAAY,EAAE,OAAO,wBAAwB,SAAS,wBAAyB;AAAA,IACjF;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,EAAE,UAAU,UAAU,QAAQ,uBAAuB;AAAA,MAC3D,SAAS,CAAC,gBAAgB,eAAe,aAAa,UAAU,gBAAgB,YAAY;AAAA,MAC5F,QAAQ,CAAC,EAAE,OAAO,gBAAgB,UAAU,UAAU,OAAO,oBAAoB,CAAC;AAAA,MAClF,MAAM,CAAC,EAAE,OAAO,cAAc,OAAO,OAAO,CAAC;AAAA,MAC7C,YAAY,EAAE,UAAU,GAAG;AAAA,IAC7B;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,EAAE,UAAU,UAAU,QAAQ,uBAAuB;AAAA,MAC3D,SAAS,CAAC,gBAAgB,eAAe,aAAa,UAAU,gBAAgB,cAAc;AAAA,MAC9F,QAAQ,CAAC,EAAE,OAAO,UAAU,UAAU,MAAM,OAAO,CAAC,YAAY,YAAY,UAAU,EAAE,CAAC;AAAA,MACzF,MAAM,CAAC,EAAE,OAAO,gBAAgB,OAAO,OAAO,CAAC;AAAA,MAC/C,YAAY,EAAE,UAAU,GAAG;AAAA,IAC7B;AAAA,IACA,cAAc;AAAA,MACZ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,EAAE,UAAU,UAAU,QAAQ,uBAAuB;AAAA,MAC3D,SAAS,CAAC,gBAAgB,eAAe,aAAa,UAAU,gBAAgB,gBAAgB,YAAY;AAAA,MAC5G,MAAM,CAAC,EAAE,OAAO,cAAc,OAAO,OAAO,CAAC;AAAA,MAC7C,YAAY,EAAE,UAAU,GAAG;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,QAAQ;AAAA,IACN,IAAI,MAAM,KAAK,EAAE,OAAO,cAAc,UAAU,MAAM,UAAU,MAAM,OAAO,SAAS,CAAC;AAAA,IAEvF,iBAAiB,MAAM,OAAO,oBAAoB;AAAA,MAChD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IAED,cAAc,MAAM,KAAK;AAAA,MACvB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,IAED,aAAa,MAAM,KAAK;AAAA,MACtB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,OAAO;AAAA,IACT,CAAC;AAAA,IAED,WAAW,MAAM,KAAK;AAAA,MACpB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,OAAO;AAAA,IACT,CAAC;AAAA,IAED,cAAc,MAAM,OAAO,YAAY;AAAA,MACrC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,mBAAmB,MAAM,SAAS;AAAA,MAChC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,QAAQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOZ,kBAAkB,IAAI,CAAC,WAAW,EAAE,OAAO,OAAO,uBAAuB,KAAK,EAAE,EAAE;AAAA,MAClF;AAAA,QACE,OAAO;AAAA,QACP,UAAU;AAAA,QACV,cAAc;AAAA,QACd,aAAa;AAAA,QACb,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,cAAc,MAAM,KAAK;AAAA,MACvB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,IAED,oBAAoB,MAAM,OAAO;AAAA,MAC/B,OAAO;AAAA,MACP,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO;AAAA,IACT,CAAC;AAAA,IAED,mBAAmB,MAAM,SAAS;AAAA,MAChC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,IAED,cAAc,MAAM,SAAS;AAAA,MAC3B,OAAO;AAAA,MACP,UAAU;AAAA,MACV,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,IAMD,aAAa,MAAM,KAAK;AAAA,MACtB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,UAAU;AAAA,MACV,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,IAED,cAAc,MAAM,KAAK;AAAA,MACvB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,UAAU;AAAA,MACV,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,IAED,kBAAkB,MAAM,SAAS;AAAA,MAC/B,OAAO;AAAA,MACP,UAAU;AAAA,MACV,UAAU;AAAA,MACV,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,IAED,cAAc,MAAM,SAAS;AAAA,MAC3B,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,YAAY,MAAM,SAAS;AAAA,MACzB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,YAAY,MAAM,SAAS,EAAE,OAAO,cAAc,UAAU,OAAO,OAAO,SAAS,CAAC;AAAA,EACtF;AAAA,EAEA,SAAS;AAAA;AAAA;AAAA,IAGP,EAAE,QAAQ,CAAC,eAAe,WAAW,EAAE;AAAA,IACvC,EAAE,QAAQ,CAAC,UAAU,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMpC,EAAE,QAAQ,CAAC,UAAU,YAAY,EAAE;AAAA,IACnC,EAAE,QAAQ,CAAC,gBAAgB,QAAQ,EAAE;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,SAAS;AAAA,IACP;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA;AAAA;AAAA;AAAA,MAIN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,QACN,EAAE,MAAM,WAAW,OAAO,WAAW,MAAM,YAAY,UAAU,MAAM;AAAA;AAAA;AAAA;AAAA,QAIvE,EAAE,MAAM,eAAe,OAAO,eAAe,MAAM,QAAQ,UAAU,MAAM,UAAU,MAAM;AAAA,MAC7F;AAAA,MACA,SAAS;AAAA,MACT,WAAW,CAAC,kBAAkB,WAAW;AAAA,MACzC,gBAAgB;AAAA,MAChB,cAAc;AAAA,IAChB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASP,aAAa;AAAA,MACb,MAAM;AAAA;AAAA;AAAA,MAGN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,QACN,EAAE,MAAM,WAAW,OAAO,WAAW,MAAM,YAAY,UAAU,MAAM;AAAA,QACvE,EAAE,MAAM,eAAe,OAAO,eAAe,MAAM,QAAQ,UAAU,MAAM,UAAU,MAAM;AAAA,MAC7F;AAAA,MACA,SAAS;AAAA,MACT,WAAW,CAAC,kBAAkB,WAAW;AAAA,MACzC,gBAAgB;AAAA,MAChB,cAAc;AAAA,IAChB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMN,EAAE,OAAO,gBAAgB,MAAM,MAAM,OAAO,gBAAgB,UAAU,MAAM,UAAU,4BAA4B;AAAA,QAClH,EAAE,MAAM,WAAW,OAAO,WAAW,MAAM,YAAY,UAAU,MAAM;AAAA,MACzE;AAAA,MACA,SAAS;AAAA,MACT,WAAW,CAAC,gBAAgB;AAAA,MAC5B,gBAAgB;AAAA,MAChB,cAAc;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,QACN,EAAE,MAAM,WAAW,OAAO,UAAU,MAAM,YAAY,UAAU,MAAM;AAAA,MACxE;AAAA,MACA,SAAS;AAAA,MACT,WAAW,CAAC,gBAAgB;AAAA,MAC5B,gBAAgB;AAAA,MAChB,cAAc;AAAA,IAChB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,QACN,EAAE,MAAM,WAAW,OAAO,qBAAqB,MAAM,YAAY,UAAU,KAAK;AAAA,MAClF;AAAA,MACA,SAAS;AAAA,MACT,WAAW,CAAC,gBAAgB;AAAA,MAC5B,gBAAgB;AAAA,MAChB,cAAc;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,QACN,EAAE,MAAM,WAAW,OAAO,QAAQ,MAAM,YAAY,UAAU,MAAM;AAAA,MACtE;AAAA,MACA,SAAS;AAAA,MACT,WAAW,CAAC,gBAAgB;AAAA,MAC5B,gBAAgB;AAAA,MAChB,cAAc;AAAA,IAChB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA;AAAA;AAAA,MAGP,aAAa;AAAA,MACb,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,QACN,EAAE,MAAM,WAAW,OAAO,WAAW,MAAM,YAAY,UAAU,MAAM;AAAA,MACzE;AAAA;AAAA;AAAA,MAGA,SAAS;AAAA,MACT,WAAW,CAAC,gBAAgB;AAAA,MAC5B,gBAAgB;AAAA,MAChB,cAAc;AAAA,IAChB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,QACN,EAAE,MAAM,WAAW,OAAO,iBAAiB,MAAM,YAAY,UAAU,MAAM;AAAA,MAC/E;AAAA,MACA,SAAS;AAAA,MACT,WAAW,CAAC,gBAAgB;AAAA,MAC5B,gBAAgB;AAAA,MAChB,cAAc;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,QAAQ;AAAA;AAAA;AAAA;AAAA,IAIN,YAAY,CAAC,OAAO,MAAM;AAAA,EAC5B;AACF,CAAC;;;ACzaD,SAAS,gBAAAA,eAAc,SAAAC,cAAa;AACpC,SAAS,uBAAuB,mCAAmC;AAa5D,IAAM,oBAAoBD,cAAa,OAAO;AAAA,EACnD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAM;AAAA,EACN,UAAU;AAAA,EACV,WAAW;AAAA,EACX,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,WAAW;AAAA;AAAA,EACX,aAAa;AAAA,EACb,iBAAiB,CAAC,cAAc,aAAa,UAAU,YAAY,gBAAgB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS/F,oBAAoB;AAAA,EAEpB,WAAW;AAAA,IACT,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,EAAE,UAAU,UAAU,QAAQ,sBAAsB;AAAA,MAC1D,SAAS,CAAC,cAAc,cAAc,aAAa,UAAU,YAAY,gBAAgB,SAAS;AAAA,MAClG,MAAM,CAAC,EAAE,OAAO,cAAc,OAAO,OAAO,CAAC;AAAA,MAC7C,YAAY,EAAE,UAAU,GAAG;AAAA,MAC3B,YAAY,EAAE,OAAO,2BAA2B,SAAS,4DAA4D;AAAA,IACvH;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,EAAE,UAAU,UAAU,QAAQ,sBAAsB;AAAA,MAC1D,SAAS,CAAC,YAAY,cAAc,cAAc,aAAa,QAAQ;AAAA,MACvE,MAAM,CAAC,EAAE,OAAO,YAAY,OAAO,MAAM,GAAG,EAAE,OAAO,cAAc,OAAO,OAAO,CAAC;AAAA,MAClF,UAAU,EAAE,QAAQ,CAAC,EAAE,OAAO,YAAY,OAAO,OAAO,WAAW,MAAM,CAAC,EAAE;AAAA,MAC5E,YAAY,EAAE,UAAU,IAAI;AAAA,IAC9B;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,EAAE,UAAU,UAAU,QAAQ,sBAAsB;AAAA,MAC1D,SAAS,CAAC,cAAc,cAAc,aAAa,UAAU,YAAY,gBAAgB,SAAS;AAAA,MAClG,MAAM,CAAC,EAAE,OAAO,cAAc,OAAO,OAAO,CAAC;AAAA,MAC7C,YAAY,EAAE,UAAU,IAAI;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,QAAQ;AAAA,IACN,IAAIC,OAAM,KAAK,EAAE,OAAO,aAAa,UAAU,MAAM,UAAU,MAAM,OAAO,SAAS,CAAC;AAAA,IAEtF,iBAAiBA,OAAM,OAAO,oBAAoB;AAAA,MAChD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IAED,YAAYA,OAAM,OAAO,wBAAwB;AAAA,MAC/C,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,WAAWA,OAAM,KAAK;AAAA,MACpB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,IAED,YAAYA,OAAM,OAAO;AAAA,MACvB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,QAAQA,OAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQZ,sBAAsB,IAAI,CAAC,WAAW,EAAE,OAAO,OAAO,4BAA4B,KAAK,EAAE,EAAE;AAAA,MAC3F;AAAA,QACE,OAAO;AAAA,QACP,UAAU;AAAA,QACV,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,UAAUA,OAAM,OAAO,YAAY;AAAA,MACjC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,SAASA,OAAM,SAAS,EAAE,OAAO,WAAW,UAAU,OAAO,OAAO,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAmB9E,cAAcA,OAAM,QAAQ;AAAA,MAC1B,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aACE;AAAA,IAEJ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOD,eAAeA,OAAM,OAAO,YAAY;AAAA,MACtC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IAED,aAAaA,OAAM,OAAO,YAAY;AAAA,MACpC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IAED,aAAaA,OAAM,KAAK;AAAA,MACtB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IAED,YAAYA,OAAM,SAAS;AAAA,MACzB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,SAAS;AAAA,IACP,EAAE,QAAQ,CAAC,cAAc,YAAY,EAAE;AAAA,IACvC,EAAE,QAAQ,CAAC,cAAc,cAAc,QAAQ,EAAE;AAAA,EACnD;AAAA,EAEA,QAAQ;AAAA;AAAA;AAAA,IAGN,YAAY,CAAC,OAAO,MAAM;AAAA,EAC5B;AACF,CAAC;;;AClMD,SAAS,gBAAAC,eAAc,SAAAC,cAAa;AAwB7B,IAAM,sBAAsBD,cAAa,OAAO;AAAA,EACrD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAM;AAAA,EACN,UAAU;AAAA,EACV,WAAW;AAAA,EACX,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,WAAW;AAAA;AAAA,EACX,aAAa;AAAA,EACb,iBAAiB,CAAC,cAAc,YAAY,YAAY;AAAA,EAExD,QAAQ;AAAA,IACN,IAAIC,OAAM,KAAK,EAAE,OAAO,UAAU,UAAU,MAAM,UAAU,MAAM,OAAO,SAAS,CAAC;AAAA,IAEnF,iBAAiBA,OAAM,OAAO,oBAAoB;AAAA,MAChD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IAED,YAAYA,OAAM,OAAO,wBAAwB;AAAA,MAC/C,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,UAAUA,OAAM,KAAK;AAAA,MACnB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,IAED,YAAYA,OAAM,SAAS;AAAA,MACzB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,SAAS;AAAA;AAAA,IAEP,EAAE,QAAQ,CAAC,YAAY,iBAAiB,EAAE;AAAA;AAAA,IAE1C,EAAE,QAAQ,CAAC,YAAY,EAAE;AAAA,EAC3B;AAAA,EAEA,QAAQ;AAAA;AAAA;AAAA,IAGN,YAAY,CAAC,OAAO,MAAM;AAAA,EAC5B;AACF,CAAC;;;AClFD,SAAS,gBAAAC,eAAc,SAAAC,cAAa;AA2B7B,IAAM,wBAAwBD,cAAa,OAAO;AAAA,EACvD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAM;AAAA,EACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAKV,WAAW;AAAA,EACX,aACE;AAAA,EACF,aAAa;AAAA,EACb,iBAAiB,CAAC,gBAAgB,eAAe,cAAc,aAAa;AAAA,EAE5E,WAAW;AAAA,IACT,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,EAAE,UAAU,UAAU,QAAQ,0BAA0B;AAAA,MAC9D,SAAS,CAAC,gBAAgB,eAAe,cAAc,eAAe,QAAQ;AAAA,MAC9E,MAAM,CAAC,EAAE,OAAO,eAAe,OAAO,MAAM,CAAC;AAAA,MAC7C,YAAY,EAAE,UAAU,GAAG;AAAA,MAC3B,YAAY;AAAA,QACV,OAAO;AAAA,QACP,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QAAQ;AAAA,IACN,IAAIC,OAAM,KAAK,EAAE,OAAO,iBAAiB,UAAU,MAAM,UAAU,MAAM,OAAO,SAAS,CAAC;AAAA,IAE1F,cAAcA,OAAM,OAAO,YAAY;AAAA,MACrC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IAED,aAAaA,OAAM,OAAO,YAAY;AAAA,MACpC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IAED,YAAYA,OAAM,SAAS;AAAA,MACzB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aACE;AAAA,IAEJ,CAAC;AAAA,IAED,aAAaA,OAAM,SAAS;AAAA,MAC1B,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aACE;AAAA,IACJ,CAAC;AAAA,IAED,QAAQA,OAAM,KAAK;AAAA,MACjB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IAED,iBAAiBA,OAAM,OAAO,oBAAoB;AAAA,MAChD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IAED,YAAYA,OAAM,SAAS;AAAA,MACzB,OAAO;AAAA,MACP,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,YAAYA,OAAM,SAAS;AAAA,MACzB,OAAO;AAAA,MACP,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,SAAS;AAAA;AAAA,IAEP,EAAE,QAAQ,CAAC,gBAAgB,iBAAiB,EAAE;AAAA,IAC9C,EAAE,QAAQ,CAAC,aAAa,EAAE;AAAA,IAC1B,EAAE,QAAQ,CAAC,aAAa,EAAE;AAAA,EAC5B;AAAA,EAEA,QAAQ;AAAA,IACN,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,YAAY;AAAA;AAAA;AAAA,IAGZ,YAAY,CAAC,OAAO,QAAQ,UAAU,UAAU,UAAU,MAAM;AAAA,EAClE;AACF,CAAC;;;AC1ID,SAAS,YAAY,mBAAmB;AACxC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,kBAAkB,iCAAiC;AAC5D,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAuBP,SAAS,gCAAgC;AACzC,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;;;ACtB9B,IAAM,gBAAgB;AAEtB,IAAM,aAAa,EAAE,UAAU,KAAK;AA8BpC,SAAS,KAAK,SAAwB;AACpC,QAAM,IAAI,MAAM,sBAAsB,OAAO,EAAE;AACjD;AAEA,eAAe,QACb,QACA,OACqB;AACrB,MAAI;AACF,UAAM,OAAO,MAAM,OAAO,KAAK,oBAAoB;AAAA,MACjD;AAAA,MACA,QAAQ,CAAC,MAAM,QAAQ,wBAAwB;AAAA,MAC/C,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAQ;AACR,WAAO,MAAM,QAAQ,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI;AAAA,EACpD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAe,cACb,QACA,OACmB;AACnB,QAAM,QAAkB,CAAC;AACzB,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAI,SAAwB;AAC5B,WAAS,QAAQ,GAAG,UAAU,QAAQ,eAAe,SAAS;AAC5D,QAAI,KAAK,IAAI,MAAM,EAAG;AACtB,SAAK,IAAI,MAAM;AACf,UAAM,KAAK,MAAM;AACjB,UAAM,MAAM,MAAM,QAAQ,QAAQ,EAAE,IAAI,OAAO,CAAC;AAChD,UAAM,SAAS,KAAK;AACpB,aAAS,SAAS,OAAO,MAAM,IAAI;AAAA,EACrC;AACA,SAAO;AACT;AAOA,eAAsB,4BACpB,MACA,aACA,cACA,cACA,iBACoC;AACpC,QAAM,WAAW,OAAO,gBAAgB,WAAW,YAAY,KAAK,IAAI;AACxE,MAAI,CAAC,SAAU,QAAO;AAMtB,MAAI,CAAC,iBAAiB;AACpB;AAAA,MACE,kBAAkB,YAAY,0EACP,QAAQ;AAAA,IAEjC;AAAA,EACF;AASA,QAAM,UAAU,KAAK,UAAU;AAC/B,MAAI,WAAW,YAAY,SAAS;AAClC;AAAA,MACE,yDAAyD,QAAQ,sEACT,OAAO;AAAA,IACjE;AAAA,EACF;AAEA,QAAM,aAAa,eAAe,OAAO,YAAY,IAAI;AACzD,MAAI,CAAC,YAAY;AACf;AAAA,MACE,kBAAkB,QAAQ;AAAA,IAE5B;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM,cAAc,KAAK,QAAQ,UAAU;AAEzD,MAAI,aAAa,SAAS;AACxB,UAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,QAAI,SAAS,cAAc,MAAM,WAAW,GAAG;AAC7C;AAAA,QACE;AAAA,MAGF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,WAAW;AAC1B,UAAM,SAAS,MAAM,CAAC;AACtB,QAAI,CAAC,QAAQ;AACX;AAAA,QACE;AAAA,MAEF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAMA,QAAM,SAAS,MAAM,QAAQ,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,MAAI,CAAC,QAAQ,IAAI;AACf;AAAA,MACE,8BAA8B,QAAQ;AAAA,IAExC;AAAA,EACF;AACA,QAAM,WAAW,OAAO,OAAO,EAAE;AACjC,MAAI,aAAa,WAAY,QAAO;AAOpC,QAAM,cAAc,MAAM,cAAc,KAAK,QAAQ,QAAQ;AAC7D,QAAM,aAAa,YAAY,YAAY,SAAS,CAAC;AACrD,QAAM,cAAc,MAAM,MAAM,SAAS,CAAC;AAC1C,MAAI,CAAC,cAAc,CAAC,eAAe,eAAe,aAAa;AAC7D;AAAA,MACE,iBAAiB,QAAQ;AAAA,IAG3B;AAAA,EACF;AACA,SAAO;AACT;AAqBA,eAAsB,0BACpB,MACA,SACA,cACA,SACmB;AACnB,QAAM,aAAa,eAAe,OAAO,YAAY,IAAI;AACzD,MAAI,CAAC,cAAc,QAAQ,WAAW,EAAG,QAAO;AAEhD,MAAI,UAAiB,CAAC;AACtB,MAAI;AACF,cAAU,MAAM,KAAK,OAAO,KAAK,cAAc;AAAA,MAC7C,OAAO,EAAE,iBAAiB,YAAY,SAAS,EAAE,KAAK,QAAQ,EAAE;AAAA,MAChE,QAAQ,CAAC,SAAS;AAAA,MAClB,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAQ;AAAA,EACV,QAAQ;AAKN,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,IAAI;AAAA,KACjB,WAAW,CAAC,GAAG,IAAI,CAAC,MAAW,OAAO,GAAG,WAAW,EAAE,CAAC,EAAE,OAAO,OAAO;AAAA,EAC1E;AACA,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;AACrD,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,OAAK,QAAQ;AAAA,IACX,4BAA4B,QAAQ,MAAM,6DAC1B,QAAQ,SAAS,QAAQ,YAAY,sBAC/C,QAAQ,cAAc,sDACtB,UAAU;AAAA,IAEhB,EAAE,SAAS,uBAAuB,YAAY,yBAAyB,QAAQ,eAAe;AAAA,EAChG;AACA,SAAO,QAAQ,OAAO,CAAC,MAAM,QAAQ,IAAI,CAAC,CAAC;AAC7C;;;AD3GO,IAAM,qBAAqB,IAAI,KAAK,KAAK;AAGzC,IAAM,sBAAsB;AAE5B,IAAM,8BAA8B,IAAI,KAAK;AAE7C,IAAM,eAAe;AAErB,IAAM,oBAAoB;AAcjC,IAAM,wBAA6C,oBAAI,IAAI;AAAA,EACzD;AAAA,EAAa;AAAA,EAAU;AAAA,EAAa;AACtC,CAAC;AAWD,IAAM,8BAA8B,CAAC,YAAY,YAAY,UAAU;AA4BhE,IAAM,sBAAsB,KAAK,KAAK,KAAK;AAiBlD,IAAMC,cAA+B,EAAE,UAAU,MAAM,WAAW,CAAC,GAAG,aAAa,CAAC,EAAE;AAwBtF,SAAS,aAAa,SAAsD;AAC1E,QAAM,SAAS,SAAS;AACxB,SAAO,OAAO,WAAW,YAAY,SAAS,SAAS;AACzD;AAOA,IAAM,gBAAgB;AAatB,IAAM,uBAA4C,oBAAI,IAAI;AAAA,EACxD;AAAA,EAAQ;AAAA,EAAc;AAAA,EAAiB;AAAA,EAAM;AAAA,EAAY;AAAA,EAAwB;AACnF,CAAC;AAuBD,IAAM,4BAA4B,oBAAI,IAAI,CAAC,WAAW,WAAW,MAAM,CAAC;AA0BxE,SAAS,IAAI,QAAwB;AACnC,QAAM,IAAS;AACf,MAAI,EAAE,QAAQ,WAAY,QAAO,GAAG,MAAM,IAAI,EAAE,OAAO,WAAW,CAAC;AACnE,SAAO,GAAG,MAAM,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACxF;AAEA,SAAS,UAAmB,KAAc,UAAgB;AACxD,MAAI,OAAO,QAAQ,QAAQ,GAAI,QAAO;AACtC,MAAI,OAAO,QAAQ,UAAU;AAC3B,QAAI;AAAE,aAAO,KAAK,MAAM,GAAG;AAAA,IAAQ,QAAQ;AAAE,aAAO;AAAA,IAAU;AAAA,EAChE;AACA,SAAO;AACT;AAEA,SAAS,SAAS,KAAwB;AACxC,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,MAAI,MAAM,QAAQ,GAAG,EAAG,QAAO,IAAI,IAAI,MAAM,EAAE,OAAO,OAAO;AAC7D,SAAO,OAAO,GAAG,EAAE,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AACjE;AAYA,SAAS,sBAAsB,OAAyB;AACtD,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,KAAK,MAAM;AACvD,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,OAAO,OAAK,MAAM,QAAQ,MAAM,UAAa,OAAO,CAAC,EAAE,KAAK,MAAM,EAAE,EAAE,WAAW;AACxH,SAAO;AACT;AAOA,SAAS,oBAAoB,KAAoD;AAC/E,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,OAAO,OAAO,GAAG,EAAE,QAAQ,UAAU,EAAE,EAAE,KAAK;AACpD,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KACJ,MAAM,UAAU,EAChB,OAAO,OAAO,EACd,IAAI,OAAK,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC,CAAC,EAC/C,KAAK,GAAG;AACb;AAEA,SAAS,eAAe,KAA8B;AAIpD,QAAM,MAAM,UAAe,IAAI,kBAAkB,MAAS;AAC1D,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,iBAAiB,IAAI,mBAAmB;AAAA,IACxC,cAAc,OAAO,IAAI,gBAAgB,EAAE;AAAA,IAC3C,aAAa,OAAO,IAAI,eAAe,EAAE;AAAA,IACzC,WAAW,OAAO,IAAI,aAAa,EAAE;AAAA,IACrC,cAAc,IAAI,gBAAgB;AAAA,IAClC,mBAAmB,IAAI,qBAAqB;AAAA,IAC5C,QAAS,IAAI,UAA6B;AAAA,IAC1C,cAAc,IAAI,gBAAgB;AAAA,IAClC,oBAAoB,IAAI,sBAAsB;AAAA,IAC9C,mBAAmB,SAAS,IAAI,iBAAiB;AAAA,IACjD,SAAS,UAAU,IAAI,cAAc,MAAS;AAAA,IAC9C,aAAa,IAAI,eAAe;AAAA,IAChC,cAAc,IAAI,gBAAgB;AAAA,IAClC,cAAc,IAAI,gBAAgB;AAAA,IAClC,YAAY,IAAI,cAAc;AAAA,IAC9B,YAAY,IAAI,cAAc;AAAA;AAAA,IAE9B,cAAc,IAAI,cAAc;AAAA,IAChC,eAAe,KAAK,eAAe,oBAAoB,IAAI,YAAY;AAAA,IACvE,YAAY,KAAK,eAAe,oBAAoB,IAAI,YAAY;AAAA,IACpE,YAAY,SAAS,IAAI,YAAY,GAAG;AAAA;AAAA,IAExC,OAAO,OAAO,KAAK,YAAY,WAAW,IAAI,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQxD,aAAa,KAAK,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQjC,IAAI,MAAM;AACR,YAAM,OAAO,yBAAyB,KAAK,eAAe;AAC1D,aAAO,KAAK,SACR,EAAE,kBAAkB,KAAK,IAAI,OAAK,EAAE,GAAG,GAAG,sBAAsB,KAAK,IACrE,CAAC;AAAA,IACP,GAAG;AAAA,EACL;AACF;AAGA,SAAS,SAAS,WAAoB,KAA8B;AAClE,QAAM,QAAQ,KAAK,YAAY;AAC/B,MAAI,OAAO,UAAU,YAAY,SAAS,KAAK,CAAC,UAAW,QAAO;AAClE,QAAM,IAAI,KAAK,MAAM,OAAO,SAAS,CAAC;AACtC,MAAI,OAAO,MAAM,CAAC,EAAG,QAAO;AAC5B,SAAO,IAAI,KAAK,IAAI,QAAQ,IAAQ,EAAE,YAAY;AACpD;AAwBA,SAAS,0BAA0B,OAAkD;AACnF,MAAI,SAAS,KAAM,QAAO;AAI1B,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,KAAK,MAAM,KAAK;AACtB,QAAI,CAAC,GAAI,QAAO;AAChB,WAAO,cAAc,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,GAAG;AAAA,EACpD;AACA,MAAI,OAAO,UAAU,UAAU;AAE7B,UAAM,KAAK,MAAM,MAAM,MAAM;AAC7B,QAAI,MAAM,QAAQ,OAAO,EAAE,MAAM,GAAI,QAAO;AAC5C,UAAM,WAAW,MAAM,YAAY,MAAM;AACzC,WAAO;AAAA,MACL,IAAI,OAAO,EAAE;AAAA,MACb,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;AAAA,MACpD,KAAK,OAAO,MAAM,QAAQ,WAAW,MAAM,MAAM;AAAA,MACjD,UAAU,OAAO,aAAa,WAAW,WAAW;AAAA,MACpD,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;AAAA,IACtD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,cAAc,KAA6B;AAClD,QAAM,cAAc,MAAM,QAAQ,IAAI,WAAW,IAC7C,IAAI,YAAY,IAAI,yBAAyB,EAAE,OAAO,CAAC,MAA2E,CAAC,CAAC,CAAC,IACrI,CAAC;AACL,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,WAAW,IAAI,aAAa;AAAA,IAC5B,YAAY,IAAI,cAAc;AAAA,IAC9B,QAAQ,IAAI;AAAA,IACZ,UAAU,IAAI,YAAY;AAAA,IAC1B,SAAS,IAAI,WAAW;AAAA;AAAA,IAExB,eAAe,IAAI,iBAAiB;AAAA,IACpC,aAAa,IAAI,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,IAKhC,cAAc,IAAI,gBAAgB,OAAO,SAAY,IAAI,iBAAiB;AAAA;AAAA;AAAA,IAG1E,aAAa,YAAY,SAAS,cAAc;AAAA,IAChD,YAAY,IAAI,cAAc;AAAA,EAChC;AACF;AA+BO,IAAM,mBAAN,MAAM,iBAA4C;AAAA,EASvD,YAAY,MAA8B;AACxC,SAAK,SAAS,KAAK;AACnB,SAAK,QAAQ,KAAK,SAAS,EAAE,KAAK,MAAM,oBAAI,KAAK,EAAE;AACnD,SAAK,SAAS,KAAK;AACnB,SAAK,aAAa,KAAK;AACvB,SAAK,YAAY,KAAK;AACtB,SAAK,iBAAiB,KAAK,iBAAiB,IAAI,QAAQ,OAAO,EAAE;AACjE,SAAK,iBAAiB,KAAK;AAAA,EAC7B;AAAA;AAAA,EAGA,qBAAqB,UAA0C;AAC7D,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA,EAGA,IAAY,eAAqC;AAC/C,WAAO;AAAA,MACL,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,gBAAgB,GAAQ,cAA6E;AACjH,UAAM,UAAU,OAAO,GAAG,QAAQ,EAAE;AACpC,WAAO;AAAA,MACL,KAAK;AAAA,MACL,GAAG;AAAA,MACH;AAAA,MACA;AAAA,MACA,wBAAwB,OAAO;AAAA,IACjC;AAAA,EACF;AAAA;AAAA,EAGA,iBAAiB,YAAyC;AACxD,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAGA,gBAAgB,WAA2C;AACzD,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA,EAGA,MAAc,OAAO,OAOD;AAClB,UAAM,WAAW,MAAM,SAAS,OAAO,OAAK,KAAK,CAAC,EAAE,SAAS,GAAG,CAAC;AACjE,QAAI,CAAC,KAAK,aAAa,CAAC,SAAS,OAAQ,QAAO;AAKhD,QAAI,UAAU,MAAM;AACpB,QACE,SAAS,cAAc,uBACpB,MAAM,QAAQ,WAAW,0BACzB,MAAM,OAAO,IAChB;AACA,gBAAU,EAAE,GAAG,SAAS,WAAW,6BAA6B,mBAAmB,MAAM,OAAO,EAAE,CAAC,GAAG;AAAA,IACxG;AACA,QAAI;AACF,YAAM,KAAK,UAAU,KAAK,EAAE,UAAU,QAAQ,GAAG,OAAO,SAAS,SAAS,CAAC;AAC3E,aAAO,SAAS;AAAA,IAClB,SAAS,KAAU;AACjB,WAAK,QAAQ,OAAO,mCAAmC;AAAA,QACrD,OAAO,MAAM;AAAA,QAAO,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,MACvD,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,eAAe,WAAiC;AAC5D,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,0CAA0C;AAC1E,UAAM,OAAO,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,MAC1D,OAAO,EAAE,IAAI,UAAU;AAAA,MAAG,OAAO;AAAA,MAAG,SAASA;AAAA,IAC/C,CAAC;AACD,UAAM,MAAW,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AACjD,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sBAAsB,SAAS,EAAE;AAC3D,QAAI,IAAI,WAAW,UAAW,OAAM,IAAI,MAAM,6BAA6B,IAAI,MAAM,EAAE;AACvF,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBQ,gBAAgB,SAA2B,YAAqC;AACtF,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,QAAQ,SAAU,QAAO;AAC7B,UAAM,QAAQ,MAAM,QAAQ,QAAQ,WAAW,IAAI,QAAQ,cAAc,CAAC;AAC1E,UAAM,YAAY,MAAM,QAAQ,QAAQ,SAAS,IAAI,QAAQ,YAAY,CAAC;AAQ1E,UAAM,UAAU,QAAQ;AACxB,UAAM,kBAAkB,YAAY,oBAC/B,MAAM,SAAS,iBAAiB,KAChC,UAAU,SAAS,+BAA+B;AACvD,QAAI,gBAAiB,QAAO;AAC5B,UAAM,gBAAgB,YAAY,kBAC7B,0BAA0B,KAAK,CAAC,MAAM,MAAM,SAAS,CAAC,CAAC,KACvD,UAAU,SAAS,0BAA0B,KAC7C,UAAU,SAAS,0BAA0B;AAClD,QAAI,CAAC,cAAe,QAAO;AAS3B,UAAM,cAAc,QAAQ,YAAa,QAAgB,kBAAkB;AAC3E,WAAO,cAAc,QAAS,eAAe,QAAQ,OAAO,UAAU,MAAM,OAAO,WAAW;AAAA,EAChG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiCA,MAAc,aACZ,SACA,SACiB;AAEjB,QAAI,SAAS,UAAU;AACrB,UAAI,CAAC,QAAS,OAAM,IAAI,MAAM,wCAAwC;AACtE,aAAO;AAAA,IACT;AACA,UAAMC,OAAM,aAAa,OAAO;AAChC,QAAI,CAACA,MAAK;AACR,YAAM,IAAI,MAAM,gEAAgE;AAAA,IAClF;AAEA,QAAI,CAAC,WAAW,OAAO,OAAO,MAAMA,KAAK,QAAOA;AAMhD,UAAM,QAAQ,OAAO,OAAO;AAC5B,eAAW,YAAY,QAAQ,aAAa,CAAC,GAAG;AAC9C,UAAI,UAAU,YAAY,QAAQ,MAAM,UAAU,QAAQ,QAAQ,GAAI,QAAO;AAAA,IAC/E;AAEA,QAAI,MAAM,SAAS,GAAG,KAAK,MAAM,KAAK,eAAeA,MAAK,KAAK,EAAG,QAAO;AAEzE,UAAM,IAAI;AAAA,MACR,6BAA6B,KAAK;AAAA,IACpC;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,eAAe,QAAgB,OAAiC;AAC5E,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,OAAO,KAAK,YAAY;AAAA,QAC9C,OAAO,EAAE,IAAI,OAAO;AAAA,QAAG,OAAO;AAAA,QAAG,SAASD;AAAA,MAC5C,CAAC;AACD,YAAM,MAAW,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AACjD,aAAO,CAAC,CAAC,KAAK,SAAS,OAAO,IAAI,KAAK,EAAE,YAAY,MAAM,MAAM,YAAY;AAAA,IAC/E,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCA,MAAc,gBACZ,MACA,QACA,gBACA,MAamB;AAl0BvB;AAm0BI,QAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,KAAK,SAAS,EAAG,QAAO,CAAC;AACrD,UAAM,MAAM,MAAM,OAAO,KAAK,MAAM,IAAI,EAAE,QAAQ;AAClD,UAAM,MAAgB,CAAC;AACvB,UAAM,QAAe,KAAK;AAC1B,aAAS,MAAM,GAAG,MAAM,MAAM,QAAQ,OAAO;AAC3C,YAAM,IAAI,MAAM,GAAG;AACnB,UAAI,CAAC,EAAG;AAGR,YAAM,WAAW,EAAE,SAAS,QAAQ,OAAO,EAAE,KAAK,MAAM,KAAK,OAAO,EAAE,KAAK,IAAI,IAAI,GAAG;AAMtF,UAAI,sBAAsB,OAAO,EAAE,IAAI,CAAC,MAAM,cAAc;AAC1D,cAAM,WAAW,MAAM,KAAK;AAAA,UAC1B;AAAA,UAAG;AAAA,UAAQ;AAAA,UAAgB;AAAA,UAAK,MAAM;AAAA,UAAe,MAAM;AAAA,QAC7D;AACA,YAAI,MAAM,aAAc,MAAK,aAAa,cAAc,GAAG,EAAE,IAAI,SAAS;AAC1E,mBAAW,SAAS,SAAS,OAAO;AAClC,cAAI,CAAC,MAAM,GAAI;AACf,cAAI,KAAK,MAAM,EAAE;AACjB,cAAI,MAAM,QAAQ;AAChB,cAAC,UAAK,QAAL,KAAY,MAAM,QAAlB,SAA0B,CAAC,IAAG,KAAK,MAAM,WAAW,GAAG,QAAQ,IAAI,MAAM,QAAQ,KAAK,QAAQ;AAAA,UACjG;AAAA,QACF;AACA;AAAA,MACF;AAEA,UAAI,MAAM,gBAAgB,sBAAsB,OAAO,EAAE,IAAI,CAAC,MAAM,WAAW,EAAE,SAAS,MAAM;AAC9F,aAAK,aAAa,SAAS,EAAE,KAAK,EAAE,IAAK,SAAiB,EAAE,KAAK,KAAK;AAAA,MACxE;AACA,YAAM,MAAM,MAAM,KAAK,oBAAoB,GAAG,QAAQ,gBAAgB,KAAK,MAAM,aAAa;AAE9F,iBAAW,KAAK,KAAK;AACnB,YAAI,CAAC,EAAG;AACR,YAAI,KAAK,CAAC;AACV,YAAI,MAAM,OAAQ,GAAC,UAAK,QAAL,eAAmB,CAAC,IAAG,KAAK,QAAQ;AAAA,MACzD;AAAA,IACF;AACA,WAAO,IAAI,OAAO,OAAO;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,oBACZ,GACA,QACA,gBACA,KACA,eACmB;AAKnB,UAAM,OAAO,sBAAsB,OAAO,EAAE,IAAI,CAAC;AACjD,QAAI,SAAS,EAAE,MAAM;AACnB,WAAK,QAAQ;AAAA,QACX,8BAA8B,EAAE,IAAI,gDAA2C,IAAI;AAAA,QACnF,EAAE,YAAY,EAAE,MAAM,WAAW,KAAK;AAAA,MACxC;AAAA,IACF;AAeA,UAAM,eAAe,MAAM,KAAK,gBAAgB,GAAG,cAAc;AACjE,UAAM,WAAW,iBAAiB;AAElC,QAAI,SAAS,QAAQ;AACnB,aAAO,KAAK,mBAAmB,OAAO,EAAE,KAAK,GAAG,KAAK,gBAAgB,aAAa;AAAA,IACpF;AACA,QAAI,SAAS,WAAW,QAAQ;AAK9B,YAAM,MAAgB,CAAC;AACvB,iBAAW,MAAM,SAAU,OAAe,EAAE,KAAK,CAAC,GAAG;AACnD,YAAI,KAAK,GAAG,MAAM,KAAK,mBAAmB,IAAI,KAAK,gBAAgB,aAAa,CAAC;AAAA,MACnF;AACA,aAAO;AAAA,IACT;AAQA,UAAM,UAAU,OAAO,UACrB,WACI,0BAA0B,KAAK,cAAc,OAAO,gBAAgB;AAAA,MACpE,cAAc;AAAA,MAAM,OAAO,EAAE,SAAS,OAAO,OAAO,EAAE,KAAK,IAAI;AAAA,MAC/D,gBAAgB;AAAA,IAClB,CAAC,IACC;AAGN,QAAI;AACF,UAAI,SAAS,QAAQ;AACnB,cAAM,QAAQ,MAAM,KAAK,gBAAgB,OAAO,EAAE,KAAK,CAAC;AACxD,YAAI,MAAM,OAAQ,QAAO;AAAA,MAC3B,WAAW,SAAS,gBAAgB,SAAS,mBAAmB,SAAS,MAAM;AAC7E,cAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,wBAAwB,OAAO,EAAE,KAAK,GAAG,YAAY,CAAC;AAC7F,YAAI,MAAM,OAAQ,QAAO;AAAA,MAC3B,WAAW,SAAS,YAAY;AAC9B,cAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,oBAAoB,OAAO,EAAE,KAAK,GAAG,YAAY,CAAC;AACzF,YAAI,MAAM,OAAQ,QAAO;AAAA,MAC3B,WAAW,SAAS,wBAAwB;AAC1C,cAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,0BAA0B,OAAO,EAAE,KAAK,GAAG,YAAY,CAAC;AAC/F,YAAI,MAAM,OAAQ,QAAO;AAAA,MAC3B,WAAW,SAAS,aAAa,QAAQ;AACvC,cAAM,UAAW,OAAe,EAAE,KAAK,KAAM,OAAe;AAC5D,YAAI,SAAS;AACX,gBAAM,MAAM,MAAM,KAAK,cAAc,OAAO,OAAO,CAAC;AACpD,cAAI,IAAK,QAAO,KAAK,mBAAmB,KAAK,KAAK,gBAAgB,aAAa;AAAA,QACjF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAAqE;AAO7E,QAAI,SAAS,SAAS;AACpB,WAAK,QAAQ;AAAA,QACX;AAAA,QACA,EAAE,OAAO,EAAE,MAAM;AAAA,MACnB;AAAA,IACF,WAAW,qBAAqB,IAAI,IAAI,GAAG;AAUzC,WAAK,QAAQ;AAAA,QACX,yBAAyB,IAAI,IAAI,EAAE,KAAK;AAAA,QAExC,EAAE,MAAM,OAAO,EAAE,OAAO,gBAAgB,kBAAkB,KAAK;AAAA,MACjE;AAAA,IACF;AACA,WAAO,CAAC,GAAG,EAAE,IAAI,IAAI,EAAE,KAAK,EAAE;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAc,2BACZ,GACA,YACA,gBACA,KACA,eACA,SAC6E;AAC7E,UAAM,SAAS,OAAO,EAAE,SAAS,EAAE,EAAE,KAAK;AAC1C,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,gEAAgE;AAAA,IAClF;AAKA,UAAM,SAAS,0BAA0B,MAAM;AAC/C,QAAI,CAAC,OAAO,IAAI;AACd,YAAM,IAAI,MAAM,0DAA0D,OAAO,KAAK,qBAAgB,MAAM,IAAI;AAAA,IAClH;AACA,UAAM,UAAU,OAAO,MAAM,OAAO,OAAK,CAAC,0BAA0B,IAAI,CAAC,CAAC;AAC1E,QAAI,QAAQ,QAAQ;AAClB,YAAM,OAAO,QAAQ,SAAS,QAAQ,KAAK,QAAQ,SAAS,UAAU,IAClE,8NAEA;AACJ,YAAM,IAAI;AAAA,QACR,uDAAuD,QAAQ,KAAK,MAAM,CAAC,6EACN,IAAI,eAAe,MAAM;AAAA,MAChG;AAAA,IACF;AAEA,UAAM,SAAS,iBAAiB;AAAA,MAC9B,EAAE,SAAS,OAAO,OAAO;AAAA,MACzB,EAAE,OAAO,EAAE,SAAS,cAAc,CAAC,GAAG,SAAS,SAAS,WAAW,CAAC,GAAG,MAAM,SAAS,QAAQ,CAAC,EAAE,EAAE;AAAA,IACrG;AACA,QAAI,CAAC,OAAO,IAAI;AACd,YAAM,IAAI;AAAA,QACR,8DAA8D,OAAO,MAAM,IAAI,MAC1E,OAAO,MAAM,OAAO,qBAAgB,MAAM;AAAA,MACjD;AAAA,IACF;AAKA,UAAM,QAAQ,OAAO;AACrB,QAAI;AACJ,QAAI,SAAS,QAAQ,UAAU,IAAI;AACjC,YAAM,CAAC;AAAA,IACT,WAAW,OAAO,UAAU,UAAU;AACpC,YAAM,SAAS,KAAK;AAAA,IACtB,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,YAAM,MAAM,MAAM,KAAK,OAAK,KAAK,QAAQ,OAAO,MAAM,YAAY,OAAO,MAAM,QAAQ;AACvF,UAAI,QAAQ,QAAW;AACrB,cAAM,IAAI;AAAA,UACR,gHAC6B,OAAO,GAAG,qBAAgB,MAAM;AAAA,QAC/D;AAAA,MACF;AACA,YAAM,MAAM,IAAI,OAAK,OAAO,KAAK,EAAE,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAAA,IAC7D,OAAO;AACL,YAAM,IAAI;AAAA,QACR,4FACS,OAAO,KAAK,qBAAgB,MAAM;AAAA,MAC7C;AAAA,IACF;AAUA,UAAM,YAAY,OAAO,EAAE,aAAa,MAAM;AAC9C,QAAI,cAAc,QAAQ;AACxB,YAAME,SAA+B,CAAC;AACtC,iBAAW,MAAM,KAAK;AACpB,mBAAW,UAAU,MAAM,KAAK,mBAAmB,IAAI,KAAK,gBAAgB,aAAa,GAAG;AAC1F,UAAAA,OAAM,KAAK,EAAE,IAAI,OAAO,CAAC;AAAA,QAC3B;AAAA,MACF;AACA,aAAO,EAAE,OAAAA,QAAO,IAAI;AAAA,IACtB;AAKA,UAAM,eAAe,MAAM,KAAK,gBAAgB,GAAG,cAAc;AACjE,UAAM,WAAW,iBAAiB;AAClC,UAAM,QAAiD,CAAC;AACxD,eAAW,OAAO,KAAK;AACrB,UAAI,QAAkB,CAAC;AACvB,UAAI;AACF,YAAI,cAAc,aAAc,SAAQ,MAAM,KAAK,wBAAwB,KAAK,YAAY;AAAA,iBACnF,cAAc,WAAY,SAAQ,MAAM,KAAK,oBAAoB,KAAK,YAAY;AAAA,iBAClF,cAAc,OAAQ,SAAQ,MAAM,KAAK,gBAAgB,GAAG;AAAA,aAChE;AACH,gBAAM,IAAI;AAAA,YACR,iEAAiE,SAAS;AAAA,UAE5E;AAAA,QACF;AAAA,MACF,SAAS,KAAU;AACjB,YAAI,OAAO,KAAK,WAAW,EAAE,EAAE,WAAW,mBAAmB,EAAG,OAAM;AACtE,gBAAQ,CAAC;AAAA,MACX;AACA,UAAI,YAAY,MAAM,QAAQ;AAC5B,gBAAQ,MAAM,0BAA0B,KAAK,cAAc,OAAO,gBAAgB;AAAA,UAChF,cAAc;AAAA,UAAW,OAAO;AAAA,UAAK,gBAAgB;AAAA,QACvD,CAAC;AAAA,MACH;AACA,UAAI,CAAC,MAAM,QAAQ;AACjB,cAAM,KAAK,EAAE,IAAI,GAAG,SAAS,IAAI,GAAG,IAAI,UAAU,IAAI,CAAC;AACvD;AAAA,MACF;AACA,iBAAW,KAAK,MAAO,OAAM,KAAK,EAAE,IAAI,GAAG,UAAU,IAAI,CAAC;AAAA,IAC5D;AACA,WAAO,EAAE,OAAO,IAAI;AAAA,EACtB;AAAA;AAAA,EAGA,MAAc,gBAAgB,QAAmC;AAC/D,QAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,QAAI,OAAc,CAAC;AACnB,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK,mBAAmB;AAAA,QAC/C,OAAO,EAAE,SAAS,OAAO;AAAA,QACzB,QAAQ,CAAC,SAAS;AAAA,QAClB,OAAO;AAAA,QACP,SAASF;AAAA,MACX,CAAQ;AAAA,IACV,QAAQ;AAAE,aAAO,CAAC;AAAA,IAAG;AACrB,WAAO,MAAM,KAAK,IAAI,KAAK,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAW,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,OAAO,CAAC,CAAC;AAAA,EAClG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBQ,qBACN,QACA,gBACyB;AACzB,QAAI,CAAC,eAAgB,QAAO;AAC5B,WAAO,EAAE,GAAG,QAAQ,KAAK,CAAC,EAAE,iBAAiB,eAAe,GAAG,EAAE,iBAAiB,KAAK,CAAC,EAAE;AAAA,EAC5F;AAAA;AAAA,EAGA,MAAc,wBAAwB,gBAAwB,gBAAmD;AAC/G,QAAI,CAAC,eAAgB,QAAO,CAAC;AAE7B,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,OAAO,KAAK,qBAAqB;AAAA,QACvD,OAAO,KAAK,qBAAqB,EAAE,IAAI,eAAe,GAAG,cAAc;AAAA,QACvE,QAAQ,CAAC,MAAM,QAAQ;AAAA,QACvB,OAAO;AAAA,QACP,SAASA;AAAA,MACX,CAAQ;AACR,YAAM,UAAe,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AACrD,UAAI,CAAC,WAAW,QAAQ,WAAW,MAAO,QAAO,CAAC;AAAA,IACpD,QAAQ;AAAE,aAAO,CAAC;AAAA,IAAG;AAErB,UAAM,OAAO,oBAAI,IAAY,CAAC,cAAc,CAAC;AAC7C,UAAM,QAAkB,CAAC,cAAc;AACvC,WAAO,MAAM,QAAQ;AACnB,YAAM,SAAS,MAAM,MAAM;AAC3B,UAAI,OAAc,CAAC;AACnB,UAAI;AACF,cAAM,SAAS,KAAK;AAAA,UAClB,EAAE,yBAAyB,QAAQ,QAAQ,EAAE,KAAK,MAAM,EAAE;AAAA,UAC1D;AAAA,QACF;AACA,eAAO,MAAM,KAAK,OAAO,KAAK,qBAAqB,EAAE,QAAQ,QAAQ,CAAC,IAAI,GAAG,OAAO,KAAM,SAASA,YAAW,CAAQ;AAAA,MACxH,QAAQ;AAAE,eAAO,CAAC;AAAA,MAAG;AACrB,iBAAW,KAAK,QAAQ,CAAC,GAAG;AAC1B,cAAM,MAAM,OAAQ,EAAU,MAAM,EAAE;AACtC,YAAI,OAAO,CAAC,KAAK,IAAI,GAAG,GAAG;AAAE,eAAK,IAAI,GAAG;AAAG,gBAAM,KAAK,GAAG;AAAA,QAAG;AAAA,MAC/D;AAAA,IACF;AACA,QAAI,OAAc,CAAC;AACnB,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK,4BAA4B;AAAA,QACxD,OAAO,EAAE,kBAAkB,EAAE,KAAK,MAAM,KAAK,IAAI,EAAE,EAAE;AAAA,QACrD,QAAQ,CAAC,SAAS;AAAA,QAClB,OAAO;AAAA,QACP,SAASA;AAAA,MACX,CAAQ;AAAA,IACV,QAAQ;AAAE,aAAO,CAAC;AAAA,IAAG;AACrB,WAAO,MAAM,KAAK,IAAI,KAAK,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAW,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,OAAO,CAAC,CAAC;AAAA,EAClG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,oBAAoB,cAAsB,gBAAmD;AACzG,QAAI,CAAC,aAAc,QAAO,CAAC;AAC3B,UAAM,QAAQ,oBAAI,IAAY;AAC9B,UAAM,SAAc,EAAE,UAAU,aAAa;AAC7C,QAAI,eAAgB,QAAO,kBAAkB;AAC7C,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,OAAO,KAAK,qBAAqB;AAAA,QACvD;AAAA,QAAQ,QAAQ,CAAC,SAAS;AAAA,QAAG,OAAO;AAAA,QAAO,SAASA;AAAA,MACtD,CAAQ;AACR,iBAAW,KAAM,QAAQ,CAAC,GAAa;AACrC,cAAMC,OAAM,OAAO,EAAE,WAAW,EAAE;AAClC,YAAIA,KAAK,OAAM,IAAIA,IAAG;AAAA,MACxB;AAAA,IACF,QAAQ;AAAA,IAAiF;AAKzF,eAAWA,QAAO,MAAM,KAAK,0BAA0B,cAAc,cAAc,EAAG,OAAM,IAAIA,IAAG;AACnG,WAAO,MAAM,KAAK,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,0BAA0B,MAAc,gBAAmD;AACvG,QAAI,CAAC,KAAM,QAAO,CAAC;AACnB,UAAM,SAAc,EAAE,MAAM,KAAK;AACjC,QAAI,eAAgB,QAAO,kBAAkB;AAC7C,QAAI,OAAc,CAAC;AACnB,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK,cAAc,EAAE,QAAQ,QAAQ,CAAC,SAAS,GAAG,OAAO,KAAO,SAASD,YAAW,CAAQ;AAAA,IACvH,QAAQ;AAAE,aAAO,CAAC;AAAA,IAAG;AACrB,WAAO,MAAM,KAAK,IAAI,KAAK,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAW,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,OAAO,CAAC,CAAC;AAAA,EAClG;AAAA,EAEA,MAAc,cAAc,QAAwC;AAClE,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,OAAO,KAAK,YAAY;AAAA,QAC9C,OAAO,EAAE,IAAI,OAAO;AAAA,QAAG,QAAQ,CAAC,MAAM,YAAY;AAAA,QAAG,OAAO;AAAA,QAAG,SAASA;AAAA,MAC1E,CAAQ;AACR,YAAM,MAAW,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AACjD,aAAO,KAAK,aAAa,OAAO,IAAI,UAAU,IAAI;AAAA,IACpD,QAAQ;AAAE,aAAO;AAAA,IAAM;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAc,mBACZ,QACA,KACA,gBACA,WACmB;AACnB,UAAM,QAAQ,OAAO,UAAU,EAAE,EAAE,KAAK;AACxC,QAAI,CAAC,MAAO,QAAO,CAAC;AACpB,QAAI,UAAU;AACd,UAAM,UAAU,oBAAI,IAAY,CAAC,OAAO,CAAC;AACzC,aAAS,MAAM,GAAG,MAAM,eAAe,OAAO;AAC5C,YAAM,MAAM,MAAM,KAAK,uBAAuB,SAAS,KAAK,cAAc;AAC1E,UAAI,CAAC,IAAK;AACV,YAAM,KAAK,OAAO,IAAI,eAAe,EAAE,EAAE,KAAK;AAC9C,UAAI,CAAC,MAAM,OAAO,WAAW,QAAQ,IAAI,EAAE,EAAG;AAC9C,iBAAW,KAAK,EAAE,MAAM,SAAS,IAAI,QAAQ,IAAI,UAAU,OAAO,OAAO,IAAI,MAAM,IAAI,KAAK,CAAC;AAC7F,cAAQ,IAAI,EAAE;AACd,gBAAU;AAAA,IACZ;AACA,WAAO,CAAC,OAAO;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,uBACZ,aACA,KACA,gBACqB;AACrB,QAAI,CAAC,YAAa,QAAO;AACzB,QAAI,OAAc,CAAC;AACnB,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK,2BAA2B;AAAA,QACvD,OAAO,EAAE,cAAc,YAAY;AAAA,QACnC,QAAQ,CAAC,MAAM,gBAAgB,eAAe,cAAc,eAAe,UAAU,iBAAiB;AAAA,QACtG,OAAO;AAAA,QACP,SAASA;AAAA,MACX,CAAQ;AAAA,IACV,QAAQ;AAAE,aAAO;AAAA,IAAM;AACvB,UAAM,UAAU,QAAQ,CAAC,GAAG,OAAO,CAAC,MAClC,cAAc,GAAG,GAAG,MAEhB,kBAAkB,QAAQ,EAAE,mBAAmB,QAAQ,OAAO,EAAE,eAAe,MAAM,OAAO,cAAc,EAAE;AAClH,QAAI,CAAC,OAAO,OAAQ,QAAO;AAC3B,WAAO,KAAK,CAAC,GAAQ,MAAW;AAC9B,YAAM,KAAK,EAAE,cAAc,KAAK,MAAM,OAAO,EAAE,WAAW,CAAC,IAAI,OAAO;AACtE,YAAM,KAAK,EAAE,cAAc,KAAK,MAAM,OAAO,EAAE,WAAW,CAAC,IAAI,OAAO;AACtE,aAAO,KAAK;AAAA,IACd,CAAC;AACD,WAAO,OAAO,CAAC;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoCA,MAAc,kBACZ,QACA,UACA,OACA,QACA,SACe;AACf,QAAI;AACF,YAAM,UAAU,UAAU,EAAE,GAAGA,aAAY,QAAQ,QAAQ,IAAIA;AAC/D,YAAM,KAAK,OAAO,OAAO,QAAQ,EAAE,IAAI,UAAU,CAAC,KAAK,GAAG,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,IACjF,SAAS,KAAU;AACjB,WAAK,QAAQ,OAAO,yCAAyC,KAAK,WAAW,GAAG,EAAE;AAAA,IACpF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAc,eAAe,QAAgB,UAAkB,UAA8B;AAC3F,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,OAAO,KAAK,QAAQ;AAAA,QAC1C,OAAO,EAAE,IAAI,SAAS;AAAA,QAAG,OAAO;AAAA,QAAG,SAASA;AAAA,MAC9C,CAAQ;AACR,YAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AAC7C,UAAI,KAAM,QAAO;AACjB,WAAK,QAAQ;AAAA,QACX,2BAA2B,MAAM,IAAI,QAAQ;AAAA,QAE7C,EAAE,QAAQ,SAAS;AAAA,MACrB;AAAA,IACF,SAAS,KAAU;AACjB,WAAK,QAAQ;AAAA,QACX,8CAA8C,MAAM,IAAI,QAAQ,KAAK,KAAK,WAAW,GAAG;AAAA,MAE1F;AAAA,IACF;AACA,WAAO,YAAY,CAAC;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,gBACJ,OAqBA,SACuD;AACvD,QAAI,CAAC,MAAM,OAAQ,OAAM,IAAI,MAAM,uCAAuC;AAC1E,QAAI,CAAC,MAAM,SAAU,OAAM,IAAI,MAAM,yCAAyC;AAC9E,QAAI,CAAC,MAAM,MAAO,OAAM,IAAI,MAAM,sCAAsC;AAGxE,UAAM,WAAW,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,MAC9D,OAAO,EAAE,aAAa,MAAM,QAAQ,WAAW,MAAM,UAAU,QAAQ,UAAU;AAAA,MACjF,OAAO;AAAA,MAAG,SAASA;AAAA,IACrB,CAAC;AACD,QAAI,MAAM,QAAQ,QAAQ,KAAK,SAAS,CAAC,GAAG;AAC1C,YAAM,IAAI,MAAM,4DAA4D,MAAM,MAAM,IAAI,MAAM,QAAQ,EAAE;AAAA,IAC9G;AAGA,UAAM,SAAU,SAAiB,kBAAkB,SAAS,YAAY,MAAM,kBAAkB;AAChG,UAAM,UAAU,KAAK,MAAM,IAAI;AAG/B,UAAM,gBAAmC,CAAC;AAI1C,UAAM,SAAmC,CAAC;AAI1C,UAAM,aAAa,MAAM,KAAK,eAAe,MAAM,QAAQ,MAAM,UAAU,MAAM,MAAM;AACvF,UAAM,eAAwC,CAAC;AAC/C,UAAM,YAAY,MAAM,KAAK;AAAA,MAC3B,EAAE,WAAW,MAAM,OAAO,UAAU;AAAA,MAAG;AAAA,MAAY;AAAA,MAAQ;AAAA,QACzD,KAAK,QAAQ,QAAQ;AAAA,QAAG;AAAA,QAAe;AAAA,QACvC,SAAS,EAAE,SAAS,MAAM,UAAU,MAAM,MAAM,MAAM,aAAa,KAAK;AAAA,QACxE;AAAA,MACF;AAAA,IACF;AAKA,QAAI,CAAC,UAAU,KAAK,OAAK,KAAK,CAAC,EAAE,SAAS,GAAG,CAAC,GAAG;AAC/C,YAAM,cAAe,MAAM,OAAe,oBAAoB;AAC9D,UAAI,gBAAgB,QAAQ;AAC1B,cAAM,IAAI;AAAA,UACR,gCAAgC,MAAM,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,QAAQ;AAAA,QAGpF;AAAA,MACF;AACA,UAAI,gBAAgB,gBAAgB;AAClC,aAAK,QAAQ;AAAA,UACX,8BAA8B,MAAM,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,QAAQ;AAAA,UAEhF,EAAE,QAAQ,MAAM,QAAQ,UAAU,MAAM,UAAU,MAAM,MAAM,QAAQ,UAAU,UAAU;AAAA,QAC5F;AACA,eAAO,EAAE,cAAc,MAAM,QAAQ,kBAAkB;AAAA,MACzD;AAMA,WAAK,QAAQ;AAAA,QACX,8BAA8B,MAAM,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,QAAQ;AAAA,QAEhF,EAAE,QAAQ,MAAM,QAAQ,UAAU,MAAM,UAAU,MAAM,MAAM,QAAQ,UAAU,UAAU;AAAA,MAC5F;AAAA,IACF;AAEA,UAAM,MAAM,QAAQ,YAAY;AAChC,UAAM,KAAK,IAAI,MAAM;AACrB,UAAM,cAAc,QAAQ,MAAM,YAAY,MAAM,MAAM;AAG1D,UAAM,iBAAsB,EAAE,GAAG,MAAM,OAAO;AAC9C,QAAI,MAAM,UAAW,gBAAe,cAAc,MAAM;AACxD,QAAI,MAAM,UAAW,gBAAe,cAAc,MAAM;AAQxD,QAAI,MAAM,OAAO,YAAY,MAAM,OAAO,aAAa,kBAAkB;AACvE,qBAAe,mBAAmB;AAAA,IACpC;AAKA,QAAI,OAAO,KAAK,YAAY,EAAE,QAAQ;AACpC,qBAAe,iBAAiB;AAAA,IAClC;AAIA,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,QAC3D,OAAO,EAAE,aAAa,MAAM,OAAO,cAAc,MAAM,OAAO;AAAA,QAAG,OAAO;AAAA,QAAK,SAASA;AAAA,MACxF,CAAC;AACD,YAAM,IAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS;AAChD,UAAI,IAAI,EAAG,gBAAe,UAAU,IAAI;AAAA,IAC1C,QAAQ;AAAA,IAAqC;AAC7C,UAAM,MAAW;AAAA,MACf;AAAA,MACA,cAAc;AAAA,MACd,aAAa,MAAM;AAAA,MACnB,WAAW,MAAM;AAAA,MACjB,cAAc,MAAM,eAAe,QAAQ,UAAU;AAAA,MACrD,QAAQ;AAAA,MACR,cAAc,MAAM;AAAA,MACpB,oBAAoB;AAAA,MACpB,mBAAmB,UAAU,KAAK,GAAG;AAAA,MACrC,cAAc,MAAM,UAAU,OAAO,KAAK,UAAU,MAAM,MAAM,IAAI;AAAA,MACpE,aAAa,MAAM;AAAA,MACnB,cAAc,MAAM;AAAA,MACpB,kBAAkB,KAAK,UAAU,cAAc;AAAA,MAC/C,iBAAiB;AAAA,MACjB,YAAY;AAAA,MACZ,YAAY;AAAA,IACd;AACA,UAAM,KAAK,OAAO,OAAO,wBAAwB,KAAK,EAAE,SAASA,YAAW,CAAC;AAC7E,UAAM,KAAK,kBAAkB,IAAI,WAAW,QAAQ,GAAG;AACvD,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY;AAAA,MAAI,iBAAiB;AAAA,MAClD,WAAW,MAAM;AAAA,MAAQ,YAAY;AAAA,MAAG,QAAQ;AAAA,MAChD,UAAU,MAAM,eAAe,QAAQ,UAAU;AAAA,MAAM,SAAS;AAAA,MAAM,YAAY;AAAA,IACpF,GAAG,EAAE,SAASA,YAAW,CAAC;AAM1B,eAAW,OAAO,eAAe;AAC/B,YAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,QAC9C,IAAI,IAAI,MAAM;AAAA,QAAG,YAAY;AAAA,QAAI,iBAAiB;AAAA,QAClD,WAAW,MAAM;AAAA,QAAQ,YAAY;AAAA,QAAG,QAAQ;AAAA,QAChD,UAAU;AAAA,QACV,SAAS,GAAG,IAAI,IAAI,WAAM,IAAI,EAAE,GAAG,IAAI,SAAS,WAAM,IAAI,MAAM,KAAK,EAAE;AAAA,QACvE,YAAY;AAAA,MACd,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B,YAAM,KAAK,OAAO;AAAA,QAChB,OAAO;AAAA,QACP,UAAU,CAAC,IAAI,EAAE;AAAA,QACjB,QAAQ,EAAE,QAAQ,wBAAwB,GAAG;AAAA,QAC7C,UAAU,gBAAgB,EAAE,IAAI,IAAI,EAAE;AAAA,QACtC,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,mCAAmC,MAAM,MAAM,IAAI,MAAM,QAAQ,UAAU,IAAI,IAAI;AAAA,UAC5F,WAAW;AAAA,QACb;AAAA,MACF,CAAC;AACD,YAAM,KAAK,OAAO;AAAA,QAChB,OAAO;AAAA,QACP,UAAU,CAAC,IAAI,IAAI;AAAA,QACnB,QAAQ,EAAE,QAAQ,wBAAwB,GAAG;AAAA,QAC7C,UAAU,qBAAqB,EAAE,IAAI,IAAI,IAAI;AAAA,QAC7C,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,kBAAkB,MAAM,MAAM,IAAI,MAAM,QAAQ,kBAAkB,IAAI,EAAE;AAAA,UACjF,WAAW;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH;AAIA,QAAI,MAAM,OAAO,qBAAqB;AAQpC,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QAAQ,MAAM;AAAA,QAAU,MAAM,OAAO;AAAA,QAAqB;AAAA,QAChE,IAAI,gBAAgB;AAAA,MACtB;AAAA,IACF;AAEA,WAAO,eAAe,GAAG;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,oBACN,UACA,QACA,UACA,UACA,UACS;AACT,QAAI,aAAa,aAAa;AAC5B,aAAO,SAAS,SAAS,KAAK,SAAS,MAAM,OAAK,SAAS,IAAI,CAAC,CAAC;AAAA,IACnE;AACA,QAAI,aAAa,UAAU;AACzB,YAAM,IAAI,SAAS,UAAU;AAC7B,YAAM,OAAO,KAAK,IAAI,KAAK,IAAI,GAAG,OAAO,gBAAgB,CAAC,GAAG,CAAC;AAE9D,aAAO,SAAS,QAAQ;AAAA,IAC1B;AACA,QAAI,aAAa,aAAa;AAC5B,YAAM,eAAe,KAAK,IAAI,GAAG,OAAO,gBAAgB,CAAC;AACzD,YAAM,OAA+B,CAAC;AACtC,iBAAW,MAAM,OAAO,OAAO,QAAQ,EAAG,YAAW,KAAK,GAAI,MAAK,CAAC,KAAK,KAAK,CAAC,KAAK,KAAK;AACzF,YAAM,SAAS,OAAO,KAAK,IAAI;AAC/B,UAAI,CAAC,OAAO,OAAQ,QAAO;AAC3B,YAAM,MAA8B,CAAC;AACrC,iBAAW,KAAK,SAAU,YAAW,KAAM,SAAS,CAAC,KAAK,CAAC,EAAI,KAAI,CAAC,KAAK,IAAI,CAAC,KAAK,KAAK;AACxF,aAAO,OAAO,MAAM,QAAM,IAAI,CAAC,KAAK,MAAM,KAAK,IAAI,cAAc,KAAK,CAAC,CAAC,CAAC;AAAA,IAC3E;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,WACJ,WACA,OACA,SAC8K;AAC9K,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,0CAA0C;AAC1E,UAAM,UAAU,MAAM,KAAK,aAAa,OAAO,SAAS,OAAO;AAC/D,QAAI,MAAM,aAAa,aAAa,MAAM,aAAa,UAAU;AAC/D,YAAM,IAAI,MAAM,oDAAoD;AAAA,IACtE;AAGA,UAAM,UAAU,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,MAC7D,OAAO,EAAE,IAAI,UAAU;AAAA,MAAG,OAAO;AAAA,MAAG,SAASA;AAAA,IAC/C,CAAC;AACD,UAAM,MAAW,MAAM,QAAQ,OAAO,IAAI,QAAQ,CAAC,IAAI;AACvD,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sBAAsB,SAAS,EAAE;AAC3D,QAAI,IAAI,WAAW,UAAW,OAAM,IAAI,MAAM,6BAA6B,IAAI,MAAM,EAAE;AAEvF,UAAM,mBAAmB,SAAS,IAAI,iBAAiB;AAIvD,UAAM,aAAa,KAAK,gBAAgB,SAAS,IAAI,mBAAmB,IAAI;AAC5E,UAAM,eAAe,iBAAiB,SAAS,OAAO;AACtD,QAAI,CAAC,gBAAgB,CAAC,YAAY;AAChC,YAAM,IAAI,MAAM,qBAAqB,OAAO,6BAA6B;AAAA,IAC3E;AAKA,UAAM,cAAc,cAAc,CAAC;AAEnC,UAAM,SAAS,UAA8B,IAAI,kBAAkB,EAAE,WAAW,CAAC,GAAG,UAAU,iBAAiB,CAAQ;AACvH,UAAM,MAAM,IAAI,mBAAmB;AACnC,UAAM,SAAwB,IAAI,gBAAgB,IAAI,gBAAgB;AACtE,UAAM,QAAuB,IAAI,eAAe;AAChD,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,YAAY;AAOzC,UAAM,aAAa,MAAM,UAAU,OAAO,KAAK,MAAM,OAAO,IAAI,CAAC;AACjE,QAAI;AAGJ,UAAM,eAAe,yBAA0B,OAAe,eAAe;AAC7E,QAAI,WAAW,QAAQ;AACrB,YAAM,WAAW,aAAa,IAAI,OAAK,EAAE,GAAG;AAC5C,UAAI,CAAC,SAAS,QAAQ;AACpB,cAAM,IAAI;AAAA,UACR,6JAC4D,WAAW,IAAI,OAAK,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA,QAEtG;AAAA,MACF;AACA,YAAM,WAAW,WAAW,OAAO,OAAK,MAAM,cAAc,MAAM,WAAW;AAC7E,UAAI,SAAS,QAAQ;AACnB,cAAM,IAAI;AAAA,UACR,+CAA+C,SAAS,KAAK,MAAM,CAAC;AAAA,QAEtE;AAAA,MACF;AACA,YAAM,aAAa,WAAW,OAAO,OAAK,CAAC,SAAS,SAAS,CAAC,CAAC;AAC/D,UAAI,WAAW,QAAQ;AACrB,cAAM,IAAI;AAAA,UACR,+CAA+C,WAAW,KAAK,MAAM,CAAC,0DAC3C,SAAS,IAAI,OAAK,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,KAAK,QAAQ;AAAA,QAC/E;AAAA,MACF;AACA,wBAAkB,EAAE,GAAG,MAAM,QAAQ;AAAA,IACvC;AAmBA,QAAI,MAAM,aAAa,WAAW;AAChC,YAAM,UAAU,aACb,OAAO,OAAK,EAAE,aAAa,QAAQ,sBAAsB,MAAM,UAAU,EAAE,GAAG,CAAC,CAAC,EAChF,IAAI,OAAK,EAAE,GAAG;AACjB,UAAI,QAAQ,QAAQ;AAClB,cAAM,IAAI;AAAA,UACR,2CAA2C,QAAQ,KAAK,MAAM,CAAC;AAAA,QAEjE;AAAA,MACF;AAAA,IACF;AASA,UAAM,KAAK,mBAAmB,OAAO,SAAS;AAG9C,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY;AAAA,MAAW,iBAAiB;AAAA,MACzD,WAAW;AAAA,MAAQ,YAAY;AAAA,MAAG,QAAQ,MAAM;AAAA,MAChD,UAAU;AAAA,MAAS,SAAS,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA,MAI7C,cAAc;AAAA,MACd,aAAa,MAAM,aAAa,SAAS,MAAM,cAAc;AAAA,MAC7D,YAAY;AAAA,IACd,GAAG,EAAE,SAASA,YAAW,CAAC;AAK1B,UAAM,WAAW,OAAO,YAAY;AAMpC,QAAI,MAAM,aAAa,aAAa,aAAa,oBAAoB,cAAc;AACjF,YAAM,OAAO,MAAM,KAAK,OAAO,KAAK,uBAAuB;AAAA,QACzD,OAAO,EAAE,YAAY,WAAW,YAAY,GAAG,QAAQ,UAAU;AAAA,QAAG,OAAO;AAAA,QAAM,SAASA;AAAA,MAC5F,CAAC;AACD,YAAM,WAAW,IAAI,KAAa,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAW,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,OAAO,CAAC;AAQvG,YAAM,iBAAkB,OAAe;AACvC,UAAI;AACJ,UAAI;AACJ,UAAI,gBAAgB;AAClB,mBAAW;AACX,mBAAW,OAAO,KAAK,cAAc;AAAA,MACvC,OAAO;AACL,mBAAW,MAAM,KAAK;AAAA,UACpB,EAAE,WAAW,OAAO,UAAU;AAAA,UAAG,UAAU,IAAI,cAAc,MAAS;AAAA,UAAG;AAAA,QAC3E;AACA,mBAAW,CAAC;AAAA,MACd;AAEA,UAAI,CAAC,KAAK,oBAAoB,UAAU,QAAQ,UAAU,UAAU,QAAQ,GAAG;AAC7E,cAAM,eAAe,SAAS,OAAO,OAAK,CAAC,SAAS,IAAI,CAAC,CAAC;AAC1D,cAAM,KAAK,OAAO,OAAO,wBAAwB;AAAA,UAC/C,IAAI;AAAA,UAAW,mBAAmB,aAAa,KAAK,GAAG;AAAA,UAAG,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,UAKtE,GAAI,kBAAkB;AAAA,YACpB,kBAAkB,KAAK,UAAU;AAAA,cAC/B,GAAG;AAAA,cACH,mBAAmB,EAAE,GAAK,OAAe,qBAAqB,CAAC,GAAI,GAAG,gBAAgB;AAAA,YACxF,CAAC;AAAA,UACH,IAAI,CAAC;AAAA,QACP,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B,cAAM,KAAK,kBAAkB,WAAW,cAAc,KAAK,GAAG;AAC9D,cAAMG,SAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO;AAC3D,eAAO,EAAE,SAASA,QAAQ,OAAO,QAAQ,WAAW,OAAO,UAAU,MAAM,SAAS;AAAA,MACtF;AAAA,IACF;AAEA,UAAM,cAAc,MAAM,aAAa,YAAY,aAAa;AAIhE,UAAM,gBACJ,mBAAoB,OAAe,oBAC/B,EAAE,GAAK,OAAe,qBAAqB,CAAC,GAAI,GAAI,mBAAmB,CAAC,EAAG,IAC3E;AACN,UAAM,KAAK,OAAO,OAAO,wBAAwB;AAAA,MAC/C,IAAI;AAAA,MAAW,QAAQ;AAAA,MAAa,mBAAmB;AAAA,MAAM,cAAc;AAAA,MAAK,YAAY;AAAA,MAC5F,GAAI,gBAAgB;AAAA,QAClB,kBAAkB,KAAK,UAAU,EAAE,GAAG,QAAQ,mBAAmB,cAAc,CAAC;AAAA,MAClF,IAAI,CAAC;AAAA,IACP,GAAG,EAAE,SAASH,YAAW,CAAC;AAC1B,UAAM,KAAK,kBAAkB,WAAW,CAAC,GAAG,KAAK,GAAG;AACpD,QAAI,OAAO,qBAAqB;AAC9B,YAAM,KAAK;AAAA,QACT,IAAI;AAAA,QAAa,IAAI;AAAA,QAAW,OAAO;AAAA,QAAqB;AAAA,QAC5D,aAAa,OAAO;AAAA,MACtB;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO;AAC3D,WAAO,EAAE,SAAS,OAAQ,OAAO,QAAQ,WAAW,MAAM,UAAU,MAAM,UAAU,SAAS,cAAc;AAAA,EAC7G;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,MAAc,cACZ,OACA,QACe;AACf,UAAM,SAAS,MAAM,KAAK,WAAY,OAAQ,OAAO,EAAE,GAAG,QAAQ,CAAC,wBAAwB,GAAG,KAAK,CAAC;AACpG,UAAM,WAAW;AAIjB,QAAI,YAAY,OAAO,aAAa,YAAY,SAAS,YAAY,OAAO;AAC1E,YAAM,MAAM,IAAI;AAAA,QACd,kBAAkB,KAAK,WAAW,SAAS,OAAO,KAAK,SAAS,IAAI,MAAM,EAAE,KAAK,SAAS,SAAS,eAAe;AAAA,MACpH;AACA,UAAI,aAAa,SAAS;AAC1B,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA,EAGA,OAAe,aAAa,KAAkC;AAC5D,WAAQ,KAA6C;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAc,mBAAmB,OAAkC,WAAkC;AACnG,QAAI,CAAC,MAAO;AACZ,QAAI,OAAO,KAAK,YAAY,WAAW,WAAY;AACnD,QAAI,OAAO,KAAK,YAAY,oBAAoB,WAAY;AAC5D,QAAI;AACJ,QAAI;AACF,cAAQ,MAAM,KAAK,WAAW,gBAAgB,KAAK;AAAA,IACrD,SAAS,KAAU;AACjB,WAAK,QAAQ,OAAO,4EAAuE;AAAA,QACzF,SAAS;AAAA,QAAW,KAAK;AAAA,QAAO,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,MACnE,CAAC;AACD;AAAA,IACF;AACA,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR,qCAAqC,KAAK,oBAAoB,SAAS;AAAA,MAGzE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkCQ,qBACN,OACA,WACA,MACA,YACoB;AACpB,UAAM,KAAK,eAAe,WAAW,KAAK,YAAY,SAAS,KAAK,YAAY;AAChF,QAAI,OAAO,OAAO,WAAY,QAAO;AACrC,SAAK,QAAQ;AAAA,MACX;AAAA,MACA,EAAE,SAAS,WAAW,KAAK,OAAO,SAAS,MAAM,WAAW;AAAA,IAC9D;AACA,WACE,GAAG,UAAU,YAAY,KAAK,6BAA6B,IAAI,4BAA4B,SAAS,kDACpD,UAAU;AAAA,EAI9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAc,sBACZ,OACA,WACA,MACA,QACqD;AACrD,UAAM,UAAU,KAAK,qBAAqB,OAAO,WAAW,MAAM,QAAQ;AAC1E,QAAI,QAAS,QAAO,EAAE,SAAS,OAAO,aAAa,QAAQ;AAC3D,QAAI;AACF,YAAM,KAAK,cAAc,OAAO,MAAM;AACtC,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB,SAAS,KAAU;AACjB,YAAM,SAAS,KAAK,WAAW,OAAO,GAAG;AACzC,UAAI,iBAAgB,aAAa,GAAG,MAAM,sBAAsB;AAC9D,aAAK,QAAQ,OAAO,yDAAoD;AAAA,UACtE,SAAS;AAAA,UAAW,KAAK;AAAA,UAAO,SAAS;AAAA,QAC3C,CAAC;AACD,eAAO,EAAE,SAAS,OAAO,aAAa,OAAO;AAAA,MAC/C;AACA,WAAK,QAAQ,QAAQ,wDAAmD;AAAA,QACtE,SAAS;AAAA,QAAW,KAAK;AAAA,QAAO,SAAS;AAAA,QAAM,OAAO;AAAA,MACxD,CAAC;AACD,YAAM,IAAI;AAAA,QACR,kBAAkB,IAAI,4BAA4B,SAAS,uBAAuB,KAAK,+CAC1C,MAAM;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,OACJ,WACA,OACA,SACiC;AACjC,UAAM,SAAS,MAAM,KAAK,WAAW,WAAW,OAAO,OAAO;AAE9D,QAAI,UAAU;AACd,QAAI;AAKJ,QAAI,OAAO,aAAa,OAAO,OAAO;AACpC,YAAM,cAAc,OAAO,aAAa,YACpC,uBAAuB,UACvB,uBAAuB;AAC3B,YAAM,UAAU,MAAM,KAAK;AAAA,QACzB,OAAO;AAAA,QAAO;AAAA,QAAW,OAAO,OAAO,QAAQ;AAAA,QAC/C;AAAA,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMA,QAAQ,EAAE,GAAI,OAAO,WAAW,CAAC,GAAI,UAAU,OAAO,UAAU,UAAU;AAAA,QAC5E;AAAA,MACF;AACA,gBAAU,QAAQ;AAClB,oBAAc,QAAQ;AAAA,IACxB;AAEA,WAAO;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,WAAW,OAAO;AAAA,MAClB,UAAU,OAAO;AAAA,MACjB,OAAO,OAAO;AAAA,MACd;AAAA,MACA,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,OACJ,WACA,OACA,SAC+B;AAC/B,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,0CAA0C;AAC1E,UAAM,UAAU,MAAM,KAAK,aAAa,OAAO,SAAS,OAAO;AAE/D,UAAM,UAAU,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,MAC7D,OAAO,EAAE,IAAI,UAAU;AAAA,MAAG,OAAO;AAAA,MAAG,SAASA;AAAA,IAC/C,CAAC;AACD,UAAM,MAAW,MAAM,QAAQ,OAAO,IAAI,QAAQ,CAAC,IAAI;AACvD,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sBAAsB,SAAS,EAAE;AAC3D,UAAM,iBAAiB,IAAI,WAAW;AACtC,QAAI,IAAI,WAAW,aAAa,CAAC,gBAAgB;AAC/C,YAAM,IAAI,MAAM,6BAA6B,IAAI,MAAM,EAAE;AAAA,IAC3D;AAGA,QAAI,CAAC,KAAK,gBAAgB,SAAS,IAAI,mBAAmB,IAAI,KACzD,IAAI,gBAAgB,OAAO,IAAI,YAAY,MAAM,OAAO,OAAO,GAAG;AACrE,YAAM,IAAI,MAAM,uDAAuD;AAAA,IACzE;AAGA,QAAI,eAAgB,OAAM,KAAK,mBAAmB,GAAG;AAErD,UAAM,SAAS,UAA8B,IAAI,kBAAkB,EAAE,WAAW,CAAC,GAAG,UAAU,iBAAiB,CAAQ;AACvH,UAAM,MAAM,IAAI,mBAAmB;AACnC,UAAM,SAAwB,IAAI,gBAAgB,IAAI,gBAAgB;AACtE,UAAM,QAAuB,IAAI,eAAe;AAChD,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,YAAY;AAEzC,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY;AAAA,MAAW,iBAAiB;AAAA,MACzD,WAAW;AAAA,MAAQ,YAAY;AAAA,MAAG,QAAQ;AAAA,MAC1C,UAAU;AAAA,MAAS,SAAS,MAAM,WAAW;AAAA,MAAM,YAAY;AAAA,IACjE,GAAG,EAAE,SAASA,YAAW,CAAC;AAE1B,UAAM,KAAK,OAAO,OAAO,wBAAwB;AAAA,MAC/C,IAAI;AAAA,MAAW,QAAQ;AAAA,MAAY,mBAAmB;AAAA,MAAM,cAAc;AAAA,MAAK,YAAY;AAAA,IAC7F,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B,UAAM,KAAK,kBAAkB,WAAW,CAAC,GAAG,KAAK,GAAG;AACpD,QAAI,OAAO,qBAAqB;AAC9B,YAAM,KAAK;AAAA,QACT,IAAI;AAAA,QAAa,IAAI;AAAA,QAAW,OAAO;AAAA,QAAqB;AAAA,QAC5D,aAAa,OAAO;AAAA,MACtB;AAAA,IACF;AAMA,QAAI,UAAU;AACd,QAAI;AACJ,QAAI,gBAAgB;AAGlB,UAAI,OAAO;AACT,sBAAc,KAAK,qBAAqB,OAAO,WAAW,cAAc,WAAW;AACnF,YAAI,CAAC,aAAa;AAChB,cAAI;AACF,kBAAM,KAAK,WAAY,UAAW,OAAO,oBAAoB,SAAS,2BAA2B;AAAA,UACnG,SAAS,KAAU;AACjB,0BAAc,KAAK,WAAW,OAAO,GAAG;AACxC,iBAAK,QAAQ,QAAQ,0FAAqF;AAAA,cACxG,SAAS;AAAA,cAAW,KAAK;AAAA,cAAO,OAAO;AAAA,YACzC,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAAW,OAAO;AAChB,oBAAc,KAAK,qBAAqB,OAAO,WAAW,cAAc,QAAQ;AAChF,UAAI,CAAC,aAAa;AAChB,YAAI;AACF,gBAAM,KAAK,cAAc,OAAO;AAAA,YAC9B,aAAa,uBAAuB;AAAA,YACpC,QAAQ,EAAE,UAAU,UAAU,UAAU;AAAA,UAC1C,CAAC;AACD,oBAAU;AAAA,QACZ,SAAS,KAAU;AACjB,wBAAc,KAAK,WAAW,OAAO,GAAG;AACxC,eAAK,QAAQ,QAAQ,yEAAoE;AAAA,YACvF,SAAS;AAAA,YAAW,KAAK;AAAA,YAAO,OAAO;AAAA,UACzC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO;AAC3D,WAAO,EAAE,SAAS,OAAQ,OAAO,SAAS,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC,EAAG;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,SACJ,WACA,OACA,SACiC;AACjC,UAAM,UAAU,MAAM,KAAK,aAAa,OAAO,SAAS,OAAO;AAC/D,UAAM,MAAM,MAAM,KAAK,eAAe,SAAS;AAC/C,UAAM,UAAU,SAAS,IAAI,iBAAiB;AAC9C,QAAI,CAAC,QAAQ,YAAY,CAAC,QAAQ,SAAS,OAAO,GAAG;AACnD,YAAM,IAAI,MAAM,qBAAqB,OAAO,6BAA6B;AAAA,IAC3E;AAEA,UAAM,SAAS,UAA8B,IAAI,kBAAkB,EAAE,WAAW,CAAC,GAAG,UAAU,iBAAiB,CAAQ;AACvH,UAAM,MAAM,IAAI,mBAAmB;AACnC,UAAM,SAAwB,IAAI,gBAAgB,IAAI,gBAAgB;AACtE,UAAM,QAAuB,IAAI,eAAe;AAEhD,UAAM,KAAK,iBAAiB,KAAK,MAAM;AAIvC,UAAM,KAAK,mBAAmB,OAAO,SAAS;AAE9C,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,YAAY;AACzC,UAAM,eAAe,OAAQ,OAAe,iBAAiB,WAAY,OAAe,eAAe;AACvG,QAAI,iBAAiB;AACrB,QAAI,SAAS,QAAQ;AACnB,YAAM,WAAW,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,QAC9D,OAAO,EAAE,aAAa,OAAO,cAAc,QAAQ,QAAQ,WAAW;AAAA,QAAG,OAAO;AAAA,QAAK,SAASA;AAAA,MAChG,CAAC;AACD,uBAAiB,MAAM,QAAQ,QAAQ,IAAI,SAAS,SAAS;AAAA,IAC/D;AAKA,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY;AAAA,MAAW,iBAAiB;AAAA,MACzD,WAAW;AAAA,MAAQ,YAAY;AAAA,MAAG,QAAQ;AAAA,MAC1C,UAAU;AAAA,MAAS,SAAS,MAAM,WAAW;AAAA,MAAM,YAAY;AAAA,IACjE,GAAG,EAAE,SAASA,YAAW,CAAC;AAE1B,QAAI,kBAAkB,cAAc;AAElC,YAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,QAC9C,IAAI,IAAI,MAAM;AAAA,QAAG,YAAY;AAAA,QAAW,iBAAiB;AAAA,QACzD,WAAW;AAAA,QAAQ,YAAY;AAAA,QAAG,QAAQ;AAAA,QAC1C,UAAU;AAAA,QACV,SAAS,kCAAkC,YAAY;AAAA,QAAc,YAAY;AAAA,MACnF,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B,YAAM,KAAK,OAAO,OAAO,wBAAwB;AAAA,QAC/C,IAAI;AAAA,QAAW,QAAQ;AAAA,QAAY,mBAAmB;AAAA,QAAM,cAAc;AAAA,QAAK,YAAY;AAAA,MAC7F,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B,YAAM,KAAK,kBAAkB,WAAW,CAAC,GAAG,KAAK,GAAG;AACpD,UAAI,OAAO,qBAAqB;AAC9B,cAAM,KAAK;AAAA,UACT,IAAI;AAAA,UAAa,IAAI;AAAA,UAAW,OAAO;AAAA,UAAqB;AAAA,UAC5D,aAAa,OAAO;AAAA,QACtB;AAAA,MACF;AACA,UAAII,WAAU;AACd,UAAIC;AACJ,UAAI,OAAO;AACT,cAAM,UAAU,MAAM,KAAK;AAAA,UACzB;AAAA,UAAO;AAAA,UAAW;AAAA,UAClB;AAAA,YACE,aAAa,uBAAuB;AAAA,YACpC,QAAQ,EAAE,UAAU,UAAU,cAAc,MAAM,UAAU;AAAA,UAC9D;AAAA,QACF;AACA,QAAAD,WAAU,QAAQ;AAClB,QAAAC,eAAc,QAAQ;AAAA,MACxB;AACA,UAAI,IAAI,cAAc;AACpB,cAAM,KAAK,OAAO;AAAA,UAChB,OAAO;AAAA,UACP,UAAU,CAAC,OAAO,IAAI,YAAY,CAAC;AAAA,UACnC;AAAA,UACA,QAAQ,EAAE,QAAQ,wBAAwB,IAAI,UAAU;AAAA,UACxD,SAAS;AAAA,YACP,OAAO;AAAA,YACP,SAAS,QAAQ,IAAI,WAAW,IAAI,IAAI,SAAS,iCAAiC,YAAY;AAAA,YAC9F,WAAW;AAAA,UACb;AAAA,QACF,CAAC;AAAA,MACH;AACA,YAAMF,SAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO;AAC3D,aAAO,EAAE,SAASA,QAAQ,OAAO,SAAAC,UAAS,cAAc,MAAM,GAAIC,eAAc,EAAE,aAAAA,aAAY,IAAI,CAAC,EAAG;AAAA,IACxG;AAEA,UAAM,KAAK,OAAO,OAAO,wBAAwB;AAAA,MAC/C,IAAI;AAAA,MAAW,QAAQ;AAAA,MAAY,mBAAmB;AAAA,MAAM,cAAc;AAAA,MAAK,YAAY;AAAA,IAC7F,GAAG,EAAE,SAASL,YAAW,CAAC;AAC1B,UAAM,KAAK,kBAAkB,WAAW,CAAC,GAAG,KAAK,GAAG;AACpD,QAAI,OAAO,qBAAqB;AAC9B,YAAM,KAAK;AAAA,QACT,IAAI;AAAA,QAAa,IAAI;AAAA,QAAW,OAAO;AAAA,QAAqB;AAAA,QAC5D,aAAa,OAAO;AAAA,MACtB;AAAA,IACF;AAEA,QAAI,UAAU;AACd,QAAI;AACJ,QAAI,OAAO;AACT,YAAM,UAAU,MAAM,KAAK;AAAA,QACzB;AAAA,QAAO;AAAA,QAAW;AAAA,QAClB;AAAA,UACE,aAAa,uBAAuB;AAAA,UACpC,QAAQ,EAAE,UAAU,UAAU,UAAU;AAAA,QAC1C;AAAA,MACF;AACA,gBAAU,QAAQ;AAClB,oBAAc,QAAQ;AAAA,IACxB;AAEA,QAAI,IAAI,cAAc;AACpB,YAAM,KAAK,OAAO;AAAA,QAChB,OAAO;AAAA,QACP,UAAU,CAAC,OAAO,IAAI,YAAY,CAAC;AAAA,QACnC;AAAA,QACA,QAAQ,EAAE,QAAQ,wBAAwB,IAAI,UAAU;AAAA,QACxD,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,MAAM,SAAS,KAAK,KAAK,QAAQ,IAAI,WAAW,IAAI,IAAI,SAAS;AAAA,UAC1E,WAAW;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,QAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO;AAC3D,WAAO,EAAE,SAAS,OAAQ,OAAO,SAAS,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC,EAAG;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,SACJ,WACA,OACA,SACiC;AACjC,UAAM,UAAU,MAAM,KAAK,aAAa,OAAO,SAAS,OAAO;AAC/D,UAAM,UAAU,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,MAC7D,OAAO,EAAE,IAAI,UAAU;AAAA,MAAG,OAAO;AAAA,MAAG,SAASA;AAAA,IAC/C,CAAC;AACD,UAAM,MAAW,MAAM,QAAQ,OAAO,IAAI,QAAQ,CAAC,IAAI;AACvD,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sBAAsB,SAAS,EAAE;AAC3D,QAAI,IAAI,WAAW,YAAY;AAC7B,YAAM,IAAI,MAAM,6BAA6B,IAAI,MAAM,0CAA0C;AAAA,IACnG;AACA,QAAI,CAAC,QAAQ,YAAY,IAAI,gBAAgB,OAAO,IAAI,YAAY,MAAM,OAAO,OAAO,GAAG;AACzF,YAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AACA,UAAM,KAAK,mBAAmB,GAAG;AAOjC,UAAM,YAAY,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,MAC/D,OAAO,EAAE,aAAa,IAAI,aAAa,WAAW,IAAI,WAAW,QAAQ,UAAU;AAAA,MACnF,OAAO;AAAA,MAAG,SAASA;AAAA,IACrB,CAAC;AACD,QAAI,MAAM,QAAQ,SAAS,KAAK,UAAU,CAAC,GAAG;AAC5C,YAAM,IAAI;AAAA,QACR,qEAAqE,IAAI,WAAW,IAAI,IAAI,SAAS;AAAA,MACvG;AAAA,IACF;AAEA,UAAM,MAAM,IAAI,mBAAmB;AACnC,UAAM,SAAwB,IAAI,gBAAgB,IAAI,gBAAgB;AACtE,UAAM,QAAuB,IAAI,eAAe;AAChD,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,YAAY;AAKzC,UAAM,KAAK,mBAAmB,OAAO,SAAS;AAE9C,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY;AAAA,MAAW,iBAAiB;AAAA,MACzD,WAAW;AAAA,MAAQ,YAAY;AAAA,MAAG,QAAQ;AAAA,MAC1C,UAAU;AAAA,MAAS,SAAS,MAAM,WAAW;AAAA,MAAM,YAAY;AAAA,IACjE,GAAG,EAAE,SAASA,YAAW,CAAC;AAE1B,QAAI,UAAU;AACd,QAAI;AACJ,QAAI,OAAO;AACT,YAAM,UAAU,MAAM,KAAK;AAAA,QACzB;AAAA,QAAO;AAAA,QAAW;AAAA,QAClB;AAAA,UACE,aAAa,uBAAuB;AAAA,UACpC,QAAQ,EAAE,aAAa,MAAM,UAAU;AAAA,QACzC;AAAA,MACF;AACA,gBAAU,QAAQ;AAClB,oBAAc,QAAQ;AAAA,IACxB;AAEA,UAAM,QAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO;AAC3D,WAAO,EAAE,SAAS,OAAQ,OAAO,SAAS,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC,EAAG;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,MAAc,iBAAiB,KAAU,QAAsC;AAC7E,UAAM,cAAc,OAAO,IAAI,gBAAgB,EAAE;AACjD,UAAM,WAAW,YAAY,WAAW,OAAO,IAAI,YAAY,MAAM,QAAQ,MAAM,IAAI;AACvF,QAAI,CAAC,YAAY,CAAC,UAAU,OAAO,KAAK,YAAY,YAAY,YAAY;AAC1E,YAAM,IAAI,MAAM,kGAAkG;AAAA,IACpH;AACA,UAAM,OAAY,MAAM,KAAK,WAAW,QAAQ,QAAQ;AACxD,UAAM,cAAc,MAAM,QAAQ,MAAM,KAAK,IACzC,KAAK,MAAM,OAAO,CAAC,MAAW,GAAG,WAAW,UAAU,GAAG,UAAU,uBAAuB,MAAM,IAChG,CAAC;AACL,QAAI,YAAY,WAAW,GAAG;AAC5B,YAAM,IAAI;AAAA,QACR,qCAAqC,MAAM,aAAa,uBAAuB,MAAM;AAAA,MAEvF;AAAA,IACF;AACA,UAAM,eAAe,IAAI;AAAA,OACtB,MAAM,QAAQ,MAAM,KAAK,IAAI,KAAK,QAAQ,CAAC,GACzC,OAAO,CAAC,MAAW,OAAO,GAAG,OAAO,QAAQ,EAC5C,IAAI,CAAC,MAAW,CAAC,EAAE,IAAc,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO,EAAE,CAAC;AAAA,IAC/E;AACA,eAAW,QAAQ,aAAa;AAC9B,YAAM,SAAS,OAAO,MAAM,WAAW,WAAW,KAAK,SAAS;AAChE,YAAM,aAAa,aAAa,IAAI,MAAM;AAC1C,UAAI,eAAe,0BAA2B;AAC9C,YAAM,IAAI;AAAA,QACR,qCAAqC,MAAM,YAAY,uBAAuB,MAAM,yBAC3E,UAAU,WAAW,OAC7B,eAAe,SAAY,qCAAqC,aAAa,cAAc,WAAW,OACvG,uCAAuC,yBAAyB,6PAGnD,UAAU,iBAAiB,cAAc,yBAAyB;AAAA,MACjF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,mBAAmB,KAAyB;AACxD,UAAM,QAAQ,IAAI;AAClB,QAAI,CAAC,MAAO;AAGZ,UAAM,OAAO,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,MAC1D,OAAO,EAAE,aAAa,MAAM;AAAA,MAC5B,SAAS,CAAC,EAAE,OAAO,cAAc,OAAO,OAAO,CAAC;AAAA,MAAG,OAAO;AAAA,MAAG,SAASA;AAAA,IACxE,CAAC;AACD,UAAM,SAAc,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AACpD,QAAI,UAAU,OAAO,OAAO,EAAE,MAAM,OAAO,IAAI,EAAE,GAAG;AAClD,YAAM,IAAI,MAAM,6DAA6D;AAAA,IAC/E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,SACJ,WACA,OACA,SAC0C;AAC1C,UAAM,UAAU,MAAM,KAAK,aAAa,OAAO,SAAS,OAAO;AAC/D,UAAM,KAAK,OAAO,OAAO,MAAM,EAAE,EAAE,KAAK;AACxC,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,oDAAoD;AAC7E,UAAM,MAAM,MAAM,KAAK,eAAe,SAAS;AAE/C,UAAM,UAAU,SAAS,IAAI,iBAAiB;AAC9C,QAAI,QAAQ,SAAS,EAAE,GAAG;AACxB,YAAM,IAAI,MAAM,uBAAuB,EAAE,iCAAiC;AAAA,IAC5E;AACA,UAAM,aAAa,KAAK,gBAAgB,SAAS,IAAI,mBAAmB,IAAI;AAC5E,UAAM,OAAO,OAAO,MAAM,QAAQ,OAAO,EAAE,KAAK;AAKhD,UAAM,cAAc,cAAc,CAAC,QAAQ,SAAS,OAAO;AAC3D,QAAI;AACJ,QAAI,QAAQ,SAAS,IAAI,GAAG;AAG1B,UAAI,CAAC,QAAQ,YAAY,CAAC,cAAc,YAAY,QAAQ,CAAC,QAAQ,SAAS,OAAO,GAAG;AACtF,cAAM,IAAI,MAAM,qBAAqB,OAAO,6BAA6B;AAAA,MAC3E;AACA,aAAO,QAAQ,IAAI,OAAM,MAAM,OAAO,KAAK,CAAE;AAAA,IAC/C,WAAW,YAAY;AAIrB,aAAO,CAAC,EAAE;AAAA,IACZ,OAAO;AACL,YAAM,IAAI,MAAM,eAAe,IAAI,6CAA6C;AAAA,IAClF;AACA,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,YAAY;AAGzC,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY;AAAA,MAAW,iBAAiB,IAAI,mBAAmB;AAAA,MAChF,WAAW,IAAI,gBAAgB,IAAI,gBAAgB;AAAA,MAAM,YAAY;AAAA,MAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,MAIhF,UAAU;AAAA,MAAS,eAAe;AAAA,MAAM,aAAa;AAAA,MACrD,cAAc;AAAA,MACd,SAAS,MAAM,WAAW;AAAA,MAAM,YAAY;AAAA,IAC9C,GAAG,EAAE,SAASA,YAAW,CAAC;AAI1B,QAAI,cAAuC,CAAC;AAC5C,QAAI;AACF,YAAM,MAAM,UAAe,IAAI,kBAAkB,IAAI;AACrD,YAAM,SAAS,KAAK;AACpB,UAAI,UAAU,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,GAAG;AACzC,eAAO,EAAE,IAAI,OAAO,IAAI;AACxB,eAAO,OAAO,IAAI;AAClB,sBAAc,EAAE,kBAAkB,KAAK,UAAU,GAAG,EAAE;AAAA,MACxD;AAAA,IACF,QAAQ;AAAA,IAAiD;AACzD,UAAM,KAAK,OAAO,OAAO,wBAAwB;AAAA,MAC/C,IAAI;AAAA,MAAW,mBAAmB,KAAK,KAAK,GAAG;AAAA,MAAG,YAAY;AAAA,MAAK,GAAG;AAAA,IACxE,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B,UAAM,KAAK,kBAAkB,WAAW,MAAM,IAAI,mBAAmB,MAAM,GAAG;AAE9E,UAAM,KAAK,OAAO;AAAA,MAChB,OAAO;AAAA,MACP,UAAU,CAAC,EAAE;AAAA,MACb;AAAA,MACA,QAAQ,EAAE,QAAQ,wBAAwB,IAAI,UAAU;AAAA,MACxD,UAAU,qBAAqB,SAAS,IAAI,EAAE;AAAA,MAC9C,SAAS;AAAA,QACP,OAAO;AAAA,QACP,SAAS,8BAA8B,IAAI,WAAW,IAAI,IAAI,SAAS;AAAA,QACvE,WAAW;AAAA,MACb;AAAA,IACF,CAAC;AAED,UAAM,QAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO;AAC3D,WAAO,EAAE,SAAS,MAAO;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OACJ,WACA,OACA,SAC4D;AAC5D,UAAM,UAAU,MAAM,KAAK,aAAa,OAAO,SAAS,OAAO;AAC/D,UAAM,MAAM,MAAM,KAAK,eAAe,SAAS;AAC/C,QAAI,CAAC,QAAQ,YAAY,IAAI,gBAAgB,OAAO,IAAI,YAAY,MAAM,OAAO,OAAO,GAAG;AACzF,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AAEA,UAAM,OAAO,MAAM,KAAK,OAAO,KAAK,uBAAuB;AAAA,MACzD,OAAO,EAAE,YAAY,WAAW,QAAQ,SAAS;AAAA,MACjD,SAAS,CAAC,EAAE,OAAO,cAAc,OAAO,OAAO,CAAC;AAAA,MAAG,OAAO;AAAA,MAAG,SAASA;AAAA,IACxE,CAAC;AACD,UAAM,OAAY,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AAClD,UAAM,MAAM,KAAK,MAAM,IAAI;AAC3B,QAAI,MAAM,cAAc,IAAI,QAAQ,IAAI,KAAK,MAAM,KAAK,UAAU,IAAI,oBAAoB;AACxF,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AAEA,UAAM,UAAU,SAAS,IAAI,iBAAiB;AAC9C,UAAM,SAAS,IAAI,YAAY;AAC/B,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY;AAAA,MAAW,iBAAiB,IAAI,mBAAmB;AAAA,MAChF,WAAW,IAAI,gBAAgB,IAAI,gBAAgB;AAAA,MAAM,YAAY;AAAA,MAAG,QAAQ;AAAA,MAChF,UAAU;AAAA,MAAS,SAAS,MAAM,WAAW;AAAA,MAAM,YAAY;AAAA,IACjE,GAAG,EAAE,SAASA,YAAW,CAAC;AAK1B,QAAI,WAAW;AACf,UAAM,WAAW,QAAQ,OAAO,OAAK,KAAK,CAAC,EAAE,SAAS,GAAG,CAAC;AAC1D,UAAM,WAAW,QAAQ,OAAO,OAAK,KAAK,EAAE,SAAS,GAAG,CAAC;AACzD,eAAW,YAAY,UAAU;AAC/B,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,kBAAkB,WAAW,QAAQ;AAC/D,oBAAY,MAAM,KAAK,OAAO;AAAA,UAC5B,OAAO;AAAA,UACP,UAAU,CAAC,QAAQ;AAAA,UACnB;AAAA,UACA,QAAQ,EAAE,QAAQ,wBAAwB,IAAI,UAAU;AAAA,UACxD,UAAU,mBAAmB,SAAS,IAAI,MAAM,IAAI,QAAQ;AAAA,UAC5D,SAAS;AAAA,YACP,OAAO;AAAA,YACP,SAAS,iBAAiB,IAAI,WAAW,IAAI,IAAI,SAAS;AAAA,YAC1D,WAAW;AAAA,YACX,SAAS;AAAA,cACP,EAAE,OAAO,WAAW,KAAK,KAAK,cAAc,OAAO,OAAO,EAAE;AAAA,cAC5D,EAAE,OAAO,UAAU,KAAK,KAAK,cAAc,OAAO,MAAM,EAAE;AAAA,YAC5D;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,SAAS,KAAU;AACjB,aAAK,QAAQ,OAAO,iDAAiD;AAAA,UACnE,SAAS;AAAA,UAAW;AAAA,UAAU,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,QACjE,CAAC;AAAA,MACH;AAAA,IACF;AACA,QAAI,SAAS,QAAQ;AACnB,kBAAY,MAAM,KAAK,OAAO;AAAA,QAC5B,OAAO;AAAA,QACP,UAAU;AAAA,QACV;AAAA,QACA,QAAQ,EAAE,QAAQ,wBAAwB,IAAI,UAAU;AAAA,QACxD,UAAU,mBAAmB,SAAS,IAAI,MAAM;AAAA,QAChD,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,iBAAiB,IAAI,WAAW,IAAI,IAAI,SAAS;AAAA,UAC1D,WAAW;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,QAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO;AAC3D,WAAO,EAAE,SAAS,OAAQ,SAAS;AAAA,EACrC;AAAA;AAAA;AAAA,EAKA,cAAc,UAA0B;AACtC,WAAO,GAAG,KAAK,aAAa,+BAA+B,mBAAmB,QAAQ,CAAC;AAAA,EACzF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBACJ,WACA,YACA,MAC8C;AAC9C,QAAI,CAAC,YAAY,KAAK,EAAG,OAAM,IAAI,MAAM,2CAA2C;AACpF,UAAM,MAAM,MAAM,KAAK,eAAe,SAAS;AAC/C,UAAM,UAAU,SAAS,IAAI,iBAAiB;AAC9C,QAAI,CAAC,QAAQ,SAAS,UAAU,GAAG;AACjC,YAAM,IAAI,MAAM,eAAe,UAAU,6CAA6C;AAAA,IACxF;AACA,UAAM,MAAM,KAAK,MAAM,IAAI;AAC3B,UAAM,UAAU,IAAI,KAAK,IAAI,QAAQ,KAAK,MAAM,SAAS,oBAAoB,EAAE,YAAY;AAC3F,UAAM,MAAM,EAAE,SAAS,IAAI,QAAQ,GAAG;AACtC,eAAW,UAAU,CAAC,WAAW,QAAQ,GAAY;AACnD,YAAM,WAAW,YAAY,EAAE,EAAE,SAAS,WAAW;AACrD,YAAM,KAAK,OAAO,OAAO,sBAAsB;AAAA,QAC7C,IAAI,IAAI,MAAM;AAAA,QACd,iBAAiB,IAAI,mBAAmB;AAAA,QACxC,YAAY,WAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAK;AAAA,QAC9D,YAAY;AAAA,QACZ;AAAA,QACA,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,aAAa;AAAA,QACb,YAAY,IAAI,YAAY;AAAA,MAC9B,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B,UAAI,MAAM,IAAI;AAAA,IAChB;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,mBAAmB,UAE/B;AACA,UAAM,UAAU,UAAU,KAAK;AAC/B,QAAI,CAAC,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,UAAU;AACpD,UAAM,OAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC9D,UAAM,OAAO,MAAM,KAAK,OAAO,KAAK,sBAAsB;AAAA,MACxD,OAAO,EAAE,YAAY,KAAK;AAAA,MAAG,OAAO;AAAA,MAAG,SAASA;AAAA,IAClD,CAAC;AACD,UAAM,QAAa,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AACnD,QAAI,CAAC,MAAO,QAAO,EAAE,IAAI,OAAO,QAAQ,UAAU;AAClD,QAAI,MAAM,YAAa,QAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AAC9D,QAAI,KAAK,MAAM,MAAM,UAAU,IAAI,KAAK,MAAM,IAAI,EAAE,QAAQ,GAAG;AAC7D,aAAO,EAAE,IAAI,OAAO,QAAQ,UAAU;AAAA,IACxC;AACA,UAAM,UAAU,MAAM,KAAK,WAAW,MAAM,YAAYA,WAAU;AAClE,QAAI,CAAC,WAAW,QAAQ,WAAW,WAAW;AAC5C,aAAO,EAAE,IAAI,OAAO,QAAQ,eAAe,SAAS,WAAW,OAAU;AAAA,IAC3E;AACA,QAAI,EAAE,QAAQ,qBAAqB,CAAC,GAAG,SAAS,MAAM,WAAW,GAAG;AAGlE,aAAO,EAAE,IAAI,OAAO,QAAQ,gBAAgB,QAAQ;AAAA,IACtD;AACA,WAAO,EAAE,IAAI,MAAM,OAAO,QAAQ;AAAA,EACpC;AAAA;AAAA,EAGA,MAAM,gBAAgB,UAA+C;AACnE,UAAM,MAAM,MAAM,KAAK,mBAAmB,QAAQ;AAClD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,WAAO,EAAE,IAAI,MAAM,QAAQ,IAAI,MAAM,QAAQ,SAAS,IAAI,SAAS,YAAY,IAAI,MAAM,YAAY;AAAA,EACvG;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBAAkB,UAA+C;AACrE,UAAM,MAAM,MAAM,KAAK,mBAAmB,QAAQ;AAClD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,KAAK,OAAO,OAAO,sBAAsB;AAAA,MAC7C,IAAI,IAAI,MAAM;AAAA,MAAI,aAAa,KAAK,MAAM,IAAI,EAAE,YAAY;AAAA,IAC9D,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B,UAAM,MAAM,MAAM,KAAK,OAAO,IAAI,MAAM,YAAY;AAAA,MAClD,UAAU,IAAI,MAAM;AAAA,MACpB,SAAS,IAAI,MAAM;AAAA,MACnB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQX,GAAG,EAAE,GAAGA,aAAY,QAAQ,IAAI,MAAM,YAAY,CAAC;AACnD,WAAO,EAAE,IAAI,MAAM,QAAQ,IAAI,MAAM,QAAQ,SAAS,IAAI,SAAS,YAAY,IAAI,MAAM,YAAY;AAAA,EACvG;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YACJ,WACA,OACA,SAC0C;AAC1C,UAAM,UAAU,MAAM,KAAK,aAAa,OAAO,SAAS,OAAO;AAC/D,QAAI,CAAC,OAAO,SAAS,KAAK,EAAG,OAAM,IAAI,MAAM,wCAAwC;AACrF,UAAM,MAAM,MAAM,KAAK,eAAe,SAAS;AAC/C,UAAM,UAAU,SAAS,IAAI,iBAAiB;AAC9C,QAAI,CAAC,QAAQ,YAAY,CAAC,QAAQ,SAAS,OAAO,GAAG;AACnD,YAAM,IAAI,MAAM,qBAAqB,OAAO,6BAA6B;AAAA,IAC3E;AAEA,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,YAAY;AACzC,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY;AAAA,MAAW,iBAAiB,IAAI,mBAAmB;AAAA,MAChF,WAAW,IAAI,gBAAgB,IAAI,gBAAgB;AAAA,MAAM,YAAY;AAAA,MAAG,QAAQ;AAAA,MAChF,UAAU;AAAA,MAAS,SAAS,MAAM,QAAQ,KAAK;AAAA,MAAG,YAAY;AAAA,IAChE,GAAG,EAAE,SAASA,YAAW,CAAC;AAE1B,QAAI,IAAI,cAAc;AACpB,YAAM,KAAK,OAAO;AAAA,QAChB,OAAO;AAAA,QACP,UAAU,CAAC,OAAO,IAAI,YAAY,CAAC;AAAA,QACnC;AAAA,QACA,QAAQ,EAAE,QAAQ,wBAAwB,IAAI,UAAU;AAAA,QACxD,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,MAAM,QAAQ,KAAK;AAAA,UAC5B,WAAW;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,QAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO;AAC3D,WAAO,EAAE,SAAS,MAAO;AAAA,EAC3B;AAAA;AAAA,EAGA,MAAM,QACJ,WACA,OACA,SAC0C;AAC1C,UAAM,UAAU,MAAM,KAAK,aAAa,OAAO,SAAS,OAAO;AAC/D,QAAI,CAAC,OAAO,SAAS,KAAK,EAAG,OAAM,IAAI,MAAM,wCAAwC;AACrF,UAAM,MAAM,MAAM,KAAK,eAAe,SAAS;AAC/C,UAAM,UAAU,SAAS,IAAI,iBAAiB;AAC9C,UAAM,cAAc,IAAI,gBAAgB,OAAO,IAAI,YAAY,MAAM,OAAO,OAAO;AACnF,QAAI,CAAC,QAAQ,YAAY,CAAC,eAAe,CAAC,QAAQ,SAAS,OAAO,GAAG;AACnE,YAAM,IAAI,MAAM,qBAAqB,OAAO,0BAA0B;AAAA,IACxE;AAEA,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,YAAY;AACzC,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY;AAAA,MAAW,iBAAiB,IAAI,mBAAmB;AAAA,MAChF,WAAW,IAAI,gBAAgB,IAAI,gBAAgB;AAAA,MAAM,YAAY;AAAA,MAAG,QAAQ;AAAA,MAChF,UAAU;AAAA,MAAS,SAAS,MAAM,QAAQ,KAAK;AAAA,MAC/C,aAAa,MAAM,aAAa,SAAS,MAAM,cAAc;AAAA,MAC7D,YAAY;AAAA,IACd,GAAG,EAAE,SAASA,YAAW,CAAC;AAG1B,UAAM,WAAW,cAAc,UAAU,CAAC,OAAO,IAAI,gBAAgB,EAAE,CAAC,EAAE,OAAO,OAAO;AACxF,UAAM,KAAK,OAAO;AAAA,MAChB,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA,QAAQ,EAAE,QAAQ,wBAAwB,IAAI,UAAU;AAAA,MACxD,SAAS;AAAA,QACP,OAAO;AAAA,QACP,SAAS,MAAM,QAAQ,KAAK;AAAA,QAC5B,WAAW;AAAA,MACb;AAAA,IACF,CAAC;AAED,UAAM,QAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO;AAC3D,WAAO,EAAE,SAAS,MAAO;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,iBAAkE;AACtE,QAAI,OAAc,CAAC;AACnB,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,QACpD,OAAO,EAAE,QAAQ,UAAU;AAAA,QAAG,OAAO;AAAA,QAAK,SAASA;AAAA,MACrD,CAAC,KAAK,CAAC;AAAA,IACT,SAAS,KAAU;AACjB,WAAK,QAAQ,OAAO,uDAAuD;AAAA,QACzE,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,MACnC,CAAC;AACD,aAAO,EAAE,SAAS,GAAG,WAAW,EAAE;AAAA,IACpC;AAEA,QAAI,YAAY;AAChB,eAAW,OAAO,MAAM;AACtB,UAAI;AACF,cAAM,MAAM,UAAe,IAAI,kBAAkB,MAAS;AAC1D,cAAMM,OAAM,KAAK;AACjB,YAAI,CAACA,QAAO,OAAOA,KAAI,iBAAiB,YAAYA,KAAI,gBAAgB,EAAG;AAC3E,cAAM,MAAM,SAAS,IAAI,YAAY,GAAG;AACxC,YAAI,CAAC,OAAO,KAAK,MAAM,GAAG,IAAI,KAAK,MAAM,IAAI,EAAE,QAAQ,EAAG;AAG1D,cAAM,QAAQ,MAAM,KAAK,OAAO,KAAK,uBAAuB;AAAA,UAC1D,OAAO,EAAE,YAAY,IAAI,IAAI,QAAQ,WAAW;AAAA,UAAG,OAAO;AAAA,UAAG,SAASN;AAAA,QACxE,CAAC;AACD,YAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,CAAC,EAAG;AAEtC,cAAM,KAAK,gBAAgB,KAAKM,IAAG;AACnC;AAAA,MACF,SAAS,KAAU;AACjB,aAAK,QAAQ,OAAO,6CAA6C;AAAA,UAC/D,SAAS,KAAK;AAAA,UAAI,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,QACrD,CAAC;AAAA,MACH;AAAA,IACF;AACA,QAAI,YAAY,GAAG;AACjB,WAAK,QAAQ,OAAO,oCAAoC,EAAE,SAAS,KAAK,QAAQ,UAAU,CAAC;AAAA,IAC7F;AACA,WAAO,EAAE,SAAS,KAAK,QAAQ,UAAU;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyEA,MAAM,wBAAwB,SAK3B;AACD,UAAM,QAAQ,EAAE,SAAS,GAAG,UAAU,CAAC,GAAgC,cAAc,EAAE;AAIvF,QAAI,OAAO,KAAK,YAAY,oBAAoB,WAAY,QAAO;AACnE,QAAI,OAAO,KAAK,YAAY,WAAW,WAAY,QAAO;AAE1D,UAAM,QAAQ,SAAS,SAAS;AAChC,QAAI,OAAc,CAAC;AACnB,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,QACpD,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,2BAA2B,EAAE,EAAE;AAAA,QAAG;AAAA,QAAO,SAASN;AAAA,MAChF,CAAC,KAAK,CAAC;AAAA,IACT,SAAS,KAAU;AACjB,WAAK,QAAQ,OAAO,6DAA6D;AAAA,QAC/E,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,MACnC,CAAC;AACD,aAAO;AAAA,IACT;AAEA,UAAM,WAAsC,CAAC;AAC7C,QAAI,eAAe;AACnB,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,KAAK,cAAc,OAAO,IAAI,WAAW,IAAI;AAC3D,UAAI,CAAC,MAAO;AAEZ,UAAI;AACJ,UAAI;AACF,oBAAY,MAAM,KAAK,WAAW,gBAAiB,KAAK;AAAA,MAC1D,SAAS,KAAU;AAIjB;AACA,aAAK,QAAQ,OAAO,yEAAyE;AAAA,UAC3F,SAAS,KAAK;AAAA,UAAI,KAAK;AAAA,UAAO,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,QACjE,CAAC;AACD;AAAA,MACF;AACA,UAAI,UAAW;AAEf,UAAI,WAAuC;AAC3C,UAAI;AACF,mBAAW,MAAM,KAAK,WAAW,OAAQ,KAAK;AAAA,MAChD,SAAS,KAAU;AACjB;AACA,aAAK,QAAQ,OAAO,oEAAoE;AAAA,UACtF,SAAS,KAAK;AAAA,UAAI,KAAK;AAAA,UAAO,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,QACjE,CAAC;AACD;AAAA,MACF;AACA,UAAI,SAAU;AAId,YAAM,SAAS;AAAA,QACb,IAAI;AAAA,QAAkB,EAAE,WAAW,CAAC,GAAG,UAAU,iBAAiB;AAAA,MACpE;AACA,YAAM,cAAc,OAAO;AAC3B,UAAI;AACJ,UAAI,aAAa;AACf,YAAI;AACF,gBAAM,OAAO,MAAM,KAAK,OAAO,KAAK,IAAI,aAAa;AAAA,YACnD,OAAO,EAAE,IAAI,IAAI,UAAU;AAAA,YAAG,OAAO;AAAA,YAAG,SAASA;AAAA,UACnD,CAAC;AACD,gBAAM,MAAW,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AACjD,cAAI,IAAK,kBAAiB,IAAI,WAAW,KAAK;AAAA,QAChD,QAAQ;AAAA,QAA8D;AAAA,MACxE;AACA,eAAS,KAAK;AAAA,QACZ,WAAW,OAAO,IAAI,EAAE;AAAA,QACxB,QAAQ,IAAI;AAAA,QACZ;AAAA,QACA,UAAU,OAAO,IAAI,iBAAiB,WAAW,IAAI,aAAa,QAAQ,UAAU,EAAE,IAAI;AAAA,QAC1F,QAAQ,IAAI,gBAAgB,IAAI,gBAAgB;AAAA,QAChD,YAAY,IAAI;AAAA,QAChB,UAAU,IAAI;AAAA,QACd,gBAAgB,IAAI,mBAAmB;AAAA,QACvC,aAAa,IAAI,gBAAgB;AAAA,QACjC;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,SAAS,UAAU,cAAc;AACnC,WAAK,QAAQ,OAAO,6EAA6E;AAAA,QAC/F,SAAS,KAAK;AAAA,QAAQ,UAAU,SAAS;AAAA,QAAQ;AAAA,QACjD,UAAU,SAAS,IAAI,OAAK,GAAG,EAAE,SAAS,IAAI,EAAE,UAAU,GAAG,eAAU,EAAE,KAAK,EAAE;AAAA,MAClF,CAAC;AAAA,IACH;AACA,WAAO,EAAE,SAAS,KAAK,QAAQ,UAAU,aAAa;AAAA,EACxD;AAAA,EAEA,MAAM,yBAAyE;AAE7E,QAAI,OAAO,KAAK,YAAY,WAAW,WAAY,QAAO,EAAE,SAAS,GAAG,UAAU,EAAE;AAEpF,QAAI,OAAc,CAAC;AACnB,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,QACpD,OAAO,EAAE,QAAQ,UAAU;AAAA,QAAG,OAAO;AAAA,QAAK,SAASA;AAAA,MACrD,CAAC,KAAK,CAAC;AAAA,IACT,SAAS,KAAU;AACjB,WAAK,QAAQ,OAAO,sDAAsD;AAAA,QACxE,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,MACnC,CAAC;AACD,aAAO,EAAE,SAAS,GAAG,UAAU,EAAE;AAAA,IACnC;AAEA,QAAI,WAAW;AACf,eAAW,OAAO,MAAM;AACtB,UAAI;AACF,cAAM,QAAQ,KAAK,cAAc,OAAO,IAAI,WAAW,IAAI;AAC3D,YAAI,CAAC,MAAO;AAEZ,YAAI;AACJ,YAAI;AACF,gBAAM,MAAM,MAAM,KAAK,WAAW,OAAQ,KAAK;AAC/C,mBAAS,OAAO,KAAK,WAAW,WAAW,IAAI,SAAS;AAAA,QAC1D,SAAS,KAAU;AAEjB,eAAK,QAAQ,OAAO,wDAAwD;AAAA,YAC1E,SAAS,KAAK;AAAA,YAAI,KAAK;AAAA,YAAO,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,UACjE,CAAC;AACD;AAAA,QACF;AACA,YAAI,CAAC,UAAU,CAAC,sBAAsB,IAAI,MAAM,EAAG;AAEnD,cAAM,KAAK,kBAAkB,KAAK,OAAO,MAAM;AAC/C;AAAA,MACF,SAAS,KAAU;AAEjB,aAAK,QAAQ,OAAO,mDAAmD;AAAA,UACrE,SAAS,KAAK;AAAA,UAAI,OAAO,KAAK,WAAW,OAAO,GAAG;AAAA,QACrD,CAAC;AAAA,MACH;AAAA,IACF;AACA,QAAI,WAAW,GAAG;AAChB,WAAK,QAAQ,OAAO,8BAA8B,EAAE,SAAS,KAAK,QAAQ,SAAS,CAAC;AAAA,IACtF;AACA,WAAO,EAAE,SAAS,KAAK,QAAQ,SAAS;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,kBAAkB,KAAU,OAAe,WAAkC;AACzF,UAAM,MAAM,IAAI,mBAAmB;AACnC,UAAM,SAAwB,IAAI,gBAAgB,IAAI,gBAAgB;AACtE,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,YAAY;AAEzC,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY,IAAI;AAAA,MAAI,iBAAiB;AAAA,MACtD,WAAW;AAAA,MAAQ,YAAY;AAAA,MAAG,QAAQ;AAAA,MAC1C,UAAU;AAAA,MACV,SAAS,mBAAmB,KAAK,OAAO,SAAS;AAAA,MACjD,YAAY;AAAA,IACd,GAAG,EAAE,SAASA,YAAW,CAAC;AAE1B,UAAM,KAAK,OAAO,OAAO,wBAAwB;AAAA,MAC/C,IAAI,IAAI;AAAA,MAAI,QAAQ;AAAA,MAAY,mBAAmB;AAAA,MAAM,cAAc;AAAA,MAAK,YAAY;AAAA,IAC1F,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B,UAAM,KAAK,kBAAkB,IAAI,IAAI,CAAC,GAAG,KAAK,GAAG;AAEjD,UAAM,SAAS;AAAA,MACb,IAAI;AAAA,MAAkB,EAAE,WAAW,CAAC,GAAG,UAAU,iBAAiB;AAAA,IACpE;AACA,QAAI,OAAO,qBAAqB;AAG9B,YAAM,KAAK;AAAA,QACT,IAAI;AAAA,QAAa,IAAI;AAAA,QAAW,OAAO;AAAA,QAAqB;AAAA,QAAY;AAAA,MAC1E;AAAA,IACF;AAEA,SAAK,QAAQ,OAAO,6DAA6D;AAAA,MAC/E,SAAS,IAAI;AAAA,MAAI,KAAK;AAAA,MAAO;AAAA,MAAW,QAAQ,IAAI;AAAA,MAAa,QAAQ,IAAI;AAAA,IAC/E,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAc,gBAAgB,KAAUM,MAAyB;AAC/D,UAAM,SAAiBA,KAAI,UAAU;AACrC,UAAM,aACJ,OAAOA,KAAI,eAAe,YAAYA,KAAI,WAAW,KAAK,IAAIA,KAAI,WAAW,KAAK,IAAI;AACxF,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,YAAY;AACzC,UAAM,UAAU,SAAS,IAAI,iBAAiB;AAM9C,QAAI,aAAuB,CAAC;AAC5B,QAAI,YAAY;AACd,UAAI;AACF,qBAAa,MAAM,KAAK,oBAAoB,YAAY,IAAI,mBAAmB,IAAI;AAAA,MACrF,QAAQ;AAAE,qBAAa,CAAC;AAAA,MAAG;AAC3B,UAAI,CAAC,WAAW,OAAQ,cAAa,CAAC,UAAU;AAAA,IAClD;AAGA,UAAM,KAAK,OAAO,OAAO,uBAAuB;AAAA,MAC9C,IAAI,IAAI,MAAM;AAAA,MAAG,YAAY,IAAI;AAAA,MAAI,iBAAiB,IAAI,mBAAmB;AAAA,MAC7E,WAAW,IAAI,gBAAgB,IAAI,gBAAgB;AAAA,MAAM,YAAY;AAAA,MAAG,QAAQ;AAAA,MAChF,UAAU;AAAA,MACV,SAAS,GAAG,MAAM,GAAG,aAAa,WAAM,UAAU,KAAK,EAAE;AAAA,MACzD,YAAY;AAAA,IACd,GAAG,EAAE,SAASN,YAAW,CAAC;AAE1B,QAAI,WAAW,cAAc,WAAW,QAAQ;AAC9C,YAAM,KAAK,OAAO,OAAO,wBAAwB;AAAA,QAC/C,IAAI,IAAI;AAAA,QAAI,mBAAmB,WAAW,KAAK,GAAG;AAAA,QAAG,YAAY;AAAA,MACnE,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B,YAAM,KAAK,kBAAkB,IAAI,IAAI,YAAY,IAAI,mBAAmB,MAAM,GAAG;AACjF,YAAM,KAAK,OAAO;AAAA,QAChB,OAAO;AAAA,QACP,UAAU;AAAA,QACV,SAAS;AAAA,QACT,QAAQ,EAAE,QAAQ,wBAAwB,IAAI,IAAI,GAAG;AAAA,QACrD,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,0BAA0B,IAAI,WAAW,IAAI,IAAI,SAAS;AAAA,UACnE,WAAW;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH,WAAW,WAAW,kBAAkB,WAAW,eAAe;AAChE,YAAM,KAAK,OAAO,IAAI,IAAI;AAAA,QACxB,UAAU,WAAW,iBAAiB,YAAY;AAAA,QAClD,SAAS;AAAA,QACT,SAAS;AAAA,MACX,GAAGA,WAAU;AAAA,IACf,OAAO;AAEL,YAAM,KAAK,OAAO;AAAA,QAChB,OAAO;AAAA,QACP,UAAU,CAAC,GAAG,SAAS,GAAG,UAAU;AAAA,QACpC,SAAS;AAAA,QACT,QAAQ,EAAE,QAAQ,wBAAwB,IAAI,IAAI,GAAG;AAAA,QACrD,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,iBAAiB,IAAI,WAAW,IAAI,IAAI,SAAS;AAAA,UAC1D,WAAW;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAIM,KAAI,oBAAoB,SAAS,IAAI,cAAc;AACrD,YAAM,KAAK,OAAO;AAAA,QAChB,OAAO;AAAA,QACP,UAAU,CAAC,OAAO,IAAI,YAAY,CAAC;AAAA,QACnC,SAAS;AAAA,QACT,QAAQ,EAAE,QAAQ,wBAAwB,IAAI,IAAI,GAAG;AAAA,QACrD,SAAS;AAAA,UACP,OAAO;AAAA,UACP,SAAS,GAAG,IAAI,WAAW,IAAI,IAAI,SAAS,wBAAwB,MAAM;AAAA,UAC1E,WAAW;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,oBAAoB,QAAoC;AAC9D,QAAI;AACF,YAAM,SAAe,KAAK,OAAe,YAAY,MAAM;AAC3D,YAAM,SAAS,QAAQ,UAAU,CAAC;AAGlC,YAAM,WAAW,QAAQ,aAAa,QAAQ;AAC9C,UAAI,YAAY,aAAa,QAAQ,OAAO,QAAQ,EAAG,QAAO;AAC9D,iBAAW,QAAQ,CAAC,QAAQ,SAAS,WAAW,OAAO,GAAG;AACxD,YAAI,OAAO,IAAI,EAAG,QAAO;AAAA,MAC3B;AAAA,IACF,QAAQ;AAAA,IAA0D;AAClE,WAAO;AAAA,EACT;AAAA,EAEA,OAAe,UAAU,KAAU,cAA2C;AAC5E,UAAM,aAAa,eACf,CAAC,cAAc,QAAQ,SAAS,WAAW,OAAO,IAClD,CAAC,QAAQ,SAAS,WAAW,OAAO;AACxC,eAAW,KAAK,YAAY;AAC1B,YAAM,IAAI,MAAM,CAAC;AACjB,UAAI,KAAK,QAAQ,OAAO,CAAC,EAAE,KAAK,KAAK,MAAM,KAAM,QAAO,OAAO,CAAC;AAAA,IAClE;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBAAiB,aAA6E;AAC1G,UAAM,QAAQ,oBAAI,IAAoB;AACtC,UAAM,UAAU,MAAM,KAAK,IAAI,IAAI,YAAY,OAAO,OAAO,CAAC,CAAC;AAC/D,QAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,OAAO,KAAK,YAAY;AAAA,QAC/C,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAAE;AAAA,QAAG,QAAQ,CAAC,MAAM,QAAQ,OAAO;AAAA,QAC/D,OAAO,QAAQ;AAAA,QAAQ,SAASN;AAAA,MAClC,CAAC;AACD,iBAAW,KAAM,SAAS,CAAC,GAAa;AACtC,YAAI,GAAG,OAAO,EAAE,QAAQ,EAAE,OAAQ,OAAM,IAAI,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC;AAAA,MACrF;AAAA,IACF,QAAQ;AAAA,IAAoB;AAC5B,UAAM,mBAAmB,QAAQ,OAAO,OAAK,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC;AAC7E,QAAI,iBAAiB,QAAQ;AAC3B,UAAI;AACF,cAAM,QAAQ,MAAM,KAAK,OAAO,KAAK,YAAY;AAAA,UAC/C,OAAO,EAAE,OAAO,EAAE,KAAK,iBAAiB,EAAE;AAAA,UAAG,QAAQ,CAAC,SAAS,MAAM;AAAA,UACrE,OAAO,iBAAiB;AAAA,UAAQ,SAASA;AAAA,QAC3C,CAAC;AACD,mBAAW,KAAM,SAAS,CAAC,GAAa;AACtC,cAAI,GAAG,SAAS,EAAE,KAAM,OAAM,IAAI,OAAO,EAAE,KAAK,GAAG,OAAO,EAAE,IAAI,CAAC;AAAA,QACnE;AAAA,MACF,QAAQ;AAAA,MAAoB;AAAA,IAC9B;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,oBAAoB,QAA2D;AACrF,QAAI;AACF,YAAM,SAAe,KAAK,OAAe,YAAY,MAAM;AAC3D,YAAM,SAAS,QAAQ,UAAU,CAAC;AAClC,YAAM,MAAiD,CAAC;AACxD,iBAAW,CAAC,KAAK,CAAC,KAAK,OAAO,QAAa,MAAM,GAAG;AAClD,aAAK,GAAG,SAAS,YAAY,GAAG,SAAS,mBAAmB,GAAG,SAAS,WAAW,GAAG,WAAW;AAC/F,cAAI,KAAK,EAAE,KAAK,WAAW,OAAO,EAAE,SAAS,EAAE,CAAC;AAAA,QAClD;AAAA,MACF;AACA,aAAO;AAAA,IACT,QAAQ;AAAE,aAAO,CAAC;AAAA,IAAG;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,mBAAmB,QAAwC;AACjE,QAAI;AACF,YAAM,SAAe,KAAK,OAAe,YAAY,MAAM;AAC3D,YAAM,SAAS,QAAQ,UAAU,CAAC;AAClC,YAAM,MAA8B,CAAC;AACrC,iBAAW,CAAC,KAAK,CAAC,KAAK,OAAO,QAAa,MAAM,GAAG;AAClD,YAAI,GAAG,MAAO,KAAI,GAAG,IAAI,OAAO,EAAE,KAAK;AAAA,MACzC;AACA,aAAO;AAAA,IACT,QAAQ;AAAE,aAAO,CAAC;AAAA,IAAG;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAc,WAAW,MAA2C;AAClE,QAAI,CAAC,KAAK,OAAQ;AAGlB,UAAM,WAAW,oBAAI,IAAyB;AAC9C,eAAW,KAAK,MAAM;AACpB,UAAI,CAAC,EAAE,eAAe,CAAC,EAAE,UAAW;AACpC,UAAI,MAAM,SAAS,IAAI,EAAE,WAAW;AACpC,UAAI,CAAC,KAAK;AAAE,cAAM,oBAAI,IAAI;AAAG,iBAAS,IAAI,EAAE,aAAa,GAAG;AAAA,MAAG;AAC/D,UAAI,IAAI,EAAE,SAAS;AAAA,IACrB;AACA,UAAM,SAAS,oBAAI,IAAoB;AACvC,UAAM,eAAe,oBAAI,IAAoB;AAC7C,eAAW,CAAC,QAAQ,KAAK,KAAK,UAAU;AACtC,UAAI;AACF,cAAM,SAAe,KAAK,OAAe,YAAY,MAAM;AAC3D,YAAI,QAAQ,MAAO,cAAa,IAAI,QAAQ,OAAO,OAAO,KAAK,CAAC;AAAA,MAClE,QAAQ;AAAA,MAAuB;AAC/B,YAAM,MAAM,MAAM,KAAK,KAAK;AAC5B,YAAM,eAAe,KAAK,oBAAoB,MAAM;AACpD,UAAI;AACF,cAAM,OAAO,MAAM,KAAK,OAAO,KAAK,QAAQ;AAAA,UAC1C,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;AAAA,UAAG,OAAO,IAAI;AAAA,UAAQ,SAASA;AAAA,QAC3D,CAAC;AACD,mBAAW,OAAQ,QAAQ,CAAC,GAAa;AACvC,gBAAM,QAAQ,iBAAgB,UAAU,KAAK,YAAY;AACzD,cAAI,KAAK,MAAM,MAAO,QAAO,IAAI,GAAG,MAAM,IAAI,IAAI,EAAE,IAAI,KAAK;AAAA,QAC/D;AAAA,MACF,QAAQ;AAAA,MAA4D;AAAA,IACtE;AAGA,UAAM,uBAAuB,oBAAI,IAAuD;AAExF,UAAM,sBAAsB,oBAAI,IAAoC;AACpE,eAAW,UAAU,SAAS,KAAK,GAAG;AACpC,YAAM,UAAU,KAAK,oBAAoB,MAAM;AAC/C,UAAI,QAAQ,OAAQ,sBAAqB,IAAI,QAAQ,OAAO;AAC5D,YAAM,SAAS,KAAK,mBAAmB,MAAM;AAC7C,UAAI,OAAO,KAAK,MAAM,EAAE,OAAQ,qBAAoB,IAAI,QAAQ,MAAM;AAAA,IACxE;AACA,UAAM,SAAS,oBAAI,IAAyB;AAC5C,eAAW,KAAK,MAAM;AACpB,YAAM,UAAU,qBAAqB,IAAI,EAAE,WAAW;AACtD,YAAM,UAAe,EAAE;AACvB,UAAI,CAAC,WAAW,CAAC,WAAW,OAAO,YAAY,SAAU;AACzD,iBAAW,EAAE,KAAK,UAAU,KAAK,SAAS;AACxC,cAAM,IAAI,QAAQ,GAAG;AACrB,YAAI,KAAK,QAAQ,OAAO,MAAM,YAAY,CAAC,OAAO,CAAC,EAAE,KAAK,EAAG;AAC7D,YAAI,MAAM,OAAO,IAAI,SAAS;AAC9B,YAAI,CAAC,KAAK;AAAE,gBAAM,oBAAI,IAAI;AAAG,iBAAO,IAAI,WAAW,GAAG;AAAA,QAAG;AACzD,YAAI,IAAI,OAAO,CAAC,CAAC;AAAA,MACnB;AAAA,IACF;AACA,UAAM,YAAY,oBAAI,IAAoB;AAC1C,eAAW,CAAC,QAAQ,KAAK,KAAK,QAAQ;AACpC,YAAM,MAAM,MAAM,KAAK,KAAK;AAC5B,YAAM,eAAe,KAAK,oBAAoB,MAAM;AACpD,UAAI;AACF,cAAM,OAAO,MAAM,KAAK,OAAO,KAAK,QAAQ;AAAA,UAC1C,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;AAAA,UAAG,OAAO,IAAI;AAAA,UAAQ,SAASA;AAAA,QAC3D,CAAC;AACD,mBAAW,OAAQ,QAAQ,CAAC,GAAa;AACvC,gBAAM,QAAQ,iBAAgB,UAAU,KAAK,YAAY;AACzD,cAAI,KAAK,MAAM,MAAO,WAAU,IAAI,GAAG,MAAM,IAAI,IAAI,EAAE,IAAI,KAAK;AAAA,QAClE;AAAA,MACF,QAAQ;AAAA,MAAwD;AAAA,IAClE;AAIA,UAAM,kBAAoD,CAAC;AAC3D,eAAW,KAAK,MAAM;AACpB,sBAAgB,KAAK,EAAE,YAAY;AACnC,iBAAW,KAAK,EAAE,qBAAqB,CAAC,GAAG;AACzC,YAAI,KAAK,CAAC,EAAE,SAAS,GAAG,EAAG,iBAAgB,KAAK,CAAC;AAAA,MACnD;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,KAAK,iBAAiB,eAAe;AAEzD,eAAW,KAAK,MAAe;AAC7B,YAAM,QAAQ,OAAO,IAAI,GAAG,EAAE,WAAW,IAAI,EAAE,SAAS,EAAE,KACrD,iBAAgB,UAAU,EAAE,SAAS,MAAS;AACnD,UAAI,MAAO,GAAE,eAAe;AAC5B,YAAM,OAAO,EAAE,eAAe,MAAM,IAAI,OAAO,EAAE,YAAY,CAAC,IAAI;AAClE,UAAI,KAAM,GAAE,iBAAiB;AAC7B,YAAM,QAAQ,aAAa,IAAI,EAAE,WAAW;AAC5C,UAAI,MAAO,GAAE,eAAe;AAE5B,YAAM,gBAAwC,CAAC;AAC/C,iBAAW,KAAK,EAAE,qBAAqB,CAAC,GAAG;AACzC,cAAM,IAAI,MAAM,IAAI,OAAO,CAAC,CAAC;AAC7B,YAAI,EAAG,eAAc,CAAC,IAAI;AAAA,MAC5B;AACA,UAAI,OAAO,KAAK,aAAa,EAAE,OAAQ,GAAE,yBAAyB;AAElE,YAAM,UAAU,qBAAqB,IAAI,EAAE,WAAW;AACtD,UAAI,WAAW,EAAE,WAAW,OAAO,EAAE,YAAY,UAAU;AACzD,cAAM,UAAkC,CAAC;AACzC,mBAAW,EAAE,KAAK,UAAU,KAAK,SAAS;AACxC,gBAAM,IAAK,EAAE,QAAgB,GAAG;AAChC,cAAI,KAAK,KAAM;AACf,gBAAM,IAAI,UAAU,IAAI,GAAG,SAAS,IAAI,OAAO,CAAC,CAAC,EAAE;AACnD,cAAI,EAAG,SAAQ,GAAG,IAAI;AAAA,QACxB;AACA,YAAI,OAAO,KAAK,OAAO,EAAE,OAAQ,GAAE,kBAAkB;AAAA,MACvD;AAIA,YAAM,cAAc,oBAAoB,IAAI,EAAE,WAAW;AACzD,UAAI,eAAe,EAAE,WAAW,OAAO,EAAE,YAAY,UAAU;AAC7D,cAAM,SAAiC,CAAC;AACxC,mBAAW,OAAO,OAAO,KAAK,EAAE,OAAkC,GAAG;AACnE,gBAAM,IAAI,YAAY,GAAG;AACzB,cAAI,EAAG,QAAO,GAAG,IAAI;AAAA,QACvB;AACA,YAAI,OAAO,KAAK,MAAM,EAAE,OAAQ,GAAE,iBAAiB;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,kBACZ,WACA,WACA,KACA,KACe;AACf,UAAM,UAAU,IAAI,IAAI,UAAU,IAAI,OAAK,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC;AAC5E,UAAM,WAAW,MAAM,KAAK,OAAO,KAAK,yBAAyB;AAAA,MAC/D,OAAO,EAAE,YAAY,UAAU;AAAA,MAAG,OAAO;AAAA,MAAK,SAASA;AAAA,IACzD,CAAC;AACD,UAAM,OAAc,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC;AAC1D,eAAW,OAAO,MAAM;AACtB,UAAI,QAAQ,IAAI,OAAO,IAAI,QAAQ,CAAC,EAAG,SAAQ,OAAO,OAAO,IAAI,QAAQ,CAAC;AAAA,UACrE,OAAM,KAAK,OAAO,OAAO,yBAAyB,EAAE,OAAO,EAAE,IAAI,IAAI,GAAG,GAAG,SAASA,YAAW,CAAC;AAAA,IACvG;AACA,eAAW,YAAY,SAAS;AAC9B,YAAM,KAAK,OAAO,OAAO,yBAAyB;AAAA,QAChD,IAAI,IAAI,MAAM;AAAA,QAAG,YAAY;AAAA,QAAW;AAAA,QACxC,iBAAiB;AAAA,QAAK,YAAY;AAAA,MACpC,GAAG,EAAE,SAASA,YAAW,CAAC;AAAA,IAC5B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,uBAAyF;AAO7F,UAAM,UAAU,oBAAI,IAA4D;AAChF,UAAM,OAAO;AACb,UAAM,WAAW;AAAA,MACf,CAAC,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,QAC9C,GAAG;AAAA,QACH,QAAQ,CAAC,MAAM,qBAAqB,iBAAiB;AAAA,QACrD,SAASA;AAAA,MACX,CAAC;AAAA,MACD,EAAE,OAAO,EAAE,QAAQ,UAAU,GAAG,UAAU,KAAK;AAAA,IACjD;AACA,qBAAiB,QAAQ,SAAS,MAAM,GAAG;AACzC,iBAAW,KAAK,MAAM;AACpB,gBAAQ,IAAI,OAAO,EAAE,EAAE,GAAG;AAAA,UACxB,WAAW,IAAI,IAAI,SAAS,EAAE,iBAAiB,CAAC;AAAA,UAChD,KAAK,EAAE,mBAAmB;AAAA,QAC5B,CAAC;AAAA,MACH;AAAA,IACF;AAMA,UAAM,YAAmB,CAAC;AAC1B,UAAM,QAAQ;AAAA,MACZ,CAAC,MAAM,KAAK,OAAO,KAAK,yBAAyB,EAAE,GAAG,GAAG,SAASA,YAAW,CAAC;AAAA,MAC9E,EAAE,UAAU,KAAK;AAAA,IACnB;AACA,qBAAiB,QAAQ,MAAM,MAAM,EAAG,WAAU,KAAK,GAAG,IAAI;AAC9D,QAAI,WAAW;AAAG,QAAI,UAAU;AAChC,UAAM,OAAO,oBAAI,IAAyB;AAC1C,eAAW,OAAO,WAAW;AAC3B,YAAM,QAAQ,OAAO,IAAI,UAAU;AACnC,YAAM,OAAO,QAAQ,IAAI,KAAK;AAC9B,YAAM,OAAO,KAAK,IAAI,KAAK,KAAK,KAAK,IAAI,OAAO,oBAAI,IAAI,CAAC,EAAE,IAAI,KAAK;AAEpE,UAAI,CAAC,QAAQ,CAAC,KAAK,UAAU,IAAI,OAAO,IAAI,QAAQ,CAAC,KAAK,KAAK,IAAI,OAAO,IAAI,QAAQ,CAAC,GAAG;AACxF,cAAM,KAAK,OAAO,OAAO,yBAAyB,EAAE,OAAO,EAAE,IAAI,IAAI,GAAG,GAAG,SAASA,YAAW,CAAC;AAChG;AACA;AAAA,MACF;AACA,WAAK,IAAI,OAAO,IAAI,QAAQ,CAAC;AAAA,IAC/B;AAEA,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,YAAY;AACzC,eAAW,CAAC,OAAO,IAAI,KAAK,SAAS;AACnC,YAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,iBAAW,YAAY,KAAK,WAAW;AACrC,YAAI,MAAM,IAAI,QAAQ,EAAG;AACzB,cAAM,KAAK,OAAO,OAAO,yBAAyB;AAAA,UAChD,IAAI,IAAI,MAAM;AAAA,UAAG,YAAY;AAAA,UAAO;AAAA,UACpC,iBAAiB,KAAK;AAAA,UAAK,YAAY;AAAA,QACzC,GAAG,EAAE,SAASA,YAAW,CAAC;AAC1B;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,UAAU,QAAQ,MAAM,UAAU,QAAQ;AAAA,EACrD;AAAA;AAAA;AAAA,EAKQ,kBACN,QAOA,SAC0C;AAC1C,UAAM,IAAS,CAAC;AAChB,QAAI,QAAQ,OAAQ,GAAE,cAAc,OAAO;AAC3C,QAAI,QAAQ,SAAU,GAAE,YAAY,OAAO;AAC3C,QAAI,QAAQ,YAAa,GAAE,eAAe,OAAO;AAQjD,UAAM,YAAa,SAAiB,kBAAkB,SAAS,YAAY;AAC3E,QAAI,UAAW,GAAE,kBAAkB;AAInC,UAAM,IAAI,QAAQ,GAAG,KAAK;AAC1B,QAAI,GAAG;AACL,QAAE,MAAM;AAAA,QACN,EAAE,cAAc,EAAE,WAAW,EAAE,EAAE;AAAA,QACjC,EAAE,aAAa,EAAE,WAAW,EAAE,EAAE;AAAA,QAChC,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE;AAAA,QAC9B,EAAE,cAAc,EAAE,WAAW,EAAE,EAAE;AAAA,QACjC,EAAE,cAAc,EAAE,WAAW,EAAE,EAAE;AAAA,MACnC;AAAA,IACF;AAGA,QAAI,MAAM,QAAQ,QAAQ,MAAM,GAAG;AACjC,YAAM,WAAY,OAAQ,OAA4B,OAAO,OAAO;AACpE,UAAI,SAAS,WAAW,EAAG,GAAE,SAAS,SAAS,CAAC;AAAA,eACvC,SAAS,SAAS,EAAG,GAAE,SAAS,EAAE,KAAK,SAAS;AAAA,IAC3D,WAAW,QAAQ,QAAQ;AACzB,QAAE,SAAS,OAAO;AAAA,IACpB;AACA,WAAO,EAAE,OAAO,GAAG,UAAU;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAc,mBACZ,SACA,WAC0B;AAC1B,QAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,UAAM,QAAa,QAAQ,WAAW,IAClC,EAAE,UAAU,QAAQ,CAAC,EAAE,IACvB,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE;AACjC,QAAI,UAAW,OAAM,kBAAkB;AACvC,UAAM,OAAO,MAAM,KAAK,OAAO,KAAK,yBAAyB;AAAA,MAC3D;AAAA,MAAO,QAAQ,CAAC,YAAY;AAAA,MAC5B,OAAO,iBAAgB;AAAA,MAAoB,SAASA;AAAA,IACtD,CAAC;AACD,UAAM,OAAc,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;AAClD,QAAI,KAAK,UAAU,iBAAgB,oBAAoB;AACrD,WAAK,QAAQ,OAAO,mFAA8E;AAAA,QAChG,KAAK,iBAAgB;AAAA,QAAoB,SAAS,QAAQ;AAAA,MAC5D,CAAC;AAAA,IACH;AACA,WAAO,CAAC,GAAG,IAAI,IAAY,KAAK,IAAI,OAAK,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAc,kBACZ,SACA,WAC6B;AAC7B,QAAI,KAAK,gBAAgB,SAAS,SAAS,EAAG,QAAO;AACrD,UAAMC,OAAM,SAAS,UAAU,OAAO,OAAO,QAAQ,MAAM,IAAI;AAE/D,QAAI,CAACA,KAAK,QAAO,oBAAI,IAAY;AAEjC,UAAM,MAAM,oBAAI,IAAY;AAC5B,UAAM,MAAM,iBAAgB;AAC5B,UAAM,MAAM,CAAC,MAAe,QAAgB;AAC1C,YAAM,OAAc,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;AAClD,iBAAW,KAAK,KAAM,KAAI,IAAI,GAAG,KAAK,KAAM,KAAI,IAAI,OAAO,EAAE,GAAG,CAAC,CAAC;AAClE,UAAI,KAAK,UAAU,KAAK;AACtB,aAAK,QAAQ;AAAA,UACX;AAAA,UACA,EAAE,KAAK,IAAI;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AAGF,iBAAW,MAAO,MAAM,KAAK,mBAAmB,CAACA,IAAG,GAAG,SAAS,KAAM,CAAC,EAAG,KAAI,IAAI,EAAE;AAEpF,YAAM,WAAW,YAAY,EAAE,iBAAiB,UAAU,IAAI,CAAC;AAC/D;AAAA,QACE,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,UAC7C,OAAO,EAAE,cAAcA,MAAK,GAAG,SAAS;AAAA,UACxC,QAAQ,CAAC,IAAI;AAAA,UAAG,OAAO;AAAA,UAAK,SAASD;AAAA,QACvC,CAAC;AAAA,QACD;AAAA,MACF;AAGA;AAAA,QACE,MAAM,KAAK,OAAO,KAAK,uBAAuB;AAAA,UAC5C,OAAO,EAAE,UAAUC,KAAI;AAAA,UACvB,QAAQ,CAAC,YAAY;AAAA,UAAG,OAAO;AAAA,UAAK,SAASD;AAAA,QAC/C,CAAC;AAAA,QACD;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AAEZ,WAAK,QAAQ,OAAO,mDAAmD;AAAA,QACrE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,gBAAgB,OAAY,SAAsC;AACxE,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,QAAQ,SAAS,EAAG,QAAO;AAC/B,QAAI,UAAU,CAAC,GAAG,OAAO;AACzB,UAAM,UAAU,MAAM;AACtB,QAAI,OAAO,YAAY,SAAU,WAAU,QAAQ,OAAO,CAAC,MAAM,MAAM,OAAO;AAAA,aACrE,WAAW,OAAO,YAAY,YAAY,MAAM,QAAQ,QAAQ,GAAG,GAAG;AAC7E,YAAM,MAAM,IAAI,IAAI,QAAQ,IAAI,IAAI,CAAC,MAAe,OAAO,CAAC,CAAC,CAAC;AAC9D,gBAAU,QAAQ,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;AAAA,IAC5C;AACA,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,UAAM,KAAK,QAAQ,WAAW,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,QAAQ;AAC9D,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aACJ,QAUA,SAC+B;AAC/B,UAAM,EAAE,OAAO,UAAU,IAAI,KAAK,kBAAkB,QAAQ,OAAO;AACnE,UAAM,mBAAmB,MAAM,QAAQ,QAAQ,UAAU,IAAI,OAAQ,aAAa,QAAQ,aAAa,CAAC,OAAO,UAAU,IAAI,CAAC,GAC3H,IAAI,OAAK,OAAO,CAAC,EAAE,KAAK,CAAC,EACzB,OAAO,OAAO;AAKjB,UAAM,MAAM,MAAM,KAAK,mBAAmB,iBAAiB,SAAS;AACpE,QAAI,KAAK;AACP,UAAI,IAAI,WAAW,EAAG,QAAO,CAAC;AAC9B,YAAM,KAAK,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI;AAAA,IACpD;AAKA,QAAI,CAAC,KAAK,gBAAgB,OAAO,MAAM,KAAK,kBAAkB,SAAS,SAAS,CAAC,EAAG,QAAO,CAAC;AAE5F,UAAM,WAAgB;AAAA,MACpB;AAAA,MACA,SAAS,CAAC,EAAE,OAAO,cAAc,OAAO,OAAO,CAAC;AAAA,MAChD,SAASA;AAAA,IACX;AACA,QAAI,QAAQ,SAAS,QAAQ,QAAQ,UAAU,MAAM;AACnD,eAAS,QAAQ,KAAK,IAAI,KAAK,IAAI,QAAQ,SAAS,IAAI,CAAC,GAAG,GAAG;AAC/D,UAAI,QAAQ,OAAQ,UAAS,SAAS,KAAK,IAAI,OAAO,QAAQ,CAAC;AAAA,IACjE,OAAO;AAEL,eAAS,QAAQ;AAAA,IACnB;AAEA,UAAM,OAAO,MAAM,KAAK,OAAO,KAAK,wBAAwB,QAAQ;AACpE,UAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,KAAK,IAAI,cAAc,IAAI,CAAC;AAC/D,UAAM,KAAK,WAAW,IAAI;AAC1B,SAAK,cAAc,MAAM,OAAO;AAChC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,cACJ,QACA,SACiB;AACjB,UAAM,EAAE,OAAO,UAAU,IAAI,KAAK,kBAAkB,QAAQ,OAAO;AACnE,UAAM,mBAAmB,MAAM,QAAQ,QAAQ,UAAU,IAAI,OAAQ,aAAa,QAAQ,aAAa,CAAC,OAAO,UAAU,IAAI,CAAC,GAC3H,IAAI,OAAK,OAAO,CAAC,EAAE,KAAK,CAAC,EACzB,OAAO,OAAO;AAEjB,UAAM,MAAM,MAAM,KAAK,mBAAmB,iBAAiB,SAAS;AACpE,QAAI,KAAK;AACP,UAAI,IAAI,WAAW,EAAG,QAAO;AAC7B,YAAM,KAAK,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI;AAAA,IACpD;AAGA,QAAI,CAAC,KAAK,gBAAgB,OAAO,MAAM,KAAK,kBAAkB,SAAS,SAAS,CAAC,EAAG,QAAO;AAE3F,UAAM,UAAW,KAAK,OAAe;AACrC,QAAI,OAAO,YAAY,YAAY;AACjC,UAAI;AACF,cAAM,IAAI,MAAM,QAAQ,KAAK,KAAK,QAAQ,wBAAwB,EAAE,OAAO,SAASA,YAAW,CAAC;AAChG,YAAI,OAAO,MAAM,SAAU,QAAO;AAAA,MACpC,QAAQ;AAAA,MAA6B;AAAA,IACvC;AAIA,UAAM,OAAO,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,MAC1D;AAAA,MAAO,QAAQ,CAAC,IAAI;AAAA,MAAG,OAAO,MAAM,KAAK,IAAI,KAAK,IAAI,MAAM,IAAI;AAAA,MAAK,SAASA;AAAA,IAChF,CAAC;AACD,WAAO,MAAM,QAAQ,IAAI,IAAI,KAAK,SAAS;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAc,gBACZ,WACA,SACoC;AACpC,WAAO,KAAK,YAAY,WAAW,SAAS,KAAK;AAAA,EACnD;AAAA,EAEA,MAAM,WAAW,WAAmB,SAA+D;AACjG,WAAO,KAAK,YAAY,WAAW,SAAS,IAAI;AAAA,EAClD;AAAA,EAEA,MAAc,YACZ,WACA,SACA,mBACoC;AACpC,QAAI,CAAC,UAAW,QAAO;AACvB,UAAM,QAAa,EAAE,IAAI,UAAU;AAEnC,UAAM,YAAa,SAAiB,kBAAkB,SAAS;AAC/D,QAAI,UAAW,OAAM,kBAAkB;AACvC,UAAM,OAAO,MAAM,KAAK,OAAO,KAAK,wBAAwB;AAAA,MAC1D;AAAA,MAAO,OAAO;AAAA,MAAG,SAASA;AAAA,IAC5B,CAAC;AACD,QAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,EAAG,QAAO;AAI7C,QAAI,mBAAmB;AACrB,YAAM,UAAU,MAAM,KAAK,kBAAkB,SAAS,aAAa,IAAI;AACvE,UAAI,WAAW,CAAC,QAAQ,IAAI,OAAO,KAAK,CAAC,EAAE,EAAE,CAAC,EAAG,QAAO;AAAA,IAC1D;AACA,UAAM,MAAM,eAAe,KAAK,CAAC,CAAC;AAClC,UAAM,KAAK,WAAW,CAAC,GAAG,CAAC;AAC3B,UAAM,KAAK,gBAAgB,GAAG;AAC9B,UAAM,KAAK,uBAAuB,KAAK,KAAK,CAAC,CAAC;AAC9C,SAAK,cAAc,CAAC,GAAG,GAAG,OAAO;AACjC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,uBAAuB,KAAyB,KAAyB;AACrF,QAAI;AACF,UAAI,IAAI,WAAW,UAAW;AAC9B,YAAM,MAAM,UAAe,IAAI,kBAAkB,MAAS;AAC1D,YAAM,WAAW,KAAK,YAAY;AAClC,UAAI,aAAa,eAAe,aAAa,YAAY,aAAa,YAAa;AAEnF,YAAM,OAAO,MAAM,KAAK,OAAO,KAAK,uBAAuB;AAAA,QACzD,OAAO,EAAE,YAAY,IAAI,IAAI,YAAY,GAAG,QAAQ,UAAU;AAAA,QAAG,OAAO;AAAA,QAAM,SAASA;AAAA,MACzF,CAAC;AACD,YAAM,WAAW,IAAI,KAAa,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAW,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,OAAO,CAAC;AAEvG,YAAM,WAAW,KAAK;AACtB,YAAM,QAAQ,WAAW,OAAO,KAAK,QAAQ,IAAI,CAAC,GAAG,UAAU,GAAI,IAAI,qBAAqB,CAAC,CAAE;AAC/F,YAAM,QAAQ,MAAM,UAAU;AAE9B,YAAM,WAAgB,EAAE,UAAU,KAAK,SAAS,MAAM,MAAM,MAAM;AAClE,UAAI,aAAa,UAAU;AACzB,iBAAS,OAAO,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,gBAAgB,KAAK,GAAG,KAAK;AAAA,MACzE,WAAW,aAAa,eAAe,UAAU;AAC/C,cAAM,eAAe,KAAK,IAAI,GAAG,KAAK,gBAAgB,CAAC;AACvD,cAAM,OAA+B,CAAC;AACtC,mBAAW,MAAM,OAAO,OAAO,QAAQ,EAAG,YAAW,KAAK,GAAI,MAAK,CAAC,KAAK,KAAK,CAAC,KAAK,KAAK;AACzF,cAAM,MAA8B,CAAC;AACrC,mBAAW,KAAK,SAAU,YAAW,KAAM,SAAS,CAAC,KAAK,CAAC,EAAI,KAAI,CAAC,KAAK,IAAI,CAAC,KAAK,KAAK;AACxF,iBAAS,SAAS,OAAO,KAAK,IAAI,EAAE,KAAK,EAAE,IAAI,OAAK;AAClD,gBAAM,OAAO,KAAK,IAAI,cAAc,KAAK,CAAC,CAAC;AAC3C,iBAAO,EAAE,OAAO,GAAG,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,MAAM,YAAY,IAAI,CAAC,KAAK,MAAM,KAAK;AAAA,QAC9F,CAAC;AACD,iBAAS,MAAM,SAAS,OAAO,OAAO,CAAC,MAAW,EAAE,SAAS,EAAE;AAC/D,iBAAS,OAAO,SAAS,OAAO;AAQhC,cAAM,gBAA0C,CAAC;AACjD,mBAAW,KAAM,IAAI,qBAAqB,CAAC,GAAI;AAC7C,gBAAM,SAAS,SAAS,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,KAAK,CAAC,CAAC;AACjE,cAAI,MAAM,OAAQ,eAAc,CAAC,IAAI;AAAA,QACvC;AACA,YAAI,OAAO,KAAK,aAAa,EAAE,QAAQ;AACrC,UAAC,IAAY,0BAA0B;AAAA,QACzC;AAAA,MACF;AACA,MAAC,IAAY,oBAAoB;AAAA,IACnC,QAAQ;AAAA,IAAgC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBQ,cAAc,MAA4B,SAAiC;AACjF,UAAMC,OAAM,SAAS,UAAU,OAAO,OAAO,QAAQ,MAAM,IAAI;AAC/D,eAAW,OAAO,MAAM;AACtB,YAAM,UAAU,IAAI,qBAAqB,CAAC;AAC1C,MAAC,IAAY,SAAS;AAAA,QACpB,SAAS,IAAI,WAAW,aAAa,CAAC,CAACA,QAAO,QAAQ,SAASA,IAAG;AAAA,QAClE,cAAc,CAAC,CAACA,QAAO,IAAI,gBAAgB,QAAQ,OAAO,IAAI,YAAY,MAAMA;AAAA,QAChF,cAAc,IAAI,WAAW,aACxB,KAAK,gBAAgB,SAAU,IAAY,mBAAmB,IAAI;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,gBAAgB,KAAwC;AACpE,QAAI;AACF,YAAM,WAAW,IAAI,cAAc,WAAW,OAAO,IAAI,IAAI,aAAa,MAAM,CAAC,IAAI;AACrF,UAAI,CAAC,YAAY,OAAO,KAAK,YAAY,YAAY,WAAY;AACjE,YAAM,OAAY,MAAM,KAAK,WAAW,QAAQ,QAAQ;AACxD,UAAI,CAAC,MAAM,OAAO,OAAQ;AAC1B,YAAM,YAAY,IAAI,IAAiB,KAAK,MAAM,IAAI,CAAC,MAAW,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAC5E,YAAM,QAA8C,CAAC;AACrD,YAAM,OAAO,oBAAI,IAAY;AAC7B,UAAI,MAAW,KAAK,MAAM,KAAK,CAAC,MAAW,EAAE,SAAS,OAAO;AAC7D,aAAO,OAAO,CAAC,KAAK,IAAI,IAAI,EAAE,GAAG;AAC/B,aAAK,IAAI,IAAI,EAAE;AACf,YAAI,IAAI,SAAS,WAAY,OAAM,KAAK,EAAE,IAAI,IAAI,IAAI,OAAO,IAAI,SAAS,IAAI,GAAG,CAAC;AAClF,cAAM,OAAO,KAAK,SAAS,CAAC,GAAG,OAAO,CAAC,MAAW,EAAE,WAAW,IAAI,EAAE;AACrE,YAAI,CAAC,IAAI,OAAQ;AACjB,cAAM,OAAO,IAAI,KAAK,CAAC,MAAW,EAAE,UAAU,SAAS,KAClD,IAAI,KAAK,CAAC,MAAW,EAAE,UAAU,MAAM,KACvC,IAAI,CAAC;AACV,cAAM,UAAU,IAAI,KAAK,MAAM;AAAA,MACjC;AACA,UAAI,MAAM,WAAW,EAAG;AACxB,YAAM,YAAY,IAAI,gBAAgB,IAAI;AAC1C,YAAM,aAAa,MAAM,UAAU,OAAK,EAAE,OAAO,SAAS;AAC1D,MAAC,IAAY,aAAa,MAAM,IAAI,CAAC,GAAG,OAAO;AAAA,QAC7C,GAAG;AAAA,QACH,OAAO,aAAa,IAAI,aACpB,IAAI,aAAa,SACjB,MAAM,aAAc,IAAI,WAAW,aAAa,SAAS,YACzD;AAAA,MACN,EAAE;AAAA,IACJ,QAAQ;AAAA,IAA2C;AAAA,EACrD;AAAA,EAEA,MAAM,YAAY,WAAmB,SAAyD;AAC5F,QAAI,CAAC,UAAW,QAAO,CAAC;AAIxB,UAAM,MAAM,MAAM,KAAK,WAAW,WAAW,OAAO;AACpD,QAAI,CAAC,IAAK,QAAO,CAAC;AAClB,UAAM,OAAO,MAAM,KAAK,OAAO,KAAK,uBAAuB;AAAA,MACzD,OAAO,EAAE,YAAY,UAAU;AAAA,MAC/B,OAAO;AAAA,MACP,SAAS,CAAC,EAAE,OAAO,cAAc,OAAO,MAAM,CAAC;AAAA,MAC/C,SAASD;AAAA,IACX,CAAC;AACD,UAAM,UAAU,MAAM,QAAQ,IAAI,IAAI,KAAK,IAAI,aAAa,IAAI,CAAC;AAIjE,UAAM,QAAQ,MAAM,KAAK;AAAA,MACvB,QACG,QAAQ,OAAK,CAAC,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,CAAC,EACzD,OAAO,QAAM,MAAM,CAAC,GAAG,SAAS,GAAG,CAAC;AAAA,IACzC;AACA,eAAW,KAAK,SAAkB;AAChC,YAAM,IAAI,EAAE,WAAW,MAAM,IAAI,OAAO,EAAE,QAAQ,CAAC,IAAI;AACvD,UAAI,EAAG,GAAE,aAAa;AACtB,YAAM,WAAW,EAAE,gBAAgB,MAAM,IAAI,OAAO,EAAE,aAAa,CAAC,IAAI;AACxE,UAAI,SAAU,GAAE,qBAAqB;AACrC,YAAM,SAAS,EAAE,cAAc,MAAM,IAAI,OAAO,EAAE,WAAW,CAAC,IAAI;AAClE,UAAI,OAAQ,GAAE,mBAAmB;AAAA,IACnC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,kBAAkB,UAAkB,SAA6C;AACrF,QAAI,CAAC,SAAU,QAAO;AACtB,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,OAAO,KAAK,uBAAuB;AAAA,QACzD,OAAO,EAAE,IAAI,SAAS;AAAA,QACtB,OAAO;AAAA,QACP,SAASA;AAAA,MACX,CAAC;AACD,YAAM,aAAa,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI,SAAY;AAC/D,UAAI,CAAC,UAAW,QAAO;AAEvB,aAAO,CAAC,CAAE,MAAM,KAAK,WAAW,OAAO,SAAS,GAAG,OAAO;AAAA,IAC5D,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAAA;AA/hHa,iBA0lGa,qBAAqB;AA1lGxC,IAAM,kBAAN;;;AEtiBP,SAAS,gBAAAO,eAAc,SAAAC,cAAa;AAa7B,IAAM,mBAAmBD,cAAa,OAAO;AAAA,EAClD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAM;AAAA,EACN,UAAU;AAAA,EACV,WAAW;AAAA,EACX,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,WAAW;AAAA;AAAA,EAEX,QAAQ;AAAA,IACN,IAAIC,OAAM,KAAK,EAAE,OAAO,YAAY,UAAU,MAAM,UAAU,MAAM,OAAO,SAAS,CAAC;AAAA,IAErF,iBAAiBA,OAAM,OAAO,oBAAoB;AAAA,MAChD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,YAAYA,OAAM,KAAK;AAAA,MACrB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,UAAU;AAAA,MACV,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,IAED,YAAYA,OAAM,KAAK;AAAA,MACrB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,QAAQA,OAAM,OAAO,CAAC,WAAW,QAAQ,GAAG;AAAA,MAC1C,OAAO;AAAA,MACP,UAAU;AAAA,MACV,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,aAAaA,OAAM,KAAK;AAAA,MACtB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,MACX,UAAU;AAAA,MACV,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,IAED,YAAYA,OAAM,SAAS;AAAA,MACzB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,aAAaA,OAAM,SAAS;AAAA,MAC1B,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,IAED,YAAYA,OAAM,SAAS;AAAA,MACzB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,cAAc;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,SAAS;AAAA,IACP,EAAE,QAAQ,CAAC,YAAY,EAAE;AAAA,IACzB,EAAE,QAAQ,CAAC,YAAY,EAAE;AAAA,EAC3B;AAAA,EAEA,QAAQ;AAAA;AAAA;AAAA,IAGN,YAAY,CAAC,OAAO,MAAM;AAAA,EAC5B;AACF,CAAC;;;ACnFD,SAAS,IAAI,GAAoB;AAC/B,SAAO,OAAO,KAAK,EAAE,EAClB,WAAW,KAAK,OAAO,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,MAAM,EACvE,WAAW,KAAK,QAAQ,EAAE,WAAW,KAAK,OAAO;AACtD;AAEA,SAAS,MAAM,OAAe,MAAsB;AAClD,SAAO;AAAA;AAAA;AAAA;AAAA,SAIA,IAAI,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yCAmBsB,IAAI;AAC7C;AAEA,SAAS,YAAY,KAAiC;AACpD,QAAM,OAAgC;AAAA,IACpC,CAAC,6BAAgB,IAAI,iBAAiB,IAAI,YAAY;AAAA,IACtD,CAAC,0BAAa,IAAI,cAAc,IAAI,gBAAgB,QAAG;AAAA,IACvD,CAAC,4BAAe,IAAI,gBAAgB,IAAI,SAAS;AAAA,IACjD,CAAC,4BAAe,IAAI,gBAAgB,IAAI,WAAW;AAAA,IACnD,CAAC,qCAAmB,IAAI,kBAAkB,IAAI,gBAAgB,QAAG;AAAA,EACnE;AACA,SAAO,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,oCAAoC,IAAI,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,YAAY,EAAE,KAAK,EAAE;AAChH;AAGO,SAAS,kBAAkB,OAMvB;AACT,QAAM,YAAY,MAAM,WAAW;AACnC,QAAM,OAAO,YAAY,8BAAiB;AAC1C,SAAO,MAAM,GAAG,IAAI,oBAAe;AAAA,UAC3B,YAAY,iCAA4B,6BAAwB;AAAA,qBACrD,YAAY,iEAAe,8DAAY;AAAA,wDAChC,IAAI,MAAM,UAAU,CAAC;AAAA,MAC3C,YAAY,MAAM,OAAO,CAAC;AAAA,kCACE,IAAI,MAAM,OAAO,CAAC;AAAA,iDACH,IAAI,MAAM,KAAK,CAAC;AAAA,qCAC5B,YAAY,YAAY,QAAQ,KAAK,IAAI;AAAA;AAAA,2KAEe;AAC7F;AAEA,IAAM,cAA4E;AAAA,EAChF,UAAc,EAAE,KAAK,MAAQ,OAAO,2CAAqB,MAAM,mFAAwC;AAAA,EACvG,UAAc,EAAE,KAAK,MAAQ,OAAO,2CAAqB,MAAM,mFAAwC;AAAA,EACvG,SAAc,EAAE,KAAK,OAAQ,OAAO,8CAAuB,MAAM,qFAA0C;AAAA,EAC3G,SAAc,EAAE,KAAK,QAAQ,OAAO,oDAAwB,MAAM,0HAA2D;AAAA,EAC7H,UAAc,EAAE,KAAK,QAAQ,OAAO,oDAAwB,MAAM,+FAA+C;AAAA,EACjH,aAAc,EAAE,KAAK,QAAQ,OAAO,uDAA2B,MAAM,mHAAoD;AAAA,EACzH,cAAc,EAAE,KAAK,QAAQ,OAAO,qEAAoC,MAAM,oHAA0D;AAC1I;AAGO,SAAS,iBAAiB,MAAgC,SAAsC;AACrG,QAAM,OAAO,YAAY,IAAI,KAAK,YAAY;AAC9C,SAAO,MAAM,KAAK,OAAO;AAAA,yBACF,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC;AAAA,MAC/C,UAAU,YAAY,OAAO,IAAI,EAAE;AAAA,SAChC,IAAI,KAAK,IAAI,CAAC;AAAA,2HACmE;AAC1F;;;AC3BO,IAAM,yBAAyB;AAmBtC,SAASC,WAAmB,KAAc,UAAgB;AACxD,MAAI,OAAO,QAAQ,QAAQ,GAAI,QAAO;AACtC,MAAI,OAAO,QAAQ,UAAU;AAC3B,QAAI;AAAE,aAAO,KAAK,MAAM,GAAG;AAAA,IAAQ,QAAQ;AAAE,aAAO;AAAA,IAAU;AAAA,EAChE;AACA,SAAO;AACT;AAYA,IAAM,qBAAqB;AAQ3B,IAAMC,cAAa,EAAE,UAAU,MAAM,WAAW,CAAC,GAAG,aAAa,CAAC,EAAE;AAEpE,SAAS,YAAY,SAAwB;AAC3C,QAAM,MAAW,IAAI,MAAM,kBAAkB,OAAO,EAAE;AACtD,MAAI,OAAO;AACX,MAAI,aAAa;AACjB,QAAM;AACR;AAUA,SAAS,SAAS,IAA4C;AAC5D,MAAI,OAAO,OAAO,SAAU,QAAO,CAAC,EAAE;AACtC,MAAI,OAAO,OAAO,SAAU,QAAO,OAAO,KAAK,OAAO,CAAC,EAAE;AACzD,MAAI,MAAM,OAAO,OAAO,YAAY,MAAM,QAAS,GAAW,GAAG,GAAG;AAClE,UAAM,MAAO,GAAW;AACxB,UAAM,UAAU,IAAI,OAAO,CAAC,MAA4B,OAAO,MAAM,YAAY,OAAO,MAAM,QAAQ;AACtG,WAAO,QAAQ,WAAW,IAAI,SAAS,UAAU;AAAA,EACnD;AACA,SAAO;AACT;AAGA,eAAe,kBACb,QACA,YACA,UACqB;AACrB,MAAI;AACF,UAAM,OAAO,MAAM,OAAO,KAAK,wBAAwB;AAAA,MACrD,OAAO,EAAE,aAAa,YAAY,WAAW,OAAO,QAAQ,GAAG,QAAQ,UAAU;AAAA,MACjF,OAAO;AAAA,MACP,SAAS,EAAE,GAAGA,YAAW;AAAA,IAC3B,CAAQ;AACR,WAAO,MAAM,QAAQ,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI;AAAA,EACpD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAe,0BACb,QACA,YACA,WACgB;AAChB,MAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AACpC,MAAI,UAAU,SAAS,oBAAoB;AACzC;AAAA,MACE,oDAAoD,kBAAkB,gBAAgB,UAAU;AAAA,IAElG;AAAA,EACF;AACA,MAAI,UAAU,WAAW,GAAG;AAC1B,UAAM,MAAM,MAAM,kBAAkB,QAAQ,YAAY,OAAO,UAAU,CAAC,CAAC,CAAC;AAC5E,WAAO,MAAM,CAAC,GAAG,IAAI,CAAC;AAAA,EACxB;AACA,MAAI;AACF,UAAM,OAAO,MAAM,OAAO,KAAK,wBAAwB;AAAA,MACrD,OAAO,EAAE,aAAa,YAAY,WAAW,EAAE,KAAK,UAAU,IAAI,MAAM,EAAE,GAAG,QAAQ,UAAU;AAAA,MAC/F,OAAO,qBAAqB;AAAA,MAC5B,SAAS,EAAE,GAAGA,YAAW;AAAA,IAC3B,CAAQ;AACR,WAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;AAAA,EACvC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAcA,eAAe,yBACb,QACA,YACuB;AACvB,MAAI;AACF,UAAM,OAAO,MAAM,OAAO,KAAK,wBAAwB;AAAA,MACrD,OAAO,EAAE,aAAa,YAAY,QAAQ,UAAU;AAAA,MACpD,OAAO,qBAAqB;AAAA,MAC5B,SAAS,EAAE,GAAGA,YAAW;AAAA,IAC3B,CAAQ;AACR,WAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;AAAA,EACvC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUA,eAAe,uBACb,QACA,YACA,OACA,YACgB;AAChB,QAAM,YAAY,WAAW,IAAI,CAAC,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;AAClE,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,OAAO,KAAK,YAAY;AAAA,MACnC,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,UAAU,EAAE,CAAC,EAAE;AAAA,MACnD,QAAQ,CAAC,IAAI;AAAA,MACb,OAAO,UAAU;AAAA,MACjB,SAAS,EAAE,GAAGA,YAAW;AAAA,IAC3B,CAAQ;AAAA,EACV,SAAS,KAAK;AACZ;AAAA,MACE,sDAAsD,UAAU,kBAC3D,KAAe,WAAW,OAAO,GAAG,CAAC,MAAM,WAAW,MAAM;AAAA,IAEnE;AAAA,EACF;AACA,QAAM,UAAU,IAAI,KAAK,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,CAAC,MAAW,OAAO,GAAG,EAAE,CAAC,CAAC;AACxF,SAAO,WAAW,OAAO,CAAC,MAAM,QAAQ,IAAI,OAAO,GAAG,aAAa,EAAE,CAAC,CAAC;AACzE;AAeA,eAAe,eACb,QACA,KACA,YACgB;AAChB,QAAM,OAAO,SAAS,KAAK,OAAO,EAAE;AACpC,MAAI,KAAM,QAAO,0BAA0B,QAAQ,YAAY,IAAI;AAInE,QAAM,WAAY,KAAK,OAAO,SAAiB;AAC/C,QAAM,WAAW,aAAa,UAAa,aAAa;AACxD,QAAM,WAAW,YAAY,OAAO,aAAa,YAAY,CAAC,MAAM,QAAQ,QAAQ,IAChF,WACA;AAEJ,QAAM,WAAW,WAAW,SAAS,SAAS,EAAE,IAAI;AACpD,QAAM,aAAa,WACf,MAAM,0BAA0B,QAAQ,YAAY,QAAQ,IAC5D,MAAM,yBAAyB,QAAQ,UAAU;AACrD,MAAI,eAAe,KAAM,QAAO,CAAC;AACjC,MAAI,WAAW,WAAW,EAAG,QAAO,CAAC;AACrC,MAAI,WAAW,SAAS,oBAAoB;AAC1C;AAAA,MACE,mCAAmC,UAAU,gBAAgB,kBAAkB;AAAA,IAEjF;AAAA,EACF;AAEA,MAAI,CAAC,SAAU,QAAO;AAEtB,MAAI,YAAY,YAAY,OAAO,KAAK,QAAQ,EAAE,MAAM,CAAC,MAAM,MAAM,IAAI,EAAG,QAAO;AACnF,SAAO,uBAAuB,QAAQ,YAAY,UAAU,UAAU;AACxE;AAMO,SAAS,qBAAqB,QAAuB,QAA8B;AACxF,SAAO,aAAa,gBAAgB,OAAO,QAAa;AACtD,UAAM,SAAU,KAAK,UAAU,KAAK;AAEpC,QAAI,CAAC,UAAU,OAAO,MAAM,EAAE,WAAW,cAAc,EAAG;AAE1D,UAAM,OAAQ,KAAK,OAAO,QAAQ,CAAC;AACnC,UAAM,gBAAgB,OAAO,KAAK,IAAI,EAAE,OAAO,CAAC,MAAM,MAAM,QAAQ,MAAM,YAAY;AACtF,QAAI,cAAc,WAAW,EAAG;AAQhC,QAAK,KAAK,SAAiB,SAAU;AAQrC,UAAM,SAAS,MAAM,eAAe,QAAQ,KAAK,MAAM;AACvD,QAAI,OAAO,WAAW,EAAG;AAqBzB,UAAM,YAAa,KAAK,YAAoB;AAK5C,eAAW,WAAW,QAAQ;AAC5B,UAAI,aAAa,SAAS,eAAe,OAAO,SAAS,MAAM,OAAO,QAAQ,WAAW,EAAG;AAE5F,YAAM,SAASD,WAAe,SAAS,kBAAkB,CAAC,CAAC;AAC3D,UAAI,QAAQ,eAAe,MAAO;AAGlC,YAAM,SAAS,QAAQ;AACvB,UAAI,OAAO,WAAW,YAAY,UAAU,cAAc,MAAM,CAAC,MAAM,MAAM,MAAM,EAAG;AAEtF;AAAA,QACE,WAAW,OAAO,SAAS,aAAa,EAAE,CAAC,SAAS,MAAM;AAAA,MAC5D;AAAA,IACF;AAAA,EACF,GAAG,EAAE,WAAW,wBAAwB,UAAU,GAAG,CAAC;AAEtD,UAAQ,OAAO,oCAAoC;AACrD;AAGO,IAAM,oBAAoB;AAiD1B,SAAS,yBAAyB,QAAuB,QAA8B;AAC5F,QAAM,YAAY,CAAC,aAAsB,OAAO,QAAa;AAC3D,UAAM,UAAW,KAAK,WAAW,CAAC;AAClC,QAAI,QAAQ,SAAU;AAKtB,UAAM,SAAS,QAAQ,UAAU,OAAO,OAAO,QAAQ,MAAM,IAAI;AACjE,UAAM,OAAO,KAAK,OAAO;AACzB,UAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,OAAQ,QAAQ,OAAO,SAAS,WAAW,CAAC,IAAI,IAAI,CAAC;AACxF,UAAM,OAAO,MAAa;AACxB,YAAM,MAAW,IAAI;AAAA,QACnB,0FACG,SAAS,MAAM,MAAM,OAAO;AAAA,MACjC;AACA,UAAI,OAAO;AACX,UAAI,aAAa;AACjB,YAAM;AAAA,IACR;AACA,eAAW,OAAO,MAAM;AACtB,UAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,EAAG;AAC3D,YAAM,MAAM,OAAO,UAAU,eAAe,KAAK,KAAK,cAAc;AACpE,YAAM,WAAW,MAAM,OAAQ,IAAY,gBAAgB,EAAE,IAAI;AACjE,UAAI,aAAa,CAAC,OAAO,aAAa,KAAK;AAGzC,YAAI,CAAC,OAAQ,MAAK;AAClB,QAAC,IAAY,eAAe;AAC5B;AAAA,MACF;AAEA,UAAI,OAAO,aAAa,OAAQ,MAAK;AAAA,IACvC;AAAA,EACF;AACA,SAAO,aAAa,gBAAgB,UAAU,IAAI,GAAG,EAAE,QAAQ,mBAAmB,WAAW,wBAAwB,UAAU,GAAG,CAAC;AACnI,SAAO,aAAa,gBAAgB,UAAU,KAAK,GAAG,EAAE,QAAQ,mBAAmB,WAAW,wBAAwB,UAAU,GAAG,CAAC;AACpI,UAAQ,OAAO,0CAA0C;AAC3D;AAGO,SAAS,eAAe,QAA+B;AAC5D,SAAO,OAAO,yBAAyB,sBAAsB;AAC/D;;;ACpcA;AAAA,EACE,0BAAAE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;;;ACKP;AAAA,EACE;AAAA,EACA,6BAAAC;AAAA,OACK;AAiBA,IAAM,qCAAqC;AAS3C,SAAS,2BACd,YACA,QACM;AACN,aAAW,qBAAqB;AAAA,IAC9B,MAAMA;AAAA,IACN,YAAY,uBAAuB;AAAA,MACjC,MAAMA;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,aACE;AAAA,MAGF,MAAM;AAAA;AAAA,MAEN,UAAU;AAAA,MACV,WAAW,CAAC,MAAM;AAAA,MAClB,QAAQ;AAAA,MACR,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,MAIf,iBAAiB;AAAA;AAAA;AAAA;AAAA,IAInB,CAAC;AAAA,IACD,MAAM,QAAQ,MAAM;AAMlB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa,GAAG,kCAAkC,GAAG,KAAK,EAAE;AAAA,MAC9D;AAAA,IACF;AAAA,EACF,CAAC;AAED,UAAQ,OAAO,6DAA6D;AAC9E;;;AD1CA,IAAMC,cAA+B,EAAE,UAAU,MAAM,WAAW,CAAC,GAAG,aAAa,CAAC,EAAE;AAStF,SAAS,cAAc,WAA0D;AAC/E,QAAM,OAAgC,CAAC;AACvC,aAAW,CAAC,KAAK,KAAK,KAAK,WAAW;AACpC,UAAM,OAAO,IAAI,MAAM,GAAG;AAC1B,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,UAAI,OAAO,OAAO,KAAK,CAAC,CAAC,MAAM,YAAY,OAAO,KAAK,CAAC,CAAC,MAAM,MAAM;AACnE,eAAO,KAAK,CAAC,CAAC,IAAI,CAAC;AAAA,MACrB;AACA,eAAS,OAAO,KAAK,CAAC,CAAC;AAAA,IACzB;AACA,WAAO,KAAK,KAAK,SAAS,CAAC,CAAC,IAAI;AAAA,EAClC;AACA,SAAO;AACT;AAcO,SAAS,qBACd,YACA,SACA,QACM;AACN,6BAA2B,YAAY,MAAM;AAC7C,aAAW,qBAAqB;AAAA,IAC9B,MAAM;AAAA,IACN,YAAYC,wBAAuB;AAAA,MACjC,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,MAEb,MAAM;AAAA,MACN,UAAU;AAAA,MACV,WAAW,CAAC,MAAM;AAAA,MAClB,QAAQ;AAAA;AAAA,MAER,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASf,iBAAiB;AAAA;AAAA;AAAA;AAAA,MAIjB,cAAc,gCAAgC;AAAA,IAChD,CAAC;AAAA,IACD,MAAM,QAAQ,MAAM,WAAW,SAAS;AACtC,YAAM,SAAS,yBAAyB,UAAU,KAAK,UAAU,CAAC,CAAC;AACnE,UAAI,CAAC,OAAO,SAAS;AACnB,cAAM,MAAM,OAAO,MAAM,OAAO,IAAI,OAAK,GAAG,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI;AACvF,eAAO,EAAE,SAAS,OAAO,OAAO,kBAAkB,KAAK,EAAE,yBAAyB,GAAG,GAAG;AAAA,MAC1F;AACA,YAAM,SAAS,OAAO;AAEtB,YAAM,QAAQ,UAAU,IAAI,QAAQ;AACpC,YAAM,SAAU,UAAU,IAAI,SAAS,KAAK,SAAS,UAAU,CAAC;AAChE,YAAM,SAAU,SAAS,UAAW,QAAgB;AACpD,YAAM,WAAY,QAAgB;AAElC,UAAI,CAAC,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,kBAAkB,KAAK,EAAE,oBAAoB;AACzF,UAAI,CAAC,OAAQ,QAAO,EAAE,SAAS,OAAO,OAAO,kBAAkB,KAAK,EAAE,iCAAiC;AACvG,UAAI,CAAC,SAAU,QAAO,EAAE,SAAS,OAAO,OAAO,kBAAkB,KAAK,EAAE,6BAA6B;AAKrG,YAAM,WAAY,UAAU,IAAI,WAAW,KAA4B,SAAS;AAChF,YAAM,YAAY,UAAU,IAAI,YAAY;AAE5C,UAAI;AACF,cAAM,UAAU,MAAM,QAAQ,gBAAgB;AAAA,UAC5C;AAAA,UACA,UAAU,OAAO,QAAQ;AAAA,UACzB,OAAO,OAAO,KAAK;AAAA,UACnB,QAAQ,KAAK;AAAA,UACb;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAAA,UACzD,aAAa,SAAS,UAAU;AAAA,UAChC;AAAA,UACA,gBAAgB,SAAS,kBAAkB,SAAS,YAAY;AAAA;AAAA;AAAA;AAAA,UAIhE,WAAW,cAAc,SAAS;AAAA,QACpC,GAAG;AAAA,UACD,GAAGD;AAAA,UACH,QAAQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAYjB,gBAAgB,SAAS;AAAA,UACzB,UAAU,SAAS;AAAA,QACrB,CAAqB;AAMrB,YAAI,kBAAkB,SAAS;AAC7B,kBAAQ,OAAO,kEAAkE;AAAA,YAC/E,MAAM,KAAK;AAAA,YAAI,KAAK,OAAO,KAAK;AAAA,UAClC,CAAC;AACD,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,aAAa;AAAA,YACb,QAAQ,EAAE,UAAU,WAAW,cAAc,KAAK;AAAA,UACpD;AAAA,QACF;AAEA,gBAAQ,OAAO,2CAA2C;AAAA,UACxD,MAAM,KAAK;AAAA,UAAI,SAAS,QAAQ;AAAA,UAAI,KAAK,OAAO,KAAK;AAAA,QACvD,CAAC;AAGD,eAAO,EAAE,SAAS,MAAM,SAAS,MAAM,aAAa,QAAQ,GAAG;AAAA,MACjE,SAAS,KAAU;AACjB,eAAO,EAAE,SAAS,OAAO,OAAO,kBAAkB,KAAK,EAAE,MAAM,KAAK,WAAW,OAAO,GAAG,CAAC,GAAG;AAAA,MAC/F;AAAA,IACF;AAAA,EACF,CAAC;AAED,UAAQ,OAAO,+CAA+C;AAChE;;;AEjKO,IAAM,yBAAN,MAA+C;AAAA,EAWpD,YAAY,UAAkC,CAAC,GAAG;AAVlD,gBAAO;AACP,mBAAU;AACV,gBAAO;AACP,wBAAe,CAAC,iCAAiC;AAKjD,SAAQ,yBAAyB;AAG/B,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAM,KAAK,KAAmC;AAC5C,QAAI,WAAuC,UAAU,EAAE,SAAS;AAAA,MAC9D,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO;AAAA,MACP,mBAAmB;AAAA,MACnB,WAAW;AAAA,MACX,SAAS,CAAC,oBAAoB,mBAAmB,qBAAqB,kBAAkB,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAc7G,yBAAyB;AAAA,QACvB;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU;AAAA,UACV,OAAO;AAAA,YACL,EAAE,IAAI,uBAAuB,MAAM,aAAa,OAAO,mBAAmB,cAAc,mBAAmB,MAAM,cAAc;AAAA,YAC/H,EAAE,IAAI,yBAAyB,MAAM,UAAU,OAAO,YAAY,YAAY,wBAAwB,MAAM,SAAS,gBAAgB,uBAAuB;AAAA,YAC5J,EAAE,IAAI,wBAAwB,MAAM,UAAU,OAAO,kBAAkB,YAAY,uBAAuB,MAAM,WAAW,gBAAgB,sBAAsB;AAAA,YACjK,EAAE,IAAI,4BAA4B,MAAM,UAAU,OAAO,qBAAqB,YAAY,2BAA2B,MAAM,cAAc,gBAAgB,0BAA0B;AAAA,UACrL;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAGD,QAAI,OAAQ,IAAY,SAAS,YAAY;AAC3C,MAAC,IAAY,KAAK,gBAAgB,YAAY;AAC5C,YAAI;AACF,gBAAM,OAAO,IAAI,WAAyB,MAAM;AAChD,cAAI,QAAQ,OAAO,KAAK,qBAAqB,YAAY;AACvD,kBAAM,EAAE,uBAAAE,uBAAsB,IAAI,MAAM;AACxC,uBAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQA,sBAAqB,GAAG;AAClE,mBAAK,iBAAiB,QAAQ,IAA+B;AAAA,YAC/D;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAAsB;AAAA,MAChC,CAAC;AAAA,IACH;AACA,QAAI,OAAO,KAAK,4CAA4C;AAAA,EAC9D;AAAA,EAEA,MAAM,MAAM,KAAmC;AAC7C,QAAI,KAAK,QAAQ,eAAgB;AAMjC,QAAI,SAAiC;AACrC,QAAI;AAAE,eAAS,IAAI,WAA4B,UAAU;AAAA,IAAG,QACtD;AAAE,UAAI;AAAE,iBAAS,IAAI,WAA4B,MAAM;AAAA,MAAG,QAAQ;AAAA,MAAe;AAAA,IAAE;AACzF,QAAI,CAAC,QAAQ;AACX,UAAI,OAAO,KAAK,0EAAqE;AACrF;AAAA,IACF;AACA,SAAK,SAAS;AAEd,SAAK,UAAU,IAAI,gBAAgB;AAAA,MACjC;AAAA,MACA,QAAQ,IAAI;AAAA,MACZ,eAAe,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAM5B,gBAAgB,MAAM;AACpB,YAAI;AACF,gBAAM,UAAU,IAAI,WAAiC,SAAS;AAC9D,gBAAM,UAAU,SAAS;AACzB,iBAAO,OAAO,YAAY,YAAY,UAAU,UAAU;AAAA,QAC5D,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC;AAMD,QAAI,CAAC,KAAK,QAAQ,kBAAkB;AAClC,UAAI;AACF,uBAAe,MAAM;AACrB,6BAAqB,QAAQ,IAAI,MAAM;AACvC,iCAAyB,QAAQ,IAAI,MAAM;AAAA,MAC7C,SAAS,KAAU;AACjB,YAAI,OAAO,OAAO,6CAA6C,EAAE,OAAO,KAAK,QAAQ,CAAC;AAAA,MACxF;AAAA,IACF;AAEA,QAAI,gBAAgB,aAAa,KAAK,OAAO;AAC7C,QAAI,OAAO,KAAK,4CAA4C;AAK5D,QAAI;AAKF,YAAM,YAAY,IAAI,WAAqC,WAAW;AACtE,UAAI,aAAa,OAAO,UAAU,SAAS,YAAY;AACrD,aAAK,QAAQ,gBAAgB,SAAS;AAAA,MACxC;AAAA,IACF,QAAQ;AAAA,IAAgC;AAOxC,UAAM,sBAAsB,YAAY;AACtC,UAAI;AACF,cAAM,OAAO,IAAI,WAAwB,KAAK;AAC9C,YAAI,CAAC,QAAQ,OAAO,KAAK,aAAa,cAAc,CAAC,KAAK,QAAS;AACnE,cAAM,MAAM,KAAK;AACjB,cAAM,aAAa,KAAK,QAAQ,4BAA4B;AAO5D,cAAM,QAAQ,YAAY;AACxB,gBAAM,UAAU,MAAM,QAAQ,WAAW;AAAA,YACvC,IAAI,eAAe;AAAA,YACnB,IAAI,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAM3B,IAAI,wBAAwB;AAAA,UAC9B,CAAC;AACD,qBAAW,KAAK,SAAS;AACvB,gBAAI,EAAE,WAAW,YAAY;AAC3B,kBAAI,OAAO,OAAO,yCAAyC;AAAA,gBACzD,OAAQ,EAAE,QAAgB,WAAW,OAAO,EAAE,MAAM;AAAA,cACtD,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AACA,cAAM,KAAK,SAAS,qBAAqB,EAAE,MAAM,YAAY,WAAW,GAAG,KAAK;AAChF,aAAK,yBAAyB;AAC9B,aAAK,MAAM,EAAE,MAAM,CAAC,QAAa;AAC/B,cAAI,OAAO,OAAO,iCAAiC,EAAE,OAAO,KAAK,QAAQ,CAAC;AAAA,QAC5E,CAAC;AACD,YAAI,OAAO,KAAK,yDAAyD,EAAE,WAAW,CAAC;AAAA,MACzF,QAAQ;AAAA,MAAkC;AAAA,IAC5C;AAIA,UAAM,mBAAmB,YAAY;AACnC,UAAI;AAWF,cAAM,aAAa,CAAC,SAA0C;AAC5D,cAAI;AAAE,mBAAO,IAAI,WAAwB,IAAI;AAAA,UAAG,QAAQ;AAAE,mBAAO;AAAA,UAAW;AAAA,QAC9E;AACA,cAAM,OAAO,WAAW,aAAa,KAAK,WAAW,aAAa;AAClE,cAAM,SAAS,QAAQ,OAAO,KAAK,cAAc,aAAa,KAAK,UAAU,IAAI;AACjF,YAAI,CAAC,UAAU,CAAC,KAAK,QAAS;AAC9B,cAAM,MAAM,KAAK;AACjB,cAAM,WAAW;AACjB,cAAM,OAAO,CAAC,GAAQ,MAAc,SAAS,QAC3C,EAAE,KAAK,MAAM,QAAQ,EAAE,gBAAgB,2BAA2B,CAAC;AACrE,eAAO,IAAI,UAAU,OAAO,MAAW;AACrC,gBAAM,QAAQ,OAAO,EAAE,IAAI,MAAM,OAAO,KAAK,EAAE;AAC/C,gBAAM,OAAO,MAAM,IAAI,gBAAgB,KAAK;AAC5C,cAAI,CAAC,KAAK,GAAI,QAAO,KAAK,GAAG,iBAAiB,KAAK,QAAQ,KAAK,OAAO,GAAG,GAAG;AAC7E,iBAAO,KAAK,GAAG,kBAAkB;AAAA,YAC/B,SAAS,KAAK;AAAA,YAAS,QAAQ,KAAK;AAAA,YAAQ,YAAY,KAAK;AAAA,YAC7D;AAAA,YAAO,SAAS;AAAA,UAClB,CAAC,CAAC;AAAA,QACJ,CAAC;AACD,eAAO,KAAK,UAAU,OAAO,MAAW;AACtC,cAAI,QAAQ;AACZ,cAAI;AACF,kBAAM,OAAO,MAAM,EAAE,IAAI,UAAU;AACnC,oBAAQ,OAAO,MAAM,SAAS,EAAE;AAAA,UAClC,QAAQ;AAAA,UAAgC;AACxC,gBAAM,MAAM,MAAM,IAAI,kBAAkB,KAAK;AAC7C,cAAI,CAAC,IAAI,GAAI,QAAO,KAAK,GAAG,iBAAiB,IAAI,QAAQ,IAAI,OAAO,GAAG,GAAG;AAC1E,iBAAO,KAAK,GAAG,iBAAiB,IAAI,WAAW,YAAY,aAAa,YAAY,IAAI,OAAO,CAAC;AAAA,QAClG,CAAC;AACD,YAAI,OAAO,KAAK,4DAA4D,QAAQ,EAAE;AAAA,MACxF,QAAQ;AAAA,MAAkC;AAAA,IAC5C;AAMA,UAAM,wBAAwB,YAAY;AACxC,UAAI;AACF,cAAM,MAAM,KAAK;AACjB,YAAI,CAAC,IAAK;AACV,cAAM,MAAM,MAAM,IAAI,qBAAqB;AAC3C,YAAI,IAAI,WAAW,KAAK,IAAI,UAAU,GAAG;AACvC,cAAI,OAAO,KAAK,kDAAkD,GAAG;AAAA,QACvE;AAAA,MACF,SAAS,KAAU;AACjB,YAAI,OAAO,OAAO,8CAA8C,EAAE,OAAO,KAAK,QAAQ,CAAC;AAAA,MACzF;AAAA,IACF;AAEA,QAAI,OAAQ,IAAY,SAAS,YAAY;AAC3C,MAAC,IAAY,KAAK,gBAAgB,mBAAmB;AACrD,MAAC,IAAY,KAAK,gBAAgB,gBAAgB;AAClD,MAAC,IAAY,KAAK,gBAAgB,qBAAqB;AAAA,IACzD,OAAO;AACL,YAAM,oBAAoB;AAC1B,YAAM,iBAAiB;AACvB,YAAM,sBAAsB;AAAA,IAC9B;AAeA,QAAI;AACJ,QAAI;AACF,mBAAa,IAAI,WAAsC,YAAY;AAAA,IACrE,QAAQ;AACN,mBAAa;AAAA,IACf;AACA,QAAI,cAAc,OAAO,WAAW,yBAAyB,YAAY;AACvE,WAAK,QAAQ,iBAAiB,UAAU;AACxC,2BAAqB,YAAY,KAAK,SAAS,IAAI,MAAM;AAAA,IAC3D,OAAO;AACL,UAAI,OAAO;AAAA,QACT;AAAA,MAGF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,KAAmC;AAC5C,QAAI,KAAK,wBAAwB;AAC/B,UAAI;AACF,cAAM,OAAO,IAAI,WAAwB,KAAK;AAC9C,cAAM,MAAM,SAAS,mBAAmB;AAAA,MAC1C,QAAQ;AAAA,MAAe;AACvB,WAAK,yBAAyB;AAAA,IAChC;AACA,QAAI,KAAK,QAAQ;AACf,UAAI;AAAE,uBAAe,KAAK,MAAM;AAAA,MAAG,QAAQ;AAAA,MAAe;AAAA,IAC5D;AAAA,EACF;AACF;","names":["ObjectSchema","Field","ObjectSchema","Field","ObjectSchema","Field","SYSTEM_CTX","uid","slots","fresh","resumed","resumeError","esc","ObjectSchema","Field","parseJson","SYSTEM_CTX","defineActionDescriptor","APPROVAL_REVISE_NODE_TYPE","SYSTEM_CTX","defineActionDescriptor","ApprovalsTranslations"]}
|
|
1
|
+
{"version":3,"sources":["../src/translations/en.objects.generated.ts","../src/translations/zh-CN.objects.generated.ts","../src/translations/ja-JP.objects.generated.ts","../src/translations/es-ES.objects.generated.ts","../src/translations/index.ts","../src/sys-approval-request.object.ts","../src/sys-approval-action.object.ts","../src/sys-approval-approver.object.ts","../src/sys-approval-delegation.object.ts","../src/approval-service.ts","../src/approver-org-scope.ts","../src/sys-approval-token.object.ts","../src/action-link-pages.ts","../src/lifecycle-hooks.ts","../src/approval-node.ts","../src/approval-revise-node.ts","../src/approvals-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'en'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n * Merge only fills gaps: correcting a source label/description does not\n * push the correction into a leaf here that already holds a translation —\n * a present-but-stale string is not a gap, so it is left as-is. Re-translate\n * it by hand when its source changes; nothing here or in `os i18n check`\n * tells a leaf a translator updated on purpose from one nobody has looked\n * at since the source moved.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const enObjects: NonNullable<TranslationData['objects']> = {\n sys_approval_request: {\n label: \"Approval Request\",\n pluralLabel: \"Approval Requests\",\n description: \"Live approval instance tracked per submission\",\n fields: {\n id: {\n label: \"Request ID\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Tenant that owns this approval request (propagated from submitter context)\"\n },\n process_name: {\n label: \"Source\",\n help: \"Origin of the request — `flow:<flowName|nodeId>` for node-driven approvals\"\n },\n object_name: {\n label: \"Object\"\n },\n record_id: {\n label: \"Record ID\"\n },\n submitter_id: {\n label: \"Submitter\"\n },\n submitter_comment: {\n label: \"Submitter Comment\"\n },\n status: {\n label: \"Status\",\n help: \"Lifecycle state of the request\",\n options: {\n pending: \"Pending\",\n approved: \"Approved\",\n rejected: \"Rejected\",\n recalled: \"Recalled\",\n returned: \"Returned\"\n }\n },\n current_step: {\n label: \"Current Step\",\n help: \"Machine name of the step awaiting approval\"\n },\n current_step_index: {\n label: \"Current Step Index\"\n },\n pending_approvers: {\n label: \"Pending Approvers\",\n help: \"Comma-separated user ids who can act on the current step\"\n },\n payload_json: {\n label: \"Snapshot\",\n help: \"Record snapshot at submission time\"\n },\n flow_run_id: {\n label: \"Flow Run\",\n help: \"Suspended automation run id this request gates (ADR-0019). The decision resumes it.\"\n },\n flow_node_id: {\n label: \"Flow Node\",\n help: \"Approval node id within the flow that opened this request (ADR-0019).\"\n },\n node_config_json: {\n label: \"Node Config\",\n help: \"Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).\"\n },\n completed_at: {\n label: \"Completed At\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n },\n _views: {\n my_pending: {\n label: \"My Pending\",\n emptyState: {\n title: \"No pending approvals\",\n message: \"You're all caught up.\"\n }\n },\n submitted_by_me: {\n label: \"I Submitted\"\n },\n completed: {\n label: \"Completed\"\n },\n all_requests: {\n label: \"All\"\n }\n },\n _actions: {\n approval_approve: {\n label: \"Approve\",\n successMessage: \"Approved.\",\n params: {\n comment: {\n label: \"Comment\"\n },\n attachments: {\n label: \"Attachments\"\n }\n }\n },\n approval_reject: {\n label: \"Reject\",\n description: \"Reject this request? A rejection is final for every approver.\",\n successMessage: \"Rejected.\",\n params: {\n comment: {\n label: \"Comment\"\n },\n attachments: {\n label: \"Attachments\"\n }\n }\n },\n approval_reassign: {\n label: \"Reassign\",\n successMessage: \"Reassigned.\",\n params: {\n to: {\n label: \"New approver\",\n helpText: \"User to hand this step to\"\n },\n comment: {\n label: \"Comment\"\n }\n }\n },\n approval_send_back: {\n label: \"Send back\",\n successMessage: \"Sent back for revision.\",\n params: {\n comment: {\n label: \"Reason\"\n }\n }\n },\n approval_request_info: {\n label: \"Request info\",\n successMessage: \"Information requested.\",\n params: {\n comment: {\n label: \"What do you need?\"\n }\n }\n },\n approval_remind: {\n label: \"Send reminder\",\n successMessage: \"Reminder sent.\",\n params: {\n comment: {\n label: \"Note\"\n }\n }\n },\n approval_recall: {\n label: \"Recall\",\n description: \"Recall this request? Approvers can no longer act on it and the record is unlocked.\",\n successMessage: \"Recalled.\",\n params: {\n comment: {\n label: \"Comment\"\n }\n }\n },\n approval_resubmit: {\n label: \"Resubmit\",\n successMessage: \"Resubmitted.\",\n params: {\n comment: {\n label: \"What changed?\"\n }\n }\n }\n }\n },\n sys_approval_action: {\n label: \"Approval Action\",\n pluralLabel: \"Approval Actions\",\n description: \"Append-only audit trail for approval actions\",\n fields: {\n id: {\n label: \"Action ID\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Tenant that owns this action (mirrors the parent request)\"\n },\n request_id: {\n label: \"Request\"\n },\n step_name: {\n label: \"Step\",\n help: \"Machine name of the step at the time of the action\"\n },\n step_index: {\n label: \"Step Index\"\n },\n action: {\n label: \"Action\",\n options: {\n submit: \"Submit\",\n approve: \"Approve\",\n reject: \"Reject\",\n recall: \"Recall\",\n escalate: \"Escalate\",\n reassign: \"Reassign\",\n remind: \"Remind\",\n request_info: \"Request Info\",\n comment: \"Comment\",\n revise: \"Revise\",\n resubmit: \"Resubmit\",\n ooo_substitute: \"Out-of-Office Substitution\"\n }\n },\n actor_id: {\n label: \"Actor\"\n },\n comment: {\n label: \"Comment\"\n },\n via_override: {\n label: \"Via Admin Override\",\n help: \"True when the actor was admitted to this action only by the privileged-override path (#3424) — they held no slot in the request’s pending-approver slate.\"\n },\n reassign_from: {\n label: \"Reassigned From\",\n help: \"User whose pending-approver slot was handed over (reassign actions only)\"\n },\n reassign_to: {\n label: \"Reassigned To\",\n help: \"User who received the pending-approver slot (reassign actions only)\"\n },\n attachments: {\n label: \"Attachments\",\n help: \"Files supporting this action — e.g. a signed contract or evidence (#3266).\"\n },\n created_at: {\n label: \"Created At\"\n }\n },\n _views: {\n recent: {\n label: \"Recent\",\n emptyState: {\n title: \"No approval actions yet\",\n message: \"Actions are logged automatically when approvals progress.\"\n }\n },\n by_actor: {\n label: \"By Actor\"\n },\n all_actions: {\n label: \"All\"\n }\n }\n },\n sys_approval_delegation: {\n label: \"Approval Delegation\",\n pluralLabel: \"Approval Delegations\",\n description: \"Self-service out-of-office rule: route this user's approver slots to a delegate within a time window (#1322 M1).\",\n fields: {\n id: {\n label: \"Delegation ID\"\n },\n delegator_id: {\n label: \"Delegator\",\n help: \"The user going out of office; their individually-routed approver slots are rerouted while active.\"\n },\n delegate_id: {\n label: \"Delegate\",\n help: \"The backup who receives the delegator's approvals while this rule is active. Acts under their own identity.\"\n },\n valid_from: {\n label: \"Valid From\",\n help: \"Rule is inactive before this instant. Null = active immediately. Enforced at resolution time via isGrantActive (ADR-0091 D2 predicate) — never by a background job.\"\n },\n valid_until: {\n label: \"Valid Until\",\n help: \"Rule is inactive AT and AFTER this instant (half-open [from, until), UTC). Null = never expires.\"\n },\n reason: {\n label: \"Reason\",\n help: \"Why the delegation exists (e.g. \\\"Annual leave 5/26–5/30\\\"). Recorded on the substitution audit row.\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Tenant that owns this rule; null = applies across tenants for this delegator.\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n },\n _views: {\n active: {\n label: \"Active\",\n emptyState: {\n title: \"No delegations\",\n message: \"Declare an out-of-office delegation so approvals route to a backup while you are away.\"\n }\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'zh-CN'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n * Merge only fills gaps: correcting a source label/description does not\n * push the correction into a leaf here that already holds a translation —\n * a present-but-stale string is not a gap, so it is left as-is. Re-translate\n * it by hand when its source changes; nothing here or in `os i18n check`\n * tells a leaf a translator updated on purpose from one nobody has looked\n * at since the source moved.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const zhCNObjects: NonNullable<TranslationData['objects']> = {\n sys_approval_request: {\n label: \"审批请求\",\n pluralLabel: \"审批请求\",\n description: \"按提交记录跟踪的实时审批实例\",\n fields: {\n id: {\n label: \"请求 ID\"\n },\n organization_id: {\n label: \"组织\",\n help: \"拥有该审批请求的租户(从提交方上下文传播)\"\n },\n process_name: {\n label: \"来源\",\n help: \"请求来源 —— 节点驱动的审批为 `flow:<flowName|nodeId>`\"\n },\n object_name: {\n label: \"对象\"\n },\n record_id: {\n label: \"记录 ID\"\n },\n submitter_id: {\n label: \"提交人\"\n },\n submitter_comment: {\n label: \"提交备注\"\n },\n status: {\n label: \"状态\",\n help: \"请求的生命周期状态\",\n options: {\n pending: \"待审批\",\n approved: \"已批准\",\n rejected: \"已拒绝\",\n recalled: \"已撤回\",\n returned: \"已退回修改\"\n }\n },\n current_step: {\n label: \"当前步骤\",\n help: \"当前等待审批的步骤机器名称\"\n },\n current_step_index: {\n label: \"当前步骤索引\"\n },\n pending_approvers: {\n label: \"待审批人\",\n help: \"可在当前步骤执行操作的用户 ID,逗号分隔\"\n },\n payload_json: {\n label: \"快照\",\n help: \"提交时的记录快照\"\n },\n flow_run_id: {\n label: \"Flow Run\",\n help: \"Suspended automation run id this request gates (ADR-0019). The decision resumes it.\"\n },\n flow_node_id: {\n label: \"Flow Node\",\n help: \"Approval node id within the flow that opened this request (ADR-0019).\"\n },\n node_config_json: {\n label: \"Node Config\",\n help: \"Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).\"\n },\n completed_at: {\n label: \"完成时间\"\n },\n created_at: {\n label: \"创建时间\"\n },\n updated_at: {\n label: \"更新时间\"\n }\n },\n _views: {\n my_pending: {\n label: \"待我审批\",\n emptyState: {\n title: \"暂无待审批的请求\",\n message: \"全部处理完毕,没有等待你审批的请求。\"\n }\n },\n submitted_by_me: {\n label: \"我提交的\"\n },\n completed: {\n label: \"已完成\"\n },\n all_requests: {\n label: \"全部\"\n }\n },\n _actions: {\n approval_approve: {\n label: \"通过\",\n successMessage: \"已通过。\",\n params: {\n comment: {\n label: \"审批意见\"\n },\n attachments: {\n label: \"附件\"\n }\n }\n },\n approval_reject: {\n label: \"拒绝\",\n description: \"拒绝该请求?拒绝对所有审批人立即生效。\",\n successMessage: \"已拒绝。\",\n params: {\n comment: {\n label: \"审批意见\"\n },\n attachments: {\n label: \"附件\"\n }\n }\n },\n approval_reassign: {\n label: \"转签\",\n successMessage: \"已转签。\",\n params: {\n to: {\n label: \"新审批人\",\n helpText: \"将此审批步骤转交给的用户\"\n },\n comment: {\n label: \"备注\"\n }\n }\n },\n approval_send_back: {\n label: \"退回修改\",\n successMessage: \"已退回修改。\",\n params: {\n comment: {\n label: \"原因\"\n }\n }\n },\n approval_request_info: {\n label: \"退回补充\",\n successMessage: \"已退回给申请人补充材料。\",\n params: {\n comment: {\n label: \"需要申请人补充什么?\"\n }\n }\n },\n approval_remind: {\n label: \"催办\",\n successMessage: \"已发送催办。\",\n params: {\n comment: {\n label: \"备注\"\n }\n }\n },\n approval_recall: {\n label: \"撤回\",\n description: \"撤回该请求?撤回后审批人将无法继续处理,记录随即解锁。\",\n successMessage: \"已撤回。\",\n params: {\n comment: {\n label: \"备注\"\n }\n }\n },\n approval_resubmit: {\n label: \"重新提交\",\n successMessage: \"已重新提交。\",\n params: {\n comment: {\n label: \"你修改了什么?\"\n }\n }\n }\n }\n },\n sys_approval_action: {\n label: \"审批动作\",\n pluralLabel: \"审批动作\",\n description: \"追加写入的审批操作审计记录\",\n fields: {\n id: {\n label: \"动作 ID\"\n },\n organization_id: {\n label: \"组织\",\n help: \"拥有该动作的租户(与父请求保持一致)\"\n },\n request_id: {\n label: \"请求\"\n },\n step_name: {\n label: \"步骤\",\n help: \"执行该动作时对应步骤的机器名称\"\n },\n step_index: {\n label: \"步骤索引\"\n },\n action: {\n label: \"操作\",\n options: {\n submit: \"提交\",\n approve: \"批准\",\n reject: \"拒绝\",\n recall: \"撤回\",\n escalate: \"升级\",\n reassign: \"转签\",\n remind: \"催办\",\n request_info: \"要求补充材料\",\n comment: \"评论\",\n revise: \"退回修改\",\n resubmit: \"重新提交\",\n ooo_substitute: \"不在岗改派\"\n }\n },\n actor_id: {\n label: \"执行人\"\n },\n comment: {\n label: \"评论\"\n },\n via_override: {\n label: \"管理员越权操作\",\n help: \"为真表示该操作者只是凭特权越权路径(#3424)被放行——他们并不在该请求的待审批人名单中。\"\n },\n reassign_from: {\n label: \"转出人\",\n help: \"被移交待审批槽位的用户(仅转签操作)\"\n },\n reassign_to: {\n label: \"转入人\",\n help: \"接收待审批槽位的用户(仅转签操作)\"\n },\n attachments: {\n label: \"附件\",\n help: \"支持该操作的文件——例如已签署的合同或证明材料(#3266)。\"\n },\n created_at: {\n label: \"创建时间\"\n }\n },\n _views: {\n recent: {\n label: \"最近\",\n emptyState: {\n title: \"暂无审批操作记录\",\n message: \"审批推进时会自动记录操作。\"\n }\n },\n by_actor: {\n label: \"按执行人\"\n },\n all_actions: {\n label: \"全部\"\n }\n }\n },\n sys_approval_delegation: {\n label: \"审批委派\",\n pluralLabel: \"审批委派\",\n description: \"自助不在岗规则:在时间窗内把该用户的审批人槽位改派给候补人(#1322 M1)。\",\n fields: {\n id: {\n label: \"委派 ID\"\n },\n delegator_id: {\n label: \"委派人\",\n help: \"即将不在岗的用户;规则生效期间,路由到其个人的审批人槽位将被改派。\"\n },\n delegate_id: {\n label: \"候补人\",\n help: \"规则生效期间接收委派人审批的候补人。以其本人身份行使。\"\n },\n valid_from: {\n label: \"生效时间\",\n help: \"此刻之前规则不生效。留空 = 立即生效。在解析时通过 isGrantActive 强制(ADR-0091 D2 判定式)——绝不依赖后台任务。\"\n },\n valid_until: {\n label: \"失效时间\",\n help: \"此刻(含)及之后规则不生效(半开区间 [from, until),UTC)。留空 = 永不过期。\"\n },\n reason: {\n label: \"原因\",\n help: \"委派存在的原因(例如“年假 5/26–5/30”)。会记入改派审计行。\"\n },\n organization_id: {\n label: \"组织\",\n help: \"拥有此规则的租户;留空 = 对该委派人跨租户生效。\"\n },\n created_at: {\n label: \"创建时间\"\n },\n updated_at: {\n label: \"更新时间\"\n }\n },\n _views: {\n active: {\n label: \"生效中\",\n emptyState: {\n title: \"暂无委派\",\n message: \"声明一条不在岗委派,休假期间审批自动改派给候补人。\"\n }\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'ja-JP'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n * Merge only fills gaps: correcting a source label/description does not\n * push the correction into a leaf here that already holds a translation —\n * a present-but-stale string is not a gap, so it is left as-is. Re-translate\n * it by hand when its source changes; nothing here or in `os i18n check`\n * tells a leaf a translator updated on purpose from one nobody has looked\n * at since the source moved.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const jaJPObjects: NonNullable<TranslationData['objects']> = {\n sys_approval_request: {\n label: \"承認リクエスト\",\n pluralLabel: \"承認リクエスト\",\n description: \"送信ごとに追跡されるライブ承認インスタンス\",\n fields: {\n id: {\n label: \"リクエスト ID\"\n },\n organization_id: {\n label: \"組織\",\n help: \"この承認リクエストを所有するテナント(送信者コンテキストから伝播)\"\n },\n process_name: {\n label: \"ソース\",\n help: \"リクエストの発生元 — ノード駆動の承認では `flow:<flowName|nodeId>`\"\n },\n object_name: {\n label: \"オブジェクト\"\n },\n record_id: {\n label: \"レコード ID\"\n },\n submitter_id: {\n label: \"送信者\"\n },\n submitter_comment: {\n label: \"送信者コメント\"\n },\n status: {\n label: \"ステータス\",\n help: \"リクエストのライフサイクル状態\",\n options: {\n pending: \"保留中\",\n approved: \"承認済み\",\n rejected: \"却下済み\",\n recalled: \"取り消し済み\",\n returned: \"差し戻し済み\"\n }\n },\n current_step: {\n label: \"現在のステップ\",\n help: \"承認待ちのステップの機械名\"\n },\n current_step_index: {\n label: \"現在のステップ番号\"\n },\n pending_approvers: {\n label: \"承認待ち承認者\",\n help: \"現在のステップを処理できるユーザー ID のカンマ区切りリスト\"\n },\n payload_json: {\n label: \"スナップショット\",\n help: \"送信時のレコードスナップショット\"\n },\n flow_run_id: {\n label: \"Flow Run\",\n help: \"Suspended automation run id this request gates (ADR-0019). The decision resumes it.\"\n },\n flow_node_id: {\n label: \"Flow Node\",\n help: \"Approval node id within the flow that opened this request (ADR-0019).\"\n },\n node_config_json: {\n label: \"Node Config\",\n help: \"Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).\"\n },\n completed_at: {\n label: \"完了日時\"\n },\n created_at: {\n label: \"作成日時\"\n },\n updated_at: {\n label: \"更新日時\"\n }\n },\n _views: {\n my_pending: {\n label: \"承認待ち\",\n emptyState: {\n title: \"承認待ちはありません\",\n message: \"すべて処理済みです。あなたの承認を待つリクエストはありません。\"\n }\n },\n submitted_by_me: {\n label: \"自分が送信\"\n },\n completed: {\n label: \"完了済み\"\n },\n all_requests: {\n label: \"すべて\"\n }\n },\n _actions: {\n approval_approve: {\n label: \"承認\",\n successMessage: \"承認しました。\",\n params: {\n comment: {\n label: \"コメント\"\n },\n attachments: {\n label: \"添付ファイル\"\n }\n }\n },\n approval_reject: {\n label: \"却下\",\n description: \"このリクエストを却下しますか?却下はすべての承認者に対して最終決定になります。\",\n successMessage: \"却下しました。\",\n params: {\n comment: {\n label: \"コメント\"\n },\n attachments: {\n label: \"添付ファイル\"\n }\n }\n },\n approval_reassign: {\n label: \"再割り当て\",\n successMessage: \"再割り当てしました。\",\n params: {\n to: {\n label: \"新しい承認者\",\n helpText: \"このステップを引き継ぐユーザー\"\n },\n comment: {\n label: \"コメント\"\n }\n }\n },\n approval_send_back: {\n label: \"差し戻し\",\n successMessage: \"修正のため差し戻しました。\",\n params: {\n comment: {\n label: \"理由\"\n }\n }\n },\n approval_request_info: {\n label: \"追加情報を依頼\",\n successMessage: \"追加情報を依頼しました。\",\n params: {\n comment: {\n label: \"何が必要ですか?\"\n }\n }\n },\n approval_remind: {\n label: \"リマインダー送信\",\n successMessage: \"リマインダーを送信しました。\",\n params: {\n comment: {\n label: \"メモ\"\n }\n }\n },\n approval_recall: {\n label: \"取り下げ\",\n description: \"このリクエストを取り下げますか?承認者は操作できなくなり、レコードのロックが解除されます。\",\n successMessage: \"取り下げました。\",\n params: {\n comment: {\n label: \"コメント\"\n }\n }\n },\n approval_resubmit: {\n label: \"再提出\",\n successMessage: \"再提出しました。\",\n params: {\n comment: {\n label: \"何を変更しましたか?\"\n }\n }\n }\n }\n },\n sys_approval_action: {\n label: \"承認アクション\",\n pluralLabel: \"承認アクション\",\n description: \"承認アクションの追記専用監査証跡\",\n fields: {\n id: {\n label: \"アクション ID\"\n },\n organization_id: {\n label: \"組織\",\n help: \"このアクションを所有するテナント(親リクエストと同じ)\"\n },\n request_id: {\n label: \"リクエスト\"\n },\n step_name: {\n label: \"ステップ\",\n help: \"アクション時点のステップの機械名\"\n },\n step_index: {\n label: \"ステップ番号\"\n },\n action: {\n label: \"アクション\",\n options: {\n submit: \"申請\",\n approve: \"承認\",\n reject: \"却下\",\n recall: \"取消\",\n escalate: \"エスカレーション\",\n reassign: \"再割り当て\",\n remind: \"リマインダー\",\n request_info: \"追加情報依頼\",\n comment: \"コメント\",\n revise: \"差し戻し\",\n resubmit: \"再提出\",\n ooo_substitute: \"不在時代理\"\n }\n },\n actor_id: {\n label: \"操作者\"\n },\n comment: {\n label: \"コメント\"\n },\n via_override: {\n label: \"管理者オーバーライド経由\",\n help: \"true の場合、実行者は特権オーバーライド経路(#3424)によってのみ許可されたことを示します — 当該リクエストの承認待ちリストには含まれていません。\"\n },\n reassign_from: {\n label: \"引き継ぎ元\",\n help: \"承認待ちスロットを引き渡したユーザー(引き継ぎ操作のみ)\"\n },\n reassign_to: {\n label: \"引き継ぎ先\",\n help: \"承認待ちスロットを受け取ったユーザー(引き継ぎ操作のみ)\"\n },\n attachments: {\n label: \"添付ファイル\",\n help: \"この操作を裏付けるファイル——署名済み契約書や証憑など(#3266)。\"\n },\n created_at: {\n label: \"作成日時\"\n }\n },\n _views: {\n recent: {\n label: \"最近\",\n emptyState: {\n title: \"承認操作はまだありません\",\n message: \"承認が進むと操作は自動的に記録されます。\"\n }\n },\n by_actor: {\n label: \"操作者別\"\n },\n all_actions: {\n label: \"すべて\"\n }\n }\n },\n sys_approval_delegation: {\n label: \"Approval Delegation\",\n pluralLabel: \"Approval Delegations\",\n description: \"Self-service out-of-office rule: route this user's approver slots to a delegate within a time window (#1322 M1).\",\n fields: {\n id: {\n label: \"Delegation ID\"\n },\n delegator_id: {\n label: \"Delegator\",\n help: \"The user going out of office; their individually-routed approver slots are rerouted while active.\"\n },\n delegate_id: {\n label: \"Delegate\",\n help: \"The backup who receives the delegator's approvals while this rule is active. Acts under their own identity.\"\n },\n valid_from: {\n label: \"Valid From\",\n help: \"Rule is inactive before this instant. Null = active immediately. Enforced at resolution time via isGrantActive (ADR-0091 D2 predicate) — never by a background job.\"\n },\n valid_until: {\n label: \"Valid Until\",\n help: \"Rule is inactive AT and AFTER this instant (half-open [from, until), UTC). Null = never expires.\"\n },\n reason: {\n label: \"Reason\",\n help: \"Why the delegation exists (e.g. \\\"Annual leave 5/26–5/30\\\"). Recorded on the substitution audit row.\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Tenant that owns this rule; null = applies across tenants for this delegator.\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n },\n _views: {\n active: {\n label: \"Active\",\n emptyState: {\n title: \"No delegations\",\n message: \"Declare an out-of-office delegation so approvals route to a backup while you are away.\"\n }\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'es-ES'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n * Merge only fills gaps: correcting a source label/description does not\n * push the correction into a leaf here that already holds a translation —\n * a present-but-stale string is not a gap, so it is left as-is. Re-translate\n * it by hand when its source changes; nothing here or in `os i18n check`\n * tells a leaf a translator updated on purpose from one nobody has looked\n * at since the source moved.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const esESObjects: NonNullable<TranslationData['objects']> = {\n sys_approval_request: {\n label: \"Solicitud de aprobación\",\n pluralLabel: \"Solicitudes de aprobación\",\n description: \"Instancia activa de aprobación registrada por envío\",\n fields: {\n id: {\n label: \"ID de solicitud\"\n },\n organization_id: {\n label: \"Organización\",\n help: \"Tenant que posee esta solicitud de aprobación (propagado desde el contexto del solicitante).\"\n },\n process_name: {\n label: \"Origen\",\n help: \"Origen de la solicitud — `flow:<flowName|nodeId>` para aprobaciones por nodo\"\n },\n object_name: {\n label: \"Objeto\"\n },\n record_id: {\n label: \"ID de registro\"\n },\n submitter_id: {\n label: \"Solicitante\"\n },\n submitter_comment: {\n label: \"Comentario del solicitante\"\n },\n status: {\n label: \"Estado\",\n help: \"Estado del ciclo de vida de la solicitud.\",\n options: {\n pending: \"Pendiente\",\n approved: \"Aprobada\",\n rejected: \"Rechazada\",\n recalled: \"Retirada\",\n returned: \"Devuelta para revisión\"\n }\n },\n current_step: {\n label: \"Paso actual\",\n help: \"Nombre técnico del paso pendiente de aprobación.\"\n },\n current_step_index: {\n label: \"Índice del paso actual\"\n },\n pending_approvers: {\n label: \"Aprobadores pendientes\",\n help: \"ID de usuario separados por comas que pueden actuar en el paso actual.\"\n },\n payload_json: {\n label: \"Instantánea\",\n help: \"Instantánea del registro en el momento del envío.\"\n },\n flow_run_id: {\n label: \"Flow Run\",\n help: \"Suspended automation run id this request gates (ADR-0019). The decision resumes it.\"\n },\n flow_node_id: {\n label: \"Flow Node\",\n help: \"Approval node id within the flow that opened this request (ADR-0019).\"\n },\n node_config_json: {\n label: \"Node Config\",\n help: \"Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).\"\n },\n completed_at: {\n label: \"Completado el\"\n },\n created_at: {\n label: \"Creado el\"\n },\n updated_at: {\n label: \"Actualizado el\"\n }\n },\n _views: {\n my_pending: {\n label: \"Aprobaciones pendientes\",\n emptyState: {\n title: \"Sin aprobaciones pendientes\",\n message: \"Estás al día: nada espera tu aprobación.\"\n }\n },\n submitted_by_me: {\n label: \"Enviadas por mí\"\n },\n completed: {\n label: \"Completadas\"\n },\n all_requests: {\n label: \"Todas\"\n }\n },\n _actions: {\n approval_approve: {\n label: \"Aprobar\",\n successMessage: \"Aprobada.\",\n params: {\n comment: {\n label: \"Comentario\"\n },\n attachments: {\n label: \"Adjuntos\"\n }\n }\n },\n approval_reject: {\n label: \"Rechazar\",\n description: \"¿Rechazar esta solicitud? Un rechazo es definitivo para todos los aprobadores.\",\n successMessage: \"Rechazada.\",\n params: {\n comment: {\n label: \"Comentario\"\n },\n attachments: {\n label: \"Adjuntos\"\n }\n }\n },\n approval_reassign: {\n label: \"Reasignar\",\n successMessage: \"Reasignada.\",\n params: {\n to: {\n label: \"Nuevo aprobador\",\n helpText: \"Usuario al que se entrega este paso\"\n },\n comment: {\n label: \"Comentario\"\n }\n }\n },\n approval_send_back: {\n label: \"Devolver\",\n successMessage: \"Devuelta para revisión.\",\n params: {\n comment: {\n label: \"Motivo\"\n }\n }\n },\n approval_request_info: {\n label: \"Pedir información\",\n successMessage: \"Información solicitada.\",\n params: {\n comment: {\n label: \"¿Qué necesitas?\"\n }\n }\n },\n approval_remind: {\n label: \"Enviar recordatorio\",\n successMessage: \"Recordatorio enviado.\",\n params: {\n comment: {\n label: \"Nota\"\n }\n }\n },\n approval_recall: {\n label: \"Retirar\",\n description: \"¿Retirar esta solicitud? Los aprobadores ya no podrán actuar sobre ella y el registro se desbloqueará.\",\n successMessage: \"Retirada.\",\n params: {\n comment: {\n label: \"Comentario\"\n }\n }\n },\n approval_resubmit: {\n label: \"Reenviar\",\n successMessage: \"Reenviada.\",\n params: {\n comment: {\n label: \"¿Qué ha cambiado?\"\n }\n }\n }\n }\n },\n sys_approval_action: {\n label: \"Acción de aprobación\",\n pluralLabel: \"Acciones de aprobación\",\n description: \"Registro de auditoría append-only para acciones de aprobación\",\n fields: {\n id: {\n label: \"ID de acción\"\n },\n organization_id: {\n label: \"Organización\",\n help: \"Tenant que posee esta acción (refleja la solicitud principal).\"\n },\n request_id: {\n label: \"Solicitud\"\n },\n step_name: {\n label: \"Paso\",\n help: \"Nombre técnico del paso en el momento de la acción.\"\n },\n step_index: {\n label: \"Índice del paso\"\n },\n action: {\n label: \"Acción\",\n options: {\n submit: \"Enviar\",\n approve: \"Aprobar\",\n reject: \"Rechazar\",\n recall: \"Retirar\",\n escalate: \"Escalar\",\n reassign: \"Reasignación\",\n remind: \"Recordatorio\",\n request_info: \"Petición de información\",\n comment: \"Comentario\",\n revise: \"Devolución\",\n resubmit: \"Reenvío\",\n ooo_substitute: \"Sustitución por ausencia\"\n }\n },\n actor_id: {\n label: \"Actor\"\n },\n comment: {\n label: \"Comentario\"\n },\n via_override: {\n label: \"Mediante anulación de administrador\",\n help: \"Verdadero cuando el actor fue admitido en esta acción únicamente por la vía de anulación privilegiada (#3424): no ocupaba ningún puesto en la lista de aprobadores pendientes de la solicitud.\"\n },\n reassign_from: {\n label: \"Reasignado de\",\n help: \"Usuario cuyo turno de aprobación pendiente fue traspasado (solo acciones de reasignación)\"\n },\n reassign_to: {\n label: \"Reasignado a\",\n help: \"Usuario que recibió el turno de aprobación pendiente (solo acciones de reasignación)\"\n },\n attachments: {\n label: \"Adjuntos\",\n help: \"Archivos que respaldan esta acción, p. ej. un contrato firmado o pruebas (#3266).\"\n },\n created_at: {\n label: \"Creado el\"\n }\n },\n _views: {\n recent: {\n label: \"Recientes\",\n emptyState: {\n title: \"Aún no hay acciones de aprobación\",\n message: \"Las acciones se registran automáticamente a medida que avanza la aprobación.\"\n }\n },\n by_actor: {\n label: \"Por actor\"\n },\n all_actions: {\n label: \"Todas\"\n }\n }\n },\n sys_approval_delegation: {\n label: \"Approval Delegation\",\n pluralLabel: \"Approval Delegations\",\n description: \"Self-service out-of-office rule: route this user's approver slots to a delegate within a time window (#1322 M1).\",\n fields: {\n id: {\n label: \"Delegation ID\"\n },\n delegator_id: {\n label: \"Delegator\",\n help: \"The user going out of office; their individually-routed approver slots are rerouted while active.\"\n },\n delegate_id: {\n label: \"Delegate\",\n help: \"The backup who receives the delegator's approvals while this rule is active. Acts under their own identity.\"\n },\n valid_from: {\n label: \"Valid From\",\n help: \"Rule is inactive before this instant. Null = active immediately. Enforced at resolution time via isGrantActive (ADR-0091 D2 predicate) — never by a background job.\"\n },\n valid_until: {\n label: \"Valid Until\",\n help: \"Rule is inactive AT and AFTER this instant (half-open [from, until), UTC). Null = never expires.\"\n },\n reason: {\n label: \"Reason\",\n help: \"Why the delegation exists (e.g. \\\"Annual leave 5/26–5/30\\\"). Recorded on the substitution audit row.\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Tenant that owns this rule; null = applies across tenants for this delegator.\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n },\n _views: {\n active: {\n label: \"Active\",\n emptyState: {\n title: \"No delegations\",\n message: \"Declare an out-of-office delegation so approvals route to a backup while you are away.\"\n }\n }\n }\n }\n};\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * ApprovalsTranslations — i18n bundle owned by this plugin (ADR-0029 D8).\n *\n * Object label/field/view/action translations for the sys_* objects this\n * plugin owns. Loaded at runtime via the plugin's `kernel:ready` hook\n * (`i18n.loadTranslations`). Regenerate with `os i18n extract` against\n * `scripts/i18n-extract.config.ts`.\n */\n\nimport type { TranslationBundle } from '@objectstack/spec/system';\nimport { enObjects } from './en.objects.generated.js';\nimport { zhCNObjects } from './zh-CN.objects.generated.js';\nimport { jaJPObjects } from './ja-JP.objects.generated.js';\nimport { esESObjects } from './es-ES.objects.generated.js';\n\nexport const ApprovalsTranslations: TranslationBundle = {\n en: { objects: enObjects },\n 'zh-CN': { objects: zhCNObjects },\n 'ja-JP': { objects: jaJPObjects },\n 'es-ES': { objects: esESObjects },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\nimport { APPROVAL_STATUSES, APPROVAL_STATUS_LABELS } from '@objectstack/spec/contracts';\n\n/**\n * sys_approval_request — Live approval instance.\n *\n * ADR-0019: opened by a flow's **Approval node** when the run reaches it; the\n * run suspends until a decision is recorded. The row's lifecycle:\n *\n * `pending` → (per-approver decisions) → `approved` | `rejected`\n * `pending` → recalled by submitter → `recalled`\n *\n * `flow_run_id` / `flow_node_id` tie the request back to the suspended run so a\n * decision can resume it; `current_step` mirrors the node id. `node_config_json`\n * snapshots the Approval node config (approvers / behaviour) the request was\n * opened with.\n *\n * `payload_json` captures a snapshot of the target record at submission\n * time — used by notifications so they can render before the record is\n * locked or changed.\n *\n * @namespace sys\n */\nexport const SysApprovalRequest = ObjectSchema.create({\n name: 'sys_approval_request',\n label: 'Approval Request',\n pluralLabel: 'Approval Requests',\n icon: 'inbox',\n isSystem: true,\n managedBy: 'engine-owned',\n description: 'Live approval instance tracked per submission',\n displayNameField: 'id',\n nameField: 'id', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)\n titleFormat: '{process_name} · {record_id}',\n highlightFields: ['process_name', 'object_name', 'record_id', 'status', 'current_step', 'submitter_id', 'updated_at'],\n\n // Curated built-in list views — render as segmented tabs in the console.\n // Filters use {current_user_id} substitution wired by the console.\n listViews: {\n my_pending: {\n type: 'grid',\n name: 'my_pending',\n label: 'My Pending',\n data: { provider: 'object', object: 'sys_approval_request' },\n columns: ['process_name', 'object_name', 'record_id', 'current_step', 'submitter_id', 'updated_at'],\n filter: [\n { field: 'status', operator: 'equals', value: 'pending' },\n { field: 'pending_approvers', operator: 'contains', value: '{current_user_id}' },\n ],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 25 },\n emptyState: { title: 'No pending approvals', message: 'You\\'re all caught up.' },\n },\n submitted_by_me: {\n type: 'grid',\n name: 'submitted_by_me',\n label: 'I Submitted',\n data: { provider: 'object', object: 'sys_approval_request' },\n columns: ['process_name', 'object_name', 'record_id', 'status', 'current_step', 'updated_at'],\n filter: [{ field: 'submitter_id', operator: 'equals', value: '{current_user_id}' }],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 25 },\n },\n completed: {\n type: 'grid',\n name: 'completed',\n label: 'Completed',\n data: { provider: 'object', object: 'sys_approval_request' },\n columns: ['process_name', 'object_name', 'record_id', 'status', 'submitter_id', 'completed_at'],\n filter: [{ field: 'status', operator: 'in', value: ['approved', 'rejected', 'recalled'] }],\n sort: [{ field: 'completed_at', order: 'desc' }],\n pagination: { pageSize: 25 },\n },\n all_requests: {\n type: 'grid',\n name: 'all_requests',\n label: 'All',\n data: { provider: 'object', object: 'sys_approval_request' },\n columns: ['process_name', 'object_name', 'record_id', 'status', 'current_step', 'submitter_id', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n },\n },\n\n fields: {\n id: Field.text({ label: 'Request ID', required: true, readonly: true, group: 'System' }),\n\n // ⚠️ MEASURED DEFECT, cloud#1395 — read this before trusting the column.\n //\n // An approval request DOES belong to an organization: it is read through the\n // organization wall by the approvals inbox, and on a shared-database\n // deployment a row carrying no organization is not filtered BY that wall —\n // it is either invisible to everyone or visible to everyone, decided by\n // whatever filter each surface happens to apply rather than by the data.\n //\n // The value is resolved from the ACTING CONTEXT only (`openNodeRequest`'s\n // `ctxOrg`), so it is NULL whenever the flow that opened the request ran\n // without one — every schedule / time-relative / api triggered run, none of\n // which sets a tenant. On a walled single-database HotCRM SaaS boot this\n // measured 27 of 27 rows org-less, each naming an `object_name` /\n // `record_id` owned by a specific customer.\n //\n // ⛔ Do NOT read that as \"platform tables do not carry an organization\".\n // `sys_audit_log` (1669 rows) was correctly attributed on the SAME boot,\n // because its writer takes the organization from the RECORD the row is\n // about, with the session only as fallback (plugin-audit\n // `resolveRecordOrganizationField`, #8707 honouring #8287's ruling). Two\n // writers read the actor; a third reads the subject. That disagreement is\n // the defect.\n //\n // Which of the two a side-table row should follow is an open contract\n // question on cloud#1395 — the audit resolver is scope-pinned to audit\n // stamping by the #8778 ruling, so this writer needs its own. The same\n // `ctxOrg` also stamps `sys_approval_action` and `sys_approval_approver`,\n // so all three move together.\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n // ⛔ String unchanged on purpose: it is extracted into the generated i18n\n // bundles (`translations/*.objects.generated.ts`, as `help`), so rewording\n // it is a translation-regeneration change and not a comment. The\n // correction it needs — it claims a propagation that measurably does not\n // happen, and says \"Tenant\" where ADR-0120 §Terminology requires\n // \"organization\" — rides the cloud#1395 write-side fix, which rewrites the\n // sentence and regenerates the four locales in one pass.\n description: 'Tenant that owns this approval request (propagated from submitter context)',\n }),\n\n process_name: Field.text({\n label: 'Source',\n required: true,\n maxLength: 100,\n description: 'Origin of the request — `flow:<flowName|nodeId>` for node-driven approvals',\n group: 'Target',\n }),\n\n object_name: Field.text({\n label: 'Object',\n required: true,\n maxLength: 100,\n group: 'Target',\n }),\n\n record_id: Field.text({\n label: 'Record ID',\n required: true,\n maxLength: 100,\n group: 'Target',\n }),\n\n submitter_id: Field.lookup('sys_user', {\n label: 'Submitter',\n required: false,\n group: 'Target',\n }),\n\n submitter_comment: Field.textarea({\n label: 'Submitter Comment',\n required: false,\n group: 'Target',\n }),\n\n status: Field.select(\n // Derived from the contract, not re-typed (#3786). `APPROVAL_STATUSES` is\n // where the list and the reason for each entry live; `ApprovalStatus` is\n // derived from it, so this column and the contract cannot disagree. The\n // authored English label per entry lives beside it in\n // `APPROVAL_STATUS_LABELS` (#8543) — mapped here, never re-typed, so the\n // `en` bundle regenerates from the contract's own text.\n APPROVAL_STATUSES.map((value) => ({ value, label: APPROVAL_STATUS_LABELS[value] })),\n {\n label: 'Status',\n required: true,\n defaultValue: 'pending',\n description: 'Lifecycle state of the request',\n group: 'State',\n },\n ),\n\n current_step: Field.text({\n label: 'Current Step',\n required: false,\n maxLength: 100,\n description: 'Machine name of the step awaiting approval',\n group: 'State',\n }),\n\n current_step_index: Field.number({\n label: 'Current Step Index',\n required: false,\n defaultValue: 0,\n group: 'State',\n }),\n\n pending_approvers: Field.textarea({\n label: 'Pending Approvers',\n required: false,\n description: 'Comma-separated user ids who can act on the current step',\n group: 'State',\n }),\n\n payload_json: Field.textarea({\n label: 'Snapshot',\n required: false,\n description: 'Record snapshot at submission time',\n group: 'State',\n }),\n\n // ── ADR-0019: approval-as-flow-node correlation ──────────────────\n // When a request is opened by an Approval *node* (rather than a standalone\n // process), these tie it back to the suspended flow run so a decision can\n // resume it. Null for legacy process-driven requests.\n flow_run_id: Field.text({\n label: 'Flow Run',\n required: false,\n maxLength: 100,\n readonly: true,\n description: 'Suspended automation run id this request gates (ADR-0019). The decision resumes it.',\n group: 'State',\n }),\n\n flow_node_id: Field.text({\n label: 'Flow Node',\n required: false,\n maxLength: 100,\n readonly: true,\n description: 'Approval node id within the flow that opened this request (ADR-0019).',\n group: 'State',\n }),\n\n node_config_json: Field.textarea({\n label: 'Node Config',\n required: false,\n readonly: true,\n description: 'Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).',\n group: 'State',\n }),\n\n completed_at: Field.datetime({\n label: 'Completed At',\n required: false,\n group: 'State',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({ label: 'Updated At', required: false, group: 'System' }),\n },\n\n indexes: [\n // Look up \"is there a pending request for this record?\" — common\n // guard on submit and on edit-while-locked checks.\n { fields: ['object_name', 'record_id'] },\n { fields: ['status', 'object_name'] },\n // Status-windowed listings (escalation sweep, \"All\" tab ordering).\n // \"My approvals\" matching no longer scans this table: the service keeps\n // a normalized per-approver index in `sys_approval_approver` (#1745) and\n // resolves approver filters there; `pending_approvers` stays the\n // human-readable CSV source of truth only.\n { fields: ['status', 'updated_at'] },\n { fields: ['submitter_id', 'status'] },\n ],\n\n // Server-declared decision actions (objectui#2678 P2-4). The console's\n // generic action runtime renders and executes these wherever this object is\n // surfaced — the approvals inbox included — so new decision capabilities\n // (and their params) ship as metadata, not as hand-written buttons. Each\n // targets the existing approvals REST route; `{id}` resolves from the row\n // and `actorId` defaults to the caller server-side. The service remains the\n // authority on who may act; `visible` gates on the server-computed\n // per-viewer block (#3310): approver actions on `record.viewer.can_act`\n // (the caller is a current pending approver — same check the service\n // authorizes a decision with, so position/team approvers resolve correctly),\n // submitter actions on `record.viewer.is_submitter`. The core decision levers\n // (approve/reject/reassign) additionally OR in `record.viewer.can_override`\n // (#3424) so a platform/tenant admin can rescue a request routed to an\n // unstaffed position — otherwise undecidable, locking the record forever — by\n // approving, rejecting, or reassigning it to a real approver. `viewer` is\n // attached by getRequest/listRequests; where it is absent the predicate fails\n // closed.\n //\n // Every predicate below is guarded for the SPARSE action face (#8990). This\n // binding is a list row or a record read carrying only what the caller\n // projected, and CEL aborts the whole expression at key resolution — so the\n // unguarded `record.viewer.can_act` faulted (`No such key: viewer`) on any\n // row without the block, and the button silently vanished, indistinguishable\n // from \"the gate said no\". `materializeDeclaredFields`'s doc comment in\n // `@objectstack/objectql` is the canonical statement of the guard rule; this\n // file follows it and does not restate it.\n //\n // `viewer` is a NESTED block, which needs one measurement the canonical rule\n // does not spell out. Measured against the `@objectstack/formula` CEL engine:\n // `has(record.viewer) && record.viewer != null && record.viewer.can_act`\n // still FAULTS on `{viewer: {}}` (`No such key: can_act`) and on\n // `{viewer: {can_act: null}}` (`Logical operator requires bool operands`).\n // Guarding the LEAF instead — `has(record.viewer) &&\n // has(record.viewer.can_act) && record.viewer.can_act == true` — is total\n // over every binding AND subsumes the parent `!= null` half, because `has()`\n // on a path whose parent is null answers `false` rather than faulting. So the\n // leaf `has()` plus the `== true` comparison is the MINIMAL safe form here,\n // not a longer one: `== true` is load-bearing (a bare truthy read of a null\n // leaf faults the logical operator), the parent `!= null` is not.\n actions: [\n {\n name: 'approval_approve',\n label: 'Approve',\n icon: 'check-circle',\n // Primary decision — the console renders this filled/highlighted so it\n // stands out from the secondary levers in the drawer's action bar,\n // matching the mobile card hierarchy (objectui#2762 P1-5).\n variant: 'primary',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/approve',\n params: [\n { name: 'comment', label: 'Comment', type: 'textarea', required: false },\n // Decision attachments (#3266). The console renders `type:'file'` params\n // through the shared upload widget and POSTs the resolved `attachments:\n // string[]`; the decision route persists them on `sys_approval_action`.\n { name: 'attachments', label: 'Attachments', type: 'file', multiple: true, required: false },\n ],\n visible:\n 'has(record.viewer) && has(record.viewer.can_act) && record.viewer.can_act == true' +\n ' || has(record.viewer) && has(record.viewer.can_override) && record.viewer.can_override == true',\n locations: ['record_section', 'list_item'],\n successMessage: 'Approved.',\n refreshAfter: true,\n },\n {\n name: 'approval_reject',\n label: 'Reject',\n // The confirm question lives HERE, not in `confirmText`: this action\n // collects params, so the console would otherwise chain a confirm dialog\n // and then the param dialog for one decision — and the first one already\n // reads as \"the action ran\" (#7278, maintainer ruling 2026-08-10). The\n // param dialog renders this as its description, and nothing is POSTed\n // until its own Confirm: one condition, one wording, one dialog.\n // NB: the top-level action `description` (#7367), never `ai.description`\n // — that one is the LLM-facing tool contract and is not shown to anyone.\n description: 'Reject this request? A rejection is final for every approver.',\n icon: 'x-circle',\n // Destructive decision — rendered in the console's danger styling so it\n // reads as the irreversible action it is (objectui#2762 P1-5).\n variant: 'danger',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/reject',\n params: [\n { name: 'comment', label: 'Comment', type: 'textarea', required: false },\n { name: 'attachments', label: 'Attachments', type: 'file', multiple: true, required: false },\n ],\n visible:\n 'has(record.viewer) && has(record.viewer.can_act) && record.viewer.can_act == true' +\n ' || has(record.viewer) && has(record.viewer.can_override) && record.viewer.can_override == true',\n locations: ['record_section', 'list_item'],\n successMessage: 'Rejected.',\n refreshAfter: true,\n },\n {\n name: 'approval_reassign',\n label: 'Reassign',\n icon: 'arrow-right-left',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/reassign',\n params: [\n // Field-backed on `submitter_id` (the object's only `sys_user` lookup):\n // the console resolves its lookup config (`reference_to: sys_user`) so the\n // dialog renders a real user picker, while `name: 'to'` overrides the\n // request-body key to the `to` the reassign route expects. This is a\n // config-borrow, not a submitter pre-fill (`defaultFromRow` stays off).\n { field: 'submitter_id', name: 'to', label: 'New approver', required: true, helpText: 'User to hand this step to' },\n { name: 'comment', label: 'Comment', type: 'textarea', required: false },\n ],\n visible:\n 'has(record.viewer) && has(record.viewer.can_act) && record.viewer.can_act == true' +\n ' || has(record.viewer) && has(record.viewer.can_override) && record.viewer.can_override == true',\n locations: ['record_section'],\n successMessage: 'Reassigned.',\n refreshAfter: true,\n },\n\n // ── Approver secondary decisions ────────────────────────────────\n // Send back for revision / request more info (ADR-0044). Both are approver\n // actions, so `visible` gates on `record.viewer.can_act` (a current pending\n // approver) — same as approve/reject. The service stays the authority.\n {\n name: 'approval_send_back',\n label: 'Send back',\n icon: 'corner-up-left',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/revise',\n params: [\n { name: 'comment', label: 'Reason', type: 'textarea', required: false },\n ],\n visible: 'has(record.viewer) && has(record.viewer.can_act) && record.viewer.can_act == true',\n locations: ['record_section'],\n successMessage: 'Sent back for revision.',\n refreshAfter: true,\n },\n {\n name: 'approval_request_info',\n label: 'Request info',\n icon: 'help-circle',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/request-info',\n params: [\n { name: 'comment', label: 'What do you need?', type: 'textarea', required: true },\n ],\n visible: 'has(record.viewer) && has(record.viewer.can_act) && record.viewer.can_act == true',\n locations: ['record_section'],\n successMessage: 'Information requested.',\n refreshAfter: true,\n },\n\n // ── Submitter continuity actions ────────────────────────────────\n // Remind / recall (pending) and resubmit / recall (returned). These are the\n // submitter's own levers, so `visible` gates on `record.viewer.is_submitter`\n // (server-computed on the current viewer). The service re-checks ownership;\n // the predicate keeps a non-submitter from ever seeing a button they cannot\n // use.\n {\n name: 'approval_remind',\n label: 'Send reminder',\n icon: 'bell-ring',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/remind',\n params: [\n { name: 'comment', label: 'Note', type: 'textarea', required: false },\n ],\n visible: 'has(record.status) && record.status == \"pending\" && has(record.viewer) && has(record.viewer.is_submitter) && record.viewer.is_submitter == true',\n locations: ['record_section'],\n successMessage: 'Reminder sent.',\n refreshAfter: true,\n },\n {\n name: 'approval_recall',\n label: 'Recall',\n // Confirm question as the param dialog's description, not `confirmText`\n // — same one-decision-one-dialog rule as `approval_reject` above (#7278).\n description: 'Recall this request? Approvers can no longer act on it and the record is unlocked.',\n icon: 'undo-2',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/recall',\n params: [\n { name: 'comment', label: 'Comment', type: 'textarea', required: false },\n ],\n // Recall applies while the request is live for the submitter — pending\n // (withdraw) or returned (abandon the revision instead of resubmitting).\n visible:\n 'has(record.status) && (record.status == \"pending\" || record.status == \"returned\")' +\n ' && has(record.viewer) && has(record.viewer.is_submitter) && record.viewer.is_submitter == true',\n locations: ['record_section'],\n successMessage: 'Recalled.',\n refreshAfter: true,\n },\n {\n name: 'approval_resubmit',\n label: 'Resubmit',\n icon: 'refresh-cw',\n type: 'api',\n method: 'POST',\n target: '/api/v1/approvals/requests/{id}/resubmit',\n params: [\n { name: 'comment', label: 'What changed?', type: 'textarea', required: false },\n ],\n visible: 'has(record.status) && record.status == \"returned\" && has(record.viewer) && has(record.viewer.is_submitter) && record.viewer.is_submitter == true',\n locations: ['record_section'],\n successMessage: 'Resubmitted.',\n refreshAfter: true,\n },\n ],\n\n enable: {\n // [ADR-0103] Engine-owned: the approval engine owns the request lifecycle\n // (SYSTEM_CTX); users act via domain actions (Submit/Approve/Recall), never\n // generic CRUD. Reads stay open.\n apiMethods: ['get', 'list'],\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\nimport { APPROVAL_ACTION_KINDS, APPROVAL_ACTION_KIND_LABELS } from '@objectstack/spec/contracts';\n\n/**\n * sys_approval_action — Audit trail row per approval action.\n *\n * Append-only: every `submit`, `approve`, `reject`, `recall`, or\n * `escalate` event lands here. The engine reads back per-step approval\n * rows to evaluate `behavior: 'unanimous'` (all approvers must approve\n * before advancing) versus `first_response` (any single approval\n * advances the step).\n *\n * @namespace sys\n */\nexport const SysApprovalAction = ObjectSchema.create({\n name: 'sys_approval_action',\n label: 'Approval Action',\n pluralLabel: 'Approval Actions',\n icon: 'check-circle',\n isSystem: true,\n managedBy: 'append-only',\n description: 'Append-only audit trail for approval actions',\n displayNameField: 'id',\n nameField: 'id', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)\n titleFormat: '{action} · {step_name}',\n highlightFields: ['request_id', 'step_name', 'action', 'actor_id', 'via_override', 'created_at'],\n\n // ADR-0104 D3 wave 2. `attachments` is a media field, so the files it holds\n // are OWNED by this row — and the storage service would otherwise authorize\n // their download by testing whether the caller can READ this row. It cannot:\n // this table is deliberately closed to ordinary approver positions, so that\n // test denies the very approver the attachment was filed for. The approvals\n // service already owns the rule for seeing a decision (visibility of the\n // parent request, exactly as `listActions` applies it), so it answers.\n fileAccessDelegate: 'approvals',\n\n listViews: {\n recent: {\n type: 'grid',\n name: 'recent',\n label: 'Recent',\n data: { provider: 'object', object: 'sys_approval_action' },\n columns: ['created_at', 'request_id', 'step_name', 'action', 'actor_id', 'via_override', 'comment'],\n sort: [{ field: 'created_at', order: 'desc' }],\n pagination: { pageSize: 50 },\n emptyState: { title: 'No approval actions yet', message: 'Actions are logged automatically when approvals progress.' },\n },\n by_actor: {\n type: 'grid',\n name: 'by_actor',\n label: 'By Actor',\n data: { provider: 'object', object: 'sys_approval_action' },\n columns: ['actor_id', 'created_at', 'request_id', 'step_name', 'action'],\n sort: [{ field: 'actor_id', order: 'asc' }, { field: 'created_at', order: 'desc' }],\n grouping: { fields: [{ field: 'actor_id', order: 'asc', collapsed: false }] },\n pagination: { pageSize: 100 },\n },\n all_actions: {\n type: 'grid',\n name: 'all_actions',\n label: 'All',\n data: { provider: 'object', object: 'sys_approval_action' },\n columns: ['created_at', 'request_id', 'step_name', 'action', 'actor_id', 'via_override', 'comment'],\n sort: [{ field: 'created_at', order: 'desc' }],\n pagination: { pageSize: 100 },\n },\n },\n\n fields: {\n id: Field.text({ label: 'Action ID', required: true, readonly: true, group: 'System' }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n description: 'Tenant that owns this action (mirrors the parent request)',\n }),\n\n request_id: Field.lookup('sys_approval_request', {\n label: 'Request',\n required: true,\n group: 'Target',\n }),\n\n step_name: Field.text({\n label: 'Step',\n required: false,\n maxLength: 100,\n description: 'Machine name of the step at the time of the action',\n group: 'Target',\n }),\n\n step_index: Field.number({\n label: 'Step Index',\n required: false,\n group: 'Target',\n }),\n\n action: Field.select(\n // Derived from the contract, not re-typed (#3786). `APPROVAL_ACTION_KINDS`\n // is where the list and the per-kind notes live (which kinds move the flow\n // and which are thread-only); `ApprovalActionKind` is derived from it, so\n // this column and the contract cannot disagree. The authored English label\n // per kind lives beside it in `APPROVAL_ACTION_KIND_LABELS` (#8580 — the\n // #7232 humanization pass missed this field) — mapped here, never\n // re-typed, so the `en` bundle regenerates from the contract's own text.\n APPROVAL_ACTION_KINDS.map((value) => ({ value, label: APPROVAL_ACTION_KIND_LABELS[value] })),\n {\n label: 'Action',\n required: true,\n group: 'Action',\n },\n ),\n\n actor_id: Field.lookup('sys_user', {\n label: 'Actor',\n required: false,\n group: 'Action',\n }),\n\n comment: Field.textarea({ label: 'Comment', required: false, group: 'Action' }),\n\n // #4466 — the one bit of \"who really decided this\" that was still dropped.\n // A privileged admin may act on a request whose staffed approver slate they\n // hold no slot in (the #3424 override path); before this column, that\n // decision was byte-for-byte identical to the designated approver's own\n // approval. A reader of the timeline saw `approve` by the admin and could\n // not tell whether the admin WAS an approver or OVERRODE the ones who were,\n // and the bypassed approver's later `409 INVALID_STATE` was the only trace\n // — existing only if they happened to try.\n //\n // The platform KNOWS at decision time: it took the `isOverrideActor` branch\n // to admit the call at all. This is dropped information, not unavailable\n // information.\n //\n // Set on exactly the decisions that were admitted BY that branch — an admin\n // who is also a genuine slot holder is approving normally and is recorded\n // as such. Nullable and additive: rows written before this column exists\n // carry `null`, which reads as \"not recorded\", never as \"not an override\".\n via_override: Field.boolean({\n label: 'Via Admin Override',\n required: false,\n group: 'Action',\n description:\n 'True when the actor was admitted to this action only by the privileged-override path (#3424) — '\n + 'they held no slot in the request’s pending-approver slate.',\n }),\n\n // Structured hand-off parties for `action: 'reassign'` (#4365). Before\n // these existed the pair lived only inside a default free-text comment\n // (\"<from_id> → <to_id>\"), which no client could parse or render readably.\n // `comment` is pure user input again; timelines render \"from A to B\" from\n // these fields.\n reassign_from: Field.lookup('sys_user', {\n label: 'Reassigned From',\n required: false,\n group: 'Action',\n description: 'User whose pending-approver slot was handed over (reassign actions only)',\n }),\n\n reassign_to: Field.lookup('sys_user', {\n label: 'Reassigned To',\n required: false,\n group: 'Action',\n description: 'User who received the pending-approver slot (reassign actions only)',\n }),\n\n attachments: Field.file({\n label: 'Attachments',\n required: false,\n multiple: true,\n group: 'Action',\n description: 'Files supporting this action — e.g. a signed contract or evidence (#3266).',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n },\n\n indexes: [\n { fields: ['request_id', 'created_at'] },\n { fields: ['request_id', 'step_index', 'action'] },\n ],\n\n enable: {\n // [ADR-0103] Engine-owned append-only decision log: appended by the approval\n // engine (SYSTEM_CTX). Reads stay open.\n apiMethods: ['get', 'list'],\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_approval_approver — Pending-approver index (issue #1745).\n *\n * One row per (request, approver identity) while the request is **pending**.\n * `sys_approval_request.pending_approvers` stays the human-readable source of\n * truth (a CSV column), but CSV substring matching can neither be indexed nor\n * pushed into an engine query — which made \"my pending\" a post-filter in\n * memory and broke pagination beyond the scan window.\n *\n * This table is that CSV, normalized: the service mirrors every change to\n * `pending_approvers` here (open / decide / recall / send-back / reassign /\n * escalate), and clears the rows when the request leaves `pending`. So the\n * table only ever holds the live work queue — its size tracks the number of\n * open approvals, not the append-only request history.\n *\n * `approver` holds one identity literal exactly as it appears in the CSV:\n * a user id, an email, or a `role:<name>` / `team:<name>` style literal.\n * Equality (or `$in`) on this column is the indexed replacement for the old\n * per-row substring match.\n *\n * @namespace sys\n */\nexport const SysApprovalApprover = ObjectSchema.create({\n name: 'sys_approval_approver',\n label: 'Approval Approver',\n pluralLabel: 'Approval Approvers',\n icon: 'users',\n isSystem: true,\n managedBy: 'engine-owned',\n description: 'Normalized pending-approver rows for indexed inbox queries',\n displayNameField: 'id',\n nameField: 'id', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)\n titleFormat: '{approver} · {request_id}',\n highlightFields: ['request_id', 'approver', 'created_at'],\n\n fields: {\n id: Field.text({ label: 'Row ID', required: true, readonly: true, group: 'System' }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n description: 'Tenant that owns this row (mirrors the parent request)',\n }),\n\n request_id: Field.lookup('sys_approval_request', {\n label: 'Request',\n required: true,\n group: 'Target',\n }),\n\n approver: Field.text({\n label: 'Approver',\n required: true,\n maxLength: 255,\n description: 'One pending-approver identity: user id, email, or role:/team: literal',\n group: 'Target',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n },\n\n indexes: [\n // \"My pending\" inbox: equality on the identity literal, scoped by tenant.\n { fields: ['approver', 'organization_id'] },\n // Sync path: rewrite all rows of one request on each approver-set change.\n { fields: ['request_id'] },\n ],\n\n enable: {\n // [ADR-0103] Engine-owned: approver rows are rewritten by the approval\n // engine (SYSTEM_CTX) on each approver-set change, never via generic CRUD.\n apiMethods: ['get', 'list'],\n },\n});\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_approval_delegation — self-service out-of-office (OOO) delegation (#1322 M1).\n *\n * A standing, self-declared rule: \"while I (the delegator) am out between\n * `valid_from` and `valid_until`, route the approver slots that would resolve\n * to me onto my delegate instead.\" The approval service consults active rows\n * in `ApprovalService.expandApprovers` when resolving an approval node's\n * INDIVIDUALLY-routed approvers (`type: user` / `field` / `manager`) — the\n * delegate becomes a real pending approver and acts under their own identity,\n * so nothing is impersonated and the audit trail stays honest.\n *\n * Modelled as its own object (not a scalar on the better-auth-locked\n * `sys_user`), mirroring the `sys_user_position` delegation precedent\n * (ADR-0091): the validity window is enforced at RESOLUTION time via the\n * shared `isGrantActive` predicate — never by a background job (ADR-0049).\n * The window is half-open `[valid_from, valid_until)` in UTC.\n *\n * Scope note: this is the community-core OOO auto-skip only. Long-term proxy\n * \"act-as\" access (viewing/acting on another user's full queue under their\n * authority), delegation governance / segregation-of-duties, and org-wide\n * administration of others' delegations are enterprise concerns tracked\n * separately (objectstack-ai/cloud#855), not here.\n *\n * @namespace sys\n */\nexport const SysApprovalDelegation = ObjectSchema.create({\n name: 'sys_approval_delegation',\n label: 'Approval Delegation',\n pluralLabel: 'Approval Delegations',\n icon: 'user-clock',\n isSystem: true,\n // [ADR-0103, #3355] Admin/user-writable DATA on a platform-defined schema: a\n // user authors their own out-of-office delegation. The bucket default is full\n // CRUD (matching the full-CRUD apiMethods below), so no `userActions` block is\n // needed — RLS/permission sets are the authz.\n managedBy: 'system-data',\n description:\n 'Self-service out-of-office rule: route this user\\'s approver slots to a delegate within a time window (#1322 M1).',\n titleFormat: '{delegator_id} → {delegate_id}',\n highlightFields: ['delegator_id', 'delegate_id', 'valid_from', 'valid_until'],\n\n listViews: {\n active: {\n type: 'grid',\n name: 'active',\n label: 'Active',\n data: { provider: 'object', object: 'sys_approval_delegation' },\n columns: ['delegator_id', 'delegate_id', 'valid_from', 'valid_until', 'reason'],\n sort: [{ field: 'valid_until', order: 'asc' }],\n pagination: { pageSize: 50 },\n emptyState: {\n title: 'No delegations',\n message: 'Declare an out-of-office delegation so approvals route to a backup while you are away.',\n },\n },\n },\n\n fields: {\n id: Field.text({ label: 'Delegation ID', required: true, readonly: true, group: 'System' }),\n\n delegator_id: Field.lookup('sys_user', {\n label: 'Delegator',\n required: true,\n group: 'Delegation',\n description: 'The user going out of office; their individually-routed approver slots are rerouted while active.',\n }),\n\n delegate_id: Field.lookup('sys_user', {\n label: 'Delegate',\n required: true,\n group: 'Delegation',\n description: 'The backup who receives the delegator\\'s approvals while this rule is active. Acts under their own identity.',\n }),\n\n valid_from: Field.datetime({\n label: 'Valid From',\n required: false,\n group: 'Delegation',\n description:\n 'Rule is inactive before this instant. Null = active immediately. ' +\n 'Enforced at resolution time via isGrantActive (ADR-0091 D2 predicate) — never by a background job.',\n }),\n\n valid_until: Field.datetime({\n label: 'Valid Until',\n required: false,\n group: 'Delegation',\n description:\n 'Rule is inactive AT and AFTER this instant (half-open [from, until), UTC). Null = never expires.',\n }),\n\n reason: Field.text({\n label: 'Reason',\n required: false,\n maxLength: 500,\n group: 'Delegation',\n description: 'Why the delegation exists (e.g. \"Annual leave 5/26–5/30\"). Recorded on the substitution audit row.',\n }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n description: 'Tenant that owns this rule; null = applies across tenants for this delegator.',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n },\n\n indexes: [\n // Resolution-time lookup: \"active delegations for this delegator\".\n { fields: ['delegator_id', 'organization_id'] },\n { fields: ['delegate_id'] },\n { fields: ['valid_until'] },\n ],\n\n enable: {\n trackHistory: true,\n searchable: true,\n apiEnabled: true,\n // `bulk` = the batch shape of the verbs above; the gate is `bulk ∧ child`\n // (#3391 P1), so omitting it 405s /batch and the *Many routes (#3026).\n apiMethods: ['get', 'list', 'create', 'update', 'delete', 'bulk'],\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport {\n APPROVAL_BRANCH_LABELS,\n APPROVAL_REVISE_NODE_TYPE,\n approverTypeIsOrgScoped,\n canonicalApproverType,\n normalizeDecisionOutputs,\n type ApprovalNodeConfig,\n} from '@objectstack/spec/automation';\nimport { ExpressionEngine, collectCelRootIdentifiers } from '@objectstack/formula';\nimport { keysetWalk } from '@objectstack/types';\nimport {\n ADMIN_FULL_ACCESS,\n ORGANIZATION_ADMIN_GRANTS,\n BUILTIN_IDENTITY_PLATFORM_ADMIN,\n BUILTIN_IDENTITY_ORG_OWNER,\n BUILTIN_IDENTITY_ORG_ADMIN,\n} from '@objectstack/spec/identity';\nimport type {\n IApprovalService,\n ApprovalRequestRow,\n ApprovalActionRow,\n ApprovalActionAttachment,\n ApprovalDecisionInput,\n ApprovalDecisionResult,\n ApprovalRecallInput,\n ApprovalRecallResult,\n ApprovalSendBackInput,\n ApprovalSendBackResult,\n ApprovalResubmitInput,\n ApprovalResubmitResult,\n ApprovalStatus,\n} from '@objectstack/spec/contracts';\n// [#7135] The full `resolveAuthzContext` envelope — what `IApprovalService`\n// declares for every one of these context parameters since #6523 (the #6206\n// ruling: enforcement adjudicates on the whole envelope, never a per-site\n// subset). Annotating the implementation with the retired six-field shape is\n// what forced this file to cast its way out of its own contract to read\n// fields the caller had already supplied.\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\nimport { RESUME_AUTHORITY_SERVICE } from '@objectstack/spec/contracts';\nimport { isFileIdToken } from '@objectstack/spec/data';\nimport { isGrantActive } from '@objectstack/core';\nimport {\n filterApproversWhoCanRead,\n resolveApproverDirectoryOrg,\n type ApproverOrgScopeDeps,\n type ApproverOrgScopeEngine,\n} from './approver-org-scope.js';\n\n/**\n * Node-era approval runtime (ADR-0019).\n *\n * Approval is no longer a standalone engine — it is a **flow node**. A flow's\n * Approval node opens a request via {@link ApprovalService.openNodeRequest} and\n * the run suspends; a human decision via {@link ApprovalService.decide}\n * finalises the request and resumes the owning run down the matching\n * `approve` / `reject` edge.\n *\n * This service owns the durable approval *state* — `sys_approval_request` /\n * `sys_approval_action`, approver resolution (team / department / position /\n * role / manager graph), and the optional status-field mirror — plus the decision\n * API. It does not author processes, submit, or walk multi-step machinery\n * anymore; that orchestration lives on the one automation engine.\n */\nexport interface ApprovalEngine {\n find(object: string, options?: any): Promise<any[]>;\n insert(object: string, data: any, options?: any): Promise<any>;\n update(object: string, idOrData: any, dataOrOptions?: any, options?: any): Promise<any>;\n delete(object: string, options?: any): Promise<any>;\n}\n\nexport interface ApprovalClock { now(): Date }\n\n/**\n * Minimal automation surface the service uses to resume a suspended flow run\n * once a decision finalises a node-driven request. Optional — attached by the\n * plugin when an automation engine is present (see `approval-node.ts`).\n */\nexport interface ApprovalResumeSurface {\n resume?(runId: string, signal?: {\n output?: Record<string, unknown>;\n branchLabel?: string;\n /**\n * #3801: the engine refuses a resume of an `approval` suspension unless\n * the signal carries this marker — the proof that the resume is the tail\n * of a decision THIS service already authorized and recorded, not a raw\n * `POST …/runs/:runId/resume` around it. Every resume below stamps it via\n * {@link ApprovalService.serviceResume}.\n */\n [RESUME_AUTHORITY_SERVICE]?: true;\n }): Promise<unknown>;\n /** Flow definition lookup, used to derive step-progress display data. */\n getFlow?(name: string): Promise<any | null>;\n /**\n * Terminally cancel a suspended run (ADR-0044). Used when a recall lands\n * during a revision window — the run is paused at the revise-window node,\n * which has no reject edge to resume down.\n */\n cancelRun?(runId: string, reason?: string): Promise<unknown>;\n /**\n * Look up a run's recorded outcome (#3456). Used by the dead-run sweep to ask\n * \"is the run behind this pending request still alive?\".\n *\n * The contract that makes the sweep safe is the answer for a run that is\n * merely SUSPENDED (the normal state of a run waiting on an approval): the\n * engine writes no execution-log entry until a run reaches a terminal state,\n * so a suspended run resolves to `null`, never to a status. The sweep\n * therefore acts only on an explicit terminal-failure status and treats\n * `null` — unknown run, evicted log, no durable store, no automation engine —\n * as \"still alive\".\n */\n getRun?(runId: string): Promise<{ status?: string } | null>;\n /**\n * Whether a suspension still exists for `runId` — the pre-flight that keeps\n * a decision from being recorded against a run that can never advance\n * (#4420). Read-only; it never consumes the suspension.\n *\n * Still distinct from {@link getRun}, but no longer on the axis this comment\n * used to name: since #8050 `getRun` also sees a run suspended by a PREVIOUS\n * process (it resolved `null` there before, unable to tell \"waiting for a\n * human\" from \"dead\"). The difference that remains is the one a pre-flight\n * turns on — this asks the suspension store and REJECTS when it cannot be\n * read, where `getRun` degrades an outage to `null`. A caller about to WRITE\n * must not accept a degraded read.\n *\n * Rejects when the durable store cannot be read — existence is then\n * unknown, and callers must not read an outage as a dead run. Optional: an\n * engine that does not implement it simply gets no pre-flight.\n */\n hasSuspendedRun?(runId: string): Promise<boolean>;\n}\n\n/**\n * Optional messaging surface (ADR-0012 `messaging` service). When attached,\n * thread interactions (reassign / remind / request-info / comment) notify the\n * affected users; without it they degrade to audit-only.\n */\nexport interface ApprovalMessagingSurface {\n emit(input: {\n topic: string;\n audience: string[];\n payload?: Record<string, unknown>;\n severity?: string;\n dedupKey?: string;\n source?: { object: string; id: string };\n actorId?: string;\n }): Promise<unknown>;\n}\n\n/** Minimum time between submitter reminders on one request. */\nexport const REMIND_COOLDOWN_MS = 4 * 60 * 60 * 1000;\n\n/** Named job under which the SLA escalation scan is registered (ADR-0042). */\nexport const ESCALATION_JOB_NAME = 'approvals-sla-escalation';\n/** Default interval between SLA escalation scans. */\nexport const ESCALATION_SCAN_INTERVAL_MS = 5 * 60 * 1000;\n/** Reserved actor id for machine decisions made by the SLA scanner. */\nexport const SLA_ACTOR_ID = 'system:sla';\n/** Reserved actor id for requests abandoned because their run died (#3456). */\nexport const DEAD_RUN_ACTOR_ID = 'system:dead-run';\n/**\n * Run statuses that mean \"this run will never resume\", so a request still\n * pending on it is orphaned (#3456). A CLOSED set, deliberately: the dead-run\n * sweep treats every other answer — `paused` (a run waiting on its approval,\n * the normal case), `running`, an unknown status, or no answer at all — as\n * alive, so an unrecognised state can never cost someone a live approval.\n *\n * `completed` belongs here with the failure states. The approval node only\n * writes a request row on the path where it also suspends the run, and every\n * in-band transition (decide / recall / send-back / resubmit) finalises the\n * request *before* it resumes the run — so a completed run with a still-pending\n * request means the run was resumed out of band and left the request behind.\n */\nconst TERMINAL_RUN_STATUSES: ReadonlySet<string> = new Set([\n 'completed', 'failed', 'cancelled', 'timed_out',\n]);\n\n/**\n * Request statuses that can leave a ZOMBIE behind (#4469) — the terminal states\n * a decision reaches only by ALSO resuming the owning run.\n *\n * `recalled` is deliberately absent: a recall ABANDONS the request on purpose,\n * and {@link ApprovalService.recall} explicitly tolerates a run it cannot\n * resume (the withdrawal and the lock release are the point). Reporting those\n * would bury the real findings under expected ones.\n */\nconst STRANDABLE_REQUEST_STATUSES = ['approved', 'rejected', 'returned'] as const;\n\n/**\n * One terminal request whose owning flow run is unrecoverable (#4469) — the\n * decision was recorded and the flow never moved. Reporting shape only: the\n * sweep never rewrites these rows (see\n * {@link ApprovalService.inspectStrandedRequests}).\n */\nexport interface StrandedApprovalRequest {\n requestId: string;\n /** Terminal status the request reached — the decision that WAS recorded. */\n status: string;\n /** The `flow_run_id` that resolves to neither a suspension nor a run history row. */\n runId: string;\n flowName?: string;\n /** Approval node the run should have continued from. */\n nodeId?: string;\n objectName: string;\n recordId: string;\n organizationId?: string | null;\n completedAt?: string;\n /** `config.approvalStatusField`, when the node mirrors status onto the record. */\n mirrorField?: string;\n /** What that mirror field currently reads — usually the stale value an operator sees. */\n mirroredStatus?: string;\n}\n\n/** Default lifetime of an actionable-link token (ADR-0043). */\nexport const ACTION_TOKEN_TTL_MS = 72 * 60 * 60 * 1000;\n\n/** Outcome of redeeming (or peeking) an actionable-link token. */\nexport type ActionTokenOutcome =\n | { ok: true; action: 'approve' | 'reject'; request: ApprovalRequestRow; approverId: string }\n | { ok: false; reason: 'invalid' | 'expired' | 'consumed' | 'not_pending' | 'not_approver'; request?: ApprovalRequestRow };\n\n/**\n * System-elevated context for this service's own metadata reads and writes.\n *\n * [#7135] Typed as the full envelope so it is passed AS ITSELF. It used to be\n * declared `as const` and forced through `as unknown as\n * SharingExecutionContext` at the three sites that handed it to a CONTRACT\n * method — a double cast on an enforcement input, which switches checking off\n * for the whole argument rather than for the readonly-array mismatch that\n * provoked it.\n */\nconst SYSTEM_CTX: ExecutionContext = { isSystem: true, positions: [], permissions: [] };\n\n/**\n * Who is acting, for the purpose of a data write made on their behalf (#3783).\n *\n * Reads the AUTHENTICATED principal off the execution context — deliberately not\n * `input.actorId`. When this was written the two could still disagree: every\n * public entrypoint took `actorId` from the request body (`body.actorId ??\n * context.userId`, see the REST approval routes) and the service only checked\n * that it named a pending approver, never that it was the caller. That was\n * called tolerable on an audit row — but the same unchecked value was the\n * authorization key, so it was in fact impersonation, and #3800 closed it:\n * {@link ApprovalService.resolveActor} now pins the actor to an identity the\n * server can prove belongs to the caller. This helper stays the separate,\n * stricter answer for a DATA WRITE, which wants the bare human id and never a\n * `type:value` slot literal or a machine sentinel.\n *\n * A caller holding a trustworthy actor with no session behind it — the ADR-0043\n * action link, whose token cryptographically binds exactly one approver — puts\n * that actor ON the context instead of relying on this.\n *\n * `null` for a machine caller (the SLA sweep passes {@link SYSTEM_CTX}), so a\n * reserved sentinel like {@link SLA_ACTOR_ID} can never surface as a `userId`.\n */\nfunction actingUserId(context: ExecutionContext | undefined): string | null {\n const userId = context?.userId;\n return typeof userId === 'string' && userId ? userId : null;\n}\n\n/**\n * Max hops when following an OOO delegation chain (#1322 M1): A out → B, B out\n * → C, … Bounds the walk so a mis-configured chain can't loop or resolve\n * unboundedly; a cycle or self-reference also stops it early.\n */\nconst OOO_MAX_CHAIN = 8;\n\n/**\n * Approver types resolved by QUERYING a graph rather than by taking `value`\n * literally (#3807). Each can legitimately come back empty — an unstaffed\n * position, an emptied team, a mis-pointed unit — and the caller then falls\n * back to a `type:value` literal that no user can act on. They are listed here\n * so that dead end gets one warning instead of passing in silence.\n *\n * `user` / `field` are deliberately absent: they resolve to the id they were\n * given without a lookup, so there is no \"expanded to nobody\" state to report.\n * `business_unit` / `bu` are the accepted dialects of `department`.\n */\nconst GRAPH_APPROVER_TYPES: ReadonlySet<string> = new Set([\n 'team', 'department', 'business_unit', 'bu', 'position', 'org_membership_level', 'manager',\n]);\n\n/** One OOO delegation hop applied while resolving an approver (#1322 M1/M4). */\ninterface OooSubstitution {\n /** The approver who was skipped (out of office). */\n from: string;\n /** The delegate the slot was routed to. */\n to: string;\n /** The delegator's declared reason, if any. */\n reason: string | null;\n}\n\n/**\n * The CLOSED set of namespace roots an `expression` approver may reference\n * (#3447 P2). Three explicit times/sources, no `record`, no bare field names:\n * `record` means \"the record at event time\" everywhere else on the platform\n * (flow conditions: trigger snapshot; hooks: the write payload), so binding it\n * here — to either time — would silently alias one meaning to the other. The\n * runtime CEL env treats unknown roots as `dyn` (→ `null` → an empty slate),\n * so out-of-contract roots MUST be rejected before evaluation; both this\n * pre-check and the lint rule read the roots via\n * {@link collectCelRootIdentifiers} so they can never drift.\n */\nconst APPROVER_EXPRESSION_ROOTS = new Set(['current', 'trigger', 'vars']);\n\n/**\n * Evaluation context an approval node hands to `expression` approvers\n * (#3447 P2). `current` (the live record) is supplied by openNodeRequest's\n * re-read; these two carry the other roots.\n */\nexport interface ApproverExpressionContext {\n /** Submit-time snapshot (the flow's `$record`) — bound as `trigger.*`. */\n trigger?: Record<string, unknown> | null;\n /** Flow variables at node entry (nested by dotted key) — bound as `vars.*`. */\n vars?: Record<string, unknown> | null;\n}\n\n/**\n * Non-request outcome of {@link ApprovalService.openNodeRequest}: the node\n * resolved an empty approver slate and its `onEmptyApprovers: 'auto_approve'`\n * policy waved it through (#3447 P2). No `sys_approval_request` row exists —\n * nobody was ever asked — so the node must complete down its `approve` edge\n * instead of suspending.\n */\nexport interface ApprovalNodeAutoOutcome {\n autoApproved: true;\n reason: 'empty_approvers';\n}\n\nfunction uid(prefix: string): string {\n const g: any = globalThis as any;\n if (g.crypto?.randomUUID) return `${prefix}_${g.crypto.randomUUID()}`;\n return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction parseJson<T = any>(raw: unknown, fallback: T): T {\n if (raw == null || raw === '') return fallback;\n if (typeof raw === 'string') {\n try { return JSON.parse(raw) as T; } catch { return fallback; }\n }\n return raw as T;\n}\n\nfunction csvSplit(raw: unknown): string[] {\n if (!raw) return [];\n if (Array.isArray(raw)) return raw.map(String).filter(Boolean);\n return String(raw).split(',').map(s => s.trim()).filter(Boolean);\n}\n\n/**\n * Is a submitted decision output blank — i.e. does it fail a `required`\n * declaration (objectui#2955)?\n *\n * \"Present but empty\" has to count as missing: a decision UI sends whatever\n * its widget holds, and an untouched picker/text box is `''` or `[]`. Letting\n * those through would satisfy `required` with a value the downstream\n * `expression` approver then resolves to nobody — the exact stall the flag\n * exists to prevent. `false` and `0` are real values and pass.\n */\nfunction isBlankDecisionOutput(value: unknown): boolean {\n if (value === undefined || value === null) return true;\n if (typeof value === 'string') return value.trim() === '';\n if (Array.isArray(value)) return value.filter(v => v !== null && v !== undefined && String(v).trim() !== '').length === 0;\n return false;\n}\n\n/**\n * Humanize a machine name for display fallback: strips a `flow:` prefix and\n * title-cases underscore/dash segments (`flow:manager_review` → \"Manager\n * Review\"). Used only when no authored label was snapshotted on the row.\n */\nfunction prettifyMachineName(raw: string | null | undefined): string | undefined {\n if (!raw) return undefined;\n const base = String(raw).replace(/^flow:/, '').trim();\n if (!base) return undefined;\n return base\n .split(/[_\\-\\s]+/)\n .filter(Boolean)\n .map(w => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n}\n\nfunction rowFromRequest(row: any): ApprovalRequestRow {\n // Authored display labels ride the node-config snapshot (`__flowLabel` /\n // `__nodeLabel`) so they survive without a schema migration; fall back to a\n // prettified machine name for rows written before labels were captured.\n const cfg = parseJson<any>(row.node_config_json, undefined);\n return {\n id: String(row.id),\n organization_id: row.organization_id ?? undefined,\n process_name: String(row.process_name ?? ''),\n object_name: String(row.object_name ?? ''),\n record_id: String(row.record_id ?? ''),\n submitter_id: row.submitter_id ?? undefined,\n submitter_comment: row.submitter_comment ?? undefined,\n status: (row.status as ApprovalStatus) ?? 'pending',\n current_step: row.current_step ?? undefined,\n current_step_index: row.current_step_index ?? undefined,\n pending_approvers: csvSplit(row.pending_approvers),\n payload: parseJson(row.payload_json, undefined),\n flow_run_id: row.flow_run_id ?? undefined,\n flow_node_id: row.flow_node_id ?? undefined,\n completed_at: row.completed_at ?? undefined,\n created_at: row.created_at ?? undefined,\n updated_at: row.updated_at ?? undefined,\n // The row is created at submission time; expose the stable inbox-facing name.\n submitted_at: row.created_at ?? undefined,\n process_label: cfg?.__flowLabel ?? prettifyMachineName(row.process_name),\n step_label: cfg?.__nodeLabel ?? prettifyMachineName(row.current_step),\n sla_due_at: slaDueAt(row.created_at, cfg),\n // ADR-0044 revision round (rides the config snapshot; absent ⇒ round 1).\n round: typeof cfg?.__round === 'number' ? cfg.__round : undefined,\n // objectui#2902: the node's record-lock policy. The lock is enforced\n // server-side in `lifecycle-hooks.ts` off THIS SAME snapshot with the\n // same `!== false` default, so the flag a client renders and the rule the\n // server applies can never drift. Without it a console can only see\n // \"a pending request exists\" and has to assume the record is locked —\n // which mislabels every `lockRecord: false` node as locked and hides an\n // edit the server would have accepted.\n lock_record: cfg?.lockRecord !== false,\n // #3447 P2: the node's author-declared decision outputs, surfaced so a\n // decision UI can render input fields for them and POST `outputs` on\n // approve/reject. Per-request (each node declares its own), which is why\n // this rides the row instead of the static action params. Two shapes for\n // version skew: `decision_outputs` stays the bare KEY list an older\n // console renders as text inputs; `decision_output_defs` carries the\n // normalized typed declarations a picker-aware console prefers.\n ...(() => {\n const defs = normalizeDecisionOutputs(cfg?.decisionOutputs);\n return defs.length\n ? { decision_outputs: defs.map(d => d.key), decision_output_defs: defs }\n : {};\n })(),\n } as any;\n}\n\n/** `created_at + escalation.timeoutHours`, when the node declares an SLA. */\nfunction slaDueAt(createdAt: unknown, cfg: any): string | undefined {\n const hours = cfg?.escalation?.timeoutHours;\n if (typeof hours !== 'number' || hours <= 0 || !createdAt) return undefined;\n const t = Date.parse(String(createdAt));\n if (Number.isNaN(t)) return undefined;\n return new Date(t + hours * 3600_000).toISOString();\n}\n\n/**\n * Normalize one raw `attachments` entry into an {@link ApprovalActionAttachment}.\n *\n * `sys_approval_action.attachments` is a `Field.file` (multiple), so the column\n * **stores opaque `sys_file` ids** — that is the stored form of every media\n * field (ADR-0104 D3). What arrives here is whichever of three forms the read\n * path produced:\n *\n * 1. the **expanded** `{ id, name, size, mimeType, url }` the ObjectQL read\n * path resolves a stored id into — the normal case;\n * 2. a **bare id**, when there was nothing to expand it into (storage service\n * absent, file not committed);\n * 3. a **legacy inline blob** (`{ file_id, name, mime_type, url, … }`) written\n * before file-as-reference, until the backfill converts it.\n *\n * The original mapping did `String(entry)`, which turned form 1 into the\n * literal `\"[object Object]\"` — so the inbox timeline showed a nameless,\n * un-openable attachment chip (#3266 follow-up; caught by browser verification).\n *\n * Note the casing: the expanded form carries `mimeType`, the legacy blob\n * `mime_type`. Both are accepted for the duration of the migration window.\n */\nfunction normalizeActionAttachment(entry: any): ApprovalActionAttachment | undefined {\n if (entry == null) return undefined;\n // Form 2 — a bare reference. `isFileIdToken` is the platform's single arbiter\n // of \"is this string an opaque file id, or a URL?\", shared with the engine's\n // read resolver, so the two cannot disagree about what counts as an id.\n if (typeof entry === 'string') {\n const id = entry.trim();\n if (!id) return undefined;\n return isFileIdToken(id) ? { id } : { id, url: id };\n }\n if (typeof entry === 'object') {\n // Forms 1 and 3 — `file_id` is the legacy blob's key for the same thing.\n const id = entry.id ?? entry.file_id;\n if (id == null || String(id) === '') return undefined;\n const mimeType = entry.mimeType ?? entry.mime_type;\n return {\n id: String(id),\n name: typeof entry.name === 'string' ? entry.name : undefined,\n url: typeof entry.url === 'string' ? entry.url : undefined,\n mimeType: typeof mimeType === 'string' ? mimeType : undefined,\n size: typeof entry.size === 'number' ? entry.size : undefined,\n };\n }\n return undefined;\n}\n\nfunction rowFromAction(row: any): ApprovalActionRow {\n const attachments = Array.isArray(row.attachments)\n ? row.attachments.map(normalizeActionAttachment).filter((a: ApprovalActionAttachment | undefined): a is ApprovalActionAttachment => !!a)\n : [];\n return {\n id: String(row.id),\n request_id: String(row.request_id),\n step_name: row.step_name ?? undefined,\n step_index: row.step_index ?? undefined,\n action: row.action,\n actor_id: row.actor_id ?? undefined,\n comment: row.comment ?? undefined,\n // Structured reassign hand-off parties (#4365).\n reassign_from: row.reassign_from ?? undefined,\n reassign_to: row.reassign_to ?? undefined,\n // #4466 — surfaced so a timeline can SAY \"overridden the approver slate\"\n // rather than render an override identically to an ordinary approval.\n // `null` (a row written before the column existed) stays `undefined`:\n // \"not recorded\" is not the same claim as \"not an override\".\n via_override: row.via_override == null ? undefined : row.via_override === true,\n // Decision attachments (#3266): rich descriptors carrying the display name +\n // download URL, so consumers label/open them without reading `sys_file`.\n attachments: attachments.length ? attachments : undefined,\n created_at: row.created_at ?? undefined,\n };\n}\n\nexport interface ApprovalServiceOptions {\n engine: ApprovalEngine;\n clock?: ApprovalClock;\n logger?: { info?: (msg: any, ...rest: any[]) => void; warn?: (msg: any, ...rest: any[]) => void; error?: (msg: any, ...rest: any[]) => void; debug?: (msg: any, ...rest: any[]) => void };\n /**\n * Optional automation surface used to resume a suspended flow run when a\n * decision finalises a request. Usually attached after construction via\n * {@link ApprovalService.attachAutomation} once the automation engine is\n * available.\n */\n automation?: ApprovalResumeSurface;\n /** Optional messaging service for thread notifications. */\n messaging?: ApprovalMessagingSurface;\n /**\n * Absolute origin prefixed onto actionable links (ADR-0043), e.g.\n * `https://app.example.com`. Defaults to relative URLs, which work inside\n * the Console and IM webviews; outbound email needs the absolute form.\n */\n publicBaseUrl?: string;\n /**\n * [ADR-0105 D9] The tenancy posture in force. Cross-organization approver\n * targeting is a `group`-posture capability; the resolver refuses the\n * declaration under any other posture rather than silently ignoring it.\n * Absent (a stack booted with no tenancy service) reads as \"unknown\" and the\n * guard stands down.\n */\n tenancyPosture?: () => string | undefined;\n /**\n * [#8652] Objects on which a user holding READ access to the target business\n * record may also see that record's approval requests and action history —\n * read-only. Empty or absent (the default) leaves visibility exactly as it\n * was. See {@link ApprovalService.recordReaderVisibleIds} for the rule and\n * its boundaries.\n */\n recordReaderVisibleObjects?: string[];\n}\n\nexport class ApprovalService implements IApprovalService {\n private readonly engine: ApprovalEngine;\n private readonly clock: ApprovalClock;\n private readonly logger?: ApprovalServiceOptions['logger'];\n private automation?: ApprovalResumeSurface;\n private messaging?: ApprovalMessagingSurface;\n private publicBaseUrl: string;\n private tenancyPosture?: () => string | undefined;\n /**\n * [#8652] The enabled object set for the record-reader visibility tier.\n * EMPTY means the tier is off — the default, and the shape every existing\n * deployment gets on upgrade.\n */\n private readonly recordReaderVisibleObjects: ReadonlySet<string>;\n\n constructor(opts: ApprovalServiceOptions) {\n this.engine = opts.engine;\n this.clock = opts.clock ?? { now: () => new Date() };\n this.logger = opts.logger;\n this.automation = opts.automation;\n this.messaging = opts.messaging;\n this.publicBaseUrl = (opts.publicBaseUrl ?? '').replace(/\\/$/, '');\n this.tenancyPosture = opts.tenancyPosture;\n this.recordReaderVisibleObjects = new Set(\n (Array.isArray(opts.recordReaderVisibleObjects) ? opts.recordReaderVisibleObjects : [])\n .map((n) => String(n ?? '').trim())\n .filter(Boolean),\n );\n }\n\n /** Attach (or replace) the ADR-0105 D9 posture provider. */\n attachTenancyPosture(provider: () => string | undefined): void {\n this.tenancyPosture = provider;\n }\n\n /** Deps bundle for the ADR-0105 D9 org-scope helpers. */\n private get orgScopeDeps(): ApproverOrgScopeDeps {\n return {\n engine: this.engine as unknown as ApproverOrgScopeEngine,\n posture: this.tenancyPosture,\n logger: this.logger,\n };\n }\n\n /**\n * [ADR-0105 D9] Which organization's directory resolves ONE approver spec.\n * Absent declaration ⇒ the request's own organization (unchanged, no reads).\n */\n private async directoryOrgFor(a: any, requestOrgId: string | null | undefined): Promise<string | null | undefined> {\n const rawType = String(a?.type ?? '');\n return resolveApproverDirectoryOrg(\n this.orgScopeDeps,\n a?.organization,\n requestOrgId,\n rawType,\n approverTypeIsOrgScoped(rawType),\n );\n }\n\n /** Attach (or replace) the automation surface used to resume flow runs. */\n attachAutomation(automation: ApprovalResumeSurface): void {\n this.automation = automation;\n }\n\n /** Attach (or replace) the messaging surface used for thread notifications. */\n attachMessaging(messaging: ApprovalMessagingSurface): void {\n this.messaging = messaging;\n }\n\n /** Best-effort notification fan-out — failures only log. */\n private async notify(input: {\n topic: string;\n audience: string[];\n payload?: Record<string, unknown>;\n dedupKey?: string;\n source?: { object: string; id: string };\n actorId?: string;\n }): Promise<number> {\n const audience = input.audience.filter(a => a && !a.includes(':'));\n if (!this.messaging || !audience.length) return 0;\n // Deep-link the inbox (#2678 P1.5): a notification about one request should\n // land on that request, not the bare inbox. Rewritten centrally so every\n // call site — and any future one — inherits it; the query param is read by\n // the console inbox to auto-open the drawer.\n let payload = input.payload;\n if (\n payload?.actionUrl === '/system/approvals'\n && input.source?.object === 'sys_approval_request'\n && input.source.id\n ) {\n payload = { ...payload, actionUrl: `/system/approvals?request=${encodeURIComponent(input.source.id)}` };\n }\n try {\n await this.messaging.emit({ severity: 'info', ...input, payload, audience });\n return audience.length;\n } catch (err: any) {\n this.logger?.warn?.('[approvals] notification failed', {\n topic: input.topic, error: err?.message ?? String(err),\n });\n return 0;\n }\n }\n\n /** Load a request row and assert it is still pending. */\n private async loadPendingRow(requestId: string): Promise<any> {\n if (!requestId) throw new Error('VALIDATION_FAILED: requestId is required');\n const rows = await this.engine.find('sys_approval_request', {\n where: { id: requestId }, limit: 1, context: SYSTEM_CTX,\n });\n const raw: any = Array.isArray(rows) ? rows[0] : null;\n if (!raw) throw new Error(`REQUEST_NOT_FOUND: ${requestId}`);\n if (raw.status !== 'pending') throw new Error(`INVALID_STATE: request is ${raw.status}`);\n return raw;\n }\n\n /**\n * Privileged-override gate (#3424). A stuck approval — one routed to a\n * position/team with no holders (so its `pending_approvers` is only an\n * unresolvable `type:value` literal) or to approvers who have all since left —\n * is otherwise undecidable: no concrete user is in the slate, so every normal\n * `decide` / `reassign` / `recall` is `FORBIDDEN` and (with `lockRecord`) the\n * record stays locked forever with no in-product recovery. A platform or\n * tenant admin — the same posture the engine's superuser bypass already\n * trusts — may always act on a PENDING request to release it: approve, reject,\n * reassign it to a real approver, or recall it.\n *\n * A platform admin crosses the tenant wall (matching the unscoped\n * `admin_full_access` evidence); a tenant admin may override only within their\n * own org (or an org-less request). A system context always passes. Signals are\n * read defensively off the resolved exec context (`permissions` / `positions` /\n * the derived `posture`, ADR-0095) so any transport that resolves through the\n * shared authz resolver lights this up without extra wiring.\n */\n private isOverrideActor(context: ExecutionContext, requestOrg?: string | null): boolean {\n if (!context) return false;\n if (context.isSystem) return true;\n const perms = Array.isArray(context.permissions) ? context.permissions : [];\n const positions = Array.isArray(context.positions) ? context.positions : [];\n // [#7135] A DECLARED read. `posture` (ADR-0095 D2) is resolved by\n // `resolveAuthzContext` and is a field of the envelope the contract has\n // named here since #6523 — the doc block above already says it is the\n // intended signal. Until this parameter widened, reading it meant an\n // unchecked `as any` on an enforcement input: a typo (`postures`,\n // `'PLATFORM-ADMIN'`) would have compiled and silently denied every\n // override, leaving a stuck approval with no in-product recovery.\n const posture = context.posture;\n const isPlatformAdmin = posture === 'PLATFORM_ADMIN'\n || perms.includes(ADMIN_FULL_ACCESS)\n || positions.includes(BUILTIN_IDENTITY_PLATFORM_ADMIN);\n if (isPlatformAdmin) return true;\n const isTenantAdmin = posture === 'TENANT_ADMIN'\n || ORGANIZATION_ADMIN_GRANTS.some((n) => perms.includes(n))\n || positions.includes(BUILTIN_IDENTITY_ORG_OWNER)\n || positions.includes(BUILTIN_IDENTITY_ORG_ADMIN);\n if (!isTenantAdmin) return false;\n // A tenant admin's authority stops at their own org; a null-org request is\n // global and any admin may release it.\n // Only the `tenantId` half of this read lost its cast: `tenantId` is a\n // declared field of the envelope, `organizationId` is not a field of it at\n // ALL. That spelling has its own history (#5858 / `check:org-identifier`)\n // and was explicitly held out of this change (#7070) — so it stays cast,\n // and the asymmetry is now the visible marker of which of the two names\n // the contract actually knows.\n const actorTenant = context.tenantId ?? (context as any).organizationId ?? null;\n return requestOrg == null || (actorTenant != null && String(requestOrg) === String(actorTenant));\n }\n\n /**\n * Pin the acting identity to the AUTHENTICATED CALLER (#3800).\n *\n * Every public entrypoint accepts an `actorId`, and the REST routes fill it\n * from `body.actorId ?? body.actor_id ?? context.userId` — so before this\n * gate the body won. The authorization checks downstream all read that value\n * (`pending_approvers.includes(input.actorId)`, `submitter_id === actorId`),\n * which made the body-supplied string not merely the audit label but the key\n * that opens the door: any authenticated user could name a pending approver\n * and have that approver's decision recorded and the owning flow resumed.\n * #3783 drew this line for the data-write identity ({@link actingUserId});\n * this closes the authorization half.\n *\n * A caller may still name an identity OTHER than their bare user id, because\n * a slot legitimately can be keyed by one: `resolveApproverSpec` stores the\n * `type:value` literal when a graph lookup yields nothing, and an author may\n * write an email as a `user` approver. So the rule is not \"actorId must equal\n * userId\" — it is **\"actorId must be an identity the SERVER can prove belongs\n * to the caller\"**. Anything else is `FORBIDDEN`.\n *\n * A system context is exempt and keeps its explicit actor: the SLA sweep\n * passes the reserved {@link SLA_ACTOR_ID} sentinel, and the ADR-0043 action\n * link passes the approver its single-use token is cryptographically bound to\n * (having also put them on the context). Those are the only two callers that\n * hold a trustworthy actor with no session behind them.\n *\n * A caller with NO identity at all cannot act. Belt-and-suspenders: the REST\n * anonymous-deny now denies every anonymous request (#3963), but this service\n * must not rely on a caller upstream — an anonymous actor could otherwise\n * decide approvals outright by naming one.\n */\n private async resolveActor(\n actorId: string | undefined,\n context: ExecutionContext,\n ): Promise<string> {\n // The machine callers — their actor is server-minted, not caller-supplied.\n if (context?.isSystem) {\n if (!actorId) throw new Error('VALIDATION_FAILED: actorId is required');\n return actorId;\n }\n const uid = actingUserId(context);\n if (!uid) {\n throw new Error('FORBIDDEN: an approval action requires an authenticated caller');\n }\n // The common case: no actor named, or the caller named themselves.\n if (!actorId || String(actorId) === uid) return uid;\n\n // Named something else — allow it ONLY if the server can prove the caller\n // holds that identity. `positions` is resolved by the shared authz resolver\n // (never client-supplied); `role:` is the ADR-0090 D3 deprecated spelling\n // that 15.x-era slots and the Console's own identity list still carry.\n const named = String(actorId);\n for (const position of context.positions ?? []) {\n if (named === `position:${position}` || named === `role:${position}`) return named;\n }\n // Email last — it costs a read, so only when nothing cheaper matched.\n if (named.includes('@') && await this.callerHasEmail(uid, named)) return named;\n\n throw new Error(\n `FORBIDDEN: cannot act as '${named}' — an approval action is recorded against the authenticated caller`,\n );\n }\n\n /** Does `userId`'s own account carry `email`? (Slots keyed by email, #3800.) */\n private async callerHasEmail(userId: string, email: string): Promise<boolean> {\n try {\n const rows = await this.engine.find('sys_user', {\n where: { id: userId }, limit: 1, context: SYSTEM_CTX,\n });\n const row: any = Array.isArray(rows) ? rows[0] : null;\n return !!row?.email && String(row.email).toLowerCase() === email.toLowerCase();\n } catch {\n return false;\n }\n }\n\n /**\n * Expand the approvers on an Approval node into user IDs by querying the\n * graph tables for `team:` / `department:` / `position:` /\n * `org_membership_level:` / `manager:` approver types. Falls back to a\n * prefixed literal (`type:value`) when graph lookups produce nothing — so\n * existing fixtures and flows that rely on substring matching keep working.\n *\n * **Graph semantics:**\n * - `team` → flat members of `sys_team` (better-auth; no BFS)\n * - `department` → recursive BFS of `sys_business_unit.parent_business_unit_id`\n * → members of every descendant via `sys_business_unit_member`\n * - `position` → holders via `sys_user_position` ∪ `sys_member.role`\n * transition source (ADR-0090 D3 / ADR-0057 D4)\n * - `org_membership_level`\n * → users with `sys_member.role = value` in tenant — the\n * better-auth MEMBERSHIP TIER (owner/admin/member), not a\n * position; author `position` for org positions\n * - `manager` → `sys_user.manager_id` of `record[value] ?? record.owner_id`\n * - `field` → literal user id stored in `record[value]`\n * - `user` → literal value\n *\n * `role` is accepted as the deprecated spelling of `org_membership_level`\n * (ADR-0090 D3) for one window: it resolves identically and logs a warning.\n *\n * **Out-of-office (#1322 M1):** individually-routed approvers — the ones that\n * resolve to a specific person (`user` / `field` / `manager`) — are passed\n * through {@link ApprovalService.applyOooDelegation}, which reroutes them onto\n * an active delegate when the resolved user has declared OOO. Group/graph\n * approvers (`team` / `department` / `position` / `org_membership_level`) are\n * left untouched: a group still has its other members, and position-routed\n * leave is already covered by ADR-0091 job delegation. Pass an `opts.now` /\n * `opts.substitutions` collector to record the hops for audit + notification.\n */\n private async expandApprovers(\n step: any,\n record?: any,\n organizationId?: string | null,\n opts?: {\n now?: number;\n substitutions?: OooSubstitution[];\n groups?: Record<string, string[]>;\n /** #3447 P2: `trigger`/`vars` roots for `expression` approvers. */\n exprCtx?: ApproverExpressionContext;\n /**\n * #3447 P2 audit collector: what each dynamic spec resolved FROM (the\n * live field value / the expression's intermediate values), snapshotted\n * as `__resolvedFrom` so \"why these people\" stays answerable later.\n */\n resolvedFrom?: Record<string, unknown>;\n },\n ): Promise<string[]> {\n if (!step || !Array.isArray(step.approvers)) return [];\n const now = opts?.now ?? this.clock.now().getTime();\n const out: string[] = [];\n const specs: any[] = step.approvers;\n for (let idx = 0; idx < specs.length; idx++) {\n const a = specs[idx];\n if (!a) continue;\n // Approvers without an explicit `group` each form their own group keyed\n // by position (#3266), so a plain per-approver list behaves predictably.\n const groupKey = a.group != null && String(a.group) !== '' ? String(a.group) : `#${idx}`;\n\n // #3447 P2: `expression` approvers resolve OUTSIDE resolveApproverSpec —\n // a graph-expanded expression (resolveAs: department/…) must key each\n // intermediate value as its own per_group group, which the flat string[]\n // contract of resolveApproverSpec cannot carry.\n if (canonicalApproverType(String(a.type)) === 'expression') {\n const resolved = await this.resolveExpressionApprovers(\n a, record, organizationId, now, opts?.substitutions, opts?.exprCtx,\n );\n if (opts?.resolvedFrom) opts.resolvedFrom[`expression#${idx}`] = resolved.raw;\n for (const entry of resolved.slots) {\n if (!entry.id) continue;\n out.push(entry.id);\n if (opts?.groups) {\n (opts.groups[entry.id] ??= []).push(entry.subGroup ? `${groupKey}:${entry.subGroup}` : groupKey);\n }\n }\n continue;\n }\n\n if (opts?.resolvedFrom && canonicalApproverType(String(a.type)) === 'field' && a.value != null) {\n opts.resolvedFrom[`field:${a.value}`] = (record as any)?.[a.value] ?? null;\n }\n const ids = await this.resolveApproverSpec(a, record, organizationId, now, opts?.substitutions);\n // per_group (#3266): tag each resolved id with this spec's group.\n for (const u of ids) {\n if (!u) continue;\n out.push(u);\n if (opts?.groups) (opts.groups[u] ??= []).push(groupKey);\n }\n }\n return out.filter(Boolean);\n }\n\n /**\n * Resolve ONE approver spec to concrete approver identities, applying OOO\n * substitution (#1322) to individually-routed types. Extracted from\n * {@link ApprovalService.expandApprovers} so the caller can tag each spec's\n * resolved ids with a group (#3266) without duplicating the resolution logic.\n * Returns the `type:value` literal as a single-element fallback when a graph\n * lookup yields nothing — same behaviour as before the extraction.\n */\n private async resolveApproverSpec(\n a: any,\n record: any,\n organizationId: string | null | undefined,\n now: number,\n substitutions?: OooSubstitution[],\n ): Promise<string[]> {\n // ADR-0090 D3: `role` is the deprecated spelling of `org_membership_level`.\n // Resolve on the canonical type, but keep the AUTHORED spelling in the\n // `type:value` fallback below — stored `sys_approval_approver` rows and\n // `pending_approvers` slots from 15.x carry the old literal.\n const type = canonicalApproverType(String(a.type));\n if (type !== a.type) {\n this.logger?.warn?.(\n `[approvals] approver type '${a.type}' is deprecated (ADR-0090 D3) — author '${type}' instead`,\n { deprecated: a.type, canonical: type },\n );\n }\n // [ADR-0105 D9] WHERE this approver is looked up — the request's own\n // organization unless the spec targets another one in the same group.\n //\n // Resolved HERE, above the `user` / `field` / `manager` early returns,\n // because refusing a declaration on a directory-less type is one of the\n // things this resolution DOES (those types name a person outright, so\n // `organization` on them cannot narrow anything and an author who wrote it\n // misunderstood the field). Resolving it after those returns made the\n // refusal unreachable and the declaration silently inert — exactly the\n // \"ignored, not refused\" behaviour ADR-0105 D9 rules out, and what the\n // cloud group-posture dogfood caught.\n //\n // Costs nothing on the overwhelmingly common path: with no `organization`\n // declared, the resolver returns the request org without reading anything.\n const directoryOrg = await this.directoryOrgFor(a, organizationId);\n const crossOrg = directoryOrg !== organizationId;\n\n if (type === 'user') {\n return this.applyOooDelegation(String(a.value), now, organizationId, substitutions);\n }\n if (type === 'field' && record) {\n // #3447: a record field can name MANY approvers — a multi-select user\n // field arrives as an array (or a legacy CSV string). Fan each out into\n // its own slot and OOO-substitute per person; collapsing to `String(...)`\n // (→ `'u1,u2'`) would mint one bogus approver id and skip every delegate.\n const out: string[] = [];\n for (const id of csvSplit((record as any)[a.value])) {\n out.push(...await this.applyOooDelegation(id, now, organizationId, substitutions));\n }\n return out;\n }\n // `directoryOrg` / `crossOrg` were resolved at the TOP of this method, so\n // the refusal reaches directory-less types too. Resolution failures\n // propagate: they are routing bugs, and that call sits OUTSIDE the\n // swallowing try below on purpose (see the catch's comment).\n //\n // A cross-org slate is filtered to the people who can actually READ the\n // request (D2 union); same-org routing is untouched and does no extra read.\n const bounded = async (users: string[]): Promise<string[]> => (\n crossOrg\n ? filterApproversWhoCanRead(this.orgScopeDeps, users, organizationId, {\n approverType: type, value: a.value != null ? String(a.value) : undefined,\n directoryOrgId: directoryOrg,\n })\n : users\n );\n\n try {\n if (type === 'team') {\n const users = await this.expandTeamUsers(String(a.value));\n if (users.length) return users;\n } else if (type === 'department' || type === 'business_unit' || type === 'bu') {\n const users = await bounded(await this.expandBusinessUnitUsers(String(a.value), directoryOrg));\n if (users.length) return users;\n } else if (type === 'position') {\n const users = await bounded(await this.expandPositionUsers(String(a.value), directoryOrg));\n if (users.length) return users;\n } else if (type === 'org_membership_level') {\n const users = await bounded(await this.expandMembershipTierUsers(String(a.value), directoryOrg));\n if (users.length) return users;\n } else if (type === 'manager' && record) {\n const subject = (record as any)[a.value] ?? (record as any).owner_id;\n if (subject) {\n const mgr = await this.lookupManager(String(subject));\n if (mgr) return this.applyOooDelegation(mgr, now, organizationId, substitutions);\n }\n }\n } catch { /* a directory lookup failed → fall through to the literal slot */ }\n // #3508: `queue` is declared-but-unenforced — there is no queue branch\n // above, so a queue approver always lands here and the `queue:<id>` slot\n // routes to nobody. The spec marks it non-authorable\n // (NON_AUTHORABLE_APPROVER_TYPES) so designers stop offering it; warn for\n // the stored flows that still carry one, so the silent dead slot is at\n // least visible to operators.\n if (type === 'queue') {\n this.logger?.warn?.(\n `[approvals] approver type 'queue' is not implemented — the slot resolves to nobody (#3508)`,\n { value: a.value },\n );\n } else if (GRAPH_APPROVER_TYPES.has(type)) {\n // #3807 follow-up — every OTHER way to land here is a graph type whose\n // lookup produced nobody, and the literal below is a slot no user can\n // ever act on. That silence is what let #3807 hide: a `department`\n // approver pointing at a seeded (env-wide) unit resolved to\n // `department:<id>` on every request, the request opened with an empty\n // slate, and nothing in the logs said so — the first symptom was a\n // permanently stuck approval (#3424). The fallback itself stays (a\n // literal keeps 15.x slots and substring fixtures working); it just\n // stops being invisible.\n this.logger?.warn?.(\n `[approvals] approver '${type}:${a.value}' expanded to nobody — the slot routes to no one `\n + `and the request cannot advance until someone is added or the approver is re-pointed (#3807)`,\n { type, value: a.value, organizationId: organizationId ?? null },\n );\n }\n return [`${a.type}:${a.value}`];\n }\n\n /**\n * Resolve an `expression` approver (#3447 P2): evaluate its CEL source at\n * node entry against the three explicit roots — `current` (live record),\n * `trigger` (submit snapshot), `vars` (flow variables) — then expand the\n * result into people per `resolveAs`.\n *\n * Every failure here THROWS (config/parse errors as `VALIDATION_FAILED`,\n * evaluation faults as `EXPRESSION_FAILED`) so the approval node fails\n * loudly instead of opening a request routed to nobody — an approver\n * expression that cannot run is a routing bug, never \"condition not met\".\n * Error messages carry the correct spelling because their primary reader is\n * the AI author fixing the flow on the next validate pass.\n *\n * Returns `slots` (approver id + optional per_group sub-key) and `raw` (the\n * expression's own values, pre-expansion) for the `__resolvedFrom` audit.\n */\n private async resolveExpressionApprovers(\n a: any,\n liveRecord: any,\n organizationId: string | null | undefined,\n now: number,\n substitutions?: OooSubstitution[],\n exprCtx?: ApproverExpressionContext,\n ): Promise<{ slots: Array<{ id: string; subGroup?: string }>; raw: string[] }> {\n const source = String(a.value ?? '').trim();\n if (!source) {\n throw new Error('VALIDATION_FAILED: expression approver has an empty expression');\n }\n\n // Closed-root pre-check. The runtime env resolves ANY unknown root as dyn →\n // null, so `record.x` / a bare field would silently yield an empty slate;\n // reject it here with the correct spelling instead.\n const parsed = collectCelRootIdentifiers(source);\n if (!parsed.ok) {\n throw new Error(`VALIDATION_FAILED: expression approver does not parse: ${parsed.error} — source: \\`${source}\\``);\n }\n const illegal = parsed.roots.filter(r => !APPROVER_EXPRESSION_ROOTS.has(r));\n if (illegal.length) {\n const hint = illegal.includes('record') || illegal.includes('previous')\n ? `\\`record\\`/\\`previous\\` are not bound here — write \\`current.<field>\\` for the record's live state `\n + `at node entry, or \\`trigger.<field>\\` for the submit-time snapshot (\\`vars.previous\\` carries the pre-update row)`\n : `did you mean \\`current.<field>\\` (live record), \\`trigger.<field>\\` (submit snapshot), or \\`vars.<name>\\` (flow variable)?`;\n throw new Error(\n `VALIDATION_FAILED: expression approver references \\`${illegal.join('`, `')}\\` — `\n + `only \\`current.*\\`, \\`trigger.*\\` and \\`vars.*\\` are available; ${hint}. Source: \\`${source}\\``,\n );\n }\n\n const result = ExpressionEngine.evaluate(\n { dialect: 'cel', source },\n { extra: { current: liveRecord ?? {}, trigger: exprCtx?.trigger ?? {}, vars: exprCtx?.vars ?? {} } },\n );\n if (!result.ok) {\n throw new Error(\n `EXPRESSION_FAILED: expression approver failed to evaluate (${result.error.kind}): `\n + `${result.error.message} — source: \\`${source}\\``,\n );\n }\n\n // Normalize to a string list: a user-id/CSV string, an array of ids, or\n // null/empty (an EMPTY slate — legal, handled by onEmptyApprovers). Any\n // other shape is a config bug, rejected loudly.\n const value = result.value as unknown;\n let raw: string[];\n if (value == null || value === '') {\n raw = [];\n } else if (typeof value === 'string') {\n raw = csvSplit(value);\n } else if (Array.isArray(value)) {\n const bad = value.find(v => v != null && typeof v !== 'string' && typeof v !== 'number');\n if (bad !== undefined) {\n throw new Error(\n `EXPRESSION_FAILED: expression approver must yield ids (string / CSV / string array), `\n + `got an array containing ${typeof bad} — source: \\`${source}\\``,\n );\n }\n raw = value.map(v => String(v ?? '').trim()).filter(Boolean);\n } else {\n throw new Error(\n `EXPRESSION_FAILED: expression approver must yield ids (string / CSV / string array), `\n + `got ${typeof value} — source: \\`${source}\\``,\n );\n }\n\n // `resolveAs` expansion. `user` (default): each value IS a person —\n // individually routed, so OOO delegation applies (#1322). Graph kinds\n // re-expand each value through the same lookups the static types use; a\n // group still has its other members, so like the static graph types they\n // are NOT OOO-substituted, and with per_group each intermediate value\n // forms its own sub-group (one sign-off per returned department). A value\n // whose expansion is empty keeps a `<kind>:<value>` literal slot — same\n // unstaffed-target behaviour (and #3424 admin rescue) as the static types.\n const resolveAs = String(a.resolveAs ?? 'user');\n if (resolveAs === 'user') {\n const slots: Array<{ id: string }> = [];\n for (const id of raw) {\n for (const routed of await this.applyOooDelegation(id, now, organizationId, substitutions)) {\n slots.push({ id: routed });\n }\n }\n return { slots, raw };\n }\n // [ADR-0105 D9] An expression that re-expands into a graph kind consults the\n // same org-scoped directories the static types do, so it honours the same\n // targeting. Resolved once for the whole slate, before the per-value loop —\n // the declaration is a property of the spec, not of what the CEL returned.\n const directoryOrg = await this.directoryOrgFor(a, organizationId);\n const crossOrg = directoryOrg !== organizationId;\n const slots: Array<{ id: string; subGroup: string }> = [];\n for (const key of raw) {\n let users: string[] = [];\n try {\n if (resolveAs === 'department') users = await this.expandBusinessUnitUsers(key, directoryOrg);\n else if (resolveAs === 'position') users = await this.expandPositionUsers(key, directoryOrg);\n else if (resolveAs === 'team') users = await this.expandTeamUsers(key);\n else {\n throw new Error(\n `VALIDATION_FAILED: expression approver has unknown resolveAs '${resolveAs}' — `\n + `use 'user', 'department', 'position', or 'team'`,\n );\n }\n } catch (err: any) {\n if (String(err?.message ?? '').startsWith('VALIDATION_FAILED')) throw err;\n users = [];\n }\n if (crossOrg && users.length) {\n users = await filterApproversWhoCanRead(this.orgScopeDeps, users, organizationId, {\n approverType: resolveAs, value: key, directoryOrgId: directoryOrg,\n });\n }\n if (!users.length) {\n slots.push({ id: `${resolveAs}:${key}`, subGroup: key });\n continue;\n }\n for (const u of users) slots.push({ id: u, subGroup: key });\n }\n return { slots, raw };\n }\n\n /** Flat team — `sys_team` is better-auth's collaboration grouping (no hierarchy). */\n private async expandTeamUsers(teamId: string): Promise<string[]> {\n if (!teamId) return [];\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_team_member', {\n where: { team_id: teamId },\n fields: ['user_id'],\n limit: 10000,\n context: SYSTEM_CTX,\n } as any);\n } catch { rows = []; }\n return Array.from(new Set((rows ?? []).map((r: any) => String(r.user_id ?? '')).filter(Boolean)));\n }\n\n /**\n * Tenant scope for a `sys_business_unit` read that may legitimately be\n * env-wide (#3807).\n *\n * `organization_id = null` on a platform object means \"owned by no\n * organization\" — a row written by a seed, the file layer, or bootstrap,\n * i.e. before (or outside) any org exists. A strict\n * `organization_id = <request org>` equality made every such row invisible:\n * the seed check below found nothing, the whole expansion returned `[]`, and\n * the approver fell back to the dead `department:<id>` literal that routes to\n * nobody. That is not an edge case — an app's org tree is normally seeded\n * (a seed cannot know the org id the runtime mints at boot) while the\n * approval request always carries one, so EVERY department approver a\n * designer could pick resolved to nobody.\n *\n * Widen to \"this org ∪ env-wide\", the same predicate `sys_metadata`'s\n * pending-draft listing settled on for the identical reason. Another org's\n * unit still fails the match, so the wall between two organizations is\n * unchanged — only rows belonging to no org at all become visible.\n */\n private businessUnitOrgScope(\n filter: Record<string, unknown>,\n organizationId?: string | null,\n ): Record<string, unknown> {\n if (!organizationId) return filter;\n return { ...filter, $or: [{ organization_id: organizationId }, { organization_id: null }] };\n }\n\n /** Recursive department — walks `sys_business_unit.parent_business_unit_id`. */\n private async expandBusinessUnitUsers(businessUnitId: string, organizationId?: string | null): Promise<string[]> {\n if (!businessUnitId) return [];\n // Seed sanity check: skip if dept doesn't exist or is inactive within tenant.\n try {\n const seed = await this.engine.find('sys_business_unit', {\n where: this.businessUnitOrgScope({ id: businessUnitId }, organizationId),\n fields: ['id', 'active'],\n limit: 1,\n context: SYSTEM_CTX,\n } as any);\n const seedRow: any = Array.isArray(seed) ? seed[0] : null;\n if (!seedRow || seedRow.active === false) return [];\n } catch { return []; }\n\n const seen = new Set<string>([businessUnitId]);\n const queue: string[] = [businessUnitId];\n while (queue.length) {\n const parent = queue.shift()!;\n let kids: any[] = [];\n try {\n const filter = this.businessUnitOrgScope(\n { parent_business_unit_id: parent, active: { $ne: false } },\n organizationId,\n );\n kids = await this.engine.find('sys_business_unit', { filter, fields: ['id'], limit: 1000, context: SYSTEM_CTX } as any);\n } catch { kids = []; }\n for (const k of kids ?? []) {\n const kid = String((k as any).id ?? '');\n if (kid && !seen.has(kid)) { seen.add(kid); queue.push(kid); }\n }\n }\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_business_unit_member', {\n where: { business_unit_id: { $in: Array.from(seen) } },\n fields: ['user_id'],\n limit: 10000,\n context: SYSTEM_CTX,\n } as any);\n } catch { rows = []; }\n return Array.from(new Set((rows ?? []).map((r: any) => String(r.user_id ?? '')).filter(Boolean)));\n }\n\n /**\n * Position holders (ADR-0090 D3): `sys_user_position` is the platform-owned\n * assignment table, keyed by the position's machine name (ADR-0057 D4),\n * unioned with the better-auth membership string (`sys_member.role`) as a\n * transition source.\n *\n * ⚠️ This is a ROUTING read (approver slates and escalation targets), and it\n * is deliberately NOT the same read as `PositionGraphService` in\n * `plugin-sharing`, whatever the shared method name suggests. Both answer\n * \"who holds position P\"; this one reads the directory RAW — neither the\n * ADR-0091 D2 validity window nor the `sys_position.active` catalogue flag is\n * applied. Maintainer ruling, 2026-08-15 (#8710, inheriting #8613), verbatim:\n *\n * > Access-conferring paths filter deactivated positions; addressing paths\n * > do not.\n *\n * Routing is an addressing path, so dropping a holder here is fail-OPEN, not\n * fail-closed: an expansion that comes back empty does not narrow the slate,\n * it falls through to the literal `position:` slot no user can ever act on —\n * the permanently stuck request of #3807 / #3424. A step routing to nobody is\n * worse than one routing to a lapsed holder, so the lapsed holder stays.\n *\n * Where the two implementations actually stand, per source. Both limbs are\n * listed because a statement about one of them is not a statement about this\n * method:\n *\n * 1. `sys_user_position` — sharing projects `valid_from` / `valid_until` and\n * drops rows on `isGrantActive` inside its own helper; we project\n * `user_id` alone, so an assignment that expired last month still routes.\n * This is the one real divergence, and it is the intended one.\n * 2. `sys_member.role` — raw on BOTH sides (`TeamGraphService.expandRoleUsers`\n * projects `user_id` too). The table carries no window columns at all and\n * `isGrantActive` reads an absent bound as unbounded, so there is nothing\n * a filter could do here; membership tier names have no `sys_position`\n * row either (#8710's \"a name with no row is untouched\" fallback), so no\n * catalogue flag either. This limb cannot be brought into parity by\n * adding a filter — see {@link expandMembershipTierUsers}.\n * 3. `sys_position.active` — the sharing engine's gate for it lives at the\n * RULE EVALUATOR's call site (`positionConfersAccess` in\n * `sharing-rule-service.ts`), not inside `PositionGraphService`; the same\n * ruling gives it no counterpart on this path.\n *\n * The omission is per-READ, not a missing dependency: `isGrantActive` is\n * imported in this file and IS applied to `sys_approval_delegation` in\n * {@link lookupActiveDelegation}. ⛔ So do not \"fix\" this by adding the window\n * filter here — that is the option #8710 rejected, on the reasoning above.\n */\n private async expandPositionUsers(positionName: string, organizationId?: string | null): Promise<string[]> {\n if (!positionName) return [];\n const users = new Set<string>();\n const filter: any = { position: positionName };\n if (organizationId) filter.organization_id = organizationId;\n try {\n const rows = await this.engine.find('sys_user_position', {\n filter, fields: ['user_id'], limit: 10000, context: SYSTEM_CTX,\n } as any);\n for (const r of (rows ?? []) as any[]) {\n const uid = String(r.user_id ?? '');\n if (uid) users.add(uid);\n }\n } catch { /* table may not exist on minimal stacks — union source below still applies */ }\n // ADR-0057 D4 transition source: pre-migration stacks still carry the\n // position name in better-auth's `sys_member.role` column, so the same\n // lookup serves a position name here and a membership tier for\n // `org_membership_level` — the column is one, the two concepts are not.\n for (const uid of await this.expandMembershipTierUsers(positionName, organizationId)) users.add(uid);\n return Array.from(users);\n }\n\n /**\n * better-auth org-membership tier (`sys_member.role`: owner/admin/member) —\n * NOT positions. Named for the projection (`org_membership_level`, ADR-0057\n * D7 / ADR-0090 D3), not for better-auth's column: the column name is theirs\n * and stays, the platform-facing word does not.\n *\n * Read RAW, like every routing read here, and with nothing available to\n * filter even if it were not: `sys_member` carries no ADR-0091 D2 window\n * columns, and a tier name has no `sys_position` row to read `active` off.\n * {@link expandPositionUsers} carries the ruling both reads inherit\n * (#8613 / #8710) — this method is also the second limb of that union, so a\n * change here changes position routing too.\n */\n private async expandMembershipTierUsers(tier: string, organizationId?: string | null): Promise<string[]> {\n if (!tier) return [];\n const filter: any = { role: tier };\n if (organizationId) filter.organization_id = organizationId;\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_member', { filter, fields: ['user_id'], limit: 10000, context: SYSTEM_CTX } as any);\n } catch { rows = []; }\n return Array.from(new Set((rows ?? []).map((r: any) => String(r.user_id ?? '')).filter(Boolean)));\n }\n\n private async lookupManager(userId: string): Promise<string | null> {\n try {\n const rows = await this.engine.find('sys_user', {\n where: { id: userId }, fields: ['id', 'manager_id'], limit: 1, context: SYSTEM_CTX,\n } as any);\n const row: any = Array.isArray(rows) ? rows[0] : null;\n return row?.manager_id ? String(row.manager_id) : null;\n } catch { return null; }\n }\n\n /**\n * Out-of-office auto-skip (#1322 M1). Given an individually-routed approver\n * id, follow any active `sys_approval_delegation` chain and return the id the\n * slot should actually go to — the delegate acts under their own identity, so\n * no impersonation is involved. Returns `[userId]` unchanged when there is no\n * active delegation. Each hop is appended to `collector` (when supplied) so\n * the caller can audit + notify (M4).\n *\n * The chain (A out → B, B out → C, …) is bounded by {@link OOO_MAX_CHAIN} and\n * stops on a self-reference or a cycle, so a mis-declared loop degrades to the\n * last reachable delegate rather than hanging.\n */\n private async applyOooDelegation(\n userId: string,\n now: number,\n organizationId?: string | null,\n collector?: OooSubstitution[],\n ): Promise<string[]> {\n const start = String(userId ?? '').trim();\n if (!start) return [];\n let current = start;\n const visited = new Set<string>([current]);\n for (let hop = 0; hop < OOO_MAX_CHAIN; hop++) {\n const del = await this.lookupActiveDelegation(current, now, organizationId);\n if (!del) break;\n const to = String(del.delegate_id ?? '').trim();\n if (!to || to === current || visited.has(to)) break; // no-op / self / cycle\n collector?.push({ from: current, to, reason: del.reason != null ? String(del.reason) : null });\n visited.add(to);\n current = to;\n }\n return [current];\n }\n\n /**\n * The active OOO delegation for a delegator at `now`, or null. Validity is the\n * shared `isGrantActive` half-open window (ADR-0091 D2), enforced here at\n * resolution time — never by a background job. When several rows are active,\n * the one expiring soonest wins (the most specific coverage window).\n */\n private async lookupActiveDelegation(\n delegatorId: string,\n now: number,\n organizationId?: string | null,\n ): Promise<any | null> {\n if (!delegatorId) return null;\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_approval_delegation', {\n where: { delegator_id: delegatorId },\n fields: ['id', 'delegator_id', 'delegate_id', 'valid_from', 'valid_until', 'reason', 'organization_id'],\n limit: 50,\n context: SYSTEM_CTX,\n } as any);\n } catch { return null; } // table absent on minimal stacks — no OOO, resolve as-is\n const active = (rows ?? []).filter((r: any) =>\n isGrantActive(r, now)\n // A null-org rule applies across tenants; a scoped rule only within its tenant.\n && (organizationId == null || r.organization_id == null || String(r.organization_id) === String(organizationId)));\n if (!active.length) return null;\n active.sort((a: any, b: any) => {\n const au = a.valid_until ? Date.parse(String(a.valid_until)) : Number.POSITIVE_INFINITY;\n const bu = b.valid_until ? Date.parse(String(b.valid_until)) : Number.POSITIVE_INFINITY;\n return au - bu;\n });\n return active[0];\n }\n\n /**\n * Mirror a request status onto a business-object field, if configured.\n *\n * **Elevated, but not anonymous (#3783).** The write stays `isSystem`: the\n * record is normally LOCKED while its approval is live and the submitter\n * cannot edit it, so only a platform write can land the status — that is what\n * the lock hook's system exemption (`lifecycle-hooks.ts`) is for. What it must\n * NOT do is throw away *who* caused the transition. Every status below is\n * something a specific human just did — a submitter submitting or recalling,\n * an approver deciding or sending back — and this write is what fires the\n * target object's record-change flows. With no `userId` on it those cascades\n * inherit no trigger user, and since #3760 a `runAs:'user'` run with no trigger\n * user has its data ops REFUSED — so \"when the invoice is approved, do X\", the\n * most natural approvals automation there is, had to declare `runAs:'system'`\n * and take blanket elevation for a case where a perfectly good scoped identity\n * existed. Re-attaching the actor lets those cascades run as the deciding user\n * with RLS enforced. Same shape the approval node already uses when it calls\n * into this service (`approval-node.ts`).\n *\n * `actorId` is `null` for the genuinely machine-driven transitions (the SLA\n * escalation's auto-decision, the dead-run sweep). There is no human to name\n * there, and naming a sentinel would put a non-user in `updated_by` and in\n * every downstream flow's identity. Those cascades stay user-less — a flow\n * that wants to react to them still has to declare `runAs:'system'`, which is\n * the honest answer rather than an oversight.\n *\n * Deliberately carries `userId` ONLY, not the request's org. On an\n * ExecutionContext `tenantId` is a driver-scoping knob, not attribution\n * (`buildDriverOptions` turns it into a tenant predicate on the update), so\n * passing it would newly org-scope this write and silently no-op the mirror on\n * a record whose org differs from the request's — while buying nothing: the\n * automation engine back-fills the run's `tenantId` from the resolved user's\n * own grants.\n */\n private async mirrorStatusField(\n object: string,\n recordId: string,\n field: string,\n status: string,\n actorId: string | null,\n ): Promise<void> {\n try {\n const context = actorId ? { ...SYSTEM_CTX, userId: actorId } : SYSTEM_CTX;\n await this.engine.update(object, { id: recordId, [field]: status }, { context });\n } catch (err: any) {\n this.logger?.warn?.(`[approvals] mirrorStatusField failed: ${err?.message ?? err}`);\n }\n }\n\n /**\n * Re-read a business record's CURRENT state by id so approver resolution binds\n * to live data at node entry, not the trigger snapshot the flow froze into\n * `$record` at submit time (#3447). A `field` / `manager` approver names *who*\n * decides, and an earlier node — or the approver of an earlier step — may have\n * written that routing field after submit (e.g. a lead reviewer picking which\n * departments co-review). Graph approvers (team / position / …) already query\n * live; this brings the in-record types into line.\n *\n * Read under system identity: approver routing is a platform concern and the\n * record is the flow's own subject, so the submitter's RLS/FLS must not narrow\n * it. Degrades to `fallback` (the snapshot) when the record can't be re-read —\n * hard-deleted between submit and node entry, or an object whose backend can't\n * serve a point read — warning rather than throwing so a transient miss can't\n * wedge an approval. That \"warn but proceed\" stance matches the\n * no-concrete-approver guard (#3424) and the \"record is gone\" enrichment path\n * that already falls back to the payload snapshot.\n */\n private async loadLiveRecord(object: string, recordId: string, fallback?: any): Promise<any> {\n try {\n const rows = await this.engine.find(object, {\n where: { id: recordId }, limit: 1, context: SYSTEM_CTX,\n } as any);\n const live = Array.isArray(rows) ? rows[0] : rows;\n if (live) return live;\n this.logger?.warn?.(\n `[approvals] live record ${object}/${recordId} not found at node entry — `\n + 'resolving approvers against the trigger snapshot (#3447 fallback).',\n { object, recordId },\n );\n } catch (err: any) {\n this.logger?.warn?.(\n `[approvals] live record re-read failed for ${object}/${recordId}: ${err?.message ?? err} — `\n + 'resolving approvers against the trigger snapshot (#3447 fallback).',\n );\n }\n return fallback ?? {};\n }\n\n // ── ADR-0019: Approval-as-flow-node ──────────────────────────\n //\n // A flow's Approval node opens a request via `openNodeRequest` (carrying its\n // own approvers/behavior config and the suspended run id), then suspends. A\n // later `decide` finalizes it and resumes the flow run down the matching\n // `approve`/`reject` edge. The record lock is enforced by a beforeUpdate hook\n // keyed on a *pending* request, so finalizing auto-releases it.\n\n /**\n * Open a pending approval request on behalf of a flow's Approval node. The\n * node config (approvers / behavior / status field) is snapshotted on the row\n * so a decision can be made without any process to resolve against.\n *\n * #3447 P2: may instead return an {@link ApprovalNodeAutoOutcome} — no\n * request opened — when the slate resolves empty and the node's\n * `onEmptyApprovers` policy is `auto_approve`.\n */\n async openNodeRequest(\n input: {\n object: string;\n recordId: string;\n runId: string;\n nodeId: string;\n config: ApprovalNodeConfig;\n flowName?: string;\n /** Authored flow label, snapshotted for inbox display. */\n flowLabel?: string;\n /** Authored node label, snapshotted for inbox display. */\n nodeLabel?: string;\n submitterId?: string | null;\n record?: any;\n organizationId?: string | null;\n /**\n * #3447 P2: flow variables at node entry (nested by dotted key, as the\n * engine's CEL conditions see them) — the `vars.*` root for `expression`\n * approvers. `input.record` doubles as their `trigger.*` root.\n */\n variables?: Record<string, unknown> | null;\n },\n context: ExecutionContext,\n ): Promise<ApprovalRequestRow | ApprovalNodeAutoOutcome> {\n if (!input.object) throw new Error('VALIDATION_FAILED: object is required');\n if (!input.recordId) throw new Error('VALIDATION_FAILED: recordId is required');\n if (!input.runId) throw new Error('VALIDATION_FAILED: runId is required');\n\n // One pending request per (object, record).\n const existing = await this.engine.find('sys_approval_request', {\n where: { object_name: input.object, record_id: input.recordId, status: 'pending' },\n limit: 1, context: SYSTEM_CTX,\n });\n if (Array.isArray(existing) && existing[0]) {\n throw new Error(`DUPLICATE_REQUEST: a pending approval already exists for ${input.object}/${input.recordId}`);\n }\n\n // `organizationId` is not on the envelope — see isOverrideActor().\n const ctxOrg = (context as any)?.organizationId ?? context?.tenantId ?? input.organizationId ?? null;\n const nowDate = this.clock.now();\n // OOO auto-skip (#1322 M1): reroute individually-routed approvers who are\n // out of office. Collected hops drive the audit + notification below (M4).\n const substitutions: OooSubstitution[] = [];\n // Group membership per resolved approver (#3266) — snapshotted so quorum /\n // per_group finalization is decided against the slate resolved at OPEN time\n // (OOO-substituted), not re-resolved live at each decision.\n const groups: Record<string, string[]> = {};\n // #3447: resolve approvers against the record's LIVE state at node entry, not\n // the trigger snapshot carried in `input.record`. This is the whole fix — an\n // earlier step may have written the field this node routes on.\n const liveRecord = await this.loadLiveRecord(input.object, input.recordId, input.record);\n const resolvedFrom: Record<string, unknown> = {};\n const approvers = await this.expandApprovers(\n { approvers: input.config.approvers }, liveRecord, ctxOrg, {\n now: nowDate.getTime(), substitutions, groups,\n exprCtx: { trigger: input.record ?? null, vars: input.variables ?? null },\n resolvedFrom,\n },\n );\n\n // Empty-slate policy (#3447 P2). \"Empty\" = no CONCRETE person — an\n // unstaffed position / empty expression result leaves only `type:value`\n // literal slots, decidable by nobody.\n if (!approvers.some(a => a && !a.includes(':'))) {\n const emptyPolicy = (input.config as any).onEmptyApprovers ?? 'admin_rescue';\n if (emptyPolicy === 'fail') {\n throw new Error(\n `NO_APPROVERS: approval node '${input.nodeId}' on ${input.object}/${input.recordId} resolved to no `\n + `concrete approver and its onEmptyApprovers policy is 'fail'. Check that the approver target(s) `\n + `are staffed / the routing field or expression yields user ids at node entry.`,\n );\n }\n if (emptyPolicy === 'auto_approve') {\n this.logger?.warn?.(\n `[approvals] approval node '${input.nodeId}' on ${input.object}/${input.recordId} resolved to no `\n + `concrete approver — auto-approving per onEmptyApprovers: 'auto_approve' (no request opened).`,\n { object: input.object, recordId: input.recordId, node: input.nodeId, resolved: approvers },\n );\n return { autoApproved: true, reason: 'empty_approvers' };\n }\n // #3424 admin_rescue (default): the request is still opened (a privileged\n // admin can override it, and legacy 15.x literal slots stay queryable) —\n // the only option that neither waves the record through nor kills the\n // run — but warn loudly so the misconfiguration surfaces instead of\n // silently locking the record with no obvious cause.\n this.logger?.warn?.(\n `[approvals] approval node '${input.nodeId}' on ${input.object}/${input.recordId} resolved to no concrete approver`\n + ' — the request is decidable only by a privileged admin. Check that the approver target(s) are staffed.',\n { object: input.object, recordId: input.recordId, node: input.nodeId, resolved: approvers },\n );\n }\n\n const now = nowDate.toISOString();\n const id = uid('areq');\n const processName = `flow:${input.flowName ?? input.nodeId}`;\n // Display labels ride the config snapshot (no schema migration needed);\n // `rowFromRequest` surfaces them as `process_label` / `step_label`.\n const configSnapshot: any = { ...input.config };\n if (input.flowLabel) configSnapshot.__flowLabel = input.flowLabel;\n if (input.nodeLabel) configSnapshot.__nodeLabel = input.nodeLabel;\n // Snapshot the resolved approver→group map for EVERY multi-approver\n // behavior (was quorum/per_group only). #3447 P2 makes this load-bearing\n // for unanimous too: an `expression` approver can only resolve at OPEN\n // time (decide has no flow variables to evaluate against), so the tally\n // must read the open-time slate — which also pins unanimous+field to the\n // slate the approvers actually saw, instead of re-reading a field that may\n // have changed again since.\n if (input.config.behavior && input.config.behavior !== 'first_response') {\n configSnapshot.__approverGroups = groups;\n }\n // #3447 P2: snapshot what the dynamic approver sources resolved FROM (the\n // live routing-field value / the expression's intermediate values) so the\n // audit trail answers \"why these people\" — the resolution INPUT, pairing\n // the resolution RESULT already persisted as `pending_approvers`.\n if (Object.keys(resolvedFrom).length) {\n configSnapshot.__resolvedFrom = resolvedFrom;\n }\n // ADR-0044 round numbering: rounds of a revise loop share the run — count\n // this (run, node)'s prior requests; the new one is round N+1. Stamped on\n // the snapshot (precedent: __flowLabel), so no schema migration.\n try {\n const prior = await this.engine.find('sys_approval_request', {\n where: { flow_run_id: input.runId, flow_node_id: input.nodeId }, limit: 500, context: SYSTEM_CTX,\n });\n const n = Array.isArray(prior) ? prior.length : 0;\n if (n > 0) configSnapshot.__round = n + 1;\n } catch { /* round display is best-effort */ }\n const row: any = {\n id,\n process_name: processName,\n object_name: input.object,\n record_id: input.recordId,\n submitter_id: input.submitterId ?? context.userId ?? null,\n status: 'pending',\n current_step: input.nodeId,\n current_step_index: 0,\n pending_approvers: approvers.join(','),\n payload_json: input.record != null ? JSON.stringify(input.record) : null,\n flow_run_id: input.runId,\n flow_node_id: input.nodeId,\n node_config_json: JSON.stringify(configSnapshot),\n organization_id: ctxOrg,\n created_at: now,\n updated_at: now,\n };\n await this.engine.insert('sys_approval_request', row, { context: SYSTEM_CTX });\n await this.syncApproverIndex(id, approvers, ctxOrg, now);\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: id, organization_id: ctxOrg,\n step_name: input.nodeId, step_index: 0, action: 'submit',\n actor_id: input.submitterId ?? context.userId ?? null, comment: null, created_at: now,\n }, { context: SYSTEM_CTX });\n\n // OOO substitution audit + notification (#1322 M4). Each hop that rerouted\n // an approver away from an out-of-office user is recorded on the request's\n // audit trail (a system action, no human actor) and notified to both the\n // delegate — who now owns the slot — and the skipped approver.\n for (const sub of substitutions) {\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: id, organization_id: ctxOrg,\n step_name: input.nodeId, step_index: 0, action: 'ooo_substitute',\n actor_id: null,\n comment: `${sub.from} → ${sub.to}${sub.reason ? ` — ${sub.reason}` : ''}`,\n created_at: now,\n }, { context: SYSTEM_CTX });\n await this.notify({\n topic: 'approval.ooo_substituted',\n audience: [sub.to],\n source: { object: 'sys_approval_request', id },\n dedupKey: `approval-ooo-${id}-${sub.to}`,\n payload: {\n title: 'Approval routed to you (out-of-office cover)',\n message: `You are covering an approval on ${input.object}/${input.recordId} while ${sub.from} is out of office.`,\n actionUrl: '/system/approvals',\n },\n });\n await this.notify({\n topic: 'approval.ooo_skipped',\n audience: [sub.from],\n source: { object: 'sys_approval_request', id },\n dedupKey: `approval-ooo-skip-${id}-${sub.from}`,\n payload: {\n title: 'Approval routed to your delegate',\n message: `An approval on ${input.object}/${input.recordId} was routed to ${sub.to} while you are out of office.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n\n // Record lock (when `lockRecord !== false`) is enforced by the beforeUpdate\n // hook keyed on the now-pending request; no extra write needed here.\n if (input.config.approvalStatusField) {\n // Attributed to whoever the row itself calls the submitter (#3783), so\n // there is exactly one answer to \"who submitted this\". Not the\n // {@link actingUserId} route: `submitterId` is server-supplied here (the\n // approval node passes the run's own trigger user) and unreachable from a\n // request body, so it carries none of the caller-controlled risk that rule\n // exists for — and it already resolves to `context.userId` in every\n // first-party path.\n await this.mirrorStatusField(\n input.object, input.recordId, input.config.approvalStatusField, 'pending',\n row.submitter_id ?? null,\n );\n }\n\n return rowFromRequest(row);\n }\n\n /**\n * True when the approve tally satisfies the node's `behavior` (#3266):\n * - `unanimous` — every resolved approver approved.\n * - `quorum` — at least `minApprovals` distinct approvals (default = all).\n * - `per_group` — every group reached `minApprovals` approvals (default 1).\n * Thresholds are clamped to the resolvable count / group size, so a mis-set\n * value can never deadlock a request.\n */\n private isApprovalSatisfied(\n behavior: string,\n config: ApprovalNodeConfig,\n original: string[],\n groupMap: Record<string, string[]>,\n approved: Set<string>,\n ): boolean {\n if (behavior === 'unanimous') {\n return original.length > 0 && original.every(a => approved.has(a));\n }\n if (behavior === 'quorum') {\n const n = original.length || 1;\n const need = Math.min(Math.max(1, config.minApprovals ?? n), n);\n // Count distinct approvals (robust to OOO/reassign changing who holds a slot).\n return approved.size >= need;\n }\n if (behavior === 'per_group') {\n const perGroupNeed = Math.max(1, config.minApprovals ?? 1);\n const size: Record<string, number> = {};\n for (const gs of Object.values(groupMap)) for (const g of gs) size[g] = (size[g] ?? 0) + 1;\n const groups = Object.keys(size);\n if (!groups.length) return true; // nothing to gate\n const got: Record<string, number> = {};\n for (const a of approved) for (const g of (groupMap[a] ?? [])) got[g] = (got[g] ?? 0) + 1;\n return groups.every(g => (got[g] ?? 0) >= Math.min(perGroupNeed, size[g]));\n }\n return true; // first_response and unknown → first approval finalizes\n }\n\n /**\n * Record a decision on a node-driven request. Honours the node's `behavior`\n * (#3266): `first_response` finalizes on the first approval; `unanimous`,\n * `quorum`, and `per_group` hold the request open until their tally is met\n * (see {@link ApprovalService.isApprovalSatisfied}). A rejection always\n * finalizes the node (one veto). When the request finalizes, returns the\n * suspended run id + node id so the caller (or {@link ApprovalService.decide})\n * can resume the flow down the matching branch.\n */\n async decideNode(\n requestId: string,\n input: { decision: 'approve' | 'reject'; actorId: string; comment?: string; attachments?: string[]; outputs?: Record<string, unknown> },\n context: ExecutionContext,\n ): Promise<{ request: ApprovalRequestRow; runId: string | null; nodeId: string | null; finalized: boolean; decision: 'approve' | 'reject'; outputs?: Record<string, unknown> }> {\n if (!requestId) throw new Error('VALIDATION_FAILED: requestId is required');\n const actorId = await this.resolveActor(input?.actorId, context);\n if (input.decision !== 'approve' && input.decision !== 'reject') {\n throw new Error('VALIDATION_FAILED: decision must be approve|reject');\n }\n\n // Read the raw row to reach flow_* correlation + the node config snapshot.\n const rawRows = await this.engine.find('sys_approval_request', {\n where: { id: requestId }, limit: 1, context: SYSTEM_CTX,\n });\n const raw: any = Array.isArray(rawRows) ? rawRows[0] : null;\n if (!raw) throw new Error(`REQUEST_NOT_FOUND: ${requestId}`);\n if (raw.status !== 'pending') throw new Error(`INVALID_STATE: request is ${raw.status}`);\n\n const pendingApprovers = csvSplit(raw.pending_approvers);\n // A privileged admin may override a stuck request (#3424) even when they\n // hold no slot — the escape hatch for an approval routed to an unstaffed\n // position or to approvers who have all left.\n const isOverride = this.isOverrideActor(context, raw.organization_id ?? null);\n const isSlotHolder = pendingApprovers.includes(actorId);\n if (!isSlotHolder && !isOverride) {\n throw new Error(`FORBIDDEN: actor '${actorId}' is not a pending approver`);\n }\n // #4466 — the audit fact this decision would otherwise drop: the actor was\n // admitted ONLY by the override branch, holding no slot in the staffed\n // slate. An admin who IS a slot holder is approving normally, so the two\n // conditions are recorded apart rather than collapsed into \"actor is admin\".\n const viaOverride = isOverride && !isSlotHolder;\n\n const config = parseJson<ApprovalNodeConfig>(raw.node_config_json, { approvers: [], behavior: 'first_response' } as any);\n const org = raw.organization_id ?? null;\n const nodeId: string | null = raw.flow_node_id ?? raw.current_step ?? null;\n const runId: string | null = raw.flow_run_id ?? null;\n const now = this.clock.now().toISOString();\n\n // #3447 P2: decision outputs — validated BEFORE any write (audit included)\n // so an out-of-contract payload rejects atomically. The trust model is a\n // `screen` node's: the AUTHOR declares the keys (`config.decisionOutputs`),\n // the approver only fills values. A decision carrying undeclared keys is a\n // caller bug; `decision`/`requestId` are reserved by the resume envelope.\n const outputKeys = input.outputs ? Object.keys(input.outputs) : [];\n let acceptedOutputs: Record<string, unknown> | undefined;\n // Typed declarations and bare keys whitelist identically — one normalizer\n // (spec) is the single reader of the union shape.\n const declaredDefs = normalizeDecisionOutputs((config as any).decisionOutputs);\n if (outputKeys.length) {\n const declared = declaredDefs.map(d => d.key);\n if (!declared.length) {\n throw new Error(\n `VALIDATION_FAILED: this approval node declares no decisionOutputs — outputs are not accepted. `\n + `Declare the keys on the node config (decisionOutputs: [${outputKeys.map(k => `'${k}'`).join(', ')}]) `\n + `to let approvers hand them to the flow.`,\n );\n }\n const reserved = outputKeys.filter(k => k === 'decision' || k === 'requestId');\n if (reserved.length) {\n throw new Error(\n `VALIDATION_FAILED: decision output key(s) \\`${reserved.join('`, `')}\\` are reserved by the resume `\n + `envelope — pick different names.`,\n );\n }\n const undeclared = outputKeys.filter(k => !declared.includes(k));\n if (undeclared.length) {\n throw new Error(\n `VALIDATION_FAILED: decision output key(s) \\`${undeclared.join('`, `')}\\` are not declared on this `\n + `node — declared keys: ${declared.map(k => `'${k}'`).join(', ') || '(none)'}.`,\n );\n }\n acceptedOutputs = { ...input.outputs };\n }\n\n // objectui#2955: `required` outputs. Unlike `type`/`multiple` — which only\n // shape the input widget — this one is a runtime contract: the flow must\n // never resume past this node with a required key missing, because that is\n // precisely what a downstream `expression` approver reads. Before this,\n // an author's only backstop was `onEmptyApprovers` (the next node opens,\n // resolves nobody, and stalls for an admin rescue).\n //\n // APPROVE only. A reject leaves down the reject edge, where the outputs\n // are not read — demanding routing data to say \"no\" would block the\n // rejection. Outputs still ride a reject when the approver filled them.\n //\n // Enforced for EVERY approve path, with no elevation bypass: a one-click\n // email action link cannot fill a form, and an `auto_approve` SLA\n // escalation has nobody to ask — both must fail rather than resume the run\n // with the key missing. The escalation sweep already isolates a throwing\n // request (it catches per-request), so that decision simply stays pending\n // and visibly overdue instead of advancing into a broken node.\n if (input.decision === 'approve') {\n const missing = declaredDefs\n .filter(d => d.required === true && isBlankDecisionOutput(input.outputs?.[d.key]))\n .map(d => d.key);\n if (missing.length) {\n throw new Error(\n `VALIDATION_FAILED: decision output(s) \\`${missing.join('`, `')}\\` are required to approve this `\n + `request — open the approval and fill them in before approving.`,\n );\n }\n }\n\n // The run behind this request must still be resumable before ANY of it is\n // written down (#4420). Last of the refusals, first before the writes: a\n // decision recorded against a dead run is a zombie nothing later can undo,\n // and the approver is told it succeeded.\n //\n // Non-finalizing votes are checked too — a co-sign that can never reach a\n // resume is just as stuck, and catching it here keeps the tally honest.\n await this.assertRunResumable(runId, requestId);\n\n // Audit the decision first so the quorum/per_group tally below sees it.\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: org,\n step_name: nodeId, step_index: 0, action: input.decision,\n actor_id: actorId, comment: input.comment ?? null,\n // #4466: the override is recorded on the DECISION, not inferred later.\n // Written as an explicit `false` for an ordinary decision so a reader can\n // tell \"checked, and it was not an override\" from a legacy row's `null`.\n via_override: viaOverride,\n attachments: input.attachments?.length ? input.attachments : null,\n created_at: now,\n }, { context: SYSTEM_CTX });\n\n // Multi-approver aggregation on approve (#3266). A rejection always\n // finalizes the node (one veto), so only the approve path can hold it open.\n // `first_response` finalizes on the first approval (falls straight through).\n const behavior = config.behavior ?? 'first_response';\n // A privileged override (an admin rescuing a stuck request, #3424) is an\n // authoritative decision, not one vote among the resolved slate — it\n // finalizes the node immediately, regardless of `unanimous`/`quorum`/\n // `per_group`. Only a real slot holder's approval feeds the multi-approver\n // tally below.\n if (input.decision === 'approve' && behavior !== 'first_response' && isSlotHolder) {\n const acts = await this.engine.find('sys_approval_action', {\n where: { request_id: requestId, step_index: 0, action: 'approve' }, limit: 1000, context: SYSTEM_CTX,\n });\n const approved = new Set<string>((acts ?? []).map((a: any) => String(a.actor_id ?? '')).filter(Boolean));\n\n // Tally against the OPEN-time snapshot (already OOO-substituted) for\n // every behavior that carries one. Re-resolution survives ONLY as the\n // back-compat path for requests opened before the snapshot existed —\n // it cannot ever run for an `expression` approver (#3447 P2: decide has\n // no flow variables to evaluate against; open time is the only\n // resolution point), and those always have a snapshot.\n const snapshotGroups = (config as any).__approverGroups as Record<string, string[]> | undefined;\n let original: string[];\n let groupMap: Record<string, string[]>;\n if (snapshotGroups) {\n groupMap = snapshotGroups;\n original = Object.keys(snapshotGroups);\n } else {\n original = await this.expandApprovers(\n { approvers: config.approvers }, parseJson(raw.payload_json, undefined), org,\n );\n groupMap = {};\n }\n\n if (!this.isApprovalSatisfied(behavior, config, original, groupMap, approved)) {\n const stillPending = original.filter(a => !approved.has(a));\n await this.engine.update('sys_approval_request', {\n id: requestId, pending_approvers: stillPending.join(','), updated_at: now,\n // #3447 P2: a mid-tally approval may carry outputs too (unanimous /\n // per_group co-sign, each approver contributing their declared keys)\n // — accumulate them on the snapshot so the FINALIZING decision hands\n // the merged set to the flow.\n ...(acceptedOutputs ? {\n node_config_json: JSON.stringify({\n ...config,\n __decisionOutputs: { ...((config as any).__decisionOutputs ?? {}), ...acceptedOutputs },\n }),\n } : {}),\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, stillPending, org, now);\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, nodeId, finalized: false, decision: input.decision };\n }\n }\n\n const finalStatus = input.decision === 'approve' ? 'approved' : 'rejected';\n // #3447 P2: the full accumulated output set — earlier co-sign votes' plus\n // this finalizing decision's — resumes the run and stays snapshotted for\n // the audit trail (\"what did the approvers hand the flow\").\n const mergedOutputs: Record<string, unknown> | undefined =\n acceptedOutputs || (config as any).__decisionOutputs\n ? { ...((config as any).__decisionOutputs ?? {}), ...(acceptedOutputs ?? {}) }\n : undefined;\n await this.engine.update('sys_approval_request', {\n id: requestId, status: finalStatus, pending_approvers: null, completed_at: now, updated_at: now,\n ...(mergedOutputs ? {\n node_config_json: JSON.stringify({ ...config, __decisionOutputs: mergedOutputs }),\n } : {}),\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, [], org, now);\n if (config.approvalStatusField) {\n await this.mirrorStatusField(\n raw.object_name, raw.record_id, config.approvalStatusField, finalStatus,\n actingUserId(context),\n );\n }\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, nodeId, finalized: true, decision: input.decision, outputs: mergedOutputs };\n }\n\n /**\n * Continue the owning flow run after an outcome this service has already\n * authorized and written down (#3801).\n *\n * The `approval` node declares `resumeAuthority: 'service'`, so the engine\n * refuses any resume of an approval suspension that does not carry\n * {@link RESUME_AUTHORITY_SERVICE}. Every approvals-side resume goes through\n * here so the marker is stamped in ONE place — a new outcome path cannot\n * quietly ship a resume that the gate then rejects at runtime, and nothing\n * in this file hands the marker to a caller-supplied signal.\n *\n * Callers still guard on `typeof this.automation?.resume === 'function'`\n * (approvals runs fine with no automation attached) and keep their own\n * try/catch, because what a failed resume means differs per path.\n *\n * Throws when the engine REPORTS failure, not only when it throws one. The\n * engine answers a lost run with `{ success: false, code: 'RUN_NOT_FOUND' }`\n * — a plain return value that every caller here used to discard, which is\n * how an approval could be recorded, reported as resumed, and leave its flow\n * stranded forever (#4420). The thrown error carries {@link resumeCodeOf}'s\n * `resumeCode` so callers can tell a benign duplicate from a dead run.\n */\n private async serviceResume(\n runId: string,\n signal: { output?: Record<string, unknown>; branchLabel?: string },\n ): Promise<void> {\n const result = await this.automation!.resume!(runId, { ...signal, [RESUME_AUTHORITY_SERVICE]: true });\n const reported = result as { success?: boolean; code?: string; error?: string } | undefined;\n // Only an explicit `success: false` is a failure. An engine (or a test\n // double) that returns nothing is reporting nothing, and has always meant\n // \"it ran\".\n if (reported && typeof reported === 'object' && reported.success === false) {\n const err = new Error(\n `resume of run '${runId}' failed${reported.code ? ` [${reported.code}]` : ''}: ${reported.error ?? 'unknown error'}`,\n ) as Error & { resumeCode?: string };\n err.resumeCode = reported.code;\n throw err;\n }\n }\n\n /** The engine failure code behind a {@link serviceResume} rejection, if any. */\n private static resumeCodeOf(err: unknown): string | undefined {\n return (err as { resumeCode?: string } | undefined)?.resumeCode;\n }\n\n /**\n * Refuse an operation whose whole point is to advance a flow run when that\n * run no longer exists — BEFORE anything is written down (#4420).\n *\n * The half-state this prevents is the one the issue reported: a request\n * flipped to `approved`, a success toast, and a flow that never moves. Once\n * the decision row is written there is nothing left to fail cleanly.\n *\n * Deliberately permissive at the edges:\n * - no automation attached, or an engine without `hasSuspendedRun` → no\n * pre-flight at all (standalone approvals compositions are unaffected);\n * - the store cannot be READ → fail OPEN. A transient outage must not block\n * every decision in the tenant; the post-resume check still catches a real\n * failure and reports it loudly.\n */\n private async assertRunResumable(runId: string | null | undefined, requestId: string): Promise<void> {\n if (!runId) return;\n if (typeof this.automation?.resume !== 'function') return;\n if (typeof this.automation?.hasSuspendedRun !== 'function') return;\n let alive: boolean;\n try {\n alive = await this.automation.hasSuspendedRun(runId);\n } catch (err: any) {\n this.logger?.warn?.('[approvals] could not verify the flow run is resumable — proceeding', {\n request: requestId, run: runId, error: err?.message ?? String(err),\n });\n return;\n }\n if (!alive) {\n throw new Error(\n `RESUME_TARGET_LOST: the flow run '${runId}' behind request ${requestId} no longer exists ` +\n `(it was cancelled, or it paused in a process that did not persist suspended runs). ` +\n `Nothing was recorded. An administrator can recall the request to release the record.`,\n );\n }\n }\n\n /**\n * The run named by a recorded outcome cannot be advanced at all, because no\n * automation engine in THIS process implements the capability it needs\n * (#4420). Returns the reason, or `undefined` when the capability is there\n * and the caller should proceed.\n *\n * **Why this is not simply \"not our problem\".** Approvals is legitimately\n * usable with no engine attached, and {@link assertRunResumable} deliberately\n * stays out of the way for that reason. But \"no engine is attached\" and \"no\n * run is waiting\" are different facts, and only the second is benign: a\n * `flow_run_id` on the row is the request's OWN declaration that a run is\n * parked on this decision. Deciding it in a process that cannot resume it\n * reproduces #4420's reported half-state exactly — a durable decision, a\n * mirrored status field frozen mid-workflow, a flow parked forever — while\n * the caller is answered HTTP 200. The engine-less composition is the one\n * path the #4420 fix left silent, because every guard it added hangs off an\n * engine that is not there.\n *\n * **So the outcome stands, but it is never silent.** Rolling the decision\n * back is not on the table (a human really did decide, and the row is\n * already durable), and refusing every such call would break the standalone\n * compositions the pre-flight protects. What is owed is the report: `error`\n * level per AGENTS.md's durability rule — persisted state and runtime state\n * disagree and nothing looks broken from the outside — plus a `resumeError`\n * on the response, so `resumed: false` carries its reason instead of leaving\n * the caller to guess whether a resume was even attempted.\n *\n * Reuses the registered `RESUME_FAILED` code (ADR-0112 ledger) and\n * {@link serviceResume}'s message shape: the fact being reported — an\n * outcome recorded whose run did not advance — is the same one, and this\n * needs no new vocabulary of its own.\n */\n private missingRunCapability(\n runId: string,\n requestId: string,\n what: string,\n capability: 'resume' | 'cancelRun',\n ): string | undefined {\n const fn = capability === 'resume' ? this.automation?.resume : this.automation?.cancelRun;\n if (typeof fn === 'function') return undefined;\n this.logger?.error?.(\n '[approvals] no automation engine to advance the recorded outcome — the run is stranded',\n { request: requestId, run: runId, outcome: what, capability },\n );\n return (\n `${capability} of run '${runId}' failed [RESUME_FAILED]: ${what} was recorded on request ${requestId}, ` +\n `but no automation engine in this process can ${capability} its flow run — the run stays parked and the ` +\n `record's mirrored status will not advance. Compose the automation service in this host, or recall the ` +\n `request to release the record.`\n );\n }\n\n /**\n * Resume the run behind an outcome that has ALREADY been written down, and\n * fail loudly when it cannot be (#4420).\n *\n * For the operations whose product is the resume — a finalised decision, a\n * send-back, a resubmit. Their rows are durable by the time this runs, so a\n * failure here cannot be undone; the one thing left worth doing is refusing\n * to call it success. {@link assertRunResumable} is what keeps this rare:\n * everything it catches never reaches a write.\n *\n * `RESUME_IN_PROGRESS` is the exception — a concurrent resume is already\n * advancing the run, so the outcome stands and only `resumed` is false. So\n * is a composition with no engine at all ({@link missingRunCapability}),\n * which cannot throw without breaking every standalone deployment — it\n * reports through `resumeError` instead.\n *\n * @param what - how the recorded outcome reads in the error, e.g.\n * `\"the approve decision\"`.\n */\n private async resumeRecordedOutcome(\n runId: string,\n requestId: string,\n what: string,\n signal: { output?: Record<string, unknown>; branchLabel?: string },\n ): Promise<{ resumed: boolean; resumeError?: string }> {\n const missing = this.missingRunCapability(runId, requestId, what, 'resume');\n if (missing) return { resumed: false, resumeError: missing };\n try {\n await this.serviceResume(runId, signal);\n return { resumed: true };\n } catch (err: any) {\n const reason = err?.message ?? String(err);\n if (ApprovalService.resumeCodeOf(err) === 'RESUME_IN_PROGRESS') {\n this.logger?.warn?.('[approvals] resume skipped — already in progress', {\n request: requestId, run: runId, outcome: what,\n });\n return { resumed: false, resumeError: reason };\n }\n this.logger?.error?.('[approvals] resume failed — the run is stranded', {\n request: requestId, run: runId, outcome: what, error: reason,\n });\n throw new Error(\n `RESUME_FAILED: ${what} was recorded on request ${requestId}, but its flow run '${runId}' ` +\n `could not be resumed and is now stranded: ${reason}`,\n );\n }\n }\n\n /**\n * Public contract entrypoint (ADR-0019). Records a decision on a node-driven\n * request via {@link ApprovalService.decideNode} and, when it finalizes,\n * resumes the owning flow run down the matching `approve` / `reject` edge.\n *\n * A finalising decision whose run cannot be resumed FAILS (#4420). The\n * decision is already durable by then, so the failure cannot be rolled back\n * — but it must not be reported as success either: this used to answer HTTP\n * 200 with `resumed: true` while the flow stayed parked forever, which left\n * the approver with no signal and the record mirroring a stage it never\n * reached. `decideNode`'s pre-flight means the common case (the run died\n * before the decision) never gets this far; what survives here is a genuine\n * race, and it names the stranded run.\n */\n async decide(\n requestId: string,\n input: ApprovalDecisionInput,\n context: ExecutionContext,\n ): Promise<ApprovalDecisionResult> {\n const result = await this.decideNode(requestId, input, context);\n\n let resumed = false;\n let resumeError: string | undefined;\n // No `typeof this.automation?.resume === 'function'` guard here (#4420):\n // skipping the call when no engine is attached is precisely how a decision\n // against a parked run returned 200 / `resumed: false` with nothing logged.\n // `resumeRecordedOutcome` reports that composition gap instead of hiding it.\n if (result.finalized && result.runId) {\n const branchLabel = result.decision === 'approve'\n ? APPROVAL_BRANCH_LABELS.approve\n : APPROVAL_BRANCH_LABELS.reject;\n const outcome = await this.resumeRecordedOutcome(\n result.runId, requestId, `the ${result.decision} decision`,\n {\n branchLabel,\n // #3447 P2: accepted decision outputs ride the resume envelope and\n // land as `<nodeId>.<key>` flow variables — a later approval node's\n // `expression` approver reads them as `vars.<nodeId>.<key>`.\n // Reserved keys are spread LAST so no output can shadow them (the\n // whitelist already rejects them; this is defense in depth).\n output: { ...(result.outputs ?? {}), decision: result.decision, requestId },\n },\n );\n resumed = outcome.resumed;\n resumeError = outcome.resumeError;\n }\n\n return {\n request: result.request,\n finalized: result.finalized,\n decision: result.decision,\n runId: result.runId,\n resumed,\n ...(resumeError ? { resumeError } : {}),\n };\n }\n\n /**\n * Withdraw a pending request (submitter only). Finalises the row as\n * `recalled`, releases the record lock (keyed on pending status), mirrors\n * the status field when configured, and resumes the owning flow run down\n * the `reject` branch with `output.decision = 'recall'` — leaving the run\n * suspended forever would leak it.\n *\n * ADR-0044: also valid on the LATEST `returned` request of its run — the\n * submitter abandons the revision window instead of resubmitting. The run\n * is then paused at the revise-window node (no reject edge), so it is\n * terminally cancelled via {@link ApprovalResumeSurface.cancelRun} rather\n * than resumed.\n */\n async recall(\n requestId: string,\n input: ApprovalRecallInput,\n context: ExecutionContext,\n ): Promise<ApprovalRecallResult> {\n if (!requestId) throw new Error('VALIDATION_FAILED: requestId is required');\n const actorId = await this.resolveActor(input?.actorId, context);\n\n const rawRows = await this.engine.find('sys_approval_request', {\n where: { id: requestId }, limit: 1, context: SYSTEM_CTX,\n });\n const raw: any = Array.isArray(rawRows) ? rawRows[0] : null;\n if (!raw) throw new Error(`REQUEST_NOT_FOUND: ${requestId}`);\n const inReviseWindow = raw.status === 'returned';\n if (raw.status !== 'pending' && !inReviseWindow) {\n throw new Error(`INVALID_STATE: request is ${raw.status}`);\n }\n // The submitter withdraws their own request; a privileged admin may recall\n // any pending request to release a stuck record (#3424).\n if (!this.isOverrideActor(context, raw.organization_id ?? null)\n && raw.submitter_id && String(raw.submitter_id) !== String(actorId)) {\n throw new Error(`FORBIDDEN: only the submitter may recall this request`);\n }\n // A returned request is only recallable while it is still the run's live\n // frontier — a resubmitted (or later-node) request supersedes it.\n if (inReviseWindow) await this.assertLatestForRun(raw);\n\n const config = parseJson<ApprovalNodeConfig>(raw.node_config_json, { approvers: [], behavior: 'first_response' } as any);\n const org = raw.organization_id ?? null;\n const nodeId: string | null = raw.flow_node_id ?? raw.current_step ?? null;\n const runId: string | null = raw.flow_run_id ?? null;\n const now = this.clock.now().toISOString();\n\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: org,\n step_name: nodeId, step_index: 0, action: 'recall',\n actor_id: actorId, comment: input.comment ?? null, created_at: now,\n }, { context: SYSTEM_CTX });\n\n await this.engine.update('sys_approval_request', {\n id: requestId, status: 'recalled', pending_approvers: null, completed_at: now, updated_at: now,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, [], org, now);\n if (config.approvalStatusField) {\n await this.mirrorStatusField(\n raw.object_name, raw.record_id, config.approvalStatusField, 'recalled',\n actingUserId(context),\n );\n }\n\n // A recall ABANDONS the request, so a run that cannot be resumed must not\n // fail the call — the withdrawal and the record-lock release are the point,\n // and they have already happened. It is still reported rather than\n // swallowed: `resumed: false` plus a reason, logged at error (#4420).\n let resumed = false;\n let resumeError: string | undefined;\n if (inReviseWindow) {\n // ADR-0044: the run is paused at the revise-window node, which has no\n // reject out-edge to resume down — terminally cancel it instead.\n if (runId) {\n resumeError = this.missingRunCapability(runId, requestId, 'the recall', 'cancelRun');\n if (!resumeError) {\n try {\n await this.automation!.cancelRun!(runId, `approval request ${requestId} recalled during revision`);\n } catch (err: any) {\n resumeError = err?.message ?? String(err);\n this.logger?.error?.('[approvals] cancelRun after revise-window recall failed — the run may be stranded', {\n request: requestId, run: runId, error: resumeError,\n });\n }\n }\n }\n } else if (runId) {\n resumeError = this.missingRunCapability(runId, requestId, 'the recall', 'resume');\n if (!resumeError) {\n try {\n await this.serviceResume(runId, {\n branchLabel: APPROVAL_BRANCH_LABELS.reject,\n output: { decision: 'recall', requestId },\n });\n resumed = true;\n } catch (err: any) {\n resumeError = err?.message ?? String(err);\n this.logger?.error?.('[approvals] resume after recall failed — the run may be stranded', {\n request: requestId, run: runId, error: resumeError,\n });\n }\n }\n }\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, resumed, ...(resumeError ? { resumeError } : {}) };\n }\n\n // ── Send back for revision / resubmit (ADR-0044) ─────────────\n\n /**\n * ADR-0044 send back for revision. Finalises the pending request as\n * `returned` (a third terminal state — approver-initiated rework, distinct\n * from submitter-initiated `recalled`) and resumes the owning flow run down\n * its `revise` edge to the revise window: the record lock (keyed on `pending`)\n * releases, the submitter reworks the data, then {@link resubmit}s.\n *\n * Requires the approval node to declare a `revise` out-edge — validated\n * BEFORE any mutation, because resuming with an unmatched `branchLabel`\n * falls back to *all* out-edges. Past the node's `maxRevisions` budget the\n * request auto-rejects instead (resumes down `reject` with\n * `output.autoRejected = true`) so instances cannot orbit forever.\n */\n async sendBack(\n requestId: string,\n input: ApprovalSendBackInput,\n context: ExecutionContext,\n ): Promise<ApprovalSendBackResult> {\n const actorId = await this.resolveActor(input?.actorId, context);\n const raw = await this.loadPendingRow(requestId);\n const pending = csvSplit(raw.pending_approvers);\n if (!context.isSystem && !pending.includes(actorId)) {\n throw new Error(`FORBIDDEN: actor '${actorId}' is not a pending approver`);\n }\n\n const config = parseJson<ApprovalNodeConfig>(raw.node_config_json, { approvers: [], behavior: 'first_response' } as any);\n const org = raw.organization_id ?? null;\n const nodeId: string | null = raw.flow_node_id ?? raw.current_step ?? null;\n const runId: string | null = raw.flow_run_id ?? null;\n\n await this.assertReviseEdge(raw, nodeId);\n // A send-back exists to move the run to its revise window. If the run\n // is gone there is nothing to send back TO, so refuse before writing —\n // same reasoning as decideNode's pre-flight (#4420).\n await this.assertRunResumable(runId, requestId);\n\n const now = this.clock.now().toISOString();\n const maxRevisions = typeof (config as any).maxRevisions === 'number' ? (config as any).maxRevisions : 3;\n let priorSendBacks = 0;\n if (runId && nodeId) {\n const siblings = await this.engine.find('sys_approval_request', {\n where: { flow_run_id: runId, flow_node_id: nodeId, status: 'returned' }, limit: 500, context: SYSTEM_CTX,\n });\n priorSendBacks = Array.isArray(siblings) ? siblings.length : 0;\n }\n\n // Audit the revise intent first (audit-first, like decideNode) — on the\n // auto-reject path the trail then reads `revise → reject`, preserving\n // what the approver actually asked for.\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: org,\n step_name: nodeId, step_index: 0, action: 'revise',\n actor_id: actorId, comment: input.comment ?? null, created_at: now,\n }, { context: SYSTEM_CTX });\n\n if (priorSendBacks >= maxRevisions) {\n // Revision budget exhausted — auto-reject (ADR-0044 loop guard).\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: org,\n step_name: nodeId, step_index: 0, action: 'reject',\n actor_id: actorId,\n comment: `Auto-rejected: revision limit (${maxRevisions}) exceeded`, created_at: now,\n }, { context: SYSTEM_CTX });\n await this.engine.update('sys_approval_request', {\n id: requestId, status: 'rejected', pending_approvers: null, completed_at: now, updated_at: now,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, [], org, now);\n if (config.approvalStatusField) {\n await this.mirrorStatusField(\n raw.object_name, raw.record_id, config.approvalStatusField, 'rejected',\n actingUserId(context),\n );\n }\n let resumed = false;\n let resumeError: string | undefined;\n if (runId) {\n const outcome = await this.resumeRecordedOutcome(\n runId, requestId, 'the auto-rejection',\n {\n branchLabel: APPROVAL_BRANCH_LABELS.reject,\n output: { decision: 'reject', autoRejected: true, requestId },\n },\n );\n resumed = outcome.resumed;\n resumeError = outcome.resumeError;\n }\n if (raw.submitter_id) {\n await this.notify({\n topic: 'approval.returned',\n audience: [String(raw.submitter_id)],\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n payload: {\n title: 'Approval auto-rejected',\n message: `Your ${raw.object_name}/${raw.record_id} exceeded the revision limit (${maxRevisions}) and was rejected.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, resumed, autoRejected: true, ...(resumeError ? { resumeError } : {}) };\n }\n\n await this.engine.update('sys_approval_request', {\n id: requestId, status: 'returned', pending_approvers: null, completed_at: now, updated_at: now,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, [], org, now);\n if (config.approvalStatusField) {\n await this.mirrorStatusField(\n raw.object_name, raw.record_id, config.approvalStatusField, 'returned',\n actingUserId(context),\n );\n }\n\n let resumed = false;\n let resumeError: string | undefined;\n if (runId) {\n const outcome = await this.resumeRecordedOutcome(\n runId, requestId, 'the send-back',\n {\n branchLabel: APPROVAL_BRANCH_LABELS.revise,\n output: { decision: 'revise', requestId },\n },\n );\n resumed = outcome.resumed;\n resumeError = outcome.resumeError;\n }\n\n if (raw.submitter_id) {\n await this.notify({\n topic: 'approval.returned',\n audience: [String(raw.submitter_id)],\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n payload: {\n title: 'Sent back for revision',\n message: input.comment?.trim() || `Your ${raw.object_name}/${raw.record_id} needs rework before it can be approved.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, resumed, ...(resumeError ? { resumeError } : {}) };\n }\n\n /**\n * ADR-0044 resubmit after rework. Valid on the LATEST `returned` request of\n * its run, submitter-only. Audits `resubmit` on the returned (round-N)\n * request and resumes the run from the revise-window node; traversal walks\n * the declared back-edge into the approval node, whose executor opens the\n * round-N+1 request — fresh approver slate, record re-locks.\n */\n async resubmit(\n requestId: string,\n input: ApprovalResubmitInput,\n context: ExecutionContext,\n ): Promise<ApprovalResubmitResult> {\n const actorId = await this.resolveActor(input?.actorId, context);\n const rawRows = await this.engine.find('sys_approval_request', {\n where: { id: requestId }, limit: 1, context: SYSTEM_CTX,\n });\n const raw: any = Array.isArray(rawRows) ? rawRows[0] : null;\n if (!raw) throw new Error(`REQUEST_NOT_FOUND: ${requestId}`);\n if (raw.status !== 'returned') {\n throw new Error(`INVALID_STATE: request is ${raw.status} (resubmit applies to returned requests)`);\n }\n if (!context.isSystem && raw.submitter_id && String(raw.submitter_id) !== String(actorId)) {\n throw new Error('FORBIDDEN: only the submitter may resubmit');\n }\n await this.assertLatestForRun(raw);\n\n // A colliding pending request on the same record (e.g. a record-change\n // trigger re-fired off an edit made inside the revise window) would make\n // the approval node's re-entry fail AFTER the engine consumed the\n // suspension — permanently killing the run. Refuse up front instead; the\n // submitter resolves the collision (recall the other request) first.\n const colliding = await this.engine.find('sys_approval_request', {\n where: { object_name: raw.object_name, record_id: raw.record_id, status: 'pending' },\n limit: 1, context: SYSTEM_CTX,\n });\n if (Array.isArray(colliding) && colliding[0]) {\n throw new Error(\n `DUPLICATE_REQUEST: another approval request is already pending on ${raw.object_name}/${raw.record_id} — resolve it before resubmitting`,\n );\n }\n\n const org = raw.organization_id ?? null;\n const nodeId: string | null = raw.flow_node_id ?? raw.current_step ?? null;\n const runId: string | null = raw.flow_run_id ?? null;\n const now = this.clock.now().toISOString();\n\n // The next round only exists if the resume lands, so a run that is already\n // gone fails the resubmit outright rather than recording a round that can\n // never open (#4420).\n await this.assertRunResumable(runId, requestId);\n\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: org,\n step_name: nodeId, step_index: 0, action: 'resubmit',\n actor_id: actorId, comment: input.comment ?? null, created_at: now,\n }, { context: SYSTEM_CTX });\n\n let resumed = false;\n let resumeError: string | undefined;\n if (runId) {\n const outcome = await this.resumeRecordedOutcome(\n runId, requestId, 'the resubmit',\n {\n branchLabel: APPROVAL_BRANCH_LABELS.resubmit,\n output: { resubmitted: true, requestId },\n },\n );\n resumed = outcome.resumed;\n resumeError = outcome.resumeError;\n }\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, runId, resumed, ...(resumeError ? { resumeError } : {}) };\n }\n\n /**\n * ADR-0044 guard: the flow's approval node must declare a `revise`\n * out-edge before send-back is allowed — the engine's branch-label fallback\n * (no matching label ⇒ ALL out-edges) must never be reachable from a user\n * action.\n *\n * Since #3823 it also checks WHAT that edge targets: the revise window must\n * be an `approval_revise` node, the pause this service owns. ADR-0044 D3\n * pointed the edge at an ordinary `wait`, which is `resumeAuthority: 'any'`,\n * so a raw engine resume walked the resubmit back-edge with no submitter\n * check, no `resubmit` audit row, and — with a pending request colliding on\n * the record — destroyed the run by consuming the suspension before the\n * re-entry failed. Refused HERE, before any mutation, for the same reason the\n * missing-edge check is: a run must never be parked in a revise window that\n * something other than {@link resubmit} can advance.\n *\n * Also refused at authoring time — `flow-approval-revise-target-not-service-owned`\n * in `@objectstack/lint` gates `os build` / `os validate` / `os lint` and the\n * runtime metadata publish path — so a flow reaching this check at all is one\n * published before that gate existed.\n */\n private async assertReviseEdge(raw: any, nodeId: string | null): Promise<void> {\n const processName = String(raw.process_name ?? '');\n const flowName = processName.startsWith('flow:') ? processName.slice('flow:'.length) : undefined;\n if (!flowName || !nodeId || typeof this.automation?.getFlow !== 'function') {\n throw new Error('VALIDATION_FAILED: send-back requires the owning flow definition (automation engine unavailable)');\n }\n const flow: any = await this.automation.getFlow(flowName);\n const reviseEdges = Array.isArray(flow?.edges)\n ? flow.edges.filter((e: any) => e?.source === nodeId && e?.label === APPROVAL_BRANCH_LABELS.revise)\n : [];\n if (reviseEdges.length === 0) {\n throw new Error(\n `VALIDATION_FAILED: approval node '${nodeId}' has no '${APPROVAL_BRANCH_LABELS.revise}' out-edge — ` +\n 'the flow does not support send-back for revision',\n );\n }\n const nodeTypeById = new Map<string, string>(\n (Array.isArray(flow?.nodes) ? flow.nodes : [])\n .filter((n: any) => typeof n?.id === 'string')\n .map((n: any) => [n.id as string, typeof n.type === 'string' ? n.type : '']),\n );\n for (const edge of reviseEdges) {\n const target = typeof edge?.target === 'string' ? edge.target : '';\n const targetType = nodeTypeById.get(target);\n if (targetType === APPROVAL_REVISE_NODE_TYPE) continue;\n throw new Error(\n `VALIDATION_FAILED: approval node '${nodeId}' has a '${APPROVAL_BRANCH_LABELS.revise}' out-edge into ` +\n `node '${target || '(unknown)'}'` +\n (targetType === undefined ? ' which the flow does not declare' : ` of type '${targetType || '(untyped)'}'`) +\n `, but the revise window must be an '${APPROVAL_REVISE_NODE_TYPE}' node — that pause continues only ` +\n 'through this service (submitter-only, audited, and refusing a colliding pending request), and any ' +\n `other node type there is resumable by anyone with the run id (amended ADR-0044, #3823). Fix the flow: ` +\n `set node '${target || '<revise target>'}' to type '${APPROVAL_REVISE_NODE_TYPE}'.`,\n );\n }\n }\n\n /**\n * ADR-0044 guard: a `returned` request is only actionable (resubmit /\n * recall) while it is still the newest request on its run — a later round\n * or a later node's request supersedes it.\n */\n private async assertLatestForRun(raw: any): Promise<void> {\n const runId = raw.flow_run_id;\n if (!runId) return;\n // SortNode's key is `order` (spec/data/query.zod.ts) — `direction` would\n // silently default to ascending and return the OLDEST row.\n const rows = await this.engine.find('sys_approval_request', {\n where: { flow_run_id: runId },\n orderBy: [{ field: 'created_at', order: 'desc' }], limit: 1, context: SYSTEM_CTX,\n });\n const latest: any = Array.isArray(rows) ? rows[0] : null;\n if (latest && String(latest.id) !== String(raw.id)) {\n throw new Error('INVALID_STATE: a newer approval request supersedes this one');\n }\n }\n\n // ── Thread interactions (no flow movement) ───────────────────\n\n /**\n * Hand a pending-approver slot to someone else. `from` defaults to the\n * actor itself; the actor must hold the slot being handed over (or be a\n * system caller). A privileged admin (#3424) may reassign a request whose\n * slate holds no real user — an unstaffed-position literal — by handing the\n * whole request to a real approver, rescuing it from the locked dead-end.\n * Audits `reassign` and notifies the new approver.\n */\n async reassign(\n requestId: string,\n input: { actorId: string; to: string; from?: string; comment?: string },\n context: ExecutionContext,\n ): Promise<{ request: ApprovalRequestRow }> {\n const actorId = await this.resolveActor(input?.actorId, context);\n const to = String(input?.to ?? '').trim();\n if (!to) throw new Error('VALIDATION_FAILED: `to` (new approver) is required');\n const raw = await this.loadPendingRow(requestId);\n\n const pending = csvSplit(raw.pending_approvers);\n if (pending.includes(to)) {\n throw new Error(`VALIDATION_FAILED: '${to}' is already a pending approver`);\n }\n const isOverride = this.isOverrideActor(context, raw.organization_id ?? null);\n const from = String(input.from ?? actorId).trim();\n // #4466 — same rule as `decideNode`: the marker records that the actor was\n // admitted only by the privileged branch, holding no slot themselves. A\n // reassign is the other action #3424 lets an admin take over a slate they\n // are not on, so it carries the same fact.\n const viaOverride = isOverride && !pending.includes(actorId);\n let next: string[];\n if (pending.includes(from)) {\n // Normal hand-off: the actor holds the slot being moved (or is a\n // system/admin caller acting on a real holder's slot).\n if (!context.isSystem && !isOverride && actorId !== from && !pending.includes(actorId)) {\n throw new Error(`FORBIDDEN: actor '${actorId}' is not a pending approver`);\n }\n next = pending.map(a => (a === from ? to : a));\n } else if (isOverride) {\n // Admin rescue (#3424): the caller holds no slot — the slate is an\n // unstaffed-position literal or a set of departed approvers. Reassign the\n // whole request to a real approver so the normal decision flow can resume.\n next = [to];\n } else {\n throw new Error(`FORBIDDEN: '${from}' is not a pending approver on this request`);\n }\n const now = this.clock.now().toISOString();\n // Audit first, then mutate — mirrors decideNode(), so a failed audit\n // write can never leave a moved slot without a trail.\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: raw.organization_id ?? null,\n step_name: raw.flow_node_id ?? raw.current_step ?? null, step_index: 0, action: 'reassign',\n // The hand-off parties are STRUCTURED fields (#4365) — the old default\n // comment (`\"<from> → <to>\"`) baked raw user ids into user-facing text.\n // `comment` is pure user input: absent unless the actor wrote one.\n actor_id: actorId, reassign_from: from, reassign_to: to,\n via_override: viaOverride,\n comment: input.comment ?? null, created_at: now,\n }, { context: SYSTEM_CTX });\n // per_group / quorum (#3266): carry the delegated slot's group membership to\n // the new approver in the snapshot, so their approval still counts for the\n // original group.\n let configPatch: Record<string, unknown> = {};\n try {\n const cfg = parseJson<any>(raw.node_config_json, null);\n const groups = cfg?.__approverGroups as Record<string, string[]> | undefined;\n if (groups && groups[from] && !groups[to]) {\n groups[to] = groups[from];\n delete groups[from];\n configPatch = { node_config_json: JSON.stringify(cfg) };\n }\n } catch { /* snapshot left untouched on parse failure */ }\n await this.engine.update('sys_approval_request', {\n id: requestId, pending_approvers: next.join(','), updated_at: now, ...configPatch,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(requestId, next, raw.organization_id ?? null, now);\n\n await this.notify({\n topic: 'approval.reassigned',\n audience: [to],\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n dedupKey: `approval-reassign-${requestId}-${to}`,\n payload: {\n title: 'Approval handed to you',\n message: `You are now an approver on ${raw.object_name}/${raw.record_id}.`,\n actionUrl: '/system/approvals',\n },\n });\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh! };\n }\n\n /**\n * Submitter nudge — notify every pending approver. Throttled to one\n * reminder per {@link REMIND_COOLDOWN_MS} per request.\n */\n async remind(\n requestId: string,\n input: { actorId: string; comment?: string },\n context: ExecutionContext,\n ): Promise<{ request: ApprovalRequestRow; notified: number }> {\n const actorId = await this.resolveActor(input?.actorId, context);\n const raw = await this.loadPendingRow(requestId);\n if (!context.isSystem && raw.submitter_id && String(raw.submitter_id) !== String(actorId)) {\n throw new Error('FORBIDDEN: only the submitter may send reminders');\n }\n\n const acts = await this.engine.find('sys_approval_action', {\n where: { request_id: requestId, action: 'remind' },\n orderBy: [{ field: 'created_at', order: 'desc' }], limit: 1, context: SYSTEM_CTX,\n });\n const last: any = Array.isArray(acts) ? acts[0] : null;\n const now = this.clock.now();\n if (last?.created_at && now.getTime() - Date.parse(last.created_at) < REMIND_COOLDOWN_MS) {\n throw new Error('THROTTLED: a reminder was already sent recently');\n }\n\n const pending = csvSplit(raw.pending_approvers);\n const nowIso = now.toISOString();\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: raw.organization_id ?? null,\n step_name: raw.flow_node_id ?? raw.current_step ?? null, step_index: 0, action: 'remind',\n actor_id: actorId, comment: input.comment ?? null, created_at: nowIso,\n }, { context: SYSTEM_CTX });\n\n // Per-approver fan-out: concrete identities (user ids / emails) each get\n // their OWN one-tap approve/reject links (ADR-0043); `role:*`-style\n // literals can't carry a personal token and fall back to a plain nudge.\n let notified = 0;\n const concrete = pending.filter(a => a && !a.includes(':'));\n const literals = pending.filter(a => a && a.includes(':'));\n for (const approver of concrete) {\n try {\n const tokens = await this.issueActionTokens(requestId, approver);\n notified += await this.notify({\n topic: 'approval.reminder',\n audience: [approver],\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n dedupKey: `approval-remind-${requestId}-${nowIso}-${approver}`,\n payload: {\n title: 'Approval reminder',\n message: `A decision on ${raw.object_name}/${raw.record_id} is still waiting on you.`,\n actionUrl: '/system/approvals',\n actions: [\n { label: 'Approve', url: this.actionLinkUrl(tokens.approve) },\n { label: 'Reject', url: this.actionLinkUrl(tokens.reject) },\n ],\n },\n });\n } catch (err: any) {\n this.logger?.warn?.('[approvals] reminder with action links failed', {\n request: requestId, approver, error: err?.message ?? String(err),\n });\n }\n }\n if (literals.length) {\n notified += await this.notify({\n topic: 'approval.reminder',\n audience: literals,\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n dedupKey: `approval-remind-${requestId}-${nowIso}`,\n payload: {\n title: 'Approval reminder',\n message: `A decision on ${raw.object_name}/${raw.record_id} is still waiting on you.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh!, notified };\n }\n\n // ── Actionable links (ADR-0043) ──────────────────────────────\n\n /** Build the session-less confirm-page URL for a raw token. */\n actionLinkUrl(rawToken: string): string {\n return `${this.publicBaseUrl}/api/v1/approvals/act?token=${encodeURIComponent(rawToken)}`;\n }\n\n /**\n * Issue one-tap approve/reject tokens for one approver on one pending\n * request. Raw tokens are returned ONCE; only SHA-256 hashes are stored\n * (`sys_approval_token`), so a DB leak yields no usable links.\n */\n async issueActionTokens(\n requestId: string,\n approverId: string,\n opts?: { ttlMs?: number },\n ): Promise<{ approve: string; reject: string }> {\n if (!approverId?.trim()) throw new Error('VALIDATION_FAILED: approverId is required');\n const raw = await this.loadPendingRow(requestId);\n const pending = csvSplit(raw.pending_approvers);\n if (!pending.includes(approverId)) {\n throw new Error(`FORBIDDEN: '${approverId}' is not a pending approver on this request`);\n }\n const now = this.clock.now();\n const expires = new Date(now.getTime() + (opts?.ttlMs ?? ACTION_TOKEN_TTL_MS)).toISOString();\n const out = { approve: '', reject: '' };\n for (const action of ['approve', 'reject'] as const) {\n const rawToken = randomBytes(32).toString('base64url');\n await this.engine.insert('sys_approval_token', {\n id: uid('atok'),\n organization_id: raw.organization_id ?? null,\n token_hash: createHash('sha256').update(rawToken).digest('hex'),\n request_id: requestId,\n action,\n approver_id: approverId,\n expires_at: expires,\n consumed_at: null,\n created_at: now.toISOString(),\n }, { context: SYSTEM_CTX });\n out[action] = rawToken;\n }\n return out;\n }\n\n /** Shared validation chain for peek/redeem. Returns the token row when live. */\n private async resolveActionToken(rawToken: string): Promise<\n { ok: true; token: any; request: ApprovalRequestRow } | Extract<ActionTokenOutcome, { ok: false }>\n > {\n const trimmed = rawToken?.trim();\n if (!trimmed) return { ok: false, reason: 'invalid' };\n const hash = createHash('sha256').update(trimmed).digest('hex');\n const rows = await this.engine.find('sys_approval_token', {\n where: { token_hash: hash }, limit: 1, context: SYSTEM_CTX,\n });\n const token: any = Array.isArray(rows) ? rows[0] : null;\n if (!token) return { ok: false, reason: 'invalid' };\n if (token.consumed_at) return { ok: false, reason: 'consumed' };\n if (Date.parse(token.expires_at) < this.clock.now().getTime()) {\n return { ok: false, reason: 'expired' };\n }\n const request = await this.getRequest(token.request_id, SYSTEM_CTX);\n if (!request || request.status !== 'pending') {\n return { ok: false, reason: 'not_pending', request: request ?? undefined };\n }\n if (!(request.pending_approvers ?? []).includes(token.approver_id)) {\n // Reassigned away / slot consumed by a unanimous round — the link died\n // with the slot (ADR-0043 invalidation row).\n return { ok: false, reason: 'not_approver', request };\n }\n return { ok: true, token, request };\n }\n\n /** GET confirm page: validate WITHOUT consuming — never mutates. */\n async peekActionToken(rawToken: string): Promise<ActionTokenOutcome> {\n const res = await this.resolveActionToken(rawToken);\n if (!res.ok) return res;\n return { ok: true, action: res.token.action, request: res.request, approverId: res.token.approver_id };\n }\n\n /**\n * POST redemption: consume the token FIRST (a failed decide still burns\n * it — replay-safe), then decide as the bound approver.\n */\n async redeemActionToken(rawToken: string): Promise<ActionTokenOutcome> {\n const res = await this.resolveActionToken(rawToken);\n if (!res.ok) return res;\n await this.engine.update('sys_approval_token', {\n id: res.token.id, consumed_at: this.clock.now().toISOString(),\n }, { context: SYSTEM_CTX });\n const out = await this.decide(res.token.request_id, {\n decision: res.token.action,\n actorId: res.token.approver_id,\n comment: 'Via action link',\n // The token IS the authentication (#3783): it is single-use, hashed at\n // rest and bound to one approver, who `resolveActionToken` has just\n // re-checked still holds a pending slot. So this decision has a real\n // acting user even though no session carried it — name them on the\n // context, so the status mirror and every flow it cascades into are\n // attributed exactly like a decision made through the UI. Elevation is\n // unchanged: `isSystem` still stands in for the missing session.\n }, { ...SYSTEM_CTX, userId: res.token.approver_id });\n return { ok: true, action: res.token.action, request: out.request, approverId: res.token.approver_id };\n }\n\n /**\n * Approver asks the submitter for more information. The request stays\n * pending — a thread interaction, not a flow decision.\n */\n async requestInfo(\n requestId: string,\n input: { actorId: string; comment: string },\n context: ExecutionContext,\n ): Promise<{ request: ApprovalRequestRow }> {\n const actorId = await this.resolveActor(input?.actorId, context);\n if (!input?.comment?.trim()) throw new Error('VALIDATION_FAILED: comment is required');\n const raw = await this.loadPendingRow(requestId);\n const pending = csvSplit(raw.pending_approvers);\n if (!context.isSystem && !pending.includes(actorId)) {\n throw new Error(`FORBIDDEN: actor '${actorId}' is not a pending approver`);\n }\n\n const now = this.clock.now().toISOString();\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: raw.organization_id ?? null,\n step_name: raw.flow_node_id ?? raw.current_step ?? null, step_index: 0, action: 'request_info',\n actor_id: actorId, comment: input.comment.trim(), created_at: now,\n }, { context: SYSTEM_CTX });\n\n if (raw.submitter_id) {\n await this.notify({\n topic: 'approval.request_info',\n audience: [String(raw.submitter_id)],\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n payload: {\n title: 'More information requested',\n message: input.comment.trim(),\n actionUrl: '/system/approvals',\n },\n });\n }\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh! };\n }\n\n /** Free-form reply on the thread (submitter or any pending approver). */\n async comment(\n requestId: string,\n input: { actorId: string; comment: string; attachments?: string[] },\n context: ExecutionContext,\n ): Promise<{ request: ApprovalRequestRow }> {\n const actorId = await this.resolveActor(input?.actorId, context);\n if (!input?.comment?.trim()) throw new Error('VALIDATION_FAILED: comment is required');\n const raw = await this.loadPendingRow(requestId);\n const pending = csvSplit(raw.pending_approvers);\n const isSubmitter = raw.submitter_id && String(raw.submitter_id) === String(actorId);\n if (!context.isSystem && !isSubmitter && !pending.includes(actorId)) {\n throw new Error(`FORBIDDEN: actor '${actorId}' is not on this request`);\n }\n\n const now = this.clock.now().toISOString();\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: requestId, organization_id: raw.organization_id ?? null,\n step_name: raw.flow_node_id ?? raw.current_step ?? null, step_index: 0, action: 'comment',\n actor_id: actorId, comment: input.comment.trim(),\n attachments: input.attachments?.length ? input.attachments : null,\n created_at: now,\n }, { context: SYSTEM_CTX });\n\n // Notify the other side of the thread.\n const audience = isSubmitter ? pending : [String(raw.submitter_id ?? '')].filter(Boolean);\n await this.notify({\n topic: 'approval.comment',\n audience,\n actorId: actorId,\n source: { object: 'sys_approval_request', id: requestId },\n payload: {\n title: 'New comment on an approval',\n message: input.comment.trim(),\n actionUrl: '/system/approvals',\n },\n });\n\n const fresh = await this.readBackRequest(requestId, context);\n return { request: fresh! };\n }\n\n // ── SLA escalation (ADR-0042) ─────────────────────────────────\n\n /**\n * One escalation sweep: every *pending* request whose node config declares\n * `escalation.timeoutHours` and whose deadline has passed is escalated\n * **at most once, ever** — the `escalate` audit row is the idempotency\n * marker, written before any mutation (audit-first, like reassign). One\n * bad row never stops the sweep.\n */\n async runEscalations(): Promise<{ scanned: number; escalated: number }> {\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_approval_request', {\n where: { status: 'pending' }, limit: 500, context: SYSTEM_CTX,\n }) ?? [];\n } catch (err: any) {\n this.logger?.warn?.('[approvals] escalation scan failed to list requests', {\n error: err?.message ?? String(err),\n });\n return { scanned: 0, escalated: 0 };\n }\n\n let escalated = 0;\n for (const raw of rows) {\n try {\n const cfg = parseJson<any>(raw.node_config_json, undefined);\n const esc = cfg?.escalation;\n if (!esc || typeof esc.timeoutHours !== 'number' || esc.timeoutHours <= 0) continue;\n const due = slaDueAt(raw.created_at, cfg);\n if (!due || Date.parse(due) > this.clock.now().getTime()) continue;\n\n // Single-shot: a prior 'escalate' action means this request is done.\n const prior = await this.engine.find('sys_approval_action', {\n where: { request_id: raw.id, action: 'escalate' }, limit: 1, context: SYSTEM_CTX,\n });\n if (Array.isArray(prior) && prior[0]) continue;\n\n await this.escalateRequest(raw, esc);\n escalated++;\n } catch (err: any) {\n this.logger?.warn?.('[approvals] escalation failed for request', {\n request: raw?.id, error: err?.message ?? String(err),\n });\n }\n }\n if (escalated > 0) {\n this.logger?.info?.('[approvals] SLA escalation sweep', { scanned: rows.length, escalated });\n }\n return { scanned: rows.length, escalated };\n }\n\n // ── Dead-run release (#3456) ──────────────────────────────────\n\n /**\n * One dead-run sweep: a pending request whose owning flow run has reached a\n * TERMINAL state can never be decided — nothing is left to resume — so the\n * request is finalised as `recalled` and, with `lockRecord`, the record it was\n * holding is released.\n *\n * This is the recovery half of #3456. The prevention half is the record lock's\n * owning-run exemption (`lifecycle-hooks.ts`), which stops a run from killing\n * itself on its own lock in the first place; this sweep cleans up the runs that\n * still die — for any reason, including a process crash, which no in-band\n * handler can catch because the process that would have run it is gone.\n *\n * **Fail-safe by construction.** It acts only on an explicit terminal status\n * from a closed set. Every other answer — `paused` (the normal state of a run\n * waiting on its approval), `running`, an unrecognised status, `null` (unknown\n * run, evicted log, no durable store), a `getRun` that throws, or no automation\n * engine at all — is read as \"still alive\" and left strictly alone. The failure\n * mode is therefore \"a dead run's lock survives until an admin recalls it\"\n * (today's behaviour, #3424), never \"a live approval is destroyed\".\n *\n * `recalled` is the finalisation because it is the platform's existing terminal\n * state for *a live request that ended without a decision*; the audit row names\n * the real cause and {@link DEAD_RUN_ACTOR_ID} the real actor, so a dead-run\n * release is never mistaken for a submitter's withdrawal.\n */\n /**\n * Read-only inspection for the OTHER dead-run shape: a request that is\n * already TERMINAL while its `flow_run_id` points at nothing (#4469).\n *\n * #4460 stopped new ones being produced; nothing found the ones already\n * stuck. The failure mode (#4420) is a request row flipped to `approved` /\n * `rejected` / `returned` whose owning run no longer exists — the decision\n * landed, the flow never moved. Any deployment on 17.0.0-rc.1 that hit the\n * wiring hole and crossed a restart mid-approval can be carrying these rows.\n *\n * {@link releaseDeadRunRequests} cannot see them, for a reason worth naming:\n * it scans `status: 'pending'`, and the very step that zombified the request\n * is the one that took it OUT of `pending`. The act of breaking it removed it\n * from the only sweeper's field of view — which is a large part of why this\n * class of failure stayed silent.\n *\n * It also could not have answered the question even if it looked: its\n * liveness oracle is `getRun`, which treats both `null` and `paused` as alive\n * (conservative, correct) — so it has no way to say \"this run is really\n * gone\". (Until #8050 that oracle was weaker still: after a restart it\n * returned `null` for a perfectly ALIVE suspended run, so \"alive\" and\n * \"unknown\" were the same answer. It now reports such a run as `paused`,\n * which does not change any branch here — both already meant \"leave alone\" —\n * but it is why the sweep below needs `hasSuspendedRun` as a second oracle\n * rather than a sharper reading of the first.)\n *\n * So this uses BOTH oracles, and a row must fail both to be reported:\n *\n * - `hasSuspendedRun(runId) === false` — the suspension store itself says no\n * live pause exists. It THROWS when the store cannot be read, and that\n * case is SKIPPED, never counted as dead: an unreadable store means\n * \"unknown\", and a storage outage must not be published as a lost run.\n * - `getRun(runId) == null` — no terminal history row either (the `run_`\n * prefixed rows in `sys_automation_run`). A run that merely finished is\n * not stranded; a request whose run neither waits nor ever completed is.\n *\n * **Reports; never rewrites.** No status is changed and no run is cancelled.\n * The decision genuinely happened — a human approved or rejected — and\n * silently rolling it back would make the audit trail disagree with the\n * facts. What an operator needs first is visibility: which requests are stuck\n * at which step, and what the mirrored status field on the business record\n * still says. Whether to re-run the downstream actions or re-open the\n * approval is a judgement call this cannot make.\n */\n async inspectStrandedRequests(options?: { limit?: number }): Promise<{\n scanned: number;\n stranded: StrandedApprovalRequest[];\n /** Rows skipped because the suspension store could not be read — NOT healthy, just unknown. */\n undetermined: number;\n }> {\n const empty = { scanned: 0, stranded: [] as StrandedApprovalRequest[], undetermined: 0 };\n // Both oracles are required. Without `hasSuspendedRun` there is no way to\n // tell a live cross-restart pause from a dead run, and reporting on\n // `getRun` alone would name every healthy paused approval as stranded.\n if (typeof this.automation?.hasSuspendedRun !== 'function') return empty;\n if (typeof this.automation?.getRun !== 'function') return empty;\n\n const limit = options?.limit ?? 500;\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_approval_request', {\n where: { status: { $in: [...STRANDABLE_REQUEST_STATUSES] } }, limit, context: SYSTEM_CTX,\n }) ?? [];\n } catch (err: any) {\n this.logger?.warn?.('[approvals] stranded-request scan failed to list requests', {\n error: err?.message ?? String(err),\n });\n return empty;\n }\n\n const stranded: StrandedApprovalRequest[] = [];\n let undetermined = 0;\n for (const raw of rows) {\n const runId = raw?.flow_run_id ? String(raw.flow_run_id) : '';\n if (!runId) continue; // not node-driven — no run was ever supposed to move\n\n let suspended: boolean;\n try {\n suspended = await this.automation.hasSuspendedRun!(runId);\n } catch (err: any) {\n // Store unreadable ⇒ existence unknown. Skipping is the only safe\n // answer; counted so \"0 stranded\" can never be read as \"all clear\"\n // when nothing could actually be checked.\n undetermined++;\n this.logger?.warn?.('[approvals] stranded-request scan could not read the suspension store', {\n request: raw?.id, run: runId, error: err?.message ?? String(err),\n });\n continue;\n }\n if (suspended) continue; // still parked — the run is alive and resumable\n\n let terminal: { status?: string } | null = null;\n try {\n terminal = await this.automation.getRun!(runId);\n } catch (err: any) {\n undetermined++;\n this.logger?.warn?.('[approvals] stranded-request scan could not read the run history', {\n request: raw?.id, run: runId, error: err?.message ?? String(err),\n });\n continue;\n }\n if (terminal) continue; // the run ran to a terminal state — it is not dangling\n\n // Neither suspended nor ever finished: the run this decision was supposed\n // to advance is genuinely gone.\n const config = parseJson<ApprovalNodeConfig>(\n raw.node_config_json, { approvers: [], behavior: 'first_response' } as any,\n );\n const mirrorField = config.approvalStatusField;\n let mirroredStatus: string | undefined;\n if (mirrorField) {\n try {\n const recs = await this.engine.find(raw.object_name, {\n where: { id: raw.record_id }, limit: 1, context: SYSTEM_CTX,\n });\n const rec: any = Array.isArray(recs) ? recs[0] : null;\n if (rec) mirroredStatus = rec[mirrorField] ?? undefined;\n } catch { /* display-only — a mirror read must never fail the scan */ }\n }\n stranded.push({\n requestId: String(raw.id),\n status: raw.status,\n runId,\n flowName: typeof raw.process_name === 'string' ? raw.process_name.replace(/^flow:/, '') : undefined,\n nodeId: raw.flow_node_id ?? raw.current_step ?? undefined,\n objectName: raw.object_name,\n recordId: raw.record_id,\n organizationId: raw.organization_id ?? null,\n completedAt: raw.completed_at ?? undefined,\n mirrorField,\n mirroredStatus,\n });\n }\n\n if (stranded.length || undetermined) {\n this.logger?.warn?.('[approvals] stranded terminal requests (decision recorded, flow run gone)', {\n scanned: rows.length, stranded: stranded.length, undetermined,\n requests: stranded.map(s => `${s.requestId}@${s.nodeId ?? '?'} → run ${s.runId}`),\n });\n }\n return { scanned: rows.length, stranded, undetermined };\n }\n\n async releaseDeadRunRequests(): Promise<{ scanned: number; released: number }> {\n // No liveness oracle → no basis to declare anything dead.\n if (typeof this.automation?.getRun !== 'function') return { scanned: 0, released: 0 };\n\n let rows: any[] = [];\n try {\n rows = await this.engine.find('sys_approval_request', {\n where: { status: 'pending' }, limit: 500, context: SYSTEM_CTX,\n }) ?? [];\n } catch (err: any) {\n this.logger?.warn?.('[approvals] dead-run sweep failed to list requests', {\n error: err?.message ?? String(err),\n });\n return { scanned: 0, released: 0 };\n }\n\n let released = 0;\n for (const raw of rows) {\n try {\n const runId = raw?.flow_run_id ? String(raw.flow_run_id) : '';\n if (!runId) continue; // not node-driven — no run owns it, nothing to check\n\n let status: string | undefined;\n try {\n const run = await this.automation.getRun!(runId);\n status = typeof run?.status === 'string' ? run.status : undefined;\n } catch (err: any) {\n // Unknown liveness is NOT death — leave the request pending.\n this.logger?.warn?.('[approvals] dead-run sweep could not read run status', {\n request: raw?.id, run: runId, error: err?.message ?? String(err),\n });\n continue;\n }\n if (!status || !TERMINAL_RUN_STATUSES.has(status)) continue;\n\n await this.abandonForDeadRun(raw, runId, status);\n released++;\n } catch (err: any) {\n // One bad row never stops the sweep (mirrors runEscalations).\n this.logger?.warn?.('[approvals] dead-run release failed for request', {\n request: raw?.id, error: err?.message ?? String(err),\n });\n }\n }\n if (released > 0) {\n this.logger?.info?.('[approvals] dead-run sweep', { scanned: rows.length, released });\n }\n return { scanned: rows.length, released };\n }\n\n /**\n * Finalise one pending request whose owning run is terminal. Mirrors the\n * shape of {@link recall} — audit row first (so a crash mid-release leaves a\n * trace of the intent), then the status transition, approver-index sync and\n * the optional status-field mirror. No resume/cancel of the run: it is already\n * terminal, which is precisely why we are here.\n */\n private async abandonForDeadRun(raw: any, runId: string, runStatus: string): Promise<void> {\n const org = raw.organization_id ?? null;\n const nodeId: string | null = raw.flow_node_id ?? raw.current_step ?? null;\n const now = this.clock.now().toISOString();\n\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: raw.id, organization_id: org,\n step_name: nodeId, step_index: 0, action: 'recall',\n actor_id: DEAD_RUN_ACTOR_ID,\n comment: `owning flow run ${runId} is ${runStatus} — request abandoned and record lock released`,\n created_at: now,\n }, { context: SYSTEM_CTX });\n\n await this.engine.update('sys_approval_request', {\n id: raw.id, status: 'recalled', pending_approvers: null, completed_at: now, updated_at: now,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(raw.id, [], org, now);\n\n const config = parseJson<ApprovalNodeConfig>(\n raw.node_config_json, { approvers: [], behavior: 'first_response' } as any,\n );\n if (config.approvalStatusField) {\n // No human did this — a sweep did. Left user-less on purpose (#3783): a\n // flow that wants to react to a dead-run release declares runAs:'system'.\n await this.mirrorStatusField(\n raw.object_name, raw.record_id, config.approvalStatusField, 'recalled', null,\n );\n }\n\n this.logger?.warn?.('[approvals] released a record held by a dead approval run', {\n request: raw.id, run: runId, runStatus, object: raw.object_name, record: raw.record_id,\n });\n }\n\n /** Execute the configured escalation action for one overdue request. */\n private async escalateRequest(raw: any, esc: any): Promise<void> {\n const action: string = esc.action ?? 'notify';\n const escalateTo: string | undefined =\n typeof esc.escalateTo === 'string' && esc.escalateTo.trim() ? esc.escalateTo.trim() : undefined;\n const now = this.clock.now().toISOString();\n const pending = csvSplit(raw.pending_approvers);\n\n // `escalateTo` is a position machine name or a user id (same contract as\n // the `position` ApproverType, ADR-0090 D3). Position holders win; an\n // empty expansion falls back to the literal, so a config naming a\n // specific user id keeps working unchanged.\n let escalatees: string[] = [];\n if (escalateTo) {\n try {\n escalatees = await this.expandPositionUsers(escalateTo, raw.organization_id ?? null);\n } catch { escalatees = []; }\n if (!escalatees.length) escalatees = [escalateTo];\n }\n\n // Audit first — this row IS the idempotency marker (ADR-0042 §1).\n await this.engine.insert('sys_approval_action', {\n id: uid('aact'), request_id: raw.id, organization_id: raw.organization_id ?? null,\n step_name: raw.flow_node_id ?? raw.current_step ?? null, step_index: 0, action: 'escalate',\n actor_id: SLA_ACTOR_ID,\n comment: `${action}${escalateTo ? ` → ${escalateTo}` : ''}`,\n created_at: now,\n }, { context: SYSTEM_CTX });\n\n if (action === 'reassign' && escalatees.length) {\n await this.engine.update('sys_approval_request', {\n id: raw.id, pending_approvers: escalatees.join(','), updated_at: now,\n }, { context: SYSTEM_CTX });\n await this.syncApproverIndex(raw.id, escalatees, raw.organization_id ?? null, now);\n await this.notify({\n topic: 'approval.escalated',\n audience: escalatees,\n actorId: SLA_ACTOR_ID,\n source: { object: 'sys_approval_request', id: raw.id },\n payload: {\n title: 'Approval escalated to you',\n message: `An overdue approval on ${raw.object_name}/${raw.record_id} was escalated to you.`,\n actionUrl: '/system/approvals',\n },\n });\n } else if (action === 'auto_approve' || action === 'auto_reject') {\n await this.decide(raw.id, {\n decision: action === 'auto_approve' ? 'approve' : 'reject',\n actorId: SLA_ACTOR_ID,\n comment: 'SLA escalation',\n }, SYSTEM_CTX);\n } else {\n // 'notify' (and the reassign-without-target fallback)\n await this.notify({\n topic: 'approval.sla_breached',\n audience: [...pending, ...escalatees],\n actorId: SLA_ACTOR_ID,\n source: { object: 'sys_approval_request', id: raw.id },\n payload: {\n title: 'Approval SLA breached',\n message: `A decision on ${raw.object_name}/${raw.record_id} is overdue.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n\n if (esc.notifySubmitter !== false && raw.submitter_id) {\n await this.notify({\n topic: 'approval.sla_breached',\n audience: [String(raw.submitter_id)],\n actorId: SLA_ACTOR_ID,\n source: { object: 'sys_approval_request', id: raw.id },\n payload: {\n title: 'Your approval request breached its SLA',\n message: `${raw.object_name}/${raw.record_id}: escalation action '${action}' was taken.`,\n actionUrl: '/system/approvals',\n },\n });\n }\n }\n\n // ── Display enrichment ───────────────────────────────────────\n\n /**\n * Resolve the schema-declared display field for an object, when the engine\n * exposes schema metadata (`getSchema`). Falls back to common title-ish\n * field names so plain `ApprovalEngine` fakes still enrich sensibly.\n */\n private resolveDisplayField(object: string): string | undefined {\n try {\n const schema: any = (this.engine as any).getSchema?.(object);\n const fields = schema?.fields ?? {};\n // [ADR-0079] `nameField` is the canonical primary-title pointer;\n // `displayNameField` is the deprecated alias (still honored).\n const declared = schema?.nameField ?? schema?.displayNameField;\n if (declared && declared !== 'id' && fields[declared]) return declared;\n for (const cand of ['name', 'title', 'subject', 'label']) {\n if (fields[cand]) return cand;\n }\n } catch { /* schema unavailable — heuristics below still apply */ }\n return undefined;\n }\n\n private static pickTitle(rec: any, displayField?: string): string | undefined {\n const candidates = displayField\n ? [displayField, 'name', 'title', 'subject', 'label']\n : ['name', 'title', 'subject', 'label'];\n for (const f of candidates) {\n const v = rec?.[f];\n if (v != null && String(v).trim() && f !== 'id') return String(v);\n }\n return undefined;\n }\n\n /**\n * Batch-resolve `sys_user` display names for identifiers that may be user\n * ids or emails. Best-effort — failures leave entries unresolved.\n */\n private async resolveUserNames(identifiers: Array<string | null | undefined>): Promise<Map<string, string>> {\n const names = new Map<string, string>();\n const targets = Array.from(new Set(identifiers.filter(Boolean))) as string[];\n if (!targets.length) return names;\n try {\n const users = await this.engine.find('sys_user', {\n where: { id: { $in: targets } }, fields: ['id', 'name', 'email'],\n limit: targets.length, context: SYSTEM_CTX,\n });\n for (const u of (users ?? []) as any[]) {\n if (u?.id && (u.name || u.email)) names.set(String(u.id), String(u.name ?? u.email));\n }\n } catch { /* best-effort */ }\n const unresolvedEmails = targets.filter(t => !names.has(t) && t.includes('@'));\n if (unresolvedEmails.length) {\n try {\n const users = await this.engine.find('sys_user', {\n where: { email: { $in: unresolvedEmails } }, fields: ['email', 'name'],\n limit: unresolvedEmails.length, context: SYSTEM_CTX,\n });\n for (const u of (users ?? []) as any[]) {\n if (u?.email && u.name) names.set(String(u.email), String(u.name));\n }\n } catch { /* best-effort */ }\n }\n return names;\n }\n\n /** Lookup-typed fields (key + referenced object) of an object's schema. */\n private resolveLookupFields(object: string): Array<{ key: string; reference: string }> {\n try {\n const schema: any = (this.engine as any).getSchema?.(object);\n const fields = schema?.fields ?? {};\n const out: Array<{ key: string; reference: string }> = [];\n for (const [key, f] of Object.entries<any>(fields)) {\n if ((f?.type === 'lookup' || f?.type === 'master_detail' || f?.type === 'user') && f?.reference) {\n out.push({ key, reference: String(f.reference) });\n }\n }\n return out;\n } catch { return []; }\n }\n\n /**\n * Field key → display label for an object's schema. Lets the inbox summary\n * show a human field name (\"考核状态\") instead of a title-cased machine key\n * (\"Assessment Status\"). For a single-locale project the schema label already\n * IS the localized string; symmetric with `resolveDisplayField`/lookup\n * resolution that power `payload_display`.\n */\n private resolveFieldLabels(object: string): Record<string, string> {\n try {\n const schema: any = (this.engine as any).getSchema?.(object);\n const fields = schema?.fields ?? {};\n const out: Record<string, string> = {};\n for (const [key, f] of Object.entries<any>(fields)) {\n if (f?.label) out[key] = String(f.label);\n }\n return out;\n } catch { return {}; }\n }\n\n /**\n * Attach inbox display fields to rows so clients never render a raw\n * identifier: `record_title`, `submitter_name`, `object_label`,\n * `pending_approver_names` (user-id approvers), `payload_display`\n * (lookup foreign keys in the snapshot → referenced record titles), and\n * `payload_labels` (snapshot field keys → the target object's field labels).\n * Batched: one query per distinct object (target + referenced) plus one\n * `sys_user` lookup. Best-effort — a deleted record falls back to the\n * payload snapshot, and any failure leaves the field unset rather than\n * failing the list.\n */\n private async enrichRows(rows: ApprovalRequestRow[]): Promise<void> {\n if (!rows.length) return;\n\n // Record titles + object labels, batched per object.\n const byObject = new Map<string, Set<string>>();\n for (const r of rows) {\n if (!r.object_name || !r.record_id) continue;\n let set = byObject.get(r.object_name);\n if (!set) { set = new Set(); byObject.set(r.object_name, set); }\n set.add(r.record_id);\n }\n const titles = new Map<string, string>();\n const objectLabels = new Map<string, string>();\n for (const [object, idSet] of byObject) {\n try {\n const schema: any = (this.engine as any).getSchema?.(object);\n if (schema?.label) objectLabels.set(object, String(schema.label));\n } catch { /* label optional */ }\n const ids = Array.from(idSet);\n const displayField = this.resolveDisplayField(object);\n try {\n const recs = await this.engine.find(object, {\n where: { id: { $in: ids } }, limit: ids.length, context: SYSTEM_CTX,\n });\n for (const rec of (recs ?? []) as any[]) {\n const title = ApprovalService.pickTitle(rec, displayField);\n if (rec?.id && title) titles.set(`${object} ${rec.id}`, title);\n }\n } catch { /* object may be unregistered — payload fallback below */ }\n }\n\n // Lookup foreign keys inside payload snapshots → referenced record titles.\n const lookupFieldsByObject = new Map<string, Array<{ key: string; reference: string }>>();\n // Field key → label per object, for the snapshot summary's field names.\n const fieldLabelsByObject = new Map<string, Record<string, string>>();\n for (const object of byObject.keys()) {\n const lookups = this.resolveLookupFields(object);\n if (lookups.length) lookupFieldsByObject.set(object, lookups);\n const labels = this.resolveFieldLabels(object);\n if (Object.keys(labels).length) fieldLabelsByObject.set(object, labels);\n }\n const refIds = new Map<string, Set<string>>();\n for (const r of rows) {\n const lookups = lookupFieldsByObject.get(r.object_name);\n const payload: any = r.payload;\n if (!lookups || !payload || typeof payload !== 'object') continue;\n for (const { key, reference } of lookups) {\n const v = payload[key];\n if (v == null || typeof v === 'object' || !String(v).trim()) continue;\n let set = refIds.get(reference);\n if (!set) { set = new Set(); refIds.set(reference, set); }\n set.add(String(v));\n }\n }\n const refTitles = new Map<string, string>();\n for (const [object, idSet] of refIds) {\n const ids = Array.from(idSet);\n const displayField = this.resolveDisplayField(object);\n try {\n const recs = await this.engine.find(object, {\n where: { id: { $in: ids } }, limit: ids.length, context: SYSTEM_CTX,\n });\n for (const rec of (recs ?? []) as any[]) {\n const title = ApprovalService.pickTitle(rec, displayField);\n if (rec?.id && title) refTitles.set(`${object} ${rec.id}`, title);\n }\n } catch { /* referenced object unreadable — leave unresolved */ }\n }\n\n // Display names for submitters AND user-id approvers in one lookup.\n // `role:<r>` (and other `type:value` literals) are already readable.\n const userIdentifiers: Array<string | null | undefined> = [];\n for (const r of rows) {\n userIdentifiers.push(r.submitter_id);\n for (const a of r.pending_approvers ?? []) {\n if (a && !a.includes(':')) userIdentifiers.push(a);\n }\n }\n const names = await this.resolveUserNames(userIdentifiers);\n\n for (const r of rows as any[]) {\n const title = titles.get(`${r.object_name} ${r.record_id}`)\n ?? ApprovalService.pickTitle(r.payload, undefined);\n if (title) r.record_title = title;\n const name = r.submitter_id ? names.get(String(r.submitter_id)) : undefined;\n if (name) r.submitter_name = name;\n const label = objectLabels.get(r.object_name);\n if (label) r.object_label = label;\n\n const approverNames: Record<string, string> = {};\n for (const a of r.pending_approvers ?? []) {\n const n = names.get(String(a));\n if (n) approverNames[a] = n;\n }\n if (Object.keys(approverNames).length) r.pending_approver_names = approverNames;\n\n const lookups = lookupFieldsByObject.get(r.object_name);\n if (lookups && r.payload && typeof r.payload === 'object') {\n const display: Record<string, string> = {};\n for (const { key, reference } of lookups) {\n const v = (r.payload as any)[key];\n if (v == null) continue;\n const t = refTitles.get(`${reference} ${String(v)}`);\n if (t) display[key] = t;\n }\n if (Object.keys(display).length) r.payload_display = display;\n }\n\n // Field labels for the snapshot keys the summary renders (only keys\n // actually present in the payload — a deleted field's label is noise).\n const fieldLabels = fieldLabelsByObject.get(r.object_name);\n if (fieldLabels && r.payload && typeof r.payload === 'object') {\n const labels: Record<string, string> = {};\n for (const key of Object.keys(r.payload as Record<string, unknown>)) {\n const l = fieldLabels[key];\n if (l) labels[key] = l;\n }\n if (Object.keys(labels).length) r.payload_labels = labels;\n }\n }\n }\n\n // ── Pending-approver index (issue #1745) ─────────────────────\n\n /**\n * Mirror one request's `pending_approvers` CSV into the normalized\n * `sys_approval_approver` index. Called by every write path that changes\n * the approver set; an empty `approvers` clears the request's rows (the\n * request left `pending`). Diff-based so reassign/unanimous churn doesn't\n * rewrite untouched rows.\n */\n private async syncApproverIndex(\n requestId: string,\n approvers: string[],\n org: string | null,\n now: string,\n ): Promise<void> {\n const desired = new Set(approvers.map(a => String(a).trim()).filter(Boolean));\n const existing = await this.engine.find('sys_approval_approver', {\n where: { request_id: requestId }, limit: 500, context: SYSTEM_CTX,\n });\n const rows: any[] = Array.isArray(existing) ? existing : [];\n for (const row of rows) {\n if (desired.has(String(row.approver))) desired.delete(String(row.approver));\n else await this.engine.delete('sys_approval_approver', { where: { id: row.id }, context: SYSTEM_CTX });\n }\n for (const approver of desired) {\n await this.engine.insert('sys_approval_approver', {\n id: uid('aapr'), request_id: requestId, approver,\n organization_id: org, created_at: now,\n }, { context: SYSTEM_CTX });\n }\n }\n\n /**\n * Rebuild the whole `sys_approval_approver` index from the CSV source of\n * truth. Idempotent; run at plugin start so rows written before the index\n * existed (or drifted past a crashed sync) become queryable. Cost tracks\n * the number of *pending* requests, not the request history.\n */\n async rebuildApproverIndex(): Promise<{ requests: number; inserted: number; deleted: number }> {\n // Both walks seek by `id` rather than counting from the start (#4363).\n // The desired-state walk fed the deletion pass below, so a row it skipped\n // was not a slow page — it was an approver silently dropped from someone's\n // queue. An offset walk over `status = 'pending'` has no such guarantee:\n // it slices an arrangement nothing holds steady, and this method is itself\n // a writer against the same tables.\n const desired = new Map<string, { approvers: Set<string>; org: string | null }>();\n const PAGE = 500;\n const requests = keysetWalk<Record<string, any>>(\n (q) => this.engine.find('sys_approval_request', {\n ...q,\n fields: ['id', 'pending_approvers', 'organization_id'],\n context: SYSTEM_CTX,\n }),\n { where: { status: 'pending' }, pageSize: PAGE },\n );\n for await (const rows of requests.pages()) {\n for (const r of rows) {\n desired.set(String(r.id), {\n approvers: new Set(csvSplit(r.pending_approvers)),\n org: r.organization_id ?? null,\n });\n }\n }\n\n // Current state: read the whole index first, THEN mutate. The seek makes\n // that separation a belt rather than the only brace — `created_at` is not\n // unique, so the previous ORDER BY could not make these pages a partition\n // even before the deletes below shifted the rows underneath them.\n const indexRows: any[] = [];\n const index = keysetWalk<Record<string, any>>(\n (q) => this.engine.find('sys_approval_approver', { ...q, context: SYSTEM_CTX }),\n { pageSize: PAGE },\n );\n for await (const rows of index.pages()) indexRows.push(...rows);\n let inserted = 0; let deleted = 0;\n const seen = new Map<string, Set<string>>();\n for (const row of indexRows) {\n const reqId = String(row.request_id);\n const want = desired.get(reqId);\n const have = seen.get(reqId) ?? seen.set(reqId, new Set()).get(reqId)!;\n // Orphan (request no longer pending), stale entry, or duplicate → drop.\n if (!want || !want.approvers.has(String(row.approver)) || have.has(String(row.approver))) {\n await this.engine.delete('sys_approval_approver', { where: { id: row.id }, context: SYSTEM_CTX });\n deleted++;\n continue;\n }\n have.add(String(row.approver));\n }\n\n const now = this.clock.now().toISOString();\n for (const [reqId, want] of desired) {\n const have = seen.get(reqId);\n for (const approver of want.approvers) {\n if (have?.has(approver)) continue;\n await this.engine.insert('sys_approval_approver', {\n id: uid('aapr'), request_id: reqId, approver,\n organization_id: want.org, created_at: now,\n }, { context: SYSTEM_CTX });\n inserted++;\n }\n }\n return { requests: desired.size, inserted, deleted };\n }\n\n // ── Read API ─────────────────────────────────────────────────\n\n /** Filter type accepted by {@link listRequests} / {@link countRequests}. */\n private buildRequestWhere(\n filter: {\n object?: string;\n recordId?: string;\n status?: ApprovalStatus | ApprovalStatus[];\n submitterId?: string;\n q?: string;\n } | undefined,\n context: ExecutionContext,\n ): { where: any; tenantOrg: string | null } {\n const f: any = {};\n if (filter?.object) f.object_name = filter.object;\n if (filter?.recordId) f.record_id = filter.recordId;\n if (filter?.submitterId) f.submitter_id = filter.submitterId;\n // Tenant isolation: when a caller context carries a tenant identifier\n // (organizationId / tenantId), scope the query to that tenant. SYSTEM\n // callers (no tenant) see all rows. This prevents the bespoke endpoint\n // from leaking other-tenant rows since we deliberately query with\n // SYSTEM_CTX to bypass RLS on the engine (the approver-visibility rule\n // spans three identity forms, which RLS can't model cleanly).\n // `organizationId` is not on the envelope — see isOverrideActor().\n const tenantOrg = (context as any)?.organizationId ?? context?.tenantId ?? null;\n if (tenantOrg) f.organization_id = tenantOrg;\n // Free-text search, pushed down: `payload_json` carries the record\n // snapshot, so record titles match without any join. `$contains` is the\n // driver's escaped-LIKE operator.\n const q = filter?.q?.trim();\n if (q) {\n f.$or = [\n { process_name: { $contains: q } },\n { object_name: { $contains: q } },\n { record_id: { $contains: q } },\n { submitter_id: { $contains: q } },\n { payload_json: { $contains: q } },\n ];\n }\n // Status pushes down whole: `$in` for arrays (all bundled drivers\n // support it), equality for a single value.\n if (Array.isArray(filter?.status)) {\n const statuses = (filter!.status as ApprovalStatus[]).filter(Boolean);\n if (statuses.length === 1) f.status = statuses[0];\n else if (statuses.length > 1) f.status = { $in: statuses };\n } else if (filter?.status) {\n f.status = filter.status;\n }\n return { where: f, tenantOrg };\n }\n\n /** Window the approver-index probe — pending queues live far below this. */\n private static readonly APPROVER_INDEX_CAP = 10_000;\n\n /**\n * Resolve an approver filter to matching request ids via the normalized\n * `sys_approval_approver` index — the indexed replacement for the old\n * in-memory CSV scan, and what makes approver-filtered pagination correct\n * past any scan window (issue #1745). A request matches when ANY of the\n * caller's identities (user id / email / role:<r>) holds a pending slot.\n * Returns null when the filter is absent (callers skip the id constraint).\n */\n private async approverRequestIds(\n targets: string[],\n tenantOrg: string | null,\n ): Promise<string[] | null> {\n if (!targets.length) return null;\n const where: any = targets.length === 1\n ? { approver: targets[0] }\n : { approver: { $in: targets } };\n if (tenantOrg) where.organization_id = tenantOrg;\n const rows = await this.engine.find('sys_approval_approver', {\n where, fields: ['request_id'],\n limit: ApprovalService.APPROVER_INDEX_CAP, context: SYSTEM_CTX,\n });\n const list: any[] = Array.isArray(rows) ? rows : [];\n if (list.length >= ApprovalService.APPROVER_INDEX_CAP) {\n this.logger?.warn?.('[approvals] approver index probe hit its window — results may be truncated', {\n cap: ApprovalService.APPROVER_INDEX_CAP, targets: targets.length,\n });\n }\n return [...new Set<string>(list.map(r => String(r.request_id)))];\n }\n\n /**\n * The request ids this caller is a PARTICIPANT of, or `null` for a caller\n * who may see everything in scope (#3590).\n *\n * These reads deliberately run with `SYSTEM_CTX` to bypass RLS — the\n * approver-visibility rule spans several identity forms that RLS cannot model\n * cleanly, which is why it has to be expressed here. Until now only the\n * TENANT half of that rule was applied, so any authenticated user could read\n * any request in their tenant (and, once attachments derived their access\n * from the request, its files too). This adds the participant half.\n *\n * A participant is the submitter, a current approver, or someone who has\n * already acted on the request (a past approver whose slot has moved on, a\n * commenter). Admins with override authority keep the unrestricted view the\n * \"all requests\" console surface depends on.\n *\n * Keying on the concrete user id is sufficient rather than an approximation:\n * position/team/manager/field approvers are resolved to concrete user ids at\n * open time, and the `type:value` literal is only the fallback for a spec\n * that resolved to NOBODY — a slot no one can act on either way (`can_act`\n * is a plain membership test over the resolved ids). So this cannot hide a\n * request from someone who could actually act on it.\n */\n private async visibleRequestIds(\n context: ExecutionContext,\n tenantOrg: string | null,\n target?: { object?: string | null; recordId?: string | null },\n ): Promise<Set<string> | null> {\n if (this.isOverrideActor(context, tenantOrg)) return null;\n const uid = context?.userId != null ? String(context.userId) : '';\n // A tokenless/anonymous caller participates in nothing. Fail closed.\n if (!uid) return new Set<string>();\n\n const ids = new Set<string>();\n const cap = ApprovalService.APPROVER_INDEX_CAP;\n const add = (rows: unknown, key: string) => {\n const list: any[] = Array.isArray(rows) ? rows : [];\n for (const r of list) if (r?.[key] != null) ids.add(String(r[key]));\n if (list.length >= cap) {\n this.logger?.warn?.(\n '[approvals] participant-visibility probe hit its window — some requests may be hidden from a legitimate participant',\n { cap, key },\n );\n }\n };\n\n try {\n // Current approver — via the normalized index, so every identity form\n // the write path recorded is covered.\n for (const id of (await this.approverRequestIds([uid], tenantOrg)) ?? []) ids.add(id);\n\n const orgWhere = tenantOrg ? { organization_id: tenantOrg } : {};\n add(\n await this.engine.find('sys_approval_request', {\n where: { submitter_id: uid, ...orgWhere },\n fields: ['id'], limit: cap, context: SYSTEM_CTX,\n }),\n 'id',\n );\n // Already acted on it: a past approver whose slot has moved on, or a\n // commenter. They saw it legitimately; keep it that way.\n add(\n await this.engine.find('sys_approval_action', {\n where: { actor_id: uid },\n fields: ['request_id'], limit: cap, context: SYSTEM_CTX,\n }),\n 'request_id',\n );\n } catch (err) {\n // Never widen on error: a failed probe yields whatever was collected.\n this.logger?.warn?.('[approvals] participant-visibility probe failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n // [#8652] The read-only record-reader tier, applied STRICTLY ON TOP of the\n // participant set above. It only ever adds ids; it can never return `null`\n // (the \"sees everything\" verdict) and never removes a constraint, so the\n // worst it can do is the thing it is for.\n await this.addRecordReaderVisibleIds(ids, context, tenantOrg, target);\n return ids;\n }\n\n /**\n * [#8652] Read-only approval visibility derived from READ ACCESS TO THE\n * TARGET BUSINESS RECORD.\n *\n * Maintainer ruling 2026-08-15: a user who can read the target record may\n * view that record's approval requests and full action history, read-only,\n * behind a switch that is default OFF, anchored on the EXISTING record-read\n * permission. The rejected alternative was a host-injected visibility hook —\n * a security predicate the platform could neither constrain nor audit.\n *\n * ## How the anchor is evaluated\n *\n * By asking the engine to read the record AS THE CALLER. That is the whole\n * check: `engine.find(object, { where: { id }, context })` runs the ordinary\n * ObjectQL middleware — object CRUD read, then RLS — so a denial throws and a\n * row the caller may not see comes back empty. Both mean \"no\". No new\n * permission, role or grant type is invented, and no second copy of the\n * access rule exists to drift from the first.\n *\n * ⚠️ The caller's context is load-bearing. Probing with {@link SYSTEM_CTX} —\n * the context every other read in this service uses — would read exactly like\n * a permission check while admitting every authenticated user in the tenant.\n *\n * ## Why it needs a NAMED TARGET, and what that deliberately excludes\n *\n * The rule is anchored on one record, so it can only be evaluated where a\n * record is named: a list filtered by `object` + `recordId` (what a record\n * page's approval tab sends), or a request loaded by id (whose own row names\n * its target). An UNTARGETED list — the inbox — is left exactly as it was:\n * answering it under this tier would mean probing every request in the tenant\n * for read access, which is unbounded, and would turn a work queue into a\n * browse surface. The confirmed consumer is the record page; the inbox is not\n * part of the ruling and is not widened here.\n *\n * ## What becomes visible (stated plainly, because the switch is an opt-in)\n *\n * The request row — including its `payload` snapshot of the record at\n * submission time — plus the full action history: actor, decision, timestamp,\n * the action's COMMENT text, and (through the same gate, via\n * {@link ApprovalService.authorizeFileRead}) any decision attachments. The\n * comment text is the ruling's \"full action history\" read literally; it is\n * flagged on the card as the one granularity edge worth a second look.\n *\n * Read-only is not enforced here and must not be: the decision paths\n * (`decideNode` / `reassign` / `recall` / `comment`) authorize on the pending\n * approver slate, the submitter, or {@link ApprovalService.isOverrideActor},\n * none of which this tier touches. Seeing a request confers nothing.\n */\n private async addRecordReaderVisibleIds(\n ids: Set<string>,\n context: ExecutionContext,\n tenantOrg: string | null,\n target?: { object?: string | null; recordId?: string | null },\n ): Promise<void> {\n // Default OFF: a deployment that declares nothing must see no behaviour\n // change at all — not even a probe whose answer is discarded.\n if (this.recordReaderVisibleObjects.size === 0) return;\n const object = String(target?.object ?? '').trim();\n const recordId = String(target?.recordId ?? '').trim();\n if (!object || !recordId) return;\n if (!this.recordReaderVisibleObjects.has(object)) return;\n // A tokenless/anonymous caller reads nothing. Fail closed, as above.\n const uid = context?.userId != null ? String(context.userId) : '';\n if (!uid) return;\n\n try {\n // The anchor. As the CALLER — see the warning in the doc block.\n const readable = await this.engine.find(object, {\n where: { id: recordId }, fields: ['id'], limit: 1, context,\n });\n if (!Array.isArray(readable) || readable.length === 0) return;\n\n // Admitted: every request on that record, inside the caller's tenant.\n const orgWhere = tenantOrg ? { organization_id: tenantOrg } : {};\n const rows = await this.engine.find('sys_approval_request', {\n where: { object_name: object, record_id: recordId, ...orgWhere },\n fields: ['id'], limit: ApprovalService.APPROVER_INDEX_CAP, context: SYSTEM_CTX,\n });\n for (const r of Array.isArray(rows) ? rows : []) {\n if (r?.id != null) ids.add(String(r.id));\n }\n } catch (err) {\n // Never widen on error — a failed or refused probe adds nothing. A CRUD\n // denial arrives here as a throw and is the ordinary \"no\", so this is\n // logged at debug rather than warn: on a deployment with the tier on it\n // is a routine answer, not a degradation.\n this.logger?.debug?.('[approvals] record-reader visibility probe declined', {\n object, error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n /** Intersect an existing `where.id` constraint with the participant set. */\n private applyVisibility(where: any, visible: Set<string> | null): boolean {\n if (!visible) return true;\n if (visible.size === 0) return false;\n let allowed = [...visible];\n const current = where.id;\n if (typeof current === 'string') allowed = allowed.filter((x) => x === current);\n else if (current && typeof current === 'object' && Array.isArray(current.$in)) {\n const set = new Set(current.$in.map((v: unknown) => String(v)));\n allowed = allowed.filter((x) => set.has(x));\n }\n if (allowed.length === 0) return false;\n where.id = allowed.length === 1 ? allowed[0] : { $in: allowed };\n return true;\n }\n\n async listRequests(\n filter: {\n object?: string;\n recordId?: string;\n status?: ApprovalStatus | ApprovalStatus[];\n approverId?: string | string[];\n submitterId?: string;\n q?: string;\n limit?: number;\n offset?: number;\n } | undefined,\n context: ExecutionContext,\n ): Promise<ApprovalRequestRow[]> {\n const { where, tenantOrg } = this.buildRequestWhere(filter, context);\n const approverTargets = (Array.isArray(filter?.approverId) ? filter!.approverId : filter?.approverId ? [filter.approverId] : [])\n .map(t => String(t).trim())\n .filter(Boolean);\n\n // Every filter now pushes into the engine (issue #1745): approver via\n // the normalized index, status arrays via $in — so the page window is\n // always engine-side and correct at any table size.\n const ids = await this.approverRequestIds(approverTargets, tenantOrg);\n if (ids) {\n if (ids.length === 0) return [];\n where.id = ids.length === 1 ? ids[0] : { $in: ids };\n }\n\n // #3590: the caller-supplied `approverId` is a FILTER, not authorization —\n // omitting it used to return every request in the tenant. Intersect with\n // what this caller actually participates in.\n // [#8652] The filter's own `object`/`recordId` is what names the target\n // record the read-only tier anchors on; without them nothing is widened.\n if (!this.applyVisibility(where, await this.visibleRequestIds(context, tenantOrg, {\n object: filter?.object, recordId: filter?.recordId,\n }))) return [];\n\n const findOpts: any = {\n where,\n orderBy: [{ field: 'created_at', order: 'desc' }],\n context: SYSTEM_CTX,\n };\n if (filter?.limit != null || filter?.offset != null) {\n findOpts.limit = Math.min(Math.max(filter?.limit ?? 50, 1), 200);\n if (filter?.offset) findOpts.offset = Math.max(filter.offset, 0);\n } else {\n // Unpaginated callers keep the legacy bounded window.\n findOpts.limit = 500;\n }\n\n const rows = await this.engine.find('sys_approval_request', findOpts);\n const list = Array.isArray(rows) ? rows.map(rowFromRequest) : [];\n await this.enrichRows(list);\n this.attachViewers(list, context);\n return list;\n }\n\n async countRequests(\n filter: Parameters<IApprovalService['listRequests']>[0],\n context: ExecutionContext,\n ): Promise<number> {\n const { where, tenantOrg } = this.buildRequestWhere(filter, context);\n const approverTargets = (Array.isArray(filter?.approverId) ? filter!.approverId : filter?.approverId ? [filter.approverId] : [])\n .map(t => String(t).trim())\n .filter(Boolean);\n\n const ids = await this.approverRequestIds(approverTargets, tenantOrg);\n if (ids) {\n if (ids.length === 0) return 0;\n where.id = ids.length === 1 ? ids[0] : { $in: ids };\n }\n\n // #3590 — the count must agree with the list it paginates, so it reads the\n // same target (#8652) as `listRequests` above.\n if (!this.applyVisibility(where, await this.visibleRequestIds(context, tenantOrg, {\n object: filter?.object, recordId: filter?.recordId,\n }))) return 0;\n\n const countFn = (this.engine as any).count;\n if (typeof countFn === 'function') {\n try {\n const n = await countFn.call(this.engine, 'sys_approval_request', { where, context: SYSTEM_CTX });\n if (typeof n === 'number') return n;\n } catch { /* fall through to scan */ }\n }\n // Engine without count(): bounded scan. The approver-filtered case is\n // exact (the id set bounds it); the unfiltered case keeps the legacy\n // 500 window.\n const rows = await this.engine.find('sys_approval_request', {\n where, fields: ['id'], limit: ids ? Math.max(500, ids.length) : 500, context: SYSTEM_CTX,\n });\n return Array.isArray(rows) ? rows.length : 0;\n }\n\n /**\n * Read the request a write path just changed, to echo back as its result.\n *\n * NOT participant-gated (#3590), deliberately: the operation authorized\n * itself by its own rule before writing, so re-asking \"may you see this?\"\n * for the echo answers a question that has already been settled — and would\n * answer it WRONG for a caller context that carries no `userId` (a\n * flow-driven resume, a service-to-service call), turning a successful write\n * into a `null` result. Gating belongs on the read API, not on an\n * operation's own return value.\n */\n private async readBackRequest(\n requestId: string,\n context: ExecutionContext,\n ): Promise<ApprovalRequestRow | null> {\n return this.loadRequest(requestId, context, false);\n }\n\n async getRequest(requestId: string, context: ExecutionContext): Promise<ApprovalRequestRow | null> {\n return this.loadRequest(requestId, context, true);\n }\n\n private async loadRequest(\n requestId: string,\n context: ExecutionContext,\n enforceVisibility: boolean,\n ): Promise<ApprovalRequestRow | null> {\n if (!requestId) return null;\n const where: any = { id: requestId };\n // `organizationId` is not on the envelope — see isOverrideActor().\n const tenantOrg = (context as any)?.organizationId ?? context?.tenantId;\n if (tenantOrg) where.organization_id = tenantOrg;\n const rows = await this.engine.find('sys_approval_request', {\n where, limit: 1, context: SYSTEM_CTX,\n });\n if (!Array.isArray(rows) || !rows[0]) return null;\n // #3590: tenant scoping alone let any authenticated user read any request\n // — and, once decision attachments derived their access from the request\n // (#3580), its files too. Participation is the rest of the rule.\n if (enforceVisibility) {\n // [#8652] The row itself names the target record, so a request loaded by\n // id carries its own anchor — which is what makes `listActions` and the\n // decision-attachment gate follow this rule without a second copy of it.\n const visible = await this.visibleRequestIds(context, tenantOrg ?? null, {\n object: rows[0].object_name, recordId: rows[0].record_id,\n });\n if (visible && !visible.has(String(rows[0].id))) return null;\n }\n const row = rowFromRequest(rows[0]);\n await this.enrichRows([row]);\n await this.attachFlowSteps(row);\n await this.attachDecisionProgress(row, rows[0]);\n this.attachViewers([row], context);\n return row;\n }\n\n /**\n * Server-computed decision aggregation progress (#3266 / objectui#2678 P1.5).\n * Single-read enrichment only (like {@link ApprovalService.attachFlowSteps}):\n * for a PENDING request whose behavior aggregates multiple approvals\n * (`unanimous` / `quorum` / `per_group`), expose\n * `decision_progress: { behavior, got, need, groups? }` so any client renders\n * \"2 of 3\" or per-group ticks without re-deriving the engine's tally rules.\n * `first_response` requests carry no progress (one approval finalizes).\n * Display-only and best-effort — errors leave the row untouched.\n */\n private async attachDecisionProgress(row: ApprovalRequestRow, raw: any): Promise<void> {\n try {\n if (row.status !== 'pending') return;\n const cfg = parseJson<any>(raw.node_config_json, undefined);\n const behavior = cfg?.behavior ?? 'first_response';\n if (behavior !== 'unanimous' && behavior !== 'quorum' && behavior !== 'per_group') return;\n\n const acts = await this.engine.find('sys_approval_action', {\n where: { request_id: row.id, step_index: 0, action: 'approve' }, limit: 1000, context: SYSTEM_CTX,\n });\n const approved = new Set<string>((acts ?? []).map((a: any) => String(a.actor_id ?? '')).filter(Boolean));\n\n const snapshot = cfg?.__approverGroups as Record<string, string[]> | undefined;\n const slate = snapshot ? Object.keys(snapshot) : [...approved, ...(row.pending_approvers ?? [])];\n const total = slate.length || 1;\n\n const progress: any = { behavior, got: approved.size, need: total };\n if (behavior === 'quorum') {\n progress.need = Math.min(Math.max(1, cfg?.minApprovals ?? total), total);\n } else if (behavior === 'per_group' && snapshot) {\n const perGroupNeed = Math.max(1, cfg?.minApprovals ?? 1);\n const size: Record<string, number> = {};\n for (const gs of Object.values(snapshot)) for (const g of gs) size[g] = (size[g] ?? 0) + 1;\n const got: Record<string, number> = {};\n for (const a of approved) for (const g of (snapshot[a] ?? [])) got[g] = (got[g] ?? 0) + 1;\n progress.groups = Object.keys(size).sort().map(g => {\n const need = Math.min(perGroupNeed, size[g]);\n return { group: g, got: Math.min(got[g] ?? 0, need), need, satisfied: (got[g] ?? 0) >= need };\n });\n progress.got = progress.groups.filter((g: any) => g.satisfied).length;\n progress.need = progress.groups.length;\n\n // Approver→group(s) for the STILL-PENDING slots (objectui#2807), so the\n // console can label each \"waiting on\" chip with the group it represents\n // rather than showing duplicate, context-free names. Only pending slots\n // matter — a resolved approver has dropped out of `pending_approvers`.\n // Synthetic (unnamed, `#N`) group keys are dropped: a `· #0` sub-tag is\n // noise, and the client would have to filter it anyway.\n const pendingGroups: Record<string, string[]> = {};\n for (const a of (row.pending_approvers ?? [])) {\n const named = (snapshot[a] ?? []).filter((g) => !/^#\\d+$/.test(g));\n if (named.length) pendingGroups[a] = named;\n }\n if (Object.keys(pendingGroups).length) {\n (row as any).pending_approver_groups = pendingGroups;\n }\n }\n (row as any).decision_progress = progress;\n } catch { /* display-only enrichment */ }\n }\n\n /**\n * Attach the per-viewer capability block (#3310) from the caller's context.\n * `can_act` mirrors the exact authorization the decision methods enforce — the\n * caller's user id is in the resolved `pending_approvers` while the request is\n * still `pending` (position/team/manager approvers are already resolved to\n * concrete user ids at open time, so a plain membership test is faithful).\n * `is_submitter` is a straight owner check. `can_override` (#3424) is true for\n * a platform/tenant admin on a PENDING request — the recovery path for an\n * approval routed to an unstaffed position or to approvers who have all left;\n * clients OR it into the decision actions' `visible` gate so an admin can act\n * even when they hold no slot. System/tokenless contexts get a both-false\n * `can_act`/`is_submitter` block (system gets `can_override` too — it may act\n * on anything). Cheap + synchronous — safe on list reads.\n */\n private attachViewers(rows: ApprovalRequestRow[], context: ExecutionContext): void {\n const uid = context?.userId != null ? String(context.userId) : null;\n for (const row of rows) {\n const pending = row.pending_approvers ?? [];\n (row as any).viewer = {\n can_act: row.status === 'pending' && !!uid && pending.includes(uid),\n is_submitter: !!uid && row.submitter_id != null && String(row.submitter_id) === uid,\n can_override: row.status === 'pending'\n && this.isOverrideActor(context, (row as any).organization_id ?? null),\n };\n }\n }\n\n /**\n * Derive approval-step progress from the owning flow's graph (single-read\n * enrichment only — list reads skip it). Walks from the start node\n * preferring `approve`/`true` edges, so the result is the flow's main\n * approval trunk; conditional side-steps show as part of the potential\n * path. Display-only and best-effort.\n */\n private async attachFlowSteps(row: ApprovalRequestRow): Promise<void> {\n try {\n const flowName = row.process_name?.startsWith('flow:') ? row.process_name.slice(5) : undefined;\n if (!flowName || typeof this.automation?.getFlow !== 'function') return;\n const flow: any = await this.automation.getFlow(flowName);\n if (!flow?.nodes?.length) return;\n const nodesById = new Map<string, any>(flow.nodes.map((n: any) => [n.id, n]));\n const steps: Array<{ id: string; label: string }> = [];\n const seen = new Set<string>();\n let cur: any = flow.nodes.find((n: any) => n.type === 'start');\n while (cur && !seen.has(cur.id)) {\n seen.add(cur.id);\n if (cur.type === 'approval') steps.push({ id: cur.id, label: cur.label || cur.id });\n const out = (flow.edges ?? []).filter((e: any) => e.source === cur.id);\n if (!out.length) break;\n const pick = out.find((e: any) => e.label === 'approve')\n ?? out.find((e: any) => e.label === 'true')\n ?? out[0];\n cur = nodesById.get(pick.target);\n }\n if (steps.length === 0) return;\n const currentId = row.flow_node_id ?? row.current_step;\n const currentIdx = steps.findIndex(s => s.id === currentId);\n (row as any).flow_steps = steps.map((s, i) => ({\n ...s,\n state: currentIdx < 0 ? 'upcoming'\n : i < currentIdx ? 'done'\n : i === currentIdx ? (row.status === 'approved' ? 'done' : 'current')\n : 'upcoming',\n }));\n } catch { /* display-only — never fail the read */ }\n }\n\n async listActions(requestId: string, context: ExecutionContext): Promise<ApprovalActionRow[]> {\n if (!requestId) return [];\n // Tenant gate: ensure the caller can see the parent request before\n // returning its action history. Skipping this would leak history rows\n // across tenants the same way the unscoped list-requests path did.\n const req = await this.getRequest(requestId, context);\n if (!req) return [];\n const rows = await this.engine.find('sys_approval_action', {\n where: { request_id: requestId },\n limit: 500,\n orderBy: [{ field: 'created_at', order: 'asc' }],\n context: SYSTEM_CTX,\n });\n const actions = Array.isArray(rows) ? rows.map(rowFromAction) : [];\n // Timeline display: resolve actor ids to names so the audit trail never\n // shows a raw identifier. Role/team literals are already readable. The\n // reassign hand-off parties (#4365) resolve through the same batch.\n const names = await this.resolveUserNames(\n actions\n .flatMap(a => [a.actor_id, a.reassign_from, a.reassign_to])\n .filter(id => id && !id.includes(':')),\n );\n for (const a of actions as any[]) {\n const n = a.actor_id ? names.get(String(a.actor_id)) : undefined;\n if (n) a.actor_name = n;\n const fromName = a.reassign_from ? names.get(String(a.reassign_from)) : undefined;\n if (fromName) a.reassign_from_name = fromName;\n const toName = a.reassign_to ? names.get(String(a.reassign_to)) : undefined;\n if (toName) a.reassign_to_name = toName;\n }\n return actions;\n }\n\n /**\n * `IFileAccessDelegate` — may this caller download a decision attachment?\n * (ADR-0104 D3 wave 2; declared by `sys_approval_action.fileAccessDelegate`.)\n *\n * A file referenced by `sys_approval_action.attachments` is owned by that\n * audit row, so the storage service would otherwise authorize the download by\n * testing whether the caller can READ the row. It cannot: `sys_approval_action`\n * is deliberately closed to ordinary approver positions, so that test denies\n * the very approver the attachment was filed for.\n *\n * The rule that actually governs seeing a decision is the one `listActions`\n * applies — can the caller see the PARENT REQUEST? — so this reuses it\n * exactly, rather than inventing a second, looser rule for the bytes. Fails\n * closed on any error.\n */\n async authorizeFileRead(actionId: string, context: ExecutionContext): Promise<boolean> {\n if (!actionId) return false;\n try {\n const rows = await this.engine.find('sys_approval_action', {\n where: { id: actionId },\n limit: 1,\n context: SYSTEM_CTX,\n });\n const requestId = (Array.isArray(rows) ? rows[0] : undefined)?.request_id;\n if (!requestId) return false;\n // Same gate as listActions: visibility of the decision's parent request.\n return !!(await this.getRequest(String(requestId), context));\n } catch {\n return false;\n }\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0105 D9] Cross-organization approver targeting — resolving WHICH\n * organization's directory an approver is looked up in.\n *\n * One organization id used to do three jobs at once in `openNodeRequest`:\n * where the request row lives, where its inbox index rows live, and where its\n * approvers are looked up. The first two are the request's own organization by\n * definition. The third is not: a group CFO holds her `cfo` position in the\n * GROUP organization while the purchase order she signs off lives in the PLANT\n * organization. Binding all three together meant\n * `expandPositionUsers('cfo', <plant>)` matched nobody and the slot fell into\n * `onEmptyApprovers` — a group escalation could not be expressed at all.\n *\n * This module resolves only the third job. The request keeps living in its own\n * organization, and D2's membership union is what lets a group-side approver\n * READ it — see `assertApproversCanRead` below for why that is a precondition\n * worth checking rather than assuming.\n */\n\n/** Cycle guard for the `parent_organization_id` walk (mirrors the BU subtree walk). */\nconst MAX_ORG_DEPTH = 32;\n\nconst SYSTEM_CTX = { isSystem: true } as const;\n\nexport interface ApproverOrgScopeEngine {\n find(object: string, options: any): Promise<any[]>;\n}\n\nexport interface ApproverOrgScopeDeps {\n engine: ApproverOrgScopeEngine;\n /**\n * The tenancy posture in force, when the host could resolve one. `undefined`\n * means \"unknown\" — a stack booted without the tenancy service — and is\n * treated as permissive so a minimal test/embedded stack is not broken by a\n * guard it cannot answer.\n */\n posture?: () => string | undefined;\n logger?: { warn?: (msg: any, ...rest: any[]) => void; debug?: (msg: any, ...rest: any[]) => void };\n}\n\n/**\n * Every failure here THROWS `VALIDATION_FAILED`, matching how `expression`\n * approvers already fail (#3447 P2): an approver declaration that cannot be\n * resolved is a ROUTING BUG, never \"condition not met\". Silently falling back\n * to the request's own organization would be the worst option available — a\n * flow that reads as \"escalate to group\" would quietly approve inside the\n * plant, and the deployment that changed posture would reroute its approvals\n * with no signal at all.\n *\n * Messages name the offending value because their primary reader is the AI\n * author fixing the flow on the next validate pass.\n */\nfunction fail(message: string): never {\n throw new Error(`VALIDATION_FAILED: ${message}`);\n}\n\nasync function findOrg(\n engine: ApproverOrgScopeEngine,\n where: Record<string, unknown>,\n): Promise<any | null> {\n try {\n const rows = await engine.find('sys_organization', {\n where,\n fields: ['id', 'slug', 'parent_organization_id'],\n limit: 1,\n context: SYSTEM_CTX,\n } as any);\n return Array.isArray(rows) && rows[0] ? rows[0] : null;\n } catch {\n return null;\n }\n}\n\n/** The id chain from `orgId` up to its root, inclusive. Fail-closed on cycles. */\nasync function ancestorChain(\n engine: ApproverOrgScopeEngine,\n orgId: string,\n): Promise<string[]> {\n const chain: string[] = [];\n const seen = new Set<string>();\n let cursor: string | null = orgId;\n for (let depth = 0; cursor && depth < MAX_ORG_DEPTH; depth++) {\n if (seen.has(cursor)) break; // cycle in the grouping metadata — stop, do not loop\n seen.add(cursor);\n chain.push(cursor);\n const row = await findOrg(engine, { id: cursor });\n const parent = row?.parent_organization_id;\n cursor = parent ? String(parent) : null;\n }\n return chain;\n}\n\n/**\n * Resolve an approver's `organization` declaration to a concrete organization\n * id for directory lookups. Returns the request's own organization when the\n * declaration is absent — the unchanged default path, which does no reads.\n */\nexport async function resolveApproverDirectoryOrg(\n deps: ApproverOrgScopeDeps,\n declaration: string | null | undefined,\n requestOrgId: string | null | undefined,\n approverType: string,\n isOrgScopedType: boolean,\n): Promise<string | null | undefined> {\n const declared = typeof declaration === 'string' ? declaration.trim() : '';\n if (!declared) return requestOrgId;\n\n // An `organization` on `user` / `field` / `manager` / `team` is not a\n // narrower routing — those types never consult an org-scoped directory, so\n // the declaration would have no effect. Refuse rather than ignore: a flow\n // author who wrote it believed it did something.\n if (!isOrgScopedType) {\n fail(\n `approver type '${approverType}' resolves people without an organization directory, `\n + `so 'organization: ${declared}' would have no effect — remove it `\n + `(ADR-0105 D9 applies to position / org_membership_level / department / expression)`,\n );\n }\n\n // Posture guard. ADR-0105 D9 applies to `group` only: in `isolated`, cross-org\n // approval remains system-context mirroring (cloud #2937 contract), and in\n // `single` there is no second organization to target. Refusing here — rather\n // than in a lint — is deliberate: posture is ENVIRONMENT configuration, so the\n // same portable flow metadata may be deployed into any posture and no static\n // check can see which. A deployment migrating group → isolated must fail\n // loudly instead of silently rerouting its approvals.\n const posture = deps.posture?.();\n if (posture && posture !== 'group') {\n fail(\n `cross-organization approver targeting ('organization: ${declared}') requires the `\n + `'group' tenancy posture; this deployment resolves '${posture}' (ADR-0105 D9)`,\n );\n }\n\n const requestOrg = requestOrgId ? String(requestOrgId) : '';\n if (!requestOrg) {\n fail(\n `'organization: ${declared}' cannot be resolved for a request that carries no `\n + `organization — cross-organization targeting needs an organization to resolve from`,\n );\n }\n\n const chain = await ancestorChain(deps.engine, requestOrg);\n\n if (declared === '$root') {\n const root = chain[chain.length - 1];\n if (root === requestOrg && chain.length === 1) {\n fail(\n `'organization: $root' resolved to the request's own organization — this organization `\n + `has no 'parent_organization_id' lineage. Declare the group hierarchy (ADR-0105 D6) `\n + `or drop the targeting`,\n );\n }\n return root;\n }\n\n if (declared === '$parent') {\n const parent = chain[1];\n if (!parent) {\n fail(\n `'organization: $parent' has no parent to resolve to — the request's organization is `\n + `already the root of its group (ADR-0105 D6 'parent_organization_id')`,\n );\n }\n return parent;\n }\n\n // A slug names one specific organization — the shape the symbols cannot\n // express (a SIBLING, e.g. a shared-services centre approving payables for\n // every plant). Slugs rather than ids because flow metadata is portable\n // across environments and organization ids are minted per deployment.\n const target = await findOrg(deps.engine, { slug: declared });\n if (!target?.id) {\n fail(\n `no organization with slug '${declared}' — 'organization' takes an organization SLUG `\n + `or a symbol ($root / $parent), never an id (ids are per-deployment, flows are portable)`,\n );\n }\n const targetId = String(target.id);\n if (targetId === requestOrg) return targetId;\n\n // Legality: the target must share a grouping root with the request's\n // organization. \"Shares a root\" rather than \"is an ancestor\" because the\n // sibling case is a first-class use. The rule depends only on the\n // organization tree — never on who submitted — so one flow routes identically\n // for every submitter, which is what makes a routing bug reproducible.\n const targetChain = await ancestorChain(deps.engine, targetId);\n const targetRoot = targetChain[targetChain.length - 1];\n const requestRoot = chain[chain.length - 1];\n if (!targetRoot || !requestRoot || targetRoot !== requestRoot) {\n fail(\n `organization '${declared}' is not in the same group as the request's organization `\n + `— cross-organization approval routes WITHIN one group (they must share a `\n + `'parent_organization_id' root, ADR-0105 D6/D9)`,\n );\n }\n return targetId;\n}\n\n/**\n * Drop approvers who could not READ the request they were routed to, and say\n * so.\n *\n * ADR-0105 D9 notes that reads by cross-org approvers \"are covered by D2\n * (membership union)\". True — but D2's union is `organization_id IN\n * accessible_org_ids`, and the request row is stamped with the REQUEST's\n * organization. So a group-side approver reaches it only if she also holds a\n * membership there. That is the intended group shape (group staff are members\n * of every plant while holding their positions in the group org — the shape the\n * ADR-0105 acceptance dogfood already models), but nothing enforces it.\n *\n * Left unchecked the failure is SILENT and expensive: routing succeeds, the\n * request and inbox rows are written, and the approver then opens a task she\n * cannot open — the wall hides the record. Filtering here converts that into\n * the empty-slate path the node already has a policy for\n * (`onEmptyApprovers`), and logs exactly who was dropped and why, so the fix\n * (\"grant the membership\" or \"retarget\") is legible without a debugger.\n */\nexport async function filterApproversWhoCanRead(\n deps: ApproverOrgScopeDeps,\n userIds: string[],\n requestOrgId: string | null | undefined,\n context: { approverType: string; value?: string; directoryOrgId?: string | null },\n): Promise<string[]> {\n const requestOrg = requestOrgId ? String(requestOrgId) : '';\n if (!requestOrg || userIds.length === 0) return userIds;\n\n let members: any[] = [];\n try {\n members = await deps.engine.find('sys_member', {\n where: { organization_id: requestOrg, user_id: { $in: userIds } },\n fields: ['user_id'],\n limit: 10000,\n context: SYSTEM_CTX,\n } as any);\n } catch {\n // Membership unreadable — do NOT drop everyone on an infrastructure\n // hiccup. Routing to someone who may not see the request is recoverable\n // (an admin can grant the membership); silently emptying a live approval\n // slate is not.\n return userIds;\n }\n\n const canRead = new Set(\n (members ?? []).map((m: any) => String(m?.user_id ?? '')).filter(Boolean),\n );\n const dropped = userIds.filter((u) => !canRead.has(u));\n if (dropped.length === 0) return userIds;\n\n deps.logger?.warn?.(\n `[approvals] ADR-0105 D9: ${dropped.length} cross-organization approver(s) dropped — `\n + `they hold '${context.value ?? context.approverType}' in organization `\n + `'${context.directoryOrgId}' but no membership in the request's organization `\n + `'${requestOrg}', so the D2 union wall would hide the request from them. `\n + `Grant those users a membership in the request's organization, or retarget the approver.`,\n { dropped, requestOrganizationId: requestOrg, directoryOrganizationId: context.directoryOrgId },\n );\n return userIds.filter((u) => canRead.has(u));\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_approval_token — single-use actionable-link tokens (ADR-0043).\n *\n * One row per issued approve/reject link. Only the SHA-256 **hash** of the\n * raw token is stored — a database leak yields no usable links. A token is\n * dead once any of these holds: `consumed_at` set, `expires_at` passed, the\n * request left `pending`, or the bound approver no longer holds a slot\n * (the last two are re-checked at redemption, not materialized here).\n *\n * @namespace sys\n */\nexport const SysApprovalToken = ObjectSchema.create({\n name: 'sys_approval_token',\n label: 'Approval Action Token',\n pluralLabel: 'Approval Action Tokens',\n icon: 'key',\n isSystem: true,\n managedBy: 'engine-owned',\n description: 'Single-use tokens behind actionable approval links',\n displayNameField: 'id',\n nameField: 'id', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)\n\n fields: {\n id: Field.text({ label: 'Token ID', required: true, readonly: true, group: 'System' }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n }),\n\n token_hash: Field.text({\n label: 'Token Hash',\n required: true,\n maxLength: 100,\n readonly: true,\n description: 'SHA-256 hex of the raw token — the raw value is never stored',\n group: 'Token',\n }),\n\n request_id: Field.text({\n label: 'Request',\n required: true,\n maxLength: 100,\n readonly: true,\n group: 'Token',\n }),\n\n action: Field.select(['approve', 'reject'], {\n label: 'Action',\n required: true,\n readonly: true,\n group: 'Token',\n }),\n\n approver_id: Field.text({\n label: 'Approver',\n required: true,\n maxLength: 200,\n readonly: true,\n description: 'Identity the token is bound to; the decision is audited as this approver',\n group: 'Token',\n }),\n\n expires_at: Field.datetime({\n label: 'Expires At',\n required: true,\n readonly: true,\n group: 'Lifecycle',\n }),\n\n consumed_at: Field.datetime({\n label: 'Consumed At',\n required: false,\n group: 'Lifecycle',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n },\n\n indexes: [\n { fields: ['token_hash'] },\n { fields: ['request_id'] },\n ],\n\n enable: {\n // [ADR-0103] Engine-owned: one-time email-approval tokens are minted and\n // consumed by the approval engine (SYSTEM_CTX), never via the data API.\n apiMethods: ['get', 'list'],\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Session-less HTML for the actionable-link confirm/result pages (ADR-0043).\n *\n * Deliberately tiny and dependency-free: these pages are reached from an\n * email or IM message by a bearer with no session, so they must not assume\n * the Console bundle, auth state, or client-side i18n. Static bilingual\n * (EN / 中文) copy keeps them readable for the demo audience without a\n * locale negotiation step.\n *\n * The GET page NEVER mutates — the decision happens only on the POST form\n * submit (mail-gateway link prefetchers must not approve requests).\n */\n\nimport type { ApprovalRequestRow, ApprovalActionKind } from '@objectstack/spec/contracts';\n\nfunction esc(s: unknown): string {\n return String(s ?? '')\n .replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>')\n .replaceAll('\"', '"').replaceAll(\"'\", ''');\n}\n\nfunction shell(title: string, body: string): string {\n return `<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta name=\"robots\" content=\"noindex\">\n<title>${esc(title)}</title>\n<style>\n body{font:15px/1.6 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n background:#f6f7f9;color:#1a202c;margin:0;display:flex;min-height:100vh;align-items:center;justify-content:center}\n .card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;max-width:440px;width:calc(100% - 32px);\n padding:28px 32px;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n h1{font-size:18px;margin:0 0 4px}\n .sub{color:#64748b;font-size:13px;margin:0 0 20px}\n .row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px solid #f1f5f9;font-size:14px}\n .row b{font-weight:600;text-align:right}\n .k{color:#64748b}\n .actions{margin-top:22px;display:flex;gap:10px}\n button{flex:1;padding:10px 16px;border-radius:8px;border:1px solid transparent;font-size:15px;font-weight:600;cursor:pointer}\n .approve{background:#059669;color:#fff}\n .reject{background:#fff;color:#dc2626;border-color:#fca5a5}\n .badge{display:inline-block;padding:2px 10px;border-radius:999px;font-size:12px;font-weight:600;margin-bottom:14px}\n .ok{background:#ecfdf5;color:#047857}.warn{background:#fffbeb;color:#b45309}.err{background:#fef2f2;color:#b91c1c}\n a{color:#2563eb;text-decoration:none}\n .foot{margin-top:18px;font-size:12px;color:#94a3b8}\n</style></head><body><div class=\"card\">${body}</div></body></html>`;\n}\n\nfunction summaryRows(req: ApprovalRequestRow): string {\n const rows: Array<[string, string]> = [\n ['Process · 流程', req.process_label || req.process_name],\n ['Step · 步骤', req.step_label || req.current_step || '—'],\n ['Record · 记录', req.record_title || req.record_id],\n ['Object · 对象', req.object_label || req.object_name],\n ['Requester · 申请人', req.submitter_name || req.submitter_id || '—'],\n ];\n return rows.map(([k, v]) => `<div class=\"row\"><span class=\"k\">${esc(k)}</span><b>${esc(v)}</b></div>`).join('');\n}\n\n/** GET page: summary + a POST form. Rendering only — no mutation. */\nexport function renderConfirmPage(input: {\n request: ApprovalRequestRow;\n action: Extract<ApprovalActionKind, 'approve' | 'reject'>;\n approverId: string;\n token: string;\n actPath: string;\n}): string {\n const approving = input.action === 'approve';\n const verb = approving ? 'Approve · 通过' : 'Reject · 拒绝';\n return shell(`${verb} — Approval`, `\n <h1>${approving ? '✅ Approve this request?' : '⛔ Reject this request?'}</h1>\n <p class=\"sub\">${approving ? '确认通过该审批请求?' : '确认拒绝该审批请求?'}\n Acting as · 操作身份:<b>${esc(input.approverId)}</b></p>\n ${summaryRows(input.request)}\n <form method=\"post\" action=\"${esc(input.actPath)}\" class=\"actions\">\n <input type=\"hidden\" name=\"token\" value=\"${esc(input.token)}\">\n <button type=\"submit\" class=\"${approving ? 'approve' : 'reject'}\">${verb}</button>\n </form>\n <p class=\"foot\">This link is single-use and expires automatically. · 此链接一次有效,过期自动失效。</p>`);\n}\n\nconst RESULT_COPY: Record<string, { cls: string; title: string; body: string }> = {\n approved: { cls: 'ok', title: '✅ Approved · 已通过', body: 'The decision was recorded. · 审批结果已记录。' },\n rejected: { cls: 'ok', title: '⛔ Rejected · 已拒绝', body: 'The decision was recorded. · 审批结果已记录。' },\n invalid: { cls: 'err', title: 'Invalid link · 链接无效', body: 'This link is not recognized. · 无法识别该链接。' },\n expired: { cls: 'warn', title: 'Link expired · 链接已过期', body: 'Ask the requester to send a new reminder. · 请让申请人重新发送催办。' },\n consumed: { cls: 'warn', title: 'Already used · 链接已使用', body: 'This link was already used once. · 该链接已被使用过。' },\n not_pending: { cls: 'warn', title: 'Already decided · 请求已处理', body: 'This request is no longer pending. · 该请求已不在待审批状态。' },\n not_approver: { cls: 'warn', title: 'No longer your approval · 已不在你名下', body: 'This approval was handed to someone else. · 该审批已转由他人处理。' },\n};\n\n/** Terminal page for every redemption outcome (and stale GETs). */\nexport function renderResultPage(kind: keyof typeof RESULT_COPY, request?: ApprovalRequestRow): string {\n const copy = RESULT_COPY[kind] ?? RESULT_COPY.invalid;\n return shell(copy.title, `\n <span class=\"badge ${copy.cls}\">${esc(copy.title)}</span>\n ${request ? summaryRows(request) : ''}\n <p>${esc(copy.body)}</p>\n <p class=\"foot\"><a href=\"/system/approvals\">Open the Approvals Inbox · 打开审批中心</a></p>`);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Lifecycle Hooks — node-era record lock (ADR-0019).\n *\n * Approval is now a flow node, so there is no per-object process registry to\n * bind auto-trigger hooks against — a flow decides *when* to open an approval.\n * What remains worth enforcing at the data layer is the **record lock**: while\n * a record has a pending `sys_approval_request`, block edits to it.\n *\n * A single global `beforeUpdate` hook handles every object (the target object\n * of an approval node is only known at flow-run time). For each update it:\n *\n * 1. Skips engine self-writes (status mirror) and `sys_approval_*` bookkeeping.\n * 2. Resolves **which records the write would touch** and looks up the pending\n * requests gating them.\n * 3. Reads the lock policy from each request's `node_config_json` snapshot:\n * - `lockRecord === false` → allow.\n * - otherwise block, EXCEPT when the only changed field is the configured\n * `approvalStatusField` (so the status mirror is never blocked) or the\n * writer is the run that opened the request (`flowRunId`, #3456 / #3712).\n *\n * ## There is no admin exemption — by design (#4839)\n *\n * The lock applies to a platform/tenant admin exactly as it applies to anyone\n * else. An admin who must release a locked record does it through the\n * **sanctioned rescue path** (#3424): `ApprovalService.recall` / `decideNode`\n * (reject) / `reassign`, all gated by `isOverrideActor` and all audited\n * (`via_override`, #4466). Finalising the request is what releases the lock —\n * the record is never edited *under* a live approval, which is precisely the\n * guarantee a compliance-minded tenant buys the lock for.\n *\n * This is also not a behaviour change. The hook used to open with\n * `if (session.roles?.includes('admin')) return`, but `roles` has no producer:\n * ObjectQL's `buildSession()` never writes it, so the branch was dead on every\n * real engine path and the lock has always applied to admins. Deleting it makes\n * the code say what the runtime does — and closes a second, forbidden admin\n * dialect: privilege in this codebase is judged by the ADR-0095 vocabulary\n * (`permissions` / `positions` / derived posture), never by a string comparison\n * against `'admin'` (ADR-0090 D3 bans the `role` spelling outright). See\n * {@link file://./approval-service.ts} `isOverrideActor` for the one predicate.\n *\n * ## Step 2 is per-row, and it covers PREDICATE writes (#4778)\n *\n * The engine extracts `input.id` only from a **scalar** `where.id`; an operator\n * object (`{ $in: [...] }`) or any other predicate is a multi-row write that\n * routes to `updateMany`, and reaches this hook with **no** `input.id`. The\n * hook used to open with `if (!id) return`, i.e. it read *\"no row was\n * resolved\"* as *\"there is nothing to authorize\"* when the truth was *\"nothing\n * was ever queried\"* — the same fail-open reasoning as #4757 (`sys_attachment`)\n * and #4630 (`sys_comment`). Rewriting the very same edit as `multi: true` then\n * bypassed the lock with **no privilege at all**: no `isSystem`, no\n * `lockRecord: false`, no whitelisted field.\n *\n * So the hook now resolves the row set the way the attachment/comment guards\n * do, with one difference the record lock forces: it is a **per-row** guard\n * (\"does THIS record have a pending approval\"), not a \"should this whole-table\n * write be refused\" guard — so an unscoped bulk update is *not* refused\n * outright. Instead the resolution is inverted, which is what keeps it cheap:\n * the bound is on the **pending requests for the object** (of which there are\n * normally none, and the hook returns after one query), never on the update's\n * match set. Only when some record of the object *is* locked does the hook ask\n * the engine which of those locked rows the caller's predicate actually\n * matches. Past {@link PENDING_LOCK_LIMIT} locked records — or if that\n * intersection query fails — the write fails **CLOSED**.\n *\n * Every exemption above is evaluated on the multi-row path exactly as on the\n * by-id path: extending a guard to more rows must move the *allow* rules with\n * the *deny* rules, or fail-open merely becomes false-positive.\n *\n * Registered under `packageId: 'plugin-approvals:lock'` so it can be cleanly\n * unbound on plugin stop.\n */\n\nexport const APPROVALS_HOOK_PACKAGE = 'plugin-approvals:lock';\n\ninterface MinimalEngine {\n registerHook(event: string, handler: (ctx: any) => any | Promise<any>, options?: {\n object?: string | string[];\n priority?: number;\n packageId?: string;\n }): void;\n unregisterHooksByPackage(packageId: string): number;\n find<T = any>(object: string, args: any, opts?: any): Promise<T[]>;\n}\n\ninterface MinimalLogger {\n debug?: (msg: any, ...rest: any[]) => void;\n info?: (msg: any, ...rest: any[]) => void;\n warn?: (msg: any, ...rest: any[]) => void;\n error?: (msg: any, ...rest: any[]) => void;\n}\n\nfunction parseJson<T = any>(raw: unknown, fallback: T): T {\n if (raw == null || raw === '') return fallback;\n if (typeof raw === 'string') {\n try { return JSON.parse(raw) as T; } catch { return fallback; }\n }\n return raw as T;\n}\n\n/**\n * Bound on the records one write may be authorized against — the sibling of\n * `sys_attachment`'s `MULTI_DELETE_AUTH_LIMIT` and `sys_comment`'s\n * `MULTI_WRITE_AUTH_LIMIT` (#4757 / #4630).\n *\n * It bounds LOCKED records (pending requests on the object) and ids the caller\n * names, never the update's match set: a mass update of 50 000 unlocked rows\n * costs one query and is allowed, while an object carrying more pending\n * approvals than this cannot be decided row by row and fails CLOSED.\n */\nconst PENDING_LOCK_LIMIT = 1_000;\n\n/**\n * The approvals bookkeeping — and the row set a predicate write would touch —\n * are read as SYSTEM. A guard's own input must never be narrowed by the\n * caller's visibility: a locked row the caller cannot READ is still a locked\n * row they must not WRITE (the #4630 rule).\n */\nconst SYSTEM_CTX = { isSystem: true, positions: [], permissions: [] } as const;\n\nfunction lockedError(message: string): never {\n const err: any = new Error(`RECORD_LOCKED: ${message}`);\n err.code = 'RECORD_LOCKED';\n err.statusCode = 409;\n throw err;\n}\n\n/**\n * The record ids a write names outright — a scalar id or an `{ $in: [...] }` —\n * or `null` when the ids cannot be read off it (any other predicate, or an\n * `$in` carrying a non-scalar, which we refuse to guess at).\n *\n * Mirrors `asIdList` in the attachment/comment kits. An empty `$in` legitimately\n * names *no* record, so it returns `[]` (nothing to gate), not `null`.\n */\nfunction asIdList(id: unknown): Array<string | number> | null {\n if (typeof id === 'number') return [id];\n if (typeof id === 'string') return id === '' ? null : [id];\n if (id && typeof id === 'object' && Array.isArray((id as any).$in)) {\n const raw = (id as any).$in as unknown[];\n const scalars = raw.filter((v): v is string | number => typeof v === 'string' || typeof v === 'number');\n return scalars.length === raw.length ? scalars : null;\n }\n return null;\n}\n\n/** The pending request gating a record, plus its snapshotted node config. */\nasync function pendingRequestFor(\n engine: MinimalEngine,\n objectName: string,\n recordId: string,\n): Promise<any | null> {\n try {\n const rows = await engine.find('sys_approval_request', {\n where: { object_name: objectName, record_id: String(recordId), status: 'pending' },\n limit: 1,\n context: { ...SYSTEM_CTX },\n } as any);\n return Array.isArray(rows) && rows[0] ? rows[0] : null;\n } catch {\n return null;\n }\n}\n\n/** Pending requests gating any of the records a write names by id. */\nasync function pendingRequestsForRecords(\n engine: MinimalEngine,\n objectName: string,\n recordIds: ReadonlyArray<string | number>,\n): Promise<any[]> {\n if (recordIds.length === 0) return [];\n if (recordIds.length > PENDING_LOCK_LIMIT) {\n lockedError(\n `refusing to authorize an update naming more than ${PENDING_LOCK_LIMIT} records of '${objectName}' — ` +\n 'the approval lock cannot check them row by row; scope the write',\n );\n }\n if (recordIds.length === 1) {\n const one = await pendingRequestFor(engine, objectName, String(recordIds[0]));\n return one ? [one] : [];\n }\n try {\n const rows = await engine.find('sys_approval_request', {\n where: { object_name: objectName, record_id: { $in: recordIds.map(String) }, status: 'pending' },\n limit: PENDING_LOCK_LIMIT + 1,\n context: { ...SYSTEM_CTX },\n } as any);\n return Array.isArray(rows) ? rows : [];\n } catch {\n return [];\n }\n}\n\n/**\n * Every record of `objectName` currently held by a pending approval, capped one\n * past the bound so the caller can tell \"at the cap\" from \"over it\".\n *\n * `null` means the bookkeeping could not be read at all. That reads as \"no\n * lock\" — deliberately the ONE fail-open left, and the pre-existing behaviour\n * of {@link pendingRequestFor}: this hook is GLOBAL over every object, so a\n * kernel where `sys_approval_request` is absent or momentarily unreadable would\n * otherwise refuse every update in the deployment. The fail-closed decisions\n * below are the ones an attacker can actually steer (a predicate they choose);\n * \"is the approvals table readable\" is not one of them.\n */\nasync function pendingRequestsForObject(\n engine: MinimalEngine,\n objectName: string,\n): Promise<any[] | null> {\n try {\n const rows = await engine.find('sys_approval_request', {\n where: { object_name: objectName, status: 'pending' },\n limit: PENDING_LOCK_LIMIT + 1,\n context: { ...SYSTEM_CTX },\n } as any);\n return Array.isArray(rows) ? rows : [];\n } catch {\n return null;\n }\n}\n\n/**\n * Narrow `candidates` (pending requests) to the ones whose record the caller's\n * predicate actually matches — asked of the engine as an intersection, so the\n * query is bounded by the number of LOCKED rows, not by the update's match set.\n *\n * A failure here fails CLOSED: we know some record of this object is locked and\n * we could not prove the write misses it.\n */\nasync function narrowToMatchedRecords(\n engine: MinimalEngine,\n objectName: string,\n where: unknown,\n candidates: any[],\n): Promise<any[]> {\n const lockedIds = candidates.map((c) => String(c?.record_id ?? ''));\n let rows: any[];\n try {\n rows = await engine.find(objectName, {\n where: { $and: [where, { id: { $in: lockedIds } }] },\n fields: ['id'],\n limit: lockedIds.length,\n context: { ...SYSTEM_CTX },\n } as any);\n } catch (err) {\n lockedError(\n `cannot determine which rows a predicate update on '${objectName}' would touch ` +\n `(${(err as Error)?.message ?? String(err)}); ${candidates.length} record(s) of it carry a pending ` +\n 'approval, so the write is refused',\n );\n }\n const matched = new Set((Array.isArray(rows) ? rows : []).map((r: any) => String(r?.id)));\n return candidates.filter((c) => matched.has(String(c?.record_id ?? '')));\n}\n\n/**\n * The pending requests gating the rows THIS write would touch.\n *\n * Four shapes, cheapest first:\n * - `input.id` (engine's scalar by-id fast path) → that record only. The\n * driver updates by primary key, so the rest of `where` never narrows it —\n * narrowing here would be a fail-open.\n * - predicate naming ids only (`{ id: { $in: [...] } }`) → those records.\n * - predicate with other keys → the object's locked records, intersected with\n * the predicate.\n * - no predicate at all (`updateMany` over the whole table) → every locked\n * record of the object.\n */\nasync function gatingRequests(\n engine: MinimalEngine,\n ctx: any,\n objectName: string,\n): Promise<any[]> {\n const byId = asIdList(ctx?.input?.id);\n if (byId) return pendingRequestsForRecords(engine, objectName, byId);\n\n // Predicate write. `where` is canonical here: the engine folds the `filter`\n // alias into it before hooks run (PD #12 — no consumer-side `?? filter`).\n const rawWhere = (ctx?.input?.options as any)?.where;\n const hasWhere = rawWhere !== undefined && rawWhere !== null;\n const whereObj = hasWhere && typeof rawWhere === 'object' && !Array.isArray(rawWhere)\n ? rawWhere as Record<string, unknown>\n : null;\n\n const namedIds = whereObj ? asIdList(whereObj.id) : null;\n const candidates = namedIds\n ? await pendingRequestsForRecords(engine, objectName, namedIds)\n : await pendingRequestsForObject(engine, objectName);\n if (candidates === null) return []; // bookkeeping unreadable — see the note above\n if (candidates.length === 0) return []; // nothing locked in reach\n if (candidates.length > PENDING_LOCK_LIMIT) {\n lockedError(\n `refusing a predicate update on '${objectName}': more than ${PENDING_LOCK_LIMIT} of its records carry a ` +\n 'pending approval, so the lock cannot decide row by row; scope the write to the rows you mean',\n );\n }\n // No predicate → the write touches every row, so every locked row is in reach.\n if (!hasWhere) return candidates;\n // The predicate was exactly the id list we already resolved against.\n if (namedIds && whereObj && Object.keys(whereObj).every((k) => k === 'id')) return candidates;\n return narrowToMatchedRecords(engine, objectName, rawWhere, candidates);\n}\n\n/**\n * Bind the global record-lock hook. Caller is responsible for calling\n * {@link unbindAllHooks} first if re-binding.\n */\nexport function bindApprovalLockHook(engine: MinimalEngine, logger?: MinimalLogger): void {\n engine.registerHook('beforeUpdate', async (ctx: any) => {\n const object = (ctx?.object ?? ctx?.objectName) as string | undefined;\n // No object name (shouldn't happen) or our own bookkeeping objects → skip.\n if (!object || String(object).startsWith('sys_approval')) return;\n\n const data = (ctx?.input?.data ?? {}) as Record<string, unknown>;\n const changedFields = Object.keys(data).filter((k) => k !== 'id' && k !== 'updated_at');\n if (changedFields.length === 0) return;\n\n // ── Caller-level exemptions. Row-independent, so they are decided once,\n // before any row is resolved — and they hold identically for a by-id and a\n // predicate write (#4778: an exemption that only survives on one path turns\n // a fail-open into a false-positive).\n\n // Allow engine self-writes (status mirror from the approvals service, etc).\n if ((ctx?.session as any)?.isSystem) return;\n\n // NOTE (#4839): there is deliberately NO admin exemption here. A privileged\n // admin releases a locked record through the audited #3424 rescue path\n // (recall / reject / reassign, gated by `isOverrideActor`), not by editing\n // the record while its approval is live. See the module docstring.\n\n // ── Which rows does this write touch, and which of them are locked?\n const gating = await gatingRequests(engine, ctx, object);\n if (gating.length === 0) return;\n\n // The run that OPENED this approval may still write its own target record\n // (#3456). Without this the lock cannot tell \"the run that owns this pending\n // request\" from \"an unrelated user edit\", so a flow that touches the record\n // between opening the approval and the decision — or a manual `resume` with\n // no decision — dies on its own `RECORD_LOCKED` and leaves the record locked\n // behind it.\n //\n // Keyed on run identity, NOT on elevation: a `runAs:'user'` run must stay\n // RLS-scoped, so widening it to `isSystem` would be the wrong tool. The\n // automation engine stamps `flowRunId` into the server-built ExecutionContext\n // (never client-supplied, like `isSystem`) and it grants nothing by itself —\n // the only write it permits is to the one record this very run already holds\n // a pending request against.\n //\n // Read off `provenance`, not `session`: provenance says WHAT produced the\n // write, and a run can own its writes while resolving no principal at all.\n // A schedule-triggered run is exactly that — it reaches here with NO\n // session, and that shape is the one that used to die on its own lock\n // (#3712).\n const writerRun = (ctx?.provenance as any)?.flowRunId;\n\n // Per-row verdict: the write is refused as soon as ONE touched record is\n // locked against it. Each request carries its own snapshotted policy, so a\n // multi-row write spanning two approvals is judged by each of them.\n for (const pending of gating) {\n if (writerRun && pending?.flow_run_id && String(writerRun) === String(pending.flow_run_id)) continue;\n\n const config = parseJson<any>(pending?.node_config_json, {});\n if (config?.lockRecord === false) continue;\n\n // Allow when every changed field is the approval status mirror.\n const mirror = config?.approvalStatusField;\n if (typeof mirror === 'string' && mirror && changedFields.every((f) => f === mirror)) continue;\n\n lockedError(\n `record '${String(pending?.record_id ?? '')}' of '${object}' is locked while an approval is in progress`,\n );\n }\n }, { packageId: APPROVALS_HOOK_PACKAGE, priority: 50 });\n\n logger?.info?.('[approvals] record-lock hook bound');\n}\n\n/** The self-service out-of-office delegation object (#1322). */\nexport const DELEGATION_OBJECT = 'sys_approval_delegation';\n\n/**\n * Self-service write guard for `sys_approval_delegation` (#1322 follow-up).\n *\n * The object is `apiEnabled` CRUD so a user can declare their own out-of-office\n * delegation. But it is a system object: it gets no auto `owner_id` anchor and\n * (with no `sharingModel`) defaults to a `public` sharing model, so an\n * unguarded member could **forge a delegation for someone else**\n * (`delegator_id = victim`) and reroute the victim's individually-routed\n * approvals to themselves. This guard forces a normal user's writes to name\n * themselves as the delegator:\n *\n * - **system** context (service / seed / import) → bypass;\n * - otherwise `delegator_id` must equal the acting user — an absent delegator\n * on insert is stamped to the caller, a foreign delegator is rejected.\n *\n * ## Delegation is SELF-MANAGED — there is no admin exemption (#4839)\n *\n * The guard used to bypass for `session.roles?.includes('admin')`, so an admin\n * could declare an out-of-office delegation *on behalf of* another user. That\n * branch never ran (`roles` has no producer — ObjectQL's `buildSession()` never\n * writes it), and it is not being restored, for two reasons:\n *\n * 1. **It could not do the job it was written for.** A delegation is consulted\n * at *slate-resolution* time only: `applyOooDelegation` runs inside\n * `resolveApproverSpec` while a request is being OPENED. Declaring a\n * delegation for an approver who has *already* gone unavailable does not\n * touch the approvals pending against them — it only redirects future ones.\n * 2. **The in-flight case already has a sanctioned path.** For the approvals\n * an unavailable approver is *currently* holding, a privileged admin uses\n * `ApprovalService.reassign` (hand that approver's slot to a substitute —\n * it even carries the slot's per_group membership over), `recall`, or\n * `decideNode` with `reject`; all three are gated by `isOverrideActor`'s\n * ADR-0095 criteria and audited via `via_override` (#4466). That is the\n * operation an admin actually needs, and it exists.\n *\n * So the semantic is final: **a delegation names its own author as delegator**;\n * acting for someone else is done through the approval-level override, in the\n * one privilege vocabulary this codebase has (ADR-0090 D3 / ADR-0095 D3) — never\n * by comparing a session field against the string `'admin'`.\n *\n * Row-level ownership on update/delete (you can only touch a delegation you\n * created) is already enforced by `member_default`'s wildcard\n * `created_by == current_user.id` RLS; this guard adds the delegator-identity\n * check that RLS alone can't express. Mirrors the ADR-0092 identity write-guard\n * shape and the security plugin's `owner_id` anchor guard, scoped to this one\n * object.\n */\nexport function bindDelegationWriteGuard(engine: MinimalEngine, logger?: MinimalLogger): void {\n const makeGuard = (isInsert: boolean) => async (ctx: any) => {\n const session = (ctx?.session ?? {}) as any;\n if (session.isSystem) return; // service / seed / import\n // NOTE (#4839): no admin exemption — a delegation names its own author as\n // delegator. Acting on another user's in-flight approvals is the approval\n // service's audited override (reassign / recall / reject), not a forged\n // delegation row. See the docstring above.\n const userId = session.userId != null ? String(session.userId) : '';\n const data = ctx?.input?.data;\n const rows = Array.isArray(data) ? data : (data && typeof data === 'object' ? [data] : []);\n const deny = (): never => {\n const err: any = new Error(\n 'FORBIDDEN: you may only manage out-of-office delegations where you are the delegator'\n + (userId ? ` ('${userId}')` : ''),\n );\n err.code = 'FORBIDDEN';\n err.statusCode = 403;\n throw err;\n };\n for (const row of rows) {\n if (!row || typeof row !== 'object' || Array.isArray(row)) continue;\n const has = Object.prototype.hasOwnProperty.call(row, 'delegator_id');\n const supplied = has ? String((row as any).delegator_id ?? '') : '';\n if (isInsert && (!has || supplied === '')) {\n // Self-service: stamp the caller as delegator when omitted (the schema's\n // `required` is the fallback if the engine doesn't persist the stamp).\n if (!userId) deny();\n (row as any).delegator_id = userId;\n continue;\n }\n // A foreign delegator on insert (forge) or update (relabel/hijack) → deny.\n if (has && supplied !== userId) deny();\n }\n };\n engine.registerHook('beforeInsert', makeGuard(true), { object: DELEGATION_OBJECT, packageId: APPROVALS_HOOK_PACKAGE, priority: 50 });\n engine.registerHook('beforeUpdate', makeGuard(false), { object: DELEGATION_OBJECT, packageId: APPROVALS_HOOK_PACKAGE, priority: 50 });\n logger?.info?.('[approvals] delegation write-guard bound');\n}\n\n/** Unregister every hook the lock module registered. */\nexport function unbindAllHooks(engine: MinimalEngine): number {\n return engine.unregisterHooksByPackage(APPROVALS_HOOK_PACKAGE);\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Approval-as-flow-node provider (ADR-0019).\n *\n * Registers an `approval` node executor on the automation engine so an approval\n * rides the one flow engine as a durable-pause node:\n *\n * 1. On entry the node opens a `sys_approval_request` (reusing the mature\n * approver-resolution / audit / lock / status-mirror machinery) and returns\n * `{ suspend: true }` — the engine persists the run and stops traversal.\n * 2. A decision (`ApprovalService.decide`) finalizes the request and resumes\n * the run down the matching `approve` / `reject` out-edge.\n *\n * The approval *state* (request/action rows) stays first-class and owned by this\n * plugin — a flow-run log can't drive an inbox / recall / audit. Only the\n * orchestration (when to pause, which branch to take) moves onto the engine.\n */\n\nimport {\n defineActionDescriptor,\n ApprovalNodeConfigSchema,\n getApprovalNodeConfigJsonSchema,\n APPROVAL_NODE_TYPE,\n type ApprovalNodeConfig,\n} from '@objectstack/spec/automation';\n// [#7135] The full `resolveAuthzContext` envelope — what\n// `IApprovalService.openNodeRequest` declares for its context parameter since\n// #6523 (the #6206 ruling: no per-site subset contracts).\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\nimport type { ApprovalService } from './approval-service.js';\nimport { registerApprovalReviseNode } from './approval-revise-node.js';\n\n/** Minimal surface of the automation engine this provider depends on. */\nexport interface ApprovalAutomationSurface {\n registerNodeExecutor(executor: {\n type: string;\n descriptor?: unknown;\n execute(node: any, variables: Map<string, unknown>, context: any): Promise<{\n success: boolean;\n output?: Record<string, unknown>;\n error?: string;\n suspend?: boolean;\n correlation?: string;\n /**\n * #3447 P2: walk this labelled out-edge on normal (non-suspend)\n * completion — how an `onEmptyApprovers: 'auto_approve'` node continues\n * down `approve` without a decision. Mirrors NodeExecutionResult.\n */\n branchLabel?: string;\n }>;\n }): void;\n resume?(runId: string, signal?: { output?: Record<string, unknown>; branchLabel?: string }): Promise<unknown>;\n}\n\ninterface MinimalLogger {\n info?: (msg: any, ...rest: any[]) => void;\n warn?: (msg: any, ...rest: any[]) => void;\n}\n\nconst SYSTEM_CTX: ExecutionContext = { isSystem: true, positions: [], permissions: [] };\n\n/**\n * Rebuild the nested object the engine's CEL conditions see from the flow's\n * flat variable Map — dotted keys (`get_rec.record`) become nested paths, so an\n * `expression` approver's `vars.get_rec.record.owner_id` reads exactly like a\n * condition's. Mirrors the engine's own evaluateCondition rebuild; kept local\n * because this plugin deliberately does not depend on service-automation.\n */\nfunction nestVariables(variables: Map<string, unknown>): Record<string, unknown> {\n const vars: Record<string, unknown> = {};\n for (const [key, value] of variables) {\n const segs = key.split('.');\n let cursor = vars;\n for (let i = 0; i < segs.length - 1; i++) {\n if (typeof cursor[segs[i]] !== 'object' || cursor[segs[i]] === null) {\n cursor[segs[i]] = {};\n }\n cursor = cursor[segs[i]] as Record<string, unknown>;\n }\n cursor[segs[segs.length - 1]] = value;\n }\n return vars;\n}\n\n/**\n * Register the `approval` node executor on the automation engine, plus the\n * `approval_revise` window the ADR-0044 send-back parks on. Idempotent at the\n * engine level (re-registering replaces). Safe to skip when no automation\n * service is present.\n *\n * The two register together deliberately (#3823): send-back resumes the run\n * down the `revise` edge onto the revise-window node, and `sendBack` refuses a\n * flow whose revise edge targets anything else — so an engine holding the\n * `approval` node without the revise window would accept approvals whose\n * send-back can never run.\n */\nexport function registerApprovalNode(\n automation: ApprovalAutomationSurface,\n service: ApprovalService,\n logger?: MinimalLogger,\n): void {\n registerApprovalReviseNode(automation, logger);\n automation.registerNodeExecutor({\n type: APPROVAL_NODE_TYPE,\n descriptor: defineActionDescriptor({\n type: APPROVAL_NODE_TYPE,\n version: '1.0.0',\n name: 'Approval',\n description: 'Route a record for human approval; suspends the flow until a decision, '\n + 'then continues down the approve / reject branch.',\n icon: 'check-circle',\n category: 'human',\n paradigms: ['flow'],\n source: 'plugin',\n // Human decision: the run suspends here awaiting an external reply.\n supportsPause: true, // (`isAsync` stood here — retired in #6748, ADR-0049: nothing read it)\n // #3801: this pause is NOT resumable through the generic run-resume\n // route. Continuing an approval is a side effect of a DECISION, and the\n // decision is the thing that must be authorized (the approver slate),\n // recorded (`sys_approval_action`) and mirrored (the status field) —\n // all of which lives in `ApprovalService.decide`. The engine now refuses\n // any resume of an approval suspension that does not carry the service's\n // in-process marker, so \"decide via the approvals API, never a raw engine\n // resume\" is enforced rather than merely documented.\n resumeAuthority: 'service',\n // Publish the node's config contract (ADR-0018 §configSchema) so the\n // Studio flow designer renders the Approval property form from the engine\n // rather than a hardcoded client form — the engine owns the shape.\n configSchema: getApprovalNodeConfigJsonSchema(),\n }),\n async execute(node, variables, context) {\n const parsed = ApprovalNodeConfigSchema.safeParse(node.config ?? {});\n if (!parsed.success) {\n const msg = parsed.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; ');\n return { success: false, error: `Approval node '${node.id}' has invalid config: ${msg}` };\n }\n const config = parsed.data as ApprovalNodeConfig;\n\n const runId = variables.get('$runId');\n const record = (variables.get('$record') ?? context?.record ?? {}) as Record<string, unknown>;\n const object = (context?.object ?? (record as any)?.object_name) as string | undefined;\n const recordId = (record as any)?.id as string | undefined;\n\n if (!runId) return { success: false, error: `Approval node '${node.id}': missing $runId` };\n if (!object) return { success: false, error: `Approval node '${node.id}': no target object in context` };\n if (!recordId) return { success: false, error: `Approval node '${node.id}': no record id in $record` };\n\n // Flow identity comes from engine-seeded variables (`$flowName` /\n // `$flowLabel`) so the request row can carry a human-readable origin;\n // `context.flowName` is a legacy fallback for direct callers.\n const flowName = (variables.get('$flowName') as string | undefined) ?? context?.flowName;\n const flowLabel = variables.get('$flowLabel') as string | undefined;\n\n try {\n const request = await service.openNodeRequest({\n object,\n recordId: String(recordId),\n runId: String(runId),\n nodeId: node.id,\n config,\n flowName,\n flowLabel,\n nodeLabel: typeof node.label === 'string' ? node.label : undefined,\n submitterId: context?.userId ?? null,\n record,\n organizationId: context?.organizationId ?? context?.tenantId ?? null,\n // #3447 P2: flow variables (nested, as CEL conditions see them) — the\n // `vars.*` root for `expression` approvers; `record` above doubles as\n // their `trigger.*` snapshot root.\n variables: nestVariables(variables),\n }, {\n ...SYSTEM_CTX,\n userId: context?.userId,\n // [#7135] ⚠️ This assertion SURVIVES the annotation widening, and it\n // is `as ExecutionContext` rather than the `as unknown as\n // SharingExecutionContext` double cast it replaces. The sole reason\n // a cast is still needed is `organizationId`, which is not a field\n // of the envelope at ALL — that spelling has its own history (#5858\n // / `check:org-identifier`) and was explicitly held out of this\n // change (#7070), so removing the key here would be a RUNTIME change\n // belonging to that card. Dropping the second hop matters: `as\n // unknown as` erases the value entirely, while a single assertion\n // still requires the literal to be comparable to the envelope, so a\n // `userId: 42` here is once again a compile error.\n organizationId: context?.organizationId,\n tenantId: context?.tenantId,\n } as ExecutionContext);\n\n // #3447 P2: empty slate + onEmptyApprovers: 'auto_approve' — nobody to\n // ask, no request row. Complete (don't suspend) straight down the\n // `approve` edge; `autoApproved` on the output keeps the waved-through\n // hop distinguishable from a human decision in the run trace.\n if ('autoApproved' in request) {\n logger?.info?.('[approvals] approval node auto-approved (empty approver slate)', {\n node: node.id, run: String(runId),\n });\n return {\n success: true,\n branchLabel: 'approve',\n output: { decision: 'approve', autoApproved: true },\n };\n }\n\n logger?.info?.('[approvals] approval node suspended run', {\n node: node.id, request: request.id, run: String(runId),\n });\n // Suspend the run; the request id is the correlation key surfaced on\n // the suspended-run record for lookup.\n return { success: true, suspend: true, correlation: request.id };\n } catch (err: any) {\n return { success: false, error: `Approval node '${node.id}': ${err?.message ?? String(err)}` };\n }\n },\n });\n\n logger?.info?.('[approvals] approval node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The ADR-0044 **revise window** as its own flow node (amended ADR-0044, #3823).\n *\n * A send-back resumes the run down the approval node's `revise` edge; the run\n * parks on the node that edge targets while the record is unlocked and the\n * submitter reworks it, and `ApprovalService.resubmit` drives it back into the\n * approval node over the declared back-edge (round N+1).\n *\n * That pause is **service-owned**: `resubmit` is the thing that authorizes\n * (submitter-only), orders (latest request for the run) and records (a\n * `resubmit` audit row) the continuation, and refuses up front when another\n * request is pending on the record — the guard that keeps the run alive,\n * because the approval node's re-entry would otherwise fail *after* the engine\n * consumed the suspension and leave the run terminally dead.\n *\n * ADR-0044 D3 originally parked it on an ordinary `wait` node, which is\n * `resumeAuthority: 'any'` — correctly so for a signal wait, and exactly wrong\n * here: a raw `POST /automation/:name/runs/:runId/resume` (empty body suffices)\n * walked the back-edge around every one of those checks. The #3801 resume gate\n * keys on the node type that produced the suspension, so the fix is a node type\n * that declares itself service-owned. Nothing about the gate changes.\n *\n * Why a dedicated type rather than re-suspending inside the approval node: the\n * revise window stays *visible flow state* — a box on the canvas, a step in the\n * run log, a row in the suspended-run store — which is the property ADR-0044\n * chose the generic `wait` for in the first place. Only the reuse was wrong.\n */\n\nimport {\n defineActionDescriptor,\n APPROVAL_REVISE_NODE_TYPE,\n} from '@objectstack/spec/automation';\nimport type { ApprovalAutomationSurface } from './approval-node.js';\n\ninterface MinimalLogger {\n info?: (msg: any, ...rest: any[]) => void;\n warn?: (msg: any, ...rest: any[]) => void;\n}\n\n/**\n * Correlation prefix for a revise-window suspension — what the suspended-run\n * record carries so an operator (or `listSuspendedRuns`) can tell a revise\n * window from a timer or signal wait at a glance.\n *\n * Deliberately NOT one of the engine's linked-run prefixes (`subflow:` /\n * `map:`): those mean \"this run is waiting on another run\", and the resume gate\n * follows them to judge the child's node. A revise window waits on a person.\n */\nexport const APPROVAL_REVISE_CORRELATION_PREFIX = 'approval_revise:';\n\n/**\n * Register the `approval_revise` node executor. Called from\n * {@link registerApprovalNode} rather than wired separately on purpose: a\n * deployment that has the `approval` node must have this one too, or every\n * send-back in it is refused at runtime — there is no half of this feature\n * worth installing.\n */\nexport function registerApprovalReviseNode(\n automation: ApprovalAutomationSurface,\n logger?: MinimalLogger,\n): void {\n automation.registerNodeExecutor({\n type: APPROVAL_REVISE_NODE_TYPE,\n descriptor: defineActionDescriptor({\n type: APPROVAL_REVISE_NODE_TYPE,\n version: '1.0.0',\n name: 'Revise Window',\n description:\n 'Durable pause an approval send-back parks the run on while the submitter reworks the '\n + 'record. Continues only through the approvals service (resubmit), which re-enters the '\n + 'approval node over the declared back-edge.',\n icon: 'pencil',\n // Waits on a human — the same category as `approval` and `screen`.\n category: 'human',\n paradigms: ['flow'],\n source: 'plugin',\n supportsPause: true, // (`isAsync` stood here — retired in #6748, ADR-0049: nothing read it)\n // #3823 / amended ADR-0044: THE point of this node type. The revise\n // window is a service-owned continuation, so the #3801 gate must refuse\n // a raw resume of it — which it does for any node type declaring this.\n resumeAuthority: 'service',\n // No config: the window is pure position in the graph. Left schemaless\n // (like `wait` / `subflow` / `decision`) rather than declaring an empty\n // object, so nothing invents an authorable surface that has no reader.\n }),\n async execute(node) {\n // Arms nothing on entry — no timer, no job, no external subscription — so\n // there is deliberately no `onSuspensionReleased` hook to pair with it\n // (contrast the wait node's timer one-shot, #5529). The only thing that\n // ends this pause is `ApprovalService.resubmit` (or a `recall`, which\n // cancels the run).\n return {\n success: true,\n suspend: true,\n correlation: `${APPROVAL_REVISE_CORRELATION_PREFIX}${node.id}`,\n };\n },\n });\n\n logger?.info?.('[approvals] approval revise-window node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type {\n IHttpServer,\n II18nService,\n IJobService,\n IObjectQLEngine,\n} from '@objectstack/spec/contracts';\nimport { SysApprovalRequest } from './sys-approval-request.object.js';\nimport { SysApprovalAction } from './sys-approval-action.object.js';\nimport { SysApprovalApprover } from './sys-approval-approver.object.js';\nimport { SysApprovalToken } from './sys-approval-token.object.js';\nimport { SysApprovalDelegation } from './sys-approval-delegation.object.js';\nimport { renderConfirmPage, renderResultPage } from './action-link-pages.js';\nimport {\n ApprovalService,\n ESCALATION_JOB_NAME,\n ESCALATION_SCAN_INTERVAL_MS,\n type ApprovalEngine,\n type ApprovalMessagingSurface,\n} from './approval-service.js';\nimport { bindApprovalLockHook, bindDelegationWriteGuard, unbindAllHooks } from './lifecycle-hooks.js';\nimport { registerApprovalNode, type ApprovalAutomationSurface } from './approval-node.js';\n\nexport interface ApprovalsPluginOptions {\n /** Disable runtime registration (schemas still register). */\n disableService?: boolean;\n /**\n * Interval between SLA escalation scans (ADR-0042). Defaults to\n * {@link ESCALATION_SCAN_INTERVAL_MS} (5 min). Only takes effect when a\n * `job` service is installed; without one, SLA stays display-only.\n */\n escalationScanIntervalMs?: number;\n /**\n * Absolute origin for actionable links in outbound notifications\n * (ADR-0043), e.g. `https://app.example.com`. Relative by default.\n */\n publicBaseUrl?: string;\n /**\n * Disable the record-lock hook. Schema + service stay intact; only the\n * engine-level lock wiring is suppressed. Useful when a caller wants the\n * manual API only (e.g. tests).\n */\n disableAutoHooks?: boolean;\n /**\n * [#8652] Objects on which a user who can READ the target business record may\n * also see that record's approval requests and full action history —\n * **read-only**. No approval action is delivered through this tier: approve,\n * reject, reassign, recall and comment keep authorizing exactly as they do\n * now, on the pending-approver slate, the submitter, or admin override.\n *\n * ```ts\n * new ApprovalsServicePlugin({ recordReaderVisibleObjects: ['exam_sheet'] })\n * ```\n *\n * **Default OFF.** Omitted (or empty) leaves visibility precisely as it is\n * today — submitter ∪ current approver ∪ historical actor, plus the admin\n * override — so an existing deployment's confidentiality posture is unchanged\n * on upgrade. Opting in is per object and deliberate: enabling it for a\n * ledger object does not enable it anywhere else.\n *\n * **What an enabled object exposes**, so the opt-in is informed: the request\n * row (including its `payload` snapshot of the record at submission time) and\n * the full action history — actor, decision, timestamp, the action's COMMENT\n * text, and any decision attachments. Enable it on objects whose approval\n * commentary the record's readers are meant to see.\n *\n * Anchored on the existing record-read permission: the platform asks the\n * engine to read the record as the caller, so object CRUD and RLS decide.\n * There is no new permission, role or grant type, and no host-side hook.\n */\n recordReaderVisibleObjects?: string[];\n}\n\n/**\n * ApprovalsServicePlugin — registers sys_approval_{request,action}, the\n * `approvals` service, the `approval` flow node executor (ADR-0019), and the\n * record-lock hook.\n *\n * ADR-0019: approval is no longer a standalone process engine. A flow's\n * Approval node opens a request and suspends the run; a decision via the\n * service resumes it down the matching branch.\n */\nexport class ApprovalsServicePlugin implements Plugin {\n name = 'com.objectstack.service.approvals';\n version = '1.0.0';\n type = 'standard';\n dependencies = ['com.objectstack.engine.objectql'];\n\n private readonly options: ApprovalsPluginOptions;\n private service?: ApprovalService;\n private engine?: IObjectQLEngine;\n private escalationJobScheduled = false;\n\n constructor(options: ApprovalsPluginOptions = {}) {\n this.options = options;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n ctx.getService<{ register(m: any): void }>('manifest').register({\n id: 'com.objectstack.service.approvals',\n name: 'Approvals Service',\n version: '1.0.0',\n type: 'plugin',\n scope: 'system',\n defaultDatasource: 'cloud',\n namespace: 'sys',\n objects: [SysApprovalRequest, SysApprovalAction, SysApprovalApprover, SysApprovalToken, SysApprovalDelegation],\n // ADR-0029 D7 — contribute the Approvals entries into the Setup app's\n // `group_approvals` slot. This plugin owns these objects (K2.b), so it\n // ships their menu too; when the plugin isn't installed the slot is empty.\n //\n // #7234 — order inside the slot is ARRAY ORDER: `applyNavContributions`\n // does `group.children.push(...c.items)` per contribution (sorted by\n // `priority`), so the inbox being first in this array is what puts it\n // above the raw tables. Do not reorder without meaning to.\n //\n // The inbox entry is the working surface (decision actions, node\n // progress, drawer); the three object entries below stay as the\n // admin/diagnostic view of the engine's own tables — reachable only here,\n // behind `group_approvals`' `manage_platform_settings` gate.\n navigationContributions: [\n {\n app: 'setup',\n group: 'group_approvals',\n priority: 100,\n items: [\n { id: 'nav_approvals_inbox', type: 'component', label: 'Approvals Inbox', componentRef: 'approvals:inbox', icon: 'list-checks' },\n { id: 'nav_approval_requests', type: 'object', label: 'Requests', objectName: 'sys_approval_request', icon: 'inbox', requiresObject: 'sys_approval_request' },\n { id: 'nav_approval_actions', type: 'object', label: 'Action History', objectName: 'sys_approval_action', icon: 'history', requiresObject: 'sys_approval_action' },\n { id: 'nav_approval_delegations', type: 'object', label: 'Delegations (OOO)', objectName: 'sys_approval_delegation', icon: 'user-clock', requiresObject: 'sys_approval_delegation' },\n ],\n },\n ],\n });\n // ADR-0029 D8 — contribute this plugin's object translations to the i18n\n // service on kernel:ready (the i18n plugin may register after this one).\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', async () => {\n try {\n const i18n = ctx.getService<II18nService>('i18n');\n if (i18n && typeof i18n.loadTranslations === 'function') {\n const { ApprovalsTranslations } = await import('./translations/index.js');\n for (const [locale, data] of Object.entries(ApprovalsTranslations)) {\n i18n.loadTranslations(locale, data as Record<string, unknown>);\n }\n }\n } catch { /* i18n optional */ }\n });\n }\n ctx.logger.info('ApprovalsServicePlugin: schemas registered');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (this.options.disableService) return;\n // This plugin needs the engine SEEN WHOLE, not the data plane: it binds\n // `registerHook` / `unregisterHooksByPackage` below. That is `objectql`,\n // whose ledger entry (`CoreServiceContracts`) records it as \"the SAME\n // instance as `data`, seen whole\" — so the alias fallback resolves the same\n // object and is typed as the same contract.\n let engine: IObjectQLEngine | null = null;\n try { engine = ctx.getService<IObjectQLEngine>('objectql'); }\n catch { try { engine = ctx.getService<IObjectQLEngine>('data'); } catch { /* ignore */ } }\n if (!engine) {\n ctx.logger.warn('ApprovalsServicePlugin: no ObjectQL engine — service NOT registered');\n return;\n }\n this.engine = engine;\n\n this.service = new ApprovalService({\n engine: engine as ApprovalEngine,\n logger: ctx.logger,\n publicBaseUrl: this.options.publicBaseUrl,\n // [#8652] Read-only record-reader visibility. Default OFF — an absent\n // declaration reaches the service as an empty set and changes nothing.\n recordReaderVisibleObjects: this.options.recordReaderVisibleObjects,\n // [ADR-0105 D9] Cross-organization approver targeting is a `group`-posture\n // capability. Read LAZILY (not captured at start) because the tenancy\n // service resolves its posture during its own start, which may not have\n // run yet; an unresolvable posture reads as \"unknown\" and the guard\n // stands down rather than refusing a legitimate flow on a minimal stack.\n tenancyPosture: () => {\n try {\n const tenancy = ctx.getService<{ posture?: string }>('tenancy');\n const posture = tenancy?.posture;\n return typeof posture === 'string' && posture ? posture : undefined;\n } catch {\n return undefined;\n }\n },\n });\n\n // Record lock: block edits to a record while it has a pending request.\n // Delegation write-guard: a self-service OOO delegation may only name the\n // acting user as delegator (#1322 follow-up). Both bind under the same\n // package id, so unbindAllHooks clears them together.\n if (!this.options.disableAutoHooks) {\n try {\n unbindAllHooks(engine);\n bindApprovalLockHook(engine, ctx.logger);\n bindDelegationWriteGuard(engine, ctx.logger);\n } catch (err: any) {\n ctx.logger.warn?.('[approvals] failed to bind approval hooks', { error: err?.message });\n }\n }\n\n ctx.registerService('approvals', this.service);\n ctx.logger.info('ApprovalsServicePlugin: service registered');\n\n // Optional messaging service (ADR-0012): thread interactions (reassign /\n // remind / request-info / comment) notify users when present; without it\n // they degrade to audit-only.\n try {\n // `messaging` has no contract in the ledger, so this is the named local\n // surface the service itself already declares — not `any`. It omits\n // members on purpose; omitting one it USES would be a compile error at\n // the `attachMessaging` call, which is the whole point.\n const messaging = ctx.getService<ApprovalMessagingSurface>('messaging');\n if (messaging && typeof messaging.emit === 'function') {\n this.service.attachMessaging(messaging);\n }\n } catch { /* messaging not installed */ }\n\n // SLA escalation clock (ADR-0042): a plugin-internal job, deliberately\n // NOT a flow trigger (ADR-0041 §1). Interval sweep + one catch-up scan at\n // boot so a restart doesn't extend a breach by a scan period. Wired on\n // kernel:ready — the job service may start after this plugin. No `job`\n // service → SLA stays display-only.\n const wireEscalationClock = async () => {\n try {\n const jobs = ctx.getService<IJobService>('job');\n if (!jobs || typeof jobs.schedule !== 'function' || !this.service) return;\n const svc = this.service;\n const intervalMs = this.options.escalationScanIntervalMs ?? ESCALATION_SCAN_INTERVAL_MS;\n // Both sweeps ride this one clock: they walk the same `pending` set, and\n // the dead-run release (#3456) is reconciliation with the same \"catch up\n // after a restart\" requirement — a run killed BY the restart is exactly\n // the shape no in-band handler can clean up.\n // Genuinely independent — an escalation failure must not strand locked\n // records, and vice versa, so neither can short-circuit the other.\n const sweep = async () => {\n const results = await Promise.allSettled([\n svc.runEscalations(),\n svc.releaseDeadRunRequests(),\n // #4469 — the other half of the dead-run picture, and the one no\n // sweeper could see: a request already TERMINAL whose run is gone.\n // Read-only by design (it reports; it never rewrites a decision\n // that really happened), so it rides the same clock purely to make\n // the finding surface without an operator knowing to go looking.\n svc.inspectStrandedRequests(),\n ]);\n for (const r of results) {\n if (r.status === 'rejected') {\n ctx.logger.warn?.('[approvals] periodic sweep leg failed', {\n error: (r.reason as any)?.message ?? String(r.reason),\n });\n }\n }\n };\n await jobs.schedule(ESCALATION_JOB_NAME, { type: 'interval', intervalMs }, sweep);\n this.escalationJobScheduled = true;\n void sweep().catch((err: any) => {\n ctx.logger.warn?.('[approvals] boot sweep failed', { error: err?.message });\n });\n ctx.logger.info('ApprovalsServicePlugin: SLA escalation scan scheduled', { intervalMs });\n } catch { /* job service not installed */ }\n };\n // Actionable-link pages (ADR-0043): session-less confirm + redemption,\n // mounted straight on the host Hono app. GET only renders; the decision\n // happens exclusively on the POST (mail-gateway prefetch safe).\n const mountActionPages = async () => {\n try {\n // [#4251 B5] Canonical name FIRST. This read was `http-server`-only,\n // and `http-server` is the deprecated alias: the ledger records\n // `http.server` as canonical and as \"the ONLY name present on all\n // provider paths\" — `runtime.ts`'s `config.server` path registers no\n // alias at all. On that path this lookup threw, the catch below\n // swallowed it, and the ADR-0043 action pages silently never mounted,\n // so every approval e-mail link 404'd. Same latent alias-only miss\n // #4393 fixed in metadata/cloud-connection; per-name `try` because\n // `getService` THROWS on an empty slot, so `a() ?? b()` in one `try`\n // never reaches `b`.\n const readServer = (name: string): IHttpServer | undefined => {\n try { return ctx.getService<IHttpServer>(name); } catch { return undefined; }\n };\n const http = readServer('http.server') ?? readServer('http-server');\n const rawApp = http && typeof http.getRawApp === 'function' ? http.getRawApp() : null;\n if (!rawApp || !this.service) return;\n const svc = this.service;\n const ACT_PATH = '/api/v1/approvals/act';\n const html = (c: any, body: string, status = 200) =>\n c.body(body, status, { 'Content-Type': 'text/html; charset=utf-8' });\n rawApp.get(ACT_PATH, async (c: any) => {\n const token = String(c.req.query('token') ?? '');\n const peek = await svc.peekActionToken(token);\n if (!peek.ok) return html(c, renderResultPage(peek.reason, peek.request), 200);\n return html(c, renderConfirmPage({\n request: peek.request, action: peek.action, approverId: peek.approverId,\n token, actPath: ACT_PATH,\n }));\n });\n rawApp.post(ACT_PATH, async (c: any) => {\n let token = '';\n try {\n const body = await c.req.parseBody();\n token = String(body?.token ?? '');\n } catch { /* fall through to invalid */ }\n const out = await svc.redeemActionToken(token);\n if (!out.ok) return html(c, renderResultPage(out.reason, out.request), 200);\n return html(c, renderResultPage(out.action === 'approve' ? 'approved' : 'rejected', out.request));\n });\n ctx.logger.info(`ApprovalsServicePlugin: actionable-link pages mounted at ${ACT_PATH}`);\n } catch { /* http server not installed */ }\n };\n\n // Pending-approver index backfill (issue #1745): rebuild the normalized\n // sys_approval_approver rows from the pending_approvers CSV so requests\n // written before the index existed (or drifted past a crashed sync) are\n // queryable. Idempotent; cost tracks the live pending queue.\n const backfillApproverIndex = async () => {\n try {\n const svc = this.service;\n if (!svc) return;\n const out = await svc.rebuildApproverIndex();\n if (out.inserted > 0 || out.deleted > 0) {\n ctx.logger.info('ApprovalsServicePlugin: approver index rebuilt', out);\n }\n } catch (err: any) {\n ctx.logger.warn?.('[approvals] approver index backfill failed', { error: err?.message });\n }\n };\n\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', wireEscalationClock);\n (ctx as any).hook('kernel:ready', mountActionPages);\n (ctx as any).hook('kernel:ready', backfillApproverIndex);\n } else {\n await wireEscalationClock();\n await mountActionPages();\n await backfillApproverIndex();\n }\n\n // ADR-0019: contribute the `approval` node to the flow engine when one is\n // present. The node lets a flow suspend on an approval and resume on\n // decision; the service is wired to the same engine so `decide()` can\n // resume the suspended run.\n //\n // #4771 — the degradation must be LOUD (#4632). This used to be one\n // try/catch logging at `info`, and dev's default log level is `warn`: the\n // one line that says \"every `approval` node in this deployment is dead\"\n // was invisible in exactly the deployment where it is true, while the flow\n // registration was warning about `approval` in the deployments where it is\n // false. The catch is also narrowed to the service *lookup*, so a genuine\n // failure inside registerApprovalNode surfaces as itself instead of being\n // relabelled \"no automation engine\".\n let automation: ApprovalAutomationSurface | undefined;\n try {\n automation = ctx.getService<ApprovalAutomationSurface>('automation');\n } catch {\n automation = undefined; // no automation service registered in this stack\n }\n if (automation && typeof automation.registerNodeExecutor === 'function') {\n this.service.attachAutomation(automation);\n registerApprovalNode(automation, this.service, ctx.logger);\n } else {\n ctx.logger.warn(\n 'ApprovalsServicePlugin: no automation engine — the `approval` flow node is NOT registered. '\n + 'Every ADR-0019 approval flow in this deployment fails at execution time with NO_EXECUTOR. '\n + 'Add @objectstack/service-automation to the stack to enable them.',\n );\n }\n }\n\n async stop(ctx: PluginContext): Promise<void> {\n if (this.escalationJobScheduled) {\n try {\n const jobs = ctx.getService<IJobService>('job');\n await jobs?.cancel?.(ESCALATION_JOB_NAME);\n } catch { /* ignore */ }\n this.escalationJobScheduled = false;\n }\n if (this.engine) {\n try { unbindAllHooks(this.engine); } catch { /* ignore */ }\n }\n }\n}\n"],"mappings":"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","names":["ObjectSchema","Field","ObjectSchema","Field","ObjectSchema","Field","SYSTEM_CTX","uid","slots","fresh","resumed","resumeError","esc","ObjectSchema","Field","parseJson","SYSTEM_CTX","defineActionDescriptor","APPROVAL_REVISE_NODE_TYPE","SYSTEM_CTX","defineActionDescriptor","ApprovalsTranslations"]}
|