@aglyn/plugins-workflows 1.0.0-beta.143

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.
Files changed (75) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +52 -0
  3. package/package.json +53 -0
  4. package/src/index.d.ts +18 -0
  5. package/src/index.js +19 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/automation-step-fields.component.d.ts +74 -0
  8. package/src/lib/components/automation-step-fields.component.js +1210 -0
  9. package/src/lib/components/automation-step-fields.component.js.map +1 -0
  10. package/src/lib/components/host-actions-card.component.d.ts +15 -0
  11. package/src/lib/components/host-actions-card.component.js +1229 -0
  12. package/src/lib/components/host-actions-card.component.js.map +1 -0
  13. package/src/lib/components/host-activity-card.component.d.ts +84 -0
  14. package/src/lib/components/host-activity-card.component.js +280 -0
  15. package/src/lib/components/host-activity-card.component.js.map +1 -0
  16. package/src/lib/components/host-run-history-card.component.d.ts +40 -0
  17. package/src/lib/components/host-run-history-card.component.js +284 -0
  18. package/src/lib/components/host-run-history-card.component.js.map +1 -0
  19. package/src/lib/components/host-webhooks-card.component.d.ts +15 -0
  20. package/src/lib/components/host-webhooks-card.component.js +421 -0
  21. package/src/lib/components/host-webhooks-card.component.js.map +1 -0
  22. package/src/lib/components/host-workflows-card.component.d.ts +18 -0
  23. package/src/lib/components/host-workflows-card.component.js +952 -0
  24. package/src/lib/components/host-workflows-card.component.js.map +1 -0
  25. package/src/lib/components/run-quota-line.component.d.ts +27 -0
  26. package/src/lib/components/run-quota-line.component.js +72 -0
  27. package/src/lib/components/run-quota-line.component.js.map +1 -0
  28. package/src/lib/components/use-automation-step-pickers.d.ts +27 -0
  29. package/src/lib/components/use-automation-step-pickers.js +205 -0
  30. package/src/lib/components/use-automation-step-pickers.js.map +1 -0
  31. package/src/lib/components/workflow-zones.d.ts +36 -0
  32. package/src/lib/components/workflow-zones.js +19 -0
  33. package/src/lib/components/workflow-zones.js.map +1 -0
  34. package/src/lib/components/workflows-console-page.d.ts +25 -0
  35. package/src/lib/components/workflows-console-page.js +112 -0
  36. package/src/lib/components/workflows-console-page.js.map +1 -0
  37. package/src/lib/components/workflows-console-sections.d.ts +34 -0
  38. package/src/lib/components/workflows-console-sections.js +50 -0
  39. package/src/lib/components/workflows-console-sections.js.map +1 -0
  40. package/src/lib/constants/bundle-common.d.ts +8 -0
  41. package/src/lib/constants/bundle-common.js +9 -0
  42. package/src/lib/constants/bundle-common.js.map +1 -0
  43. package/src/lib/declarations.server.d.ts +41 -0
  44. package/src/lib/declarations.server.js +54 -0
  45. package/src/lib/declarations.server.js.map +1 -0
  46. package/src/lib/engine/crm-action-steps.d.ts +129 -0
  47. package/src/lib/engine/crm-action-steps.js +432 -0
  48. package/src/lib/engine/crm-action-steps.js.map +1 -0
  49. package/src/lib/engine/flow-enrollments.d.ts +264 -0
  50. package/src/lib/engine/flow-enrollments.js +386 -0
  51. package/src/lib/engine/flow-enrollments.js.map +1 -0
  52. package/src/lib/engine/run-event-actions.d.ts +232 -0
  53. package/src/lib/engine/run-event-actions.js +1392 -0
  54. package/src/lib/engine/run-event-actions.js.map +1 -0
  55. package/src/lib/engine/run-event-automations.d.ts +28 -0
  56. package/src/lib/engine/run-event-automations.js +37 -0
  57. package/src/lib/engine/run-event-automations.js.map +1 -0
  58. package/src/lib/engine/run-event-workflows.d.ts +75 -0
  59. package/src/lib/engine/run-event-workflows.js +311 -0
  60. package/src/lib/engine/run-event-workflows.js.map +1 -0
  61. package/src/lib/engine/workflow-steps.d.ts +107 -0
  62. package/src/lib/engine/workflow-steps.js +124 -0
  63. package/src/lib/engine/workflow-steps.js.map +1 -0
  64. package/src/lib/plugin.d.ts +33 -0
  65. package/src/lib/plugin.js +94 -0
  66. package/src/lib/plugin.js.map +1 -0
  67. package/src/lib/server-automation-drafts.d.ts +90 -0
  68. package/src/lib/server-automation-drafts.js +429 -0
  69. package/src/lib/server-automation-drafts.js.map +1 -0
  70. package/src/lib/server-console.d.ts +27 -0
  71. package/src/lib/server-console.js +35 -0
  72. package/src/lib/server-console.js.map +1 -0
  73. package/src/lib/server.d.ts +29 -0
  74. package/src/lib/server.js +298 -0
  75. package/src/lib/server.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/engine/run-event-actions.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ACTION_MAX_EVENT_DEPTH,\n ACTION_MAX_STEPS,\n checkEntitlement,\n planLabelGrantingFeature,\n checkQuota,\n type HostWebhook,\n WEBHOOK_URL_PATTERN,\n evaluateExpression,\n evaluateStepGuard,\n evaluateTriggerConditions,\n FLOW_TIMED_OUT_FIELD,\n flowEmailTopicId,\n hostPublicOrigin,\n isClientActionStep,\n isCrmActionStep,\n isFlowSuspendingStep,\n type HostAction,\n type HostActionAlert,\n type HostActionStep,\n type HostActionStepType,\n type HostFunction,\n type HostVariable,\n type HostWorkflow,\n type HostWorkflowStep,\n buildDatasetRecordValues,\n datasetDisplayName,\n contactCampaignFieldPath,\n datasetIntegrityFields,\n datasetIntegrityUpdate,\n describeStepOutcome,\n effectiveDatasetModel,\n normalizeTriggerConditions,\n type PluginJobHostGate,\n resolveOrgEntitlements,\n runWorkflow,\n WORKFLOW_MAX_STEPS,\n} from '@aglyn/aglyn/server'\nimport {\n isDeferrableSendResult,\n isEmailConfigured,\n sendEmail,\n sendFailureReason,\n} from '@aglyn/shared-util-email'\nimport {\n dataStorageRefusal,\n enrollListMember,\n firebaseAdmin,\n flowEmailRefusal,\n getOrgForHost,\n hostSendingIdentity,\n meterHostEmail,\n notifyHostManagers,\n consentGroupForSite,\n orgDataCollectionForHost,\n orgDataQueryForHost,\n resolveOrgIdForHost,\n} from '@aglyn/tenant-data-admin'\nimport { announceDatasetRecordChange } from '@aglyn/tenant-data-admin/server/dataset-live-pages'\n// The leaf, not the barrel: this library's specs substitute the barrel\n// wholesale, and the lookup must reach the real index logic under them.\nimport { findContactByEmail } from '@aglyn/tenant-data-admin/server/contact-email-index'\nimport { createHmac } from 'crypto'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport {\n advanceFlowEnrollment,\n claimFlowEnrollment,\n deferFlowEnrollment,\n endFlowEnrollment,\n enrollInFlow,\n findFlowEnrollmentsAwaiting,\n type FlowEnrollment,\n type FlowSweepCursor,\n type FlowSweepResult,\n sweepDueFlowEnrollments,\n} from './flow-enrollments'\nimport {\n logCrmEmailActivity,\n prepareCrmEmailActivity,\n runCrmActionStep,\n} from './crm-action-steps'\n// The runtime's leaves rather than its barrel: the engine's specs substitute\n// each leaf, and a mock of the barrel would take the rest of it down too.\nimport type { HostEventPayload } from '@aglyn/tenant-runtime/host-event-listeners'\nimport { resolveDatasetDoc } from '@aglyn/tenant-runtime/resolve-dataset'\nimport {\n resumeWorkflowEnrollment,\n runEventWorkflows,\n} from './run-event-workflows'\nimport {\n type AutomationWorkflow,\n isWorkflowActionStep,\n type WorkflowStep,\n workflowActionStepRefusal,\n workflowHasActionSteps,\n workflowStepTypeLabel,\n} from './workflow-steps'\n\n/** Bounded fan-out per event, mirroring the workflow runner. */\nconst MAX_TRIGGERED_ACTIONS = 10\n\nexport interface ActionRunEnv {\n hostId: string\n hostRef: FirebaseFirestore.DocumentReference\n alerts: HostActionAlert[]\n /**\n * Whether the owning org holds the actions builder (`actions`, Pro and up)\n * — the gate every Actions step takes. An action run is admitted by it\n * before its first step, so it is always true there; a workflow run is not,\n * because its function calls need no such plan, so the workflow executor\n * asks it of each Actions step it reaches.\n */\n actionsAllowed: boolean\n webhooksAllowed: boolean\n /**\n * Whether the owning org holds the CRM suite (AGL-2611) — the gate the\n * five CRM steps take, resolved once beside `webhooksAllowed` and for the\n * same reason: a step gated on the org doc it already read costs no\n * second read per step.\n */\n crmAllowed: boolean\n depth: number\n /**\n * The owning org's billing doc, already read by the entitlement gate that\n * admitted this run, and the org's id beside it.\n *\n * Carried rather than re-read: both entry points resolve `getOrgForHost`\n * before they build this, so the dataset caps below cost no extra org read.\n * Null only when the host has no resolvable org, which the gate treats as\n * the free plan.\n */\n org: unknown\n orgId: string | null\n loadWorkflowContext: () => Promise<WorkflowContext>\n}\n\n/**\n * The site's functions, variables and workflows, double-keyed by document id\n * and by name (AGL-261). A workflow value also carries its document id as\n * `$id`, so a step that names a workflow by its name can still run it as the\n * automation it is.\n */\nexport interface WorkflowContext {\n functions: Record<string, HostFunction>\n variables: Record<string, HostVariable>\n workflows: Record<string, HostWorkflow & { $id?: string }>\n}\n\n/**\n * The automation a run belongs to, as the step executors need it: which kind,\n * its document id, and its name for the history and the enrollment.\n */\nexport interface AutomationRun {\n kind: 'action' | 'workflow'\n id: string\n name: string\n}\n\n/**\n * The run environment for one automation run: the org's plan gates resolved\n * once, from the org document the caller already read.\n */\nexport function automationRunEnv(input: {\n hostId: string\n hostRef: FirebaseFirestore.DocumentReference\n owner: { org?: unknown; orgId?: string | null } | null | undefined\n depth: number\n alerts?: HostActionAlert[]\n loadWorkflowContext?: ActionRunEnv['loadWorkflowContext']\n}): ActionRunEnv {\n const org = input.owner?.org ?? null\n return {\n hostId: input.hostId,\n hostRef: input.hostRef,\n alerts: input.alerts ?? [],\n actionsAllowed: checkEntitlement(org as any, 'actions'),\n webhooksAllowed: checkEntitlement(org as any, 'webhooks'),\n crmAllowed: checkEntitlement(org as any, 'crm'),\n depth: input.depth,\n org,\n orgId: input.owner?.orgId ?? null,\n loadWorkflowContext:\n input.loadWorkflowContext ?? makeWorkflowContextLoader(input.hostRef),\n }\n}\n\nexport function makeWorkflowContextLoader(\n hostRef: FirebaseFirestore.DocumentReference,\n) {\n let workflowContext: WorkflowContext | null = null\n return async (): Promise<WorkflowContext> => {\n if (workflowContext) return workflowContext\n const [functionDocs, variableDocs, workflowDocs] = await Promise.all([\n hostRef.collection('functions').limit(100).get(),\n hostRef.collection('variables').limit(100).get(),\n hostRef.collection('workflows').limit(100).get(),\n ])\n workflowContext = workflowContextFromDocs(\n functionDocs.docs,\n variableDocs.docs,\n workflowDocs.docs,\n )\n return workflowContext\n }\n}\n\n/**\n * The working set a run reads, from documents already in hand — for a door\n * that read them itself and must not pay for them twice.\n */\nexport function workflowContextFromDocs(\n functionDocs: readonly FirebaseFirestore.QueryDocumentSnapshot[],\n variableDocs: readonly FirebaseFirestore.QueryDocumentSnapshot[],\n workflowDocs: readonly FirebaseFirestore.QueryDocumentSnapshot[],\n): WorkflowContext {\n // Double-keyed by doc id AND name (AGL-261): id references are\n // rename-safe; legacy name references keep resolving.\n const byName = <T extends { name?: string; deletedAt?: unknown }>(\n docs: readonly FirebaseFirestore.QueryDocumentSnapshot[],\n ) => {\n const map: Record<string, T> = {}\n for (const doc of docs) {\n const data = doc.data() as T\n if (data.deletedAt) continue\n map[doc.id] = data\n if (data?.name) map[data.name] = data\n }\n return map\n }\n // A workflow is carried with its id so a step naming it by its legacy\n // name still runs it as the automation it is (see `WorkflowContext`).\n const workflows: WorkflowContext['workflows'] = {}\n for (const doc of workflowDocs) {\n const data = doc.data() as HostWorkflow\n if ((data as { deletedAt?: unknown }).deletedAt) continue\n const withId = { ...data, $id: doc.id }\n workflows[doc.id] = withId\n if (data?.name) workflows[data.name] = withId\n }\n return {\n functions: byName<HostFunction>(functionDocs),\n variables: byName<HostVariable>(variableDocs),\n workflows,\n }\n}\n\n/**\n * Whether this dataset may take ANOTHER record, on the plan of the org that\n * owns the site — the row band and the byte band, in that order. Null when\n * the append may proceed; a reason string when it may not.\n *\n * ## Why an append needs a gate at all\n *\n * Every other door onto `datasets/{id}/records` already has one: the console\n * route re-checks `recordsPerDataset` inside the creating transaction, the\n * `/v1` record route checks it below its idempotency claim, and the public\n * form-submission leg checks the rows and the bytes. A workflow step wrote\n * with no check of either — and it is the door a visitor drives hardest,\n * because an action fires per event on a published site. A cap enforced at\n * three of four doors is not a cap; it is the shape of the one that is left.\n *\n * ## What it does NOT do\n *\n * It refuses the WRITE, never the dataset. A dataset already holding more\n * rows than the plan includes keeps every row it has and keeps being read —\n * nothing here deletes, truncates, or hides anything, and nothing may be\n * added that does. What is refused is the next row, which is the same\n * boundary the other three doors draw, and the reason a plan change cannot\n * cost a customer data they already have.\n *\n * The update leg of `updateDataset` is deliberately NOT gated: merging fields\n * into a record that already exists adds no row, so refusing it would refuse\n * the state of being over rather than the raise.\n *\n * ## What it costs\n *\n * Nothing on the plans that sell the data store. The row count is read only\n * when `recordsPerDataset` is FINITE, so an uncapped plan pays nothing; and\n * `dataStorageRefusal` answers null with no read at all whenever the plan\n * carries an `extraDataGbMonthlyUsd` rate, which every metered plan does. The\n * reads are paid on the shapes that can actually refuse.\n */\n/**\n * Refresh the live pages showing the dataset this step just wrote to\n * (AGL-3113).\n *\n * An automation that appends a row is the same change to a visitor as a form\n * submission or a console edit: the pages repeating over the dataset go on\n * serving the rows they were built from. Announced from the step rather than\n * from the run, so a workflow whose steps write two different datasets\n * refreshes both — the announce coalesces repeats of the SAME dataset itself,\n * which is what a run hitting one dataset several times needs.\n *\n * Silent without an org: datasets are org-scoped, so a host with no resolvable\n * org has no dataset to have written to. Best effort, and never thrown: the\n * row is already stored.\n */\nasync function announceDatasetStepWrite(\n env: ActionRunEnv,\n datasetId: string,\n): Promise<void> {\n if (!env.orgId) return\n await announceDatasetRecordChange({\n firestore: firebaseAdmin.app().firestore(),\n orgId: env.orgId,\n datasetId,\n })\n}\n\nasync function datasetAppendRefusal(\n env: ActionRunEnv,\n datasetRef: FirebaseFirestore.DocumentReference,\n): Promise<string | null> {\n const limit = resolveOrgEntitlements(env.org as never).recordsPerDataset\n if (Number.isFinite(limit)) {\n const used = (\n await datasetRef.collection('records').count().get()\n ).data().count\n if (!checkQuota(env.org as never, 'recordsPerDataset', used).allowed) {\n return `dataset is full (${limit} records on this plan)`\n }\n }\n if (!env.orgId) return null\n const bytes = await dataStorageRefusal(\n env.org as never,\n firebaseAdmin.app().firestore().collection('orgs').doc(env.orgId),\n )\n if (!bytes) return null\n return `dataset storage is full (${bytes.includedMb} MB on this plan)`\n}\n\n/** How a run of an action's step list ended. */\ntype ActionRunEnding = 'ran' | 'waiting' | 'exited' | 'deferred'\n\ninterface ExecuteActionOptions {\n /**\n * The step to start at. Non-zero only on a resume, where it is the\n * enrollment's `nextStepIndex`.\n */\n startIndex?: number\n /**\n * The step list to run.\n *\n * A resume passes the enrollment's SNAPSHOT rather than the action's\n * current steps — see `FlowEnrollment.steps` for why a position in a list\n * is meaningless against a list that has since been edited.\n */\n steps?: readonly HostActionStep[]\n /**\n * The enrollment this run belongs to. Present only on a resume; a first run\n * mints one if it reaches a wait.\n */\n enrollmentRef?: FirebaseFirestore.DocumentReference | null\n}\n\n/** What one step is run against: its place, the list it is in, the event. */\ninterface ServerStepContext {\n /** The step's index in {@link steps}. */\n index: number\n /** The list being run — the snapshot a wait enrolls with. */\n steps: readonly WorkflowStep[]\n event: string\n /** The payload the step reads its values from. */\n payload: HostEventPayload\n /** The scope a step's `when` is evaluated against. */\n scope: Record<string, unknown>\n enrollmentRef: FirebaseFirestore.DocumentReference | null\n /**\n * Set when this run is itself a step of another automation — a workflow a\n * `runWorkflow` step started. Such a run finishes inside its caller's run,\n * so it has no enrollment of its own to wait in.\n */\n nested?: boolean\n}\n\n/**\n * What one Actions step did, for the run that holds it.\n *\n * - `skipped` — its `when` was not met, or it is a step the visitor's page\n * runs; nothing is recorded.\n * - `done` — it did its work; `detail` is the fact the history line carries.\n * - `failed` — it did not; the run records the error and continues.\n * - `exited` — an `exitFlow`: the run ends here.\n * - `waiting` — a wait enrolled the person; the rest runs from the beat.\n * - `halted` — a wait that could not enroll; the run ends with the error.\n * - `deferred` — a resumed send was refused for now; the enrollment retries.\n */\nexport type ServerStepVerdict =\n | { kind: 'skipped' }\n | { kind: 'done'; detail?: string }\n | { kind: 'failed'; error: string }\n | { kind: 'exited' }\n | { kind: 'waiting'; detail?: string }\n | { kind: 'halted'; error: string }\n | { kind: 'deferred' }\n\n/** A run's tally so far: how it is ending, its errors, what its steps did. */\ninterface RunTally {\n ending: ActionRunEnding\n errors: string[]\n outcomes: string[]\n}\n\n/**\n * Folds one step's verdict into its run's tally, in the run-history phrasing\n * every automation shares; true when the run stops at this step.\n */\nfunction tallyStep(\n tally: RunTally,\n type: HostActionStepType,\n verdict: ServerStepVerdict,\n): boolean {\n switch (verdict.kind) {\n case 'skipped':\n return false\n case 'done':\n tally.outcomes.push(describeStepOutcome(type, verdict.detail))\n return false\n case 'failed':\n tally.errors.push(verdict.error)\n return false\n case 'exited':\n tally.ending = 'exited'\n tally.outcomes.push(describeStepOutcome(type))\n return true\n case 'waiting':\n tally.ending = 'waiting'\n tally.outcomes.push(describeStepOutcome(type, verdict.detail))\n return true\n case 'halted':\n tally.errors.push(verdict.error)\n return true\n case 'deferred':\n tally.ending = 'deferred'\n return true\n }\n}\n\n/**\n * Runs ONE Actions step on the server: the executor every automation shares.\n *\n * An action's step list runs through it one step at a time, and so does\n * every Actions step inside a workflow — so the two engines cannot disagree\n * about what `sendEmail` or `datasetAppend` does, what a step is gated on, or\n * how its outcome reads in the run history. There is no second copy of any\n * branch below.\n *\n * Client-side steps (AGL-257) are skipped — the tenant page runtime runs\n * those in the visitor's browser.\n *\n * Never throws: a step that throws is a failed step, and the run continues.\n */\nasync function runServerStep(\n env: ActionRunEnv,\n run: AutomationRun,\n step: HostActionStep,\n context: ServerStepContext,\n): Promise<ServerStepVerdict> {\n const { hostId, hostRef, alerts, depth } = env\n const { event, payload, enrollmentRef } = context\n /**\n * The one fact worth carrying into the summary — the dataset's name,\n * the webhook's status. Set by the branch that knows it.\n */\n let detail: string | undefined\n const failed = (error: string): ServerStepVerdict => ({ kind: 'failed', error })\n try {\n /*\n * BRANCHING INSIDE A FLOW: the step's own condition, evaluated against\n * the same scope the trigger's is. An unmet guard skips this step and\n * only this step — the run continues, which is what makes \"wait three\n * days, then, only if they have not ordered, send the reminder\" a thing\n * an author can write without a second action.\n */\n if (!evaluateStepGuard(step.when, context.scope)) return { kind: 'skipped' }\n if (isClientActionStep(step) && step.type !== 'siteAlert') {\n return { kind: 'skipped' } // Runs in the visitor's page (AGL-257).\n }\n if (step.type === 'exitFlow') return { kind: 'exited' }\n if (isFlowSuspendingStep(step)) {\n // A run inside another automation's run finishes inside it: there is\n // no enrollment of its own for the person to wait in.\n if (context.nested) {\n return {\n kind: 'halted',\n error:\n 'a workflow run as a step of another automation cannot wait — ' +\n 'give it its own trigger instead',\n }\n }\n const suspended = await suspendFlow(env, run, {\n step,\n steps: context.steps,\n nextStepIndex: context.index + 1,\n event,\n payload,\n enrollmentRef,\n })\n if (suspended.error) return { kind: 'halted', error: suspended.error }\n return { kind: 'waiting', detail: suspended.detail }\n }\n if (step.type === 'siteAlert') {\n alerts.push({\n message: String(step.message ?? '').slice(0, 300),\n severity: step.severity ?? 'info',\n })\n } else if (step.type === 'runWorkflow') {\n const workflowContext = await env.loadWorkflowContext()\n const workflow =\n workflowContext.workflows[step.workflowId?.trim() ?? ''] ??\n workflowContext.workflows[step.workflowName?.trim() ?? '']\n if (!workflow) {\n return failed(`unknown workflow \"${step.workflowName || step.workflowId}\"`)\n }\n if (workflowHasActionSteps(workflow)) {\n /*\n * A workflow with Actions steps is PERFORMED, not evaluated: its\n * steps run here, inside this run, one level deeper under the same\n * guard a custom-event chain runs under. It is part of this run, so\n * it is not metered or recorded as a run of its own.\n */\n if (depth + 1 > ACTION_MAX_EVENT_DEPTH) {\n return failed(`workflow \"${workflow.name}\" is nested too deeply`)\n }\n const nested = await executeWorkflow(\n { ...env, depth: depth + 1 },\n {\n kind: 'workflow',\n id: workflow.$id ?? step.workflowId?.trim() ?? '',\n name: workflow.name ?? '',\n },\n workflow as AutomationWorkflow,\n event,\n payload,\n { nested: true },\n )\n if (nested.errors.length) {\n return failed(\n `workflow \"${workflow.name}\": ${nested.errors.join('; ')}`.slice(0, 300),\n )\n }\n } else {\n const evaluated = runWorkflow(\n workflow,\n workflowContext.functions,\n workflowContext.variables,\n { event, ...payload },\n { workflows: workflowContext.workflows },\n )\n if (evaluated.ok === false) return failed(evaluated.error)\n }\n } else if (step.type === 'customEvent') {\n const nested = await runEventActions(\n hostId,\n step.eventName.trim(),\n payload,\n depth + 1,\n )\n alerts.push(...nested)\n } else if (step.type === 'webhookPost') {\n if (!env.webhooksAllowed) return failed('webhooks require a Business plan')\n // Id-first lookup (AGL-261); the name query is the legacy path.\n const hookDoc = step.webhookId?.trim()\n ? await hostRef\n .collection('webhooks')\n .doc(step.webhookId.trim())\n .get()\n : (\n await hostRef\n .collection('webhooks')\n .where('name', '==', step.webhookName?.trim() ?? '')\n .limit(1)\n .get()\n ).docs[0]\n const hook = hookDoc?.exists\n ? (hookDoc.data() as HostWebhook)\n : undefined\n if (\n !hook ||\n hookDoc.get('deletedAt') ||\n hook.enabled === false ||\n hook.direction !== 'outbound' ||\n !hook.url ||\n !WEBHOOK_URL_PATTERN.test(hook.url)\n ) {\n return failed(`unknown webhook \"${step.webhookName || step.webhookId}\"`)\n }\n const body = JSON.stringify({\n event,\n payload,\n sentAt: new Date().toISOString(),\n })\n const signature = hook.secret\n ? createHmac('sha256', hook.secret).update(body).digest('hex')\n : ''\n // Two quick retries — serverless-friendly; longer retry queues\n // are a follow-up.\n let delivered = false\n let lastStatus: number | undefined\n for (let attempt = 0; attempt < 3 && !delivered; attempt += 1) {\n try {\n const response = await fetch(hook.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(signature && { 'X-Aglyn-Signature': signature }),\n },\n body,\n signal: AbortSignal.timeout(5000),\n })\n lastStatus = response.status\n delivered = response.ok\n } catch {\n // Retry below.\n }\n if (!delivered && attempt < 2) {\n await new Promise((resolve) =>\n setTimeout(resolve, 500 * (attempt + 1)),\n )\n }\n }\n if (!delivered) {\n return failed(\n `webhook \"${step.webhookName || step.webhookId}\" delivery failed`,\n )\n }\n // The status the mockup prints — discarded on the line it arrived\n // until AGL-2171. A 200 and a 204 are both `ok`, and knowing which is\n // the whole reason anyone opens a run history after a webhook.\n detail = String(lastStatus ?? '')\n } else if (step.type === 'datasetAppend') {\n // Id-first lookup (AGL-261/556); the name query is the legacy path.\n const datasetsRef = await orgDataCollectionForHost(hostId, 'datasets')\n const datasetDoc = await resolveDatasetDoc(datasetsRef, step, hostId)\n if (!datasetDoc?.exists || datasetDoc.get('deletedAt')) {\n return failed(`unknown dataset \"${step.datasetName || step.datasetId}\"`)\n }\n // Restrict to the model's field ids (AGL-556) — covers model-only\n // datasets whose flat v1 `fields` mirror is absent.\n const appendDataset = {\n model: datasetDoc.get('model'),\n fields: Array.isArray(datasetDoc.get('fields'))\n ? datasetDoc.get('fields')\n : [],\n }\n const values = buildDatasetRecordValues(appendDataset, payload)\n // Same name precedence `findDatasetByName` resolves in.\n const appendLabel = (\n datasetDisplayName({\n displayName: datasetDoc.get('displayName'),\n name: datasetDoc.get('name'),\n }) ||\n step.datasetName ||\n ''\n ).slice(0, 60)\n // No event field matched a field of the dataset, so there is nothing\n // to write. An error rather than a quiet success: a run history that\n // says `saved to Leads` while nothing saves is how a mismatched field\n // name goes unnoticed.\n if (!Object.keys(values).length) {\n return failed(\n `no event field matches a field in dataset \"${appendLabel || step.datasetId}\"`,\n )\n }\n const refusal = await datasetAppendRefusal(env, datasetDoc.ref)\n if (refusal) return failed(refusal)\n await datasetDoc.ref.collection('records').add({\n values,\n // The integrity index the console's delete check queries —\n // carried by every write that sets `values`, or the index\n // describes rows this one never held.\n ...datasetIntegrityFields(\n effectiveDatasetModel(appendDataset),\n values,\n ),\n createdAt: FieldValue.serverTimestamp(),\n })\n await announceDatasetStepWrite(env, datasetDoc.id)\n // `saved to Leads` beats `saved to dataset` (AGL-2171).\n detail = appendLabel\n } else if (step.type === 'updateDataset') {\n // Update-or-append (AGL-257): matches the record whose `email`\n // field equals the payload's email; appends when nothing matches.\n const datasetsRef = await orgDataCollectionForHost(hostId, 'datasets')\n const datasetDoc = await resolveDatasetDoc(datasetsRef, step, hostId)\n if (!datasetDoc?.exists || datasetDoc.get('deletedAt')) {\n return failed(`unknown dataset \"${step.datasetName || step.datasetId}\"`)\n }\n const updateDataset = {\n model: datasetDoc.get('model'),\n fields: Array.isArray(datasetDoc.get('fields'))\n ? datasetDoc.get('fields')\n : [],\n }\n const updateModel = effectiveDatasetModel(updateDataset)\n const values = buildDatasetRecordValues(updateDataset, payload)\n // Nothing to merge or append — an error, for the reason the append\n // branch above gives.\n if (!Object.keys(values).length) {\n const updateLabel =\n datasetDisplayName({\n displayName: datasetDoc.get('displayName'),\n name: datasetDoc.get('name'),\n }) ||\n step.datasetName ||\n step.datasetId\n return failed(\n `no event field matches a field in dataset \"${String(updateLabel ?? '').slice(0, 60)}\"`,\n )\n }\n const email = String((payload as any).email ?? '').trim()\n // `records.values` is exempt from indexing, so this lookup is served\n // only by the `values.email` field override in\n // cloud/firebase-firestore.indexes.json. Without that override\n // production refuses the query and neither leg below runs.\n const existing = email\n ? await datasetDoc.ref\n .collection('records')\n .where('values.email', '==', email)\n .limit(1)\n .get()\n : null\n if (existing && !existing.empty) {\n const merged = {\n ...(existing.docs[0].get('values') ?? {}),\n ...values,\n }\n await existing.docs[0].ref.set(\n {\n values: merged,\n // The merging form: an update that clears the last reference\n // has to REMOVE the index rather than omit it, or a stale\n // array refuses a delete nothing is holding.\n ...datasetIntegrityUpdate(\n updateModel,\n merged,\n FieldValue.delete(),\n ),\n updatedAt: FieldValue.serverTimestamp(),\n },\n { merge: true },\n )\n } else {\n // The APPEND leg of update-or-append, and the only one of the two\n // that adds a row — the merge above rewrites a record that already\n // counts against the band.\n const refusal = await datasetAppendRefusal(env, datasetDoc.ref)\n if (refusal) return failed(refusal)\n await datasetDoc.ref.collection('records').add({\n values,\n ...datasetIntegrityFields(updateModel, values),\n createdAt: FieldValue.serverTimestamp(),\n })\n }\n // Both legs changed a row, so both make the same pages stale — an edited\n // record reads no differently from a new one on a page that lists them.\n await announceDatasetStepWrite(env, datasetDoc.id)\n } else if (step.type === 'notifyAdmins') {\n await notifyHostManagers(hostId, {\n type: 'system.announcement',\n title: String(step.title ?? '').slice(0, 200),\n ...(step.body ? { body: String(step.body).slice(0, 500) } : {}),\n link: `/${hostId}`,\n })\n } else if (step.type === 'sendEmail') {\n const to = String(\n (payload as any)[step.toField?.trim() || 'email'] ?? '',\n ).trim()\n if (!isEmailConfigured()) return failed('email is not configured')\n if (!to || !to.includes('@')) {\n return failed('no recipient email in the event payload')\n }\n // The site's own origin, for the unsubscribe link. Read here rather\n // than carried on the run env because most action runs send no email\n // at all, and a document read every workflow pays for is a read on\n // the hot path for a link nine runs in ten never need.\n const siteBase =\n hostPublicOrigin(\n (await hostRef.get().catch(() => null))?.data() as never,\n ) ?? ''\n /*\n * MARKETING. The subject and body are merchant-authored and the\n * recipient comes out of the event payload — which, for the collect\n * route, is a write triggered by an anonymous visitor. So this is a\n * site mailing an address on the merchant's say-so, and it owes what\n * every other such message owes: the unsubscribe header pair and a\n * visible link, both suppression lists, and a share of the ceiling on\n * how much one person receives from this site.\n *\n * Priority stays transactional. An action run is not resumable — the\n * event has already happened and there is no beat that comes back for\n * it — and the rule on `'bulk'` is that only a resumable sweep may\n * refuse in a way the recipient survives.\n *\n * A merchant who wants an internal alert that no suppression can stop\n * uses the `notifyAdmins` step beside this one: it reaches managers\n * in the console rather than the shared sending domain, which is the\n * right instrument for a notification nobody consented to receive.\n */\n /*\n * A STEP THAT RUNS AFTER A WAIT IS A CAMPAIGN, not a reply.\n *\n * The paragraph above is exactly right about an IMMEDIATE step: the\n * event has already happened, the recipient just did something, and\n * the message is the response to it. None of that survives a three-day\n * delay. Everything after a wait goes out on the merchant's schedule,\n * to somebody who did one thing once — which is `marketing-send.ts`'s\n * own definition of marketing mail, and it earns the full consent\n * split and the default stream, exactly as a campaign does.\n *\n * An immediate step is not ungated, which it used to be. `to` is read\n * out of the event payload — an anonymous visitor's write on the\n * collect route — so nothing here establishes that whoever typed the\n * address is whoever receives the mail, and a person with a RECORDED\n * REFUSAL on this site was mailed merchant-authored content because a\n * third party entered their address in a form. `'immediate'` asks the\n * narrower question that catches exactly that and cannot refuse a\n * new visitor their auto-response: see `FlowEmailScope`.\n *\n * Refused BEFORE `sendEmail` rather than inside it, because these two\n * are the merchant's own policy over their own audience, where the\n * ones the seam asks are platform controls over the shared sending\n * domain. Both refusals are permanent for this message, so the\n * enrollment moves on rather than retrying.\n */\n const scope = enrollmentRef ? 'scheduled' : 'immediate'\n /*\n * The stream this message belongs to, resolved ONCE and read twice:\n * the gate below filters on it, and it rides the `marketing` context\n * so the opt-out link the seam mints names it. Without that the\n * preference page opens on a list of every stream the site has, and\n * the recipient has to find the one they were trying to leave.\n */\n const topicId = flowEmailTopicId(step.topicId, scope)\n const gate = await flowEmailRefusal({\n hostId,\n email: to,\n topicId: step.topicId ?? null,\n org: env.org,\n scope,\n })\n if (gate) {\n return failed(\n gate !== 'consent-withheld'\n ? 'the recipient has left this email topic'\n : // Named by what was actually asked. An immediate step refuses\n // only on a stated refusal, so reporting it as a missing\n // record would send a merchant looking for a consent field to\n // fill in that would change nothing.\n enrollmentRef\n ? 'the recipient has no marketing consent record on this site'\n : 'the recipient has declined marketing from this site',\n )\n }\n const emailSubject = String(step.subject ?? '').slice(0, 200)\n const emailText = String(step.body ?? '').slice(0, 5000)\n /*\n * THE TIMELINE ENTRY (AGL-2615). A message addressed to the contact\n * the event is about is logged on that contact's timeline as an\n * email activity — the same row the console's own send logs — so an\n * automated welcome shows beside the calls a rep made, with its\n * delivery state. Prepared first because the row's id has to be on\n * the message for the webhook to find it; written only after the\n * provider accepted. Behind the suite gate like every other CRM\n * write an action makes.\n */\n const emailActivity = env.crmAllowed\n ? await prepareCrmEmailActivity(\n { hostId, org: env.org, orgId: env.orgId },\n to,\n payload,\n )\n : null\n const result = await sendEmail({\n to,\n subject: emailSubject,\n text: emailText,\n sendingIdentity: await hostSendingIdentity(hostId),\n ...(emailActivity ? { tags: emailActivity.tags } : {}),\n audience: 'tenant',\n context: enrollmentRef ? 'flow step' : 'event action',\n /*\n * A resumed step may take `'bulk'` where an immediate one may not,\n * and the reason is the same one the abandoned-checkout sweep gives:\n * only a RESUMABLE sender may be refused in a way the recipient\n * survives. The enrollment is the thing that makes it resumable —\n * a deferral below leaves the row waiting and the next beat sends\n * the same step to the same person.\n */\n ...(enrollmentRef ? { priority: 'bulk' as const } : {}),\n // `topicId` is `''` for a step that belongs to no stream, which\n // every reader of it treats as absent — see `flowEmailTopicId`.\n marketing: { hostId, siteBase, topicId },\n })\n /*\n * DEFERRED IS NOT FAILED, and it is not SENT either.\n *\n * The platform's hourly ceiling and this person's own frequency window\n * are both refusals a later beat can pass. Advancing past this step\n * would turn \"not this hour\" into an email nobody ever receives, which\n * is the defect the campaign processor and the cart sweep each name.\n * So the enrollment is put back with the SAME `nextStepIndex` and the\n * run ends here.\n */\n if (enrollmentRef && isDeferrableSendResult(result)) {\n return { kind: 'deferred' }\n }\n // Named rather than lumped into \"delivery failed\": a suppression and\n // a frequency ceiling are the controls working, and a merchant\n // reading the run's alerts has a different thing to do about each.\n const refusal = sendFailureReason(result)\n const sendError = refusal\n ? refusal === 'suppressed'\n ? 'the recipient is unsubscribed or suppressed'\n : refusal === 'frequency-capped'\n ? 'the recipient has already had today’s limit of email ' +\n 'from this site'\n : 'email delivery failed'\n : null\n // Cost meter (AGL-1438). A workflow notification is transactional:\n // counted, never capped. `sent` is false when Resend refused or the\n // environment is unconfigured, and an email that never left is not a\n // cost.\n if (result.sent) {\n await meterHostEmail(hostId)\n if (emailActivity) {\n await logCrmEmailActivity(\n { hostId, org: env.org, orgId: env.orgId },\n emailActivity,\n { subject: emailSubject, body: emailText, to },\n run.id,\n )\n }\n }\n if (sendError) return failed(sendError)\n } else if (step.type === 'enrollList') {\n const orgId = await resolveOrgIdForHost(hostId)\n const email = String((payload as any).email ?? '')\n .trim()\n .toLowerCase()\n if (!orgId || !email || !email.includes('@')) {\n return failed('no email to enroll')\n }\n const listsRef = firebaseAdmin\n .app()\n .firestore()\n .collection('orgs')\n .doc(orgId)\n .collection('lists')\n const listDoc = step.listId?.trim()\n ? await listsRef.doc(step.listId.trim()).get()\n : (\n await listsRef\n .where('name', '==', step.listName?.trim() ?? '')\n .limit(1)\n .get()\n ).docs[0]\n if (!listDoc?.exists) {\n return failed(`unknown list \"${step.listName || step.listId}\"`)\n }\n // `enrollListMember` owns the document id: the commerce newsletter\n // handler enrolls into the same collection, and an id derived here\n // would be a second answer to which document describes which person.\n await enrollListMember({\n listRef: listDoc.ref,\n group: await consentGroupForSite(hostId),\n email,\n // Which automation enrolled them: `action:<id>` or `workflow:<id>`.\n source: `${run.kind}:${run.id}`,\n })\n } else if (step.type === 'assignCampaign') {\n const email = String((payload as any).email ?? '')\n .trim()\n .toLowerCase()\n if (!email || !email.includes('@')) {\n return failed('no contact email to assign')\n }\n // Scoped to this host (AGL-1039): a site must not reach a contact\n // it cannot see, even to tag it onto a campaign. Through the org's\n // address index (AGL-2633), so an address a merge folded into\n // another record still names the person who now holds it.\n const { ref: contactsRef } = await orgDataQueryForHost(hostId, 'contacts')\n const contact = await findContactByEmail(contactsRef, email, { hostId })\n if (!contact) return failed(`no contact for ${email}`)\n /*\n * THE CAMPAIGN IS RESOLVED TO A DOCUMENT, exactly as `enrollList`\n * resolves a list one branch above.\n *\n * A step may name a campaign by id or by name — the picker writes the\n * id, an imported automation may carry only the name — and what gets\n * stored is the id either way. Storing whichever of the two the step\n * happened to hold would put names and ids in one array, and every\n * reader of that array resolves ids: a name in it renders as a chip\n * nobody can click and matches no campaign the console can find.\n *\n * An unknown campaign is an ERROR rather than a stored string. The\n * reference audit already reports a step pointing at a campaign that\n * does not exist; a run that wrote the dangling name anyway would\n * make the audit's finding untrue the moment it fired.\n */\n const campaignsRef = hostRef.collection('emailCampaigns')\n const namedId = step.campaignId?.trim() ?? ''\n const campaignDoc = namedId\n ? await campaignsRef.doc(namedId).get()\n : (\n await campaignsRef\n .where('name', '==', step.campaignName?.trim() ?? '')\n .limit(1)\n .get()\n ).docs[0]\n if (!campaignDoc?.exists) {\n return failed(`unknown campaign \"${step.campaignName || step.campaignId}\"`)\n }\n /*\n * INSIDE THIS SITE'S FACET, not at the top of the document.\n *\n * A contact is one row shared by every site in the org, and which\n * campaigns a merchant has filed somebody under is that merchant's\n * business record on the same footing as their notes and their tags.\n * Written at the top it would be readable by every other site in an\n * agency's account.\n *\n * `update` with a dotted path, never `set({merge:true})`: a `set`\n * treats the string as a literal field NAME and would mint a\n * top-level key with dots in it. The document was just read, so the\n * update cannot fail for absence.\n */\n const group = await consentGroupForSite(hostId)\n await contact.ref.update({\n [contactCampaignFieldPath(group.groupId)]: FieldValue.arrayUnion(\n campaignDoc.id,\n ),\n updatedAt: FieldValue.serverTimestamp(),\n })\n } else if (isCrmActionStep(step)) {\n // The plan gate, the way `webhookPost` takes the `webhooks` one:\n // refused into the run history with the tier that carries it, so\n // a Free workspace whose flow names a CRM step reads why the step\n // did nothing rather than a log that says it ran.\n if (!env.crmAllowed) {\n return failed(\n `CRM steps require the ${planLabelGrantingFeature('crm')} plan`,\n )\n }\n // The five CRM steps (AGL-2605) share a resolver and a scope, so\n // they share a module; see `crm-action-steps.ts`.\n const outcome = await runCrmActionStep(\n { hostId, org: env.org, orgId: env.orgId },\n run.id,\n step,\n payload,\n )\n if (outcome.error) return failed(outcome.error)\n detail = outcome.detail\n /*\n * A stage set by an automation IS a stage change, and whatever\n * listens for one must hear it — fanned out here, under the same\n * depth guard a `customEvent` chain runs under, rather than through\n * `emitHostEvent`, which starts every chain at depth zero and would\n * let an automation that sets the stage it listens for run forever.\n * Workflows take the guard too, now that a workflow can set a stage.\n */\n if (outcome.emit) {\n const [fromWorkflows, fromActions] = await Promise.all([\n runEventWorkflows(\n hostId,\n outcome.emit.event,\n outcome.emit.payload,\n depth + 1,\n ),\n runEventActions(\n hostId,\n outcome.emit.event,\n outcome.emit.payload,\n depth + 1,\n ),\n ])\n alerts.push(...fromActions, ...fromWorkflows)\n }\n }\n } catch (error) {\n return failed((error as Error).message)\n }\n return { kind: 'done', detail }\n}\n\n/**\n * Executes one action's SERVER steps in order, collecting per-step errors\n * into the activity summary. Each step runs through {@link runServerStep}.\n *\n * A `wait` step ENDS this call and hands the rest of the list to the job\n * beat: everything after the wait belongs to a run that has not happened yet,\n * so nothing below the wait may execute in this request.\n */\nasync function executeAction(\n env: ActionRunEnv,\n actionId: string,\n action: HostAction,\n event: string,\n payload: HostEventPayload,\n options: ExecuteActionOptions = {},\n): Promise<ActionRunEnding> {\n const { hostRef } = env\n const run: AutomationRun = {\n kind: 'action',\n id: actionId,\n name: action.name ?? '',\n }\n const enrollmentRef = options.enrollmentRef ?? null\n const steps = (options.steps ?? action.steps ?? []).slice(0, ACTION_MAX_STEPS)\n const startIndex = Math.max(0, options.startIndex ?? 0)\n /**\n * What each step actually DID (AGL-2171). Only failures were recorded,\n * so a run that sent an email, wrote a row and posted a webhook logged\n * the same eight words as a run that did nothing — and\n * `/product/workflows` advertises a `What happened` column reading\n * `Sent email · saved to Leads · webhook 200`.\n */\n const tally: RunTally = { ending: 'ran', errors: [], outcomes: [] }\n const scope = { event, ...payload }\n for (let index = startIndex; index < steps.length; index += 1) {\n const step = steps[index] as HostActionStep\n const verdict = await runServerStep(env, run, step, {\n index,\n steps,\n event,\n payload,\n scope,\n enrollmentRef,\n })\n if (tallyStep(tally, step.type, verdict)) break\n }\n const { ending, errors: stepErrors, outcomes } = tally\n /*\n * A DEFERRED run writes no history line and leaves the enrollment where it\n * was. Nothing happened that a merchant should read as a run: the step is\n * still ahead of this person, and a row per refused beat would bury the\n * runs that did something under a log of the ceiling working.\n */\n if (ending === 'deferred') return ending\n const summary = stepErrors.length\n ? `Action ran on ${event} with errors: ${stepErrors.join('; ')}`.slice(\n 0,\n 300,\n )\n : ending === 'waiting'\n ? `Action is waiting, on ${event}`\n : `Action ran on ${event}`\n await hostRef\n .collection('activity')\n .add({\n actorId: null,\n actorEmail: null,\n // The prose line stays exactly as it was: `activityPrimaryText` and\n // three other renderers read it, and the run table is not the only\n // thing this collection feeds.\n action: summary,\n // The structured half (AGL-2171) — the two columns the advertised\n // run-history table could not otherwise fill.\n result: stepErrors.length ? 'failed' : 'succeeded',\n trigger: event,\n summary: (outcomes.length ? outcomes.join(' · ') : 'Ran').slice(0, 300),\n target: { type: 'workflow', id: actionId, name: action.name ?? '' },\n createdAt: FieldValue.serverTimestamp(),\n })\n .catch(() => undefined)\n return ending\n}\n\n/** How a workflow run went, for whoever records or answers for it. */\nexport interface WorkflowExecution {\n ending: ActionRunEnding\n /**\n * Every error, in step order. An Actions step's error is recorded and the\n * run goes on, as it does in an action; a function call's error ENDS the\n * run, as it always has, because the steps after it read its result.\n */\n errors: string[]\n /** What each step that ran did, in the Actions run-history phrasing. */\n outcomes: string[]\n /** The workflow's return value, as the pure evaluator reports it. */\n value: number | string | boolean\n /** Every result the function calls bound, by name. */\n results: Record<string, number | string | boolean>\n}\n\ninterface ExecuteWorkflowOptions {\n /** The step to start at: a resume's `nextStepIndex`. */\n startIndex?: number\n /** The list to run: a resume's snapshot. */\n steps?: readonly WorkflowStep[]\n /** The enrollment a resume belongs to. */\n enrollmentRef?: FirebaseFirestore.DocumentReference | null\n /** A run started by another automation's `runWorkflow` step. */\n nested?: boolean\n}\n\n/**\n * Runs a workflow whose steps include Actions steps: function calls and\n * Actions steps, in order, in one scope.\n *\n * - A FUNCTION CALL is evaluated by the platform's pure evaluator,\n * `runWorkflow`, one call at a time, so its expressions see the event, the\n * site's variables and every result bound before it — exactly the scope a\n * function-only workflow gives it. A call that fails ends the run.\n * - An ACTIONS STEP runs through {@link runServerStep}, the executor actions\n * use, and reads the event payload together with every result bound so\n * far: a workflow can compute a score and write it to a dataset. Each one\n * takes the Actions tier gate (`actions`, Pro and up) on top of its own —\n * `webhookPost` on Business, the CRM steps on the CRM suite — and a step\n * only the visitor's browser can run is refused.\n * - `wait` and `waitForEvent` enroll the person and end this call; the rest\n * of the list, results included, continues from the beat. `exitFlow` ends\n * the run.\n *\n * Records nothing and meters nothing: the caller that admitted the run does\n * both, once.\n */\nexport async function executeWorkflow(\n env: ActionRunEnv,\n run: AutomationRun,\n workflow: AutomationWorkflow,\n event: string,\n payload: HostEventPayload,\n options: ExecuteWorkflowOptions = {},\n): Promise<WorkflowExecution> {\n const steps = options.steps ?? workflow.steps ?? []\n const tally: RunTally = { ending: 'ran', errors: [], outcomes: [] }\n const results: Record<string, number | string | boolean> = {}\n if (steps.length > WORKFLOW_MAX_STEPS) {\n return {\n ...tally,\n errors: [`Workflows are capped at ${WORKFLOW_MAX_STEPS} steps`],\n value: '',\n results,\n }\n }\n const context = await env.loadWorkflowContext()\n const enrollmentRef = options.enrollmentRef ?? null\n const startIndex = Math.max(0, options.startIndex ?? 0)\n /** The payload an Actions step reads: the event's, then every result. */\n const stepPayload = (): HostEventPayload => ({ ...payload, ...results })\n for (let index = startIndex; index < steps.length; index += 1) {\n const step = steps[index]\n if (!isWorkflowActionStep(step)) {\n const call = step as HostWorkflowStep\n const resultName = call.resultName?.trim() || `step${index + 1}`\n const evaluated = runWorkflow(\n { name: workflow.name, steps: [{ ...call, resultName }] },\n context.functions,\n context.variables,\n { event, ...stepPayload() },\n { workflows: context.workflows },\n )\n if (evaluated.ok === false) {\n // The evaluator numbers the one step it was handed; the author\n // numbers it by its place in the workflow.\n tally.errors.push(\n evaluated.error.replace(/^Step 1\\b/, `Step ${index + 1}`),\n )\n break\n }\n results[resultName] = evaluated.results[resultName]\n tally.outcomes.push(\n `ran ${String(call.functionName || call.functionId || 'a function').trim()}`,\n )\n continue\n }\n const refusal = workflowActionStepRefusal(step)\n if (refusal) {\n tally.errors.push(refusal)\n continue\n }\n if (!env.actionsAllowed) {\n tally.errors.push(\n `“${workflowStepTypeLabel(step.type)}” needs the ` +\n `${planLabelGrantingFeature('actions')} plan`,\n )\n continue\n }\n const verdict = await runServerStep(env, run, step, {\n index,\n steps,\n event,\n payload: stepPayload(),\n scope: { event, ...stepPayload() },\n enrollmentRef,\n nested: options.nested,\n })\n if (tallyStep(tally, step.type, verdict)) break\n }\n /*\n * The return value the pure evaluator would give: the named scope entry —\n * a result, a payload field or a variable — when the workflow names one,\n * and the last result otherwise. Asked of the evaluator itself, with no\n * steps, so a variable resolves by the evaluator's own rules.\n */\n const returnName = workflow.returnValue?.trim()\n const value = returnName\n ? (() => {\n const named = runWorkflow(\n { name: workflow.name, steps: [], returnValue: returnName },\n context.functions,\n context.variables,\n { event, ...stepPayload() },\n )\n return named.ok === false ? '' : named.value\n })()\n : (Object.values(results).at(-1) ?? '')\n return { ...tally, value, results }\n}\n\n/**\n * Suspends the run at a `wait` or `waitForEvent` step.\n *\n * One function for both the first suspension and every later one, because the\n * two differ only in whether a row already exists — and writing \"create here,\n * update there\" twice is how the two drift into disagreeing about which\n * fields a waiting enrollment carries.\n */\nasync function suspendFlow(\n env: ActionRunEnv,\n run: AutomationRun,\n request: {\n step: HostActionStep\n steps: readonly WorkflowStep[]\n nextStepIndex: number\n event: string\n payload: HostEventPayload\n enrollmentRef: FirebaseFirestore.DocumentReference | null\n },\n): Promise<{ error?: string; detail?: string }> {\n const { step } = request\n const minutes =\n step.type === 'wait'\n ? Number(step.delayMinutes)\n : step.type === 'waitForEvent'\n ? Number(step.timeoutMinutes)\n : 0\n if (!Number.isFinite(minutes) || minutes <= 0) {\n return { error: 'the wait has no duration' }\n }\n const nowMs = Date.now()\n const resumeAtMs = nowMs + minutes * 60_000\n const awaitingEvent =\n step.type === 'waitForEvent' ? step.eventName.trim() : null\n const detail =\n step.type === 'waitForEvent'\n ? `${awaitingEvent} (up to ${minutes}m)`\n : `${minutes}m`\n\n if (request.enrollmentRef) {\n await advanceFlowEnrollment(\n request.enrollmentRef,\n {\n nextStepIndex: request.nextStepIndex,\n resumeAtMs,\n awaitingEvent,\n payload: request.payload as Record<string, unknown>,\n },\n nowMs,\n )\n return { detail }\n }\n\n const enrolled = await enrollInFlow({\n hostId: env.hostId,\n actionId: run.id,\n // Absent for an action, which is every enrollment written before a\n // workflow could wait — the resume reads absent as `action`.\n ...(run.kind === 'workflow' ? { automation: 'workflow' as const } : {}),\n // The SNAPSHOT is the list this run is executing, which on a first run is\n // the automation's own — so one edited later cannot move this person's\n // remaining steps out from under them.\n action: { name: run.name, steps: [...request.steps] },\n email: String((request.payload as any)?.['email'] ?? ''),\n event: request.event,\n payload: (request.payload ?? {}) as Record<string, unknown>,\n nextStepIndex: request.nextStepIndex,\n resumeAtMs,\n awaitingEvent,\n nowMs,\n })\n if (enrolled.enrolled === true) return { detail }\n /*\n * Both refusals are stated rather than swallowed, because both are things\n * an author can act on: a flow that waits needs an address in its trigger\n * payload, and a person already inside this flow is not put through it\n * twice concurrently.\n */\n return {\n error:\n enrolled.reason === 'no-person'\n ? 'a flow that waits needs the person’s email in the event payload'\n : 'this person is already waiting inside this flow',\n }\n}\n\n/**\n * Events too frequent to log a skip for.\n *\n * `runEventActions` fires on EVERY page view of every published site. A\n * Firestore write per visitor per non-matching action is not a run\n * history, it is an outage — and a page-view condition is one an author\n * tunes by watching the site, not by reading a log. The events people\n * actually debug are the server-emitted ones (a form submission, a\n * booking, a sign-up), and those are low-volume by construction.\n */\nconst SKIP_LOG_EXCLUDED_EVENTS = new Set(['pageView'])\n\n/**\n * Writes the `Skipped` row (AGL-2171): which condition stopped the run,\n * so the answer to \"why didn't it fire?\" is in the same place as every\n * run that did.\n *\n * Never counts against `actionRunsPerMonth` — nothing executed, and\n * charging for a condition that said no would be its own bug.\n */\nasync function recordSkippedRun(\n hostRef: FirebaseFirestore.DocumentReference,\n actionId: string,\n action: HostAction,\n event: string,\n): Promise<void> {\n if (SKIP_LOG_EXCLUDED_EVENTS.has(event)) return\n // `normalizeTriggerConditions`, not `trigger.conditions` — a pre-AGL-565\n // action carries a single `condition` and reading only the array would\n // give every one of them the nameless fallback.\n const named = normalizeTriggerConditions(action.trigger)\n .map((condition) => String(condition?.field ?? '').trim())\n .filter(Boolean)\n const reason = named.length\n ? `Condition on ${named.slice(0, 3).join(', ')} not met`\n : 'Trigger condition not met'\n await hostRef\n .collection('activity')\n .add({\n actorId: null,\n actorEmail: null,\n action: `Action skipped on ${event}`,\n result: 'skipped',\n trigger: event,\n summary: reason.slice(0, 300),\n target: { type: 'workflow', id: actionId, name: action.name ?? '' },\n createdAt: FieldValue.serverTimestamp(),\n })\n .catch(() => undefined)\n}\n\n/**\n * Event-triggered action runner (AGL-148): loads enabled actions whose\n * `trigger.event` matches (built-in, site event, or custom), evaluates\n * optional filters over the payload, and executes each step list in\n * order. Never throws into the emitting request. Paid feature: the\n * `actions` flag gates and `actionRunsPerMonth` meters runs.\n */\nexport async function runEventActions(\n hostId: string,\n event: string,\n payload: HostEventPayload = {},\n depth = 0,\n): Promise<HostActionAlert[]> {\n const alerts: HostActionAlert[] = []\n if (depth > ACTION_MAX_EVENT_DEPTH) return alerts\n /*\n * A flow waiting for THIS event, for THIS person, resumes here.\n *\n * Above the action lookup and outside its try, because the two are\n * unrelated: an event that matches no action can still be the one a flow\n * has been waiting a week for, and a site with no actions at all can hold\n * enrollments from an action that has since been rewritten.\n */\n await wakeFlowsAwaitingEvent(hostId, event, payload).catch(() => undefined)\n try {\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const triggered = await hostRef\n .collection('actions')\n .where('trigger.event', '==', event)\n .limit(MAX_TRIGGERED_ACTIONS)\n .get()\n const actions = triggered.docs.filter(\n (doc) => !doc.get('deletedAt') && doc.get('enabled') !== false,\n )\n if (!actions.length) return alerts\n\n const monthKey = new Date().toISOString().slice(0, 7)\n const runCounterRef = hostRef.collection('counters').doc('actionRuns')\n // Webhook steps take the higher `webhooks` gate (AGL-149); plan gates\n // ride the owning org's doc (AGL-238).\n let webhooksAllowed = true\n let crmAllowed = true\n // Plan-less orgs resolve as free (AGL-247) — gates always run. Held for\n // the rest of the run so the dataset caps below cost no second read.\n const owner = await getOrgForHost(hostId)\n {\n const org = owner?.org\n if (!checkEntitlement(org as any, 'actions')) return alerts\n webhooksAllowed = checkEntitlement(org as any, 'webhooks')\n crmAllowed = checkEntitlement(org as any, 'crm')\n const limit = resolveOrgEntitlements(\n org as any,\n ).actionRunsPerMonth\n const counterSnapshot = await runCounterRef.get()\n const used = Number(counterSnapshot.get(monthKey) ?? 0)\n if (used + actions.length > limit) return alerts\n }\n\n const env: ActionRunEnv = {\n hostId,\n hostRef,\n alerts,\n // Admitted by the `actions` gate above.\n actionsAllowed: true,\n webhooksAllowed,\n crmAllowed,\n depth,\n org: owner?.org ?? null,\n orgId: owner?.orgId ?? null,\n loadWorkflowContext: makeWorkflowContextLoader(hostRef),\n }\n\n let executed = 0\n for (const doc of actions) {\n const action = doc.data() as HostAction\n const filter = action.trigger?.filter?.trim()\n if (filter) {\n try {\n if (!evaluateExpression(filter, { event, ...payload })) continue\n } catch {\n continue // A broken filter never fires.\n }\n }\n // Structured payload conditions (AGL-557; AND/OR chaining AGL-565):\n // same scope as the filter; unmet conditions skip the action and\n // never count as a run against the quota.\n if (!evaluateTriggerConditions(action.trigger, { event, ...payload })) {\n // …but they are RECORDED now (AGL-2171). This was a bare\n // `continue`, so \"why didn't my automation fire?\" — the most\n // common support question about automations — had no answer\n // anywhere in the product, while `/product/workflows` advertises\n // an amber `Skipped` row that answers it.\n await recordSkippedRun(hostRef, doc.id, action, event)\n continue\n }\n executed += 1\n await executeAction(env, doc.id, action, event, payload)\n }\n if (executed > 0) {\n await runCounterRef\n .set({ [monthKey]: FieldValue.increment(executed) }, { merge: true })\n .catch(() => undefined)\n }\n } catch (error) {\n console.error('runEventActions failed', hostId, event, error)\n }\n return alerts\n}\n\n/**\n * Runs ONE action's server steps (AGL-256): the tenant page runtime\n * evaluates site-event trigger conditions (scroll thresholds, selectors)\n * client-side and dispatches the specific action here — re-matching by\n * event name would wrongly fire sibling actions with different\n * thresholds. Same gates and metering as the event runner.\n */\nexport async function runSingleAction(\n hostId: string,\n actionId: string,\n event: string,\n payload: HostEventPayload = {},\n): Promise<HostActionAlert[]> {\n const alerts: HostActionAlert[] = []\n try {\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const doc = await hostRef.collection('actions').doc(actionId).get()\n if (!doc.exists || doc.get('deletedAt') || doc.get('enabled') === false) {\n return alerts\n }\n const action = doc.data() as HostAction\n // Only site-event actions may be dispatched externally — server\n // events flow through their own emitters.\n if (String(action.trigger?.event ?? '') !== String(event)) return alerts\n // Structured payload conditions (AGL-557; AND/OR chaining AGL-565):\n // the single-action dispatch path honors them too, so\n // client-evaluated triggers can't bypass them.\n if (!evaluateTriggerConditions(action.trigger, { event, ...payload })) {\n return alerts\n }\n\n const monthKey = new Date().toISOString().slice(0, 7)\n const runCounterRef = hostRef.collection('counters').doc('actionRuns')\n let webhooksAllowed = true\n let crmAllowed = true\n // Held for the rest of the run, as in `runEventActions` above.\n const owner = await getOrgForHost(hostId)\n {\n const org = owner?.org\n if (!checkEntitlement(org as any, 'actions')) return alerts\n webhooksAllowed = checkEntitlement(org as any, 'webhooks')\n crmAllowed = checkEntitlement(org as any, 'crm')\n const limit = resolveOrgEntitlements(\n org as any,\n ).actionRunsPerMonth\n const counterSnapshot = await runCounterRef.get()\n const used = Number(counterSnapshot.get(monthKey) ?? 0)\n if (used + 1 > limit) return alerts\n }\n\n const env: ActionRunEnv = {\n hostId,\n hostRef,\n alerts,\n // Admitted by the `actions` gate above.\n actionsAllowed: true,\n webhooksAllowed,\n crmAllowed,\n depth: 0,\n org: owner?.org ?? null,\n orgId: owner?.orgId ?? null,\n loadWorkflowContext: makeWorkflowContextLoader(hostRef),\n }\n await executeAction(env, doc.id, action, event, payload)\n await runCounterRef\n .set({ [monthKey]: FieldValue.increment(1) }, { merge: true })\n .catch(() => undefined)\n } catch (error) {\n console.error('runSingleAction failed', hostId, actionId, error)\n }\n return alerts\n}\n\n/**\n * Ends an enrollment its automation may no longer run, and says why in the\n * run history — the automation was deleted or switched off, or the plan that\n * carried it lapsed. Shared by both kinds of enrollment, so \"stopped mid-wait\"\n * reads the same for an action and a workflow.\n */\nexport async function stopFlowEnrollment(\n enrollment: FlowEnrollment,\n ref: FirebaseFirestore.DocumentReference,\n reason: string,\n): Promise<'stopped'> {\n const hostRef = firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(enrollment.hostId)\n await endFlowEnrollment(ref)\n await hostRef\n .collection('activity')\n .add({\n actorId: null,\n actorEmail: null,\n action: `Flow stopped mid-wait: ${reason}`.slice(0, 300),\n result: 'skipped',\n trigger: enrollment.event,\n summary: reason.slice(0, 300),\n target: {\n type: 'workflow',\n id: enrollment.actionId,\n name: enrollment.actionName ?? '',\n },\n createdAt: FieldValue.serverTimestamp(),\n })\n .catch(() => undefined)\n return 'stopped'\n}\n\n/**\n * Continues one enrollment from where its wait ended.\n *\n * The enrollment carries everything the run needs except the gates: the step\n * list it entered with, the position inside it, the payload the trigger\n * produced, and who it is about. What it deliberately does NOT carry is\n * permission — the `actions` entitlement, the site's lockdown and the\n * action's own enabled flag are all re-asked here, because a flow that waits\n * three days is a flow that can outlive the plan, the site and the merchant's\n * decision to run it.\n */\nexport async function resumeFlowEnrollment(\n enrollment: FlowEnrollment,\n ref: FirebaseFirestore.DocumentReference,\n options?: { timedOut?: boolean; nowMs?: number },\n): Promise<'ran' | 'waiting' | 'exited' | 'deferred' | 'stopped'> {\n const nowMs = options?.nowMs ?? Date.now()\n // A workflow's enrollment continues the workflow, against the workflow's\n // own kill switch and meter; every enrollment without the field is an\n // action's, which is every one written before a workflow could wait.\n if (enrollment.automation === 'workflow') {\n return await resumeWorkflowEnrollment(enrollment, ref, {\n ...options,\n nowMs,\n })\n }\n const hostId = enrollment.hostId\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n\n const stop = (reason: string) => stopFlowEnrollment(enrollment, ref, reason)\n\n const doc = await hostRef\n .collection('actions')\n .doc(enrollment.actionId)\n .get()\n .catch(() => null)\n /*\n * A KILL SWITCH HAS TO KILL, including for the people already inside.\n *\n * Editing a flow leaves an enrollment alone — it runs the snapshot it\n * entered with. DELETING or DISABLING one does not: \"off\" that keeps mailing\n * the queue for the next three days is not off, and it is the control a\n * merchant reaches for when a flow is doing something wrong. So the two\n * cases are deliberately different, and this is the one that stops.\n */\n if (!doc?.exists || doc.get('deletedAt') || doc.get('enabled') === false) {\n return await stop('the automation was turned off or deleted')\n }\n const action = doc.data() as HostAction\n const owner = await getOrgForHost(hostId).catch(() => null)\n if (!checkEntitlement(owner?.org as any, 'actions')) {\n return await stop('this site’s plan no longer includes automations')\n }\n\n const env: ActionRunEnv = {\n hostId,\n hostRef,\n alerts: [],\n // Admitted by the `actions` gate above.\n actionsAllowed: true,\n webhooksAllowed: checkEntitlement(owner?.org as any, 'webhooks'),\n crmAllowed: checkEntitlement(owner?.org as any, 'crm'),\n depth: 0,\n org: owner?.org ?? null,\n orgId: owner?.orgId ?? null,\n loadWorkflowContext: makeWorkflowContextLoader(hostRef),\n }\n\n const payload: HostEventPayload = {\n ...(enrollment.payload ?? {}),\n // The timeout BRANCH. A `waitForEvent` resumes either way, and this is\n // the one field that says which — so the step after it carries a `when`\n // naming it, and the author gets a timeout path with no nested step list.\n ...(options?.timedOut ? { [FLOW_TIMED_OUT_FIELD]: true } : {}),\n }\n\n const ending = await executeAction(\n env,\n enrollment.actionId,\n action,\n enrollment.event,\n payload,\n {\n startIndex: enrollment.nextStepIndex,\n // An action's enrollment holds only Actions steps: its snapshot is the\n // action's own list.\n steps: enrollment.steps as HostActionStep[],\n enrollmentRef: ref,\n },\n )\n\n if (ending === 'deferred') {\n /*\n * Pushed a beat down the queue rather than retried immediately: the two\n * refusals that reach here are an hour-long platform ceiling and a\n * day-long per-person window, so retrying in sixty seconds would spend a\n * read to be told the same thing sixty more times.\n */\n await deferFlowEnrollment(ref, nowMs + FLOW_CLAIM_RETRY_MS, nowMs)\n return ending\n }\n if (ending !== 'waiting') await endFlowEnrollment(ref)\n\n /*\n * COUNTED, NEVER REFUSED.\n *\n * A resume is real work and belongs on the run meter, so the usage card and\n * the COGS rollup see it. It is not GATED on `actionRunsPerMonth` the way a\n * new run is, because the person is already inside the flow: refusing here\n * would abandon somebody half-way through a sequence, which is a capacity\n * limit enforced against a person rather than against the decision that\n * added them. The gate belongs at enrollment, and that is where it is.\n */\n const monthKey = new Date(nowMs).toISOString().slice(0, 7)\n await hostRef\n .collection('counters')\n .doc('actionRuns')\n .set({ [monthKey]: FieldValue.increment(1) }, { merge: true })\n .catch(() => undefined)\n return ending\n}\n\n/** How long a deferred enrollment waits before the next attempt. */\nexport const FLOW_CLAIM_RETRY_MS = 15 * 60_000\n\n/**\n * The job beat's entry point: resume every flow whose wait has ended.\n *\n * Thin on purpose. The scheduling contract — due-ness, the transactional\n * claim, the scan budget, the lockdown skip — is `sweepDueFlowEnrollments`,\n * and the work is `resumeFlowEnrollment`; this is the wire between them, so\n * neither has to import the other's dependencies to be tested.\n */\nexport async function runDueFlowEnrollments(\n gate: PluginJobHostGate,\n options?: {\n nowMs?: number\n scanBudget?: number\n cursor?: FlowSweepCursor | null\n },\n): Promise<FlowSweepResult> {\n return await sweepDueFlowEnrollments(gate, {\n ...(options?.nowMs !== undefined ? { nowMs: options.nowMs } : {}),\n ...(options?.scanBudget !== undefined\n ? { scanBudget: options.scanBudget }\n : {}),\n ...(options?.cursor ? { cursor: options.cursor } : {}),\n resume: async (enrollment, ref) => {\n await resumeFlowEnrollment(enrollment, ref, {\n // Reaching the sweep IS the timeout for a `waitForEvent`: the event\n // it was watching for never arrived before `resumeAtMs`. A plain\n // `wait` has no timeout to report, so the flag rides the presence of\n // an awaited event rather than the fact of being swept.\n timedOut: Boolean(enrollment.awaitingEvent),\n ...(options?.nowMs !== undefined ? { nowMs: options.nowMs } : {}),\n })\n },\n })\n}\n\n/**\n * Wakes the flows this person's event was being waited for.\n *\n * NOT A POLL. Nothing here scans the enrolled population asking whose turn it\n * is — the event arrives already naming a person, and the lookup is three\n * equality filters against that person's key. A site with ten thousand people\n * waiting costs the same as one with ten.\n *\n * The COST GUARD is the caller's, and it is why this takes an email rather\n * than reading one: `runEventActions` fires on every page view of every\n * published site, so it asks only for events that name a person. A page view\n * does not, and pays nothing.\n */\nasync function wakeFlowsAwaitingEvent(\n hostId: string,\n event: string,\n payload: HostEventPayload,\n): Promise<void> {\n const email = String((payload as any)?.['email'] ?? '').trim()\n if (!email || !email.includes('@')) return\n const waiting = await findFlowEnrollmentsAwaiting({ hostId, event, email })\n for (const doc of waiting) {\n const claimed = await claimFlowEnrollment(doc.ref).catch(() => null)\n if (!claimed) continue\n try {\n /*\n * The awaited event ARRIVED, so this is not the timeout branch — and\n * the arriving payload joins the carried one, so a step after the wait\n * can read the order total the flow was waiting for.\n */\n await resumeFlowEnrollment(\n {\n ...claimed,\n payload: { ...(claimed.payload ?? {}), ...(payload ?? {}) },\n },\n doc.ref,\n { timedOut: false },\n )\n } catch (error) {\n console.error('[flow] event wake failed', doc.ref.path, error)\n await deferFlowEnrollment(doc.ref, Date.now() + FLOW_CLAIM_RETRY_MS)\n }\n }\n}\n\nexport default runEventActions\n"],"names":["ACTION_MAX_EVENT_DEPTH","ACTION_MAX_STEPS","checkEntitlement","planLabelGrantingFeature","checkQuota","WEBHOOK_URL_PATTERN","evaluateExpression","evaluateStepGuard","evaluateTriggerConditions","FLOW_TIMED_OUT_FIELD","flowEmailTopicId","hostPublicOrigin","isClientActionStep","isCrmActionStep","isFlowSuspendingStep","buildDatasetRecordValues","datasetDisplayName","contactCampaignFieldPath","datasetIntegrityFields","datasetIntegrityUpdate","describeStepOutcome","effectiveDatasetModel","normalizeTriggerConditions","resolveOrgEntitlements","runWorkflow","WORKFLOW_MAX_STEPS","isDeferrableSendResult","isEmailConfigured","sendEmail","sendFailureReason","dataStorageRefusal","enrollListMember","firebaseAdmin","flowEmailRefusal","getOrgForHost","hostSendingIdentity","meterHostEmail","notifyHostManagers","consentGroupForSite","orgDataCollectionForHost","orgDataQueryForHost","resolveOrgIdForHost","announceDatasetRecordChange","findContactByEmail","createHmac","FieldValue","advanceFlowEnrollment","claimFlowEnrollment","deferFlowEnrollment","endFlowEnrollment","enrollInFlow","findFlowEnrollmentsAwaiting","sweepDueFlowEnrollments","logCrmEmailActivity","prepareCrmEmailActivity","runCrmActionStep","resolveDatasetDoc","resumeWorkflowEnrollment","runEventWorkflows","isWorkflowActionStep","workflowActionStepRefusal","workflowHasActionSteps","workflowStepTypeLabel","MAX_TRIGGERED_ACTIONS","automationRunEnv","input","org","owner","hostId","hostRef","alerts","actionsAllowed","webhooksAllowed","crmAllowed","depth","orgId","loadWorkflowContext","makeWorkflowContextLoader","workflowContext","functionDocs","variableDocs","workflowDocs","Promise","all","collection","limit","get","workflowContextFromDocs","docs","byName","map","doc","data","deletedAt","id","name","workflows","withId","$id","functions","variables","announceDatasetStepWrite","env","datasetId","firestore","app","datasetAppendRefusal","datasetRef","recordsPerDataset","Number","isFinite","used","count","allowed","bytes","includedMb","tallyStep","tally","type","verdict","kind","outcomes","push","detail","errors","error","ending","runServerStep","run","step","context","event","payload","enrollmentRef","failed","when","scope","nested","suspended","suspendFlow","steps","nextStepIndex","index","message","String","slice","severity","workflow","workflowId","trim","workflowName","executeWorkflow","length","join","evaluated","ok","runEventActions","eventName","hookDoc","webhookId","where","webhookName","hook","exists","undefined","enabled","direction","url","test","body","JSON","stringify","sentAt","Date","toISOString","signature","secret","update","digest","delivered","lastStatus","attempt","response","fetch","method","headers","signal","AbortSignal","timeout","status","resolve","setTimeout","datasetsRef","datasetDoc","datasetName","appendDataset","model","fields","Array","isArray","values","appendLabel","displayName","Object","keys","refusal","ref","add","createdAt","serverTimestamp","updateDataset","updateModel","updateLabel","email","existing","empty","merged","set","delete","updatedAt","merge","title","link","to","toField","includes","siteBase","catch","topicId","gate","emailSubject","subject","emailText","emailActivity","result","text","sendingIdentity","tags","audience","priority","marketing","sendError","sent","toLowerCase","listsRef","listDoc","listId","listName","listRef","group","source","contactsRef","contact","campaignsRef","namedId","campaignId","campaignDoc","campaignName","groupId","arrayUnion","outcome","emit","fromWorkflows","fromActions","executeAction","actionId","action","options","startIndex","Math","max","stepErrors","summary","actorId","actorEmail","trigger","target","results","value","stepPayload","call","resultName","replace","functionName","functionId","returnName","returnValue","named","at","request","minutes","delayMinutes","timeoutMinutes","nowMs","now","resumeAtMs","awaitingEvent","enrolled","automation","reason","SKIP_LOG_EXCLUDED_EVENTS","Set","recordSkippedRun","has","condition","field","filter","Boolean","wakeFlowsAwaitingEvent","triggered","actions","monthKey","runCounterRef","counterSnapshot","actionRunsPerMonth","executed","increment","console","runSingleAction","stopFlowEnrollment","enrollment","actionName","resumeFlowEnrollment","stop","timedOut","FLOW_CLAIM_RETRY_MS","runDueFlowEnrollments","scanBudget","cursor","resume","waiting","claimed","path"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,sBAAsB,EACtBC,gBAAgB,EAChBC,gBAAgB,EAChBC,wBAAwB,EACxBC,UAAU,EAEVC,mBAAmB,EACnBC,kBAAkB,EAClBC,iBAAiB,EACjBC,yBAAyB,EACzBC,oBAAoB,EACpBC,gBAAgB,EAChBC,gBAAgB,EAChBC,kBAAkB,EAClBC,eAAe,EACfC,oBAAoB,EASpBC,wBAAwB,EACxBC,kBAAkB,EAClBC,wBAAwB,EACxBC,sBAAsB,EACtBC,sBAAsB,EACtBC,mBAAmB,EACnBC,qBAAqB,EACrBC,0BAA0B,EAE1BC,sBAAsB,EACtBC,WAAW,EACXC,kBAAkB,QACb,sBAAqB;AAC5B,SACEC,sBAAsB,EACtBC,iBAAiB,EACjBC,SAAS,EACTC,iBAAiB,QACZ,2BAA0B;AACjC,SACEC,kBAAkB,EAClBC,gBAAgB,EAChBC,aAAa,EACbC,gBAAgB,EAChBC,aAAa,EACbC,mBAAmB,EACnBC,cAAc,EACdC,kBAAkB,EAClBC,mBAAmB,EACnBC,wBAAwB,EACxBC,mBAAmB,EACnBC,mBAAmB,QACd,2BAA0B;AACjC,SAASC,2BAA2B,QAAQ,qDAAoD;AAChG,uEAAuE;AACvE,wEAAwE;AACxE,SAASC,kBAAkB,QAAQ,sDAAqD;AACxF,SAASC,UAAU,QAAQ,SAAQ;AACnC,SAASC,UAAU,QAAQ,2BAA0B;AACrD,SACEC,qBAAqB,EACrBC,mBAAmB,EACnBC,mBAAmB,EACnBC,iBAAiB,EACjBC,YAAY,EACZC,2BAA2B,EAI3BC,uBAAuB,QAClB,wBAAoB;AAC3B,SACEC,mBAAmB,EACnBC,uBAAuB,EACvBC,gBAAgB,QACX,wBAAoB;AAI3B,SAASC,iBAAiB,QAAQ,wCAAuC;AACzE,SACEC,wBAAwB,EACxBC,iBAAiB,QACZ,2BAAuB;AAC9B,SAEEC,oBAAoB,EAEpBC,yBAAyB,EACzBC,sBAAsB,EACtBC,qBAAqB,QAChB,sBAAkB;AAEzB,8DAA8D,GAC9D,MAAMC,wBAAwB;AA2D9B;;;CAGC,GACD,OAAO,SAASC,iBAAiBC,KAOhC;cAKWA,sBAQNA;QAZQA,cAUHA;IAVT,MAAMC,eAAMD,eAAAA,MAAME,KAAK,qBAAXF,aAAaC,GAAG,mBAAI;IAChC,OAAO;QACLE,QAAQH,MAAMG,MAAM;QACpBC,SAASJ,MAAMI,OAAO;QACtBC,MAAM,GAAEL,gBAAAA,MAAMK,MAAM,YAAZL,gBAAgB,EAAE;QAC1BM,gBAAgBrE,iBAAiBgE,KAAY;QAC7CM,iBAAiBtE,iBAAiBgE,KAAY;QAC9CO,YAAYvE,iBAAiBgE,KAAY;QACzCQ,OAAOT,MAAMS,KAAK;QAClBR;QACAS,KAAK,YAAEV,gBAAAA,MAAME,KAAK,qBAAXF,cAAaU,KAAK,oBAAI;QAC7BC,mBAAmB,GACjBX,6BAAAA,MAAMW,mBAAmB,YAAzBX,6BAA6BY,0BAA0BZ,MAAMI,OAAO;IACxE;AACF;AAEA,OAAO,SAASQ,0BACdR,OAA4C;IAE5C,IAAIS,kBAA0C;IAC9C,OAAO;QACL,IAAIA,iBAAiB,OAAOA;QAC5B,MAAM,CAACC,cAAcC,cAAcC,aAAa,GAAG,MAAMC,QAAQC,GAAG,CAAC;YACnEd,QAAQe,UAAU,CAAC,aAAaC,KAAK,CAAC,KAAKC,GAAG;YAC9CjB,QAAQe,UAAU,CAAC,aAAaC,KAAK,CAAC,KAAKC,GAAG;YAC9CjB,QAAQe,UAAU,CAAC,aAAaC,KAAK,CAAC,KAAKC,GAAG;SAC/C;QACDR,kBAAkBS,wBAChBR,aAAaS,IAAI,EACjBR,aAAaQ,IAAI,EACjBP,aAAaO,IAAI;QAEnB,OAAOV;IACT;AACF;AAEA;;;CAGC,GACD,OAAO,SAASS,wBACdR,YAAgE,EAChEC,YAAgE,EAChEC,YAAgE;IAEhE,+DAA+D;IAC/D,sDAAsD;IACtD,MAAMQ,SAAS,CACbD;QAEA,MAAME,MAAyB,CAAC;QAChC,KAAK,MAAMC,OAAOH,KAAM;YACtB,MAAMI,OAAOD,IAAIC,IAAI;YACrB,IAAIA,KAAKC,SAAS,EAAE;YACpBH,GAAG,CAACC,IAAIG,EAAE,CAAC,GAAGF;YACd,IAAIA,wBAAAA,KAAMG,IAAI,EAAEL,GAAG,CAACE,KAAKG,IAAI,CAAC,GAAGH;QACnC;QACA,OAAOF;IACT;IACA,sEAAsE;IACtE,sEAAsE;IACtE,MAAMM,YAA0C,CAAC;IACjD,KAAK,MAAML,OAAOV,aAAc;QAC9B,MAAMW,OAAOD,IAAIC,IAAI;QACrB,IAAI,AAACA,KAAiCC,SAAS,EAAE;QACjD,MAAMI,SAAS,aAAKL;YAAMM,KAAKP,IAAIG,EAAE;;QACrCE,SAAS,CAACL,IAAIG,EAAE,CAAC,GAAGG;QACpB,IAAIL,wBAAAA,KAAMG,IAAI,EAAEC,SAAS,CAACJ,KAAKG,IAAI,CAAC,GAAGE;IACzC;IACA,OAAO;QACLE,WAAWV,OAAqBV;QAChCqB,WAAWX,OAAqBT;QAChCgB;IACF;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCC,GACD;;;;;;;;;;;;;;CAcC,GACD,eAAeK,yBACbC,GAAiB,EACjBC,SAAiB;IAEjB,IAAI,CAACD,IAAI3B,KAAK,EAAE;IAChB,MAAMjC,4BAA4B;QAChC8D,WAAWxE,cAAcyE,GAAG,GAAGD,SAAS;QACxC7B,OAAO2B,IAAI3B,KAAK;QAChB4B;IACF;AACF;AAEA,eAAeG,qBACbJ,GAAiB,EACjBK,UAA+C;IAE/C,MAAMtB,QAAQ9D,uBAAuB+E,IAAIpC,GAAG,EAAW0C,iBAAiB;IACxE,IAAIC,OAAOC,QAAQ,CAACzB,QAAQ;QAC1B,MAAM0B,OAAO,AACX,CAAA,MAAMJ,WAAWvB,UAAU,CAAC,WAAW4B,KAAK,GAAG1B,GAAG,EAAC,EACnDM,IAAI,GAAGoB,KAAK;QACd,IAAI,CAAC5G,WAAWkG,IAAIpC,GAAG,EAAW,qBAAqB6C,MAAME,OAAO,EAAE;YACpE,OAAO,CAAC,iBAAiB,EAAE5B,MAAM,sBAAsB,CAAC;QAC1D;IACF;IACA,IAAI,CAACiB,IAAI3B,KAAK,EAAE,OAAO;IACvB,MAAMuC,QAAQ,MAAMpF,mBAClBwE,IAAIpC,GAAG,EACPlC,cAAcyE,GAAG,GAAGD,SAAS,GAAGpB,UAAU,CAAC,QAAQO,GAAG,CAACW,IAAI3B,KAAK;IAElE,IAAI,CAACuC,OAAO,OAAO;IACnB,OAAO,CAAC,yBAAyB,EAAEA,MAAMC,UAAU,CAAC,iBAAiB,CAAC;AACxE;AA0EA;;;CAGC,GACD,SAASC,UACPC,KAAe,EACfC,IAAwB,EACxBC,OAA0B;IAE1B,OAAQA,QAAQC,IAAI;QAClB,KAAK;YACH,OAAO;QACT,KAAK;YACHH,MAAMI,QAAQ,CAACC,IAAI,CAACtG,oBAAoBkG,MAAMC,QAAQI,MAAM;YAC5D,OAAO;QACT,KAAK;YACHN,MAAMO,MAAM,CAACF,IAAI,CAACH,QAAQM,KAAK;YAC/B,OAAO;QACT,KAAK;YACHR,MAAMS,MAAM,GAAG;YACfT,MAAMI,QAAQ,CAACC,IAAI,CAACtG,oBAAoBkG;YACxC,OAAO;QACT,KAAK;YACHD,MAAMS,MAAM,GAAG;YACfT,MAAMI,QAAQ,CAACC,IAAI,CAACtG,oBAAoBkG,MAAMC,QAAQI,MAAM;YAC5D,OAAO;QACT,KAAK;YACHN,MAAMO,MAAM,CAACF,IAAI,CAACH,QAAQM,KAAK;YAC/B,OAAO;QACT,KAAK;YACHR,MAAMS,MAAM,GAAG;YACf,OAAO;IACX;AACF;AAEA;;;;;;;;;;;;;CAaC,GACD,eAAeC,cACbzB,GAAiB,EACjB0B,GAAkB,EAClBC,IAAoB,EACpBC,OAA0B;IAE1B,MAAM,EAAE9D,MAAM,EAAEC,OAAO,EAAEC,MAAM,EAAEI,KAAK,EAAE,GAAG4B;IAC3C,MAAM,EAAE6B,KAAK,EAAEC,OAAO,EAAEC,aAAa,EAAE,GAAGH;IAC1C;;;GAGC,GACD,IAAIP;IACJ,MAAMW,SAAS,CAACT,QAAsC,CAAA;YAAEL,MAAM;YAAUK;QAAM,CAAA;IAC9E,IAAI;QACF;;;;;;KAMC,GACD,IAAI,CAACtH,kBAAkB0H,KAAKM,IAAI,EAAEL,QAAQM,KAAK,GAAG,OAAO;YAAEhB,MAAM;QAAU;QAC3E,IAAI5G,mBAAmBqH,SAASA,KAAKX,IAAI,KAAK,aAAa;YACzD,OAAO;gBAAEE,MAAM;YAAU,EAAE,wCAAwC;;QACrE;QACA,IAAIS,KAAKX,IAAI,KAAK,YAAY,OAAO;YAAEE,MAAM;QAAS;QACtD,IAAI1G,qBAAqBmH,OAAO;YAC9B,qEAAqE;YACrE,sDAAsD;YACtD,IAAIC,QAAQO,MAAM,EAAE;gBAClB,OAAO;oBACLjB,MAAM;oBACNK,OACE,kEACA;gBACJ;YACF;YACA,MAAMa,YAAY,MAAMC,YAAYrC,KAAK0B,KAAK;gBAC5CC;gBACAW,OAAOV,QAAQU,KAAK;gBACpBC,eAAeX,QAAQY,KAAK,GAAG;gBAC/BX;gBACAC;gBACAC;YACF;YACA,IAAIK,UAAUb,KAAK,EAAE,OAAO;gBAAEL,MAAM;gBAAUK,OAAOa,UAAUb,KAAK;YAAC;YACrE,OAAO;gBAAEL,MAAM;gBAAWG,QAAQe,UAAUf,MAAM;YAAC;QACrD;QACA,IAAIM,KAAKX,IAAI,KAAK,aAAa;gBAEXW,eACNA;YAFZ3D,OAAOoD,IAAI,CAAC;gBACVqB,SAASC,QAAOf,gBAAAA,KAAKc,OAAO,YAAZd,gBAAgB,IAAIgB,KAAK,CAAC,GAAG;gBAC7CC,QAAQ,GAAEjB,iBAAAA,KAAKiB,QAAQ,YAAbjB,iBAAiB;YAC7B;QACF,OAAO,IAAIA,KAAKX,IAAI,KAAK,eAAe;gBAGpCxC;gBAA0BmD,kBACAA;YAH5B,MAAMnD,kBAAkB,MAAMwB,IAAI1B,mBAAmB;YACrD,MAAMuE,YACJrE,8BAAAA,gBAAgBkB,SAAS,UAACiC,mBAAAA,KAAKmB,UAAU,qBAAfnB,iBAAiBoB,IAAI,qBAAM,GAAG,YAAxDvE,8BACAA,gBAAgBkB,SAAS,WAACiC,qBAAAA,KAAKqB,YAAY,qBAAjBrB,mBAAmBoB,IAAI,sBAAM,GAAG;YAC5D,IAAI,CAACF,UAAU;gBACb,OAAOb,OAAO,CAAC,kBAAkB,EAAEL,KAAKqB,YAAY,IAAIrB,KAAKmB,UAAU,CAAC,CAAC,CAAC;YAC5E;YACA,IAAIvF,uBAAuBsF,WAAW;oBAc5BA,OAAAA,eACEA;oBADclB;gBAbxB;;;;;SAKC,GACD,IAAIvD,QAAQ,IAAI1E,wBAAwB;oBACtC,OAAOsI,OAAO,CAAC,UAAU,EAAEa,SAASpD,IAAI,CAAC,sBAAsB,CAAC;gBAClE;gBACA,MAAM0C,SAAS,MAAMc,gBACnB,aAAKjD;oBAAK5B,OAAOA,QAAQ;oBACzB;oBACE8C,MAAM;oBACN1B,EAAE,GAAEqD,SAAAA,gBAAAA,SAASjD,GAAG,YAAZiD,iBAAgBlB,oBAAAA,KAAKmB,UAAU,qBAAfnB,kBAAiBoB,IAAI,cAArCF,QAA2C;oBAC/CpD,IAAI,GAAEoD,iBAAAA,SAASpD,IAAI,YAAboD,iBAAiB;gBACzB,GACAA,UACAhB,OACAC,SACA;oBAAEK,QAAQ;gBAAK;gBAEjB,IAAIA,OAAOb,MAAM,CAAC4B,MAAM,EAAE;oBACxB,OAAOlB,OACL,CAAC,UAAU,EAAEa,SAASpD,IAAI,CAAC,GAAG,EAAE0C,OAAOb,MAAM,CAAC6B,IAAI,CAAC,OAAO,CAACR,KAAK,CAAC,GAAG;gBAExE;YACF,OAAO;gBACL,MAAMS,YAAYlI,YAChB2H,UACArE,gBAAgBqB,SAAS,EACzBrB,gBAAgBsB,SAAS,EACzB;oBAAE+B;mBAAUC,UACZ;oBAAEpC,WAAWlB,gBAAgBkB,SAAS;gBAAC;gBAEzC,IAAI0D,UAAUC,EAAE,KAAK,OAAO,OAAOrB,OAAOoB,UAAU7B,KAAK;YAC3D;QACF,OAAO,IAAII,KAAKX,IAAI,KAAK,eAAe;YACtC,MAAMmB,SAAS,MAAMmB,gBACnBxF,QACA6D,KAAK4B,SAAS,CAACR,IAAI,IACnBjB,SACA1D,QAAQ;YAEVJ,OAAOoD,IAAI,IAAIe;QACjB,OAAO,IAAIR,KAAKX,IAAI,KAAK,eAAe;;gBAGtBW,iBAQaA;YAV7B,IAAI,CAAC3B,IAAI9B,eAAe,EAAE,OAAO8D,OAAO;YACxC,gEAAgE;YAChE,MAAMwB,UAAU7B,EAAAA,kBAAAA,KAAK8B,SAAS,qBAAd9B,gBAAgBoB,IAAI,MAChC,MAAMhF,QACHe,UAAU,CAAC,YACXO,GAAG,CAACsC,KAAK8B,SAAS,CAACV,IAAI,IACvB/D,GAAG,KACN,AACE,CAAA,MAAMjB,QACHe,UAAU,CAAC,YACX4E,KAAK,CAAC,QAAQ,gBAAM/B,oBAAAA,KAAKgC,WAAW,qBAAhBhC,kBAAkBoB,IAAI,sBAAM,IAChDhE,KAAK,CAAC,GACNC,GAAG,EAAC,EACPE,IAAI,CAAC,EAAE;YACb,MAAM0E,OAAOJ,CAAAA,2BAAAA,QAASK,MAAM,IACvBL,QAAQlE,IAAI,KACbwE;YACJ,IACE,CAACF,QACDJ,QAAQxE,GAAG,CAAC,gBACZ4E,KAAKG,OAAO,KAAK,SACjBH,KAAKI,SAAS,KAAK,cACnB,CAACJ,KAAKK,GAAG,IACT,CAAClK,oBAAoBmK,IAAI,CAACN,KAAKK,GAAG,GAClC;gBACA,OAAOjC,OAAO,CAAC,iBAAiB,EAAEL,KAAKgC,WAAW,IAAIhC,KAAK8B,SAAS,CAAC,CAAC,CAAC;YACzE;YACA,MAAMU,OAAOC,KAAKC,SAAS,CAAC;gBAC1BxC;gBACAC;gBACAwC,QAAQ,IAAIC,OAAOC,WAAW;YAChC;YACA,MAAMC,YAAYb,KAAKc,MAAM,GACzBpI,WAAW,UAAUsH,KAAKc,MAAM,EAAEC,MAAM,CAACR,MAAMS,MAAM,CAAC,SACtD;YACJ,+DAA+D;YAC/D,mBAAmB;YACnB,IAAIC,YAAY;YAChB,IAAIC;YACJ,IAAK,IAAIC,UAAU,GAAGA,UAAU,KAAK,CAACF,WAAWE,WAAW,EAAG;gBAC7D,IAAI;oBACF,MAAMC,WAAW,MAAMC,MAAMrB,KAAKK,GAAG,EAAE;wBACrCiB,QAAQ;wBACRC,SAAS;4BACP,gBAAgB;2BACZV,aAAa;4BAAE,qBAAqBA;wBAAU;wBAEpDN;wBACAiB,QAAQC,YAAYC,OAAO,CAAC;oBAC9B;oBACAR,aAAaE,SAASO,MAAM;oBAC5BV,YAAYG,SAAS3B,EAAE;gBACzB,EAAE,eAAM;gBACN,eAAe;gBACjB;gBACA,IAAI,CAACwB,aAAaE,UAAU,GAAG;oBAC7B,MAAM,IAAInG,QAAQ,CAAC4G,UACjBC,WAAWD,SAAS,MAAOT,CAAAA,UAAU,CAAA;gBAEzC;YACF;YACA,IAAI,CAACF,WAAW;gBACd,OAAO7C,OACL,CAAC,SAAS,EAAEL,KAAKgC,WAAW,IAAIhC,KAAK8B,SAAS,CAAC,iBAAiB,CAAC;YAErE;YACA,kEAAkE;YAClE,sEAAsE;YACtE,+DAA+D;YAC/DpC,SAASqB,OAAOoC,qBAAAA,aAAc;QAChC,OAAO,IAAInD,KAAKX,IAAI,KAAK,iBAAiB;YACxC,oEAAoE;YACpE,MAAM0E,cAAc,MAAMzJ,yBAAyB6B,QAAQ;YAC3D,MAAM6H,aAAa,MAAMzI,kBAAkBwI,aAAa/D,MAAM7D;YAC9D,IAAI,EAAC6H,8BAAAA,WAAY9B,MAAM,KAAI8B,WAAW3G,GAAG,CAAC,cAAc;gBACtD,OAAOgD,OAAO,CAAC,iBAAiB,EAAEL,KAAKiE,WAAW,IAAIjE,KAAK1B,SAAS,CAAC,CAAC,CAAC;YACzE;YACA,kEAAkE;YAClE,oDAAoD;YACpD,MAAM4F,gBAAgB;gBACpBC,OAAOH,WAAW3G,GAAG,CAAC;gBACtB+G,QAAQC,MAAMC,OAAO,CAACN,WAAW3G,GAAG,CAAC,aACjC2G,WAAW3G,GAAG,CAAC,YACf,EAAE;YACR;YACA,MAAMkH,SAASzL,yBAAyBoL,eAAe/D;YACvD,wDAAwD;YACxD,MAAMqE,cAAc,AAClBzL,CAAAA,mBAAmB;gBACjB0L,aAAaT,WAAW3G,GAAG,CAAC;gBAC5BS,MAAMkG,WAAW3G,GAAG,CAAC;YACvB,MACA2C,KAAKiE,WAAW,IAChB,EAAC,EACDjD,KAAK,CAAC,GAAG;YACX,qEAAqE;YACrE,qEAAqE;YACrE,sEAAsE;YACtE,uBAAuB;YACvB,IAAI,CAAC0D,OAAOC,IAAI,CAACJ,QAAQhD,MAAM,EAAE;gBAC/B,OAAOlB,OACL,CAAC,2CAA2C,EAAEmE,eAAexE,KAAK1B,SAAS,CAAC,CAAC,CAAC;YAElF;YACA,MAAMsG,UAAU,MAAMnG,qBAAqBJ,KAAK2F,WAAWa,GAAG;YAC9D,IAAID,SAAS,OAAOvE,OAAOuE;YAC3B,MAAMZ,WAAWa,GAAG,CAAC1H,UAAU,CAAC,WAAW2H,GAAG,CAAC;gBAC7CP;eAIGtL,uBACDG,sBAAsB8K,gBACtBK;gBAEFQ,WAAWnK,WAAWoK,eAAe;;YAEvC,MAAM5G,yBAAyBC,KAAK2F,WAAWnG,EAAE;YACjD,wDAAwD;YACxD6B,SAAS8E;QACX,OAAO,IAAIxE,KAAKX,IAAI,KAAK,iBAAiB;gBA8BnB;YA7BrB,+DAA+D;YAC/D,kEAAkE;YAClE,MAAM0E,cAAc,MAAMzJ,yBAAyB6B,QAAQ;YAC3D,MAAM6H,aAAa,MAAMzI,kBAAkBwI,aAAa/D,MAAM7D;YAC9D,IAAI,EAAC6H,8BAAAA,WAAY9B,MAAM,KAAI8B,WAAW3G,GAAG,CAAC,cAAc;gBACtD,OAAOgD,OAAO,CAAC,iBAAiB,EAAEL,KAAKiE,WAAW,IAAIjE,KAAK1B,SAAS,CAAC,CAAC,CAAC;YACzE;YACA,MAAM2G,gBAAgB;gBACpBd,OAAOH,WAAW3G,GAAG,CAAC;gBACtB+G,QAAQC,MAAMC,OAAO,CAACN,WAAW3G,GAAG,CAAC,aACjC2G,WAAW3G,GAAG,CAAC,YACf,EAAE;YACR;YACA,MAAM6H,cAAc9L,sBAAsB6L;YAC1C,MAAMV,SAASzL,yBAAyBmM,eAAe9E;YACvD,mEAAmE;YACnE,sBAAsB;YACtB,IAAI,CAACuE,OAAOC,IAAI,CAACJ,QAAQhD,MAAM,EAAE;gBAC/B,MAAM4D,cACJpM,mBAAmB;oBACjB0L,aAAaT,WAAW3G,GAAG,CAAC;oBAC5BS,MAAMkG,WAAW3G,GAAG,CAAC;gBACvB,MACA2C,KAAKiE,WAAW,IAChBjE,KAAK1B,SAAS;gBAChB,OAAO+B,OACL,CAAC,2CAA2C,EAAEU,OAAOoE,sBAAAA,cAAe,IAAInE,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YAE3F;YACA,MAAMoE,QAAQrE,QAAO,iBAAA,AAACZ,QAAgBiF,KAAK,YAAtB,iBAA0B,IAAIhE,IAAI;YACvD,qEAAqE;YACrE,+CAA+C;YAC/C,+DAA+D;YAC/D,2DAA2D;YAC3D,MAAMiE,WAAWD,QACb,MAAMpB,WAAWa,GAAG,CACjB1H,UAAU,CAAC,WACX4E,KAAK,CAAC,gBAAgB,MAAMqD,OAC5BhI,KAAK,CAAC,GACNC,GAAG,KACN;YACJ,IAAIgI,YAAY,CAACA,SAASC,KAAK,EAAE;oBAEzBD;gBADN,MAAME,SAAS,cACTF,sBAAAA,SAAS9H,IAAI,CAAC,EAAE,CAACF,GAAG,CAAC,qBAArBgI,sBAAkC,CAAC,GACpCd;gBAEL,MAAMc,SAAS9H,IAAI,CAAC,EAAE,CAACsH,GAAG,CAACW,GAAG,CAC5B;oBACEjB,QAAQgB;mBAILrM,uBACDgM,aACAK,QACA3K,WAAW6K,MAAM;oBAEnBC,WAAW9K,WAAWoK,eAAe;oBAEvC;oBAAEW,OAAO;gBAAK;YAElB,OAAO;gBACL,kEAAkE;gBAClE,mEAAmE;gBACnE,2BAA2B;gBAC3B,MAAMf,UAAU,MAAMnG,qBAAqBJ,KAAK2F,WAAWa,GAAG;gBAC9D,IAAID,SAAS,OAAOvE,OAAOuE;gBAC3B,MAAMZ,WAAWa,GAAG,CAAC1H,UAAU,CAAC,WAAW2H,GAAG,CAAC;oBAC7CP;mBACGtL,uBAAuBiM,aAAaX;oBACvCQ,WAAWnK,WAAWoK,eAAe;;YAEzC;YACA,yEAAyE;YACzE,wEAAwE;YACxE,MAAM5G,yBAAyBC,KAAK2F,WAAWnG,EAAE;QACnD,OAAO,IAAImC,KAAKX,IAAI,KAAK,gBAAgB;gBAGvBW;YAFhB,MAAM5F,mBAAmB+B,QAAQ;gBAC/BkD,MAAM;gBACNuG,OAAO7E,QAAOf,cAAAA,KAAK4F,KAAK,YAAV5F,cAAc,IAAIgB,KAAK,CAAC,GAAG;eACrChB,KAAKwC,IAAI,GAAG;gBAAEA,MAAMzB,OAAOf,KAAKwC,IAAI,EAAExB,KAAK,CAAC,GAAG;YAAK,IAAI,CAAC;gBAC7D6E,MAAM,CAAC,CAAC,EAAE1J,QAAQ;;QAEtB,OAAO,IAAI6D,KAAKX,IAAI,KAAK,aAAa;gBAElC,WAWA3G,mBA4DSsH,eAiBiBA,eACHA;gBAzFNA,eAYd;YAbL,MAAM8F,KAAK/E,QACT,YAAA,AAACZ,OAAe,CAACH,EAAAA,gBAAAA,KAAK+F,OAAO,qBAAZ/F,cAAcoB,IAAI,OAAM,QAAQ,YAAjD,YAAqD,IACrDA,IAAI;YACN,IAAI,CAAC1H,qBAAqB,OAAO2G,OAAO;YACxC,IAAI,CAACyF,MAAM,CAACA,GAAGE,QAAQ,CAAC,MAAM;gBAC5B,OAAO3F,OAAO;YAChB;YACA,oEAAoE;YACpE,qEAAqE;YACrE,mEAAmE;YACnE,uDAAuD;YACvD,MAAM4F,YACJvN,oBAAAA,kBACG,QAAA,MAAM0D,QAAQiB,GAAG,GAAG6I,KAAK,CAAC,IAAM,0BAAjC,AAAC,MAAwCvI,IAAI,eAD/CjF,oBAEK;YACP;;;;;;;;;;;;;;;;;;OAkBC,GACD;;;;;;;;;;;;;;;;;;;;;;;;;OAyBC,GACD,MAAM6H,QAAQH,gBAAgB,cAAc;YAC5C;;;;;;OAMC,GACD,MAAM+F,UAAU1N,iBAAiBuH,KAAKmG,OAAO,EAAE5F;YAC/C,MAAM6F,OAAO,MAAMpM,iBAAiB;gBAClCmC;gBACAiJ,OAAOU;gBACPK,OAAO,GAAEnG,gBAAAA,KAAKmG,OAAO,YAAZnG,gBAAgB;gBACzB/D,KAAKoC,IAAIpC,GAAG;gBACZsE;YACF;YACA,IAAI6F,MAAM;gBACR,OAAO/F,OACL+F,SAAS,qBACL,4CAEA,yDAAyD;gBACzD,8DAA8D;gBAC9D,qCAAqC;gBACrChG,gBACE,+DACA;YAEV;YACA,MAAMiG,eAAetF,QAAOf,gBAAAA,KAAKsG,OAAO,YAAZtG,gBAAgB,IAAIgB,KAAK,CAAC,GAAG;YACzD,MAAMuF,YAAYxF,QAAOf,aAAAA,KAAKwC,IAAI,YAATxC,aAAa,IAAIgB,KAAK,CAAC,GAAG;YACnD;;;;;;;;;OASC,GACD,MAAMwF,gBAAgBnI,IAAI7B,UAAU,GAChC,MAAMnB,wBACJ;gBAAEc;gBAAQF,KAAKoC,IAAIpC,GAAG;gBAAES,OAAO2B,IAAI3B,KAAK;YAAC,GACzCoJ,IACA3F,WAEF;YACJ,MAAMsG,SAAS,MAAM9M,UAAU;gBAC7BmM;gBACAQ,SAASD;gBACTK,MAAMH;gBACNI,iBAAiB,MAAMzM,oBAAoBiC;eACvCqK,gBAAgB;gBAAEI,MAAMJ,cAAcI,IAAI;YAAC,IAAI,CAAC;gBACpDC,UAAU;gBACV5G,SAASG,gBAAgB,cAAc;eASnCA,gBAAgB;gBAAE0G,UAAU;YAAgB,IAAI,CAAC;gBACrD,gEAAgE;gBAChE,gEAAgE;gBAChEC,WAAW;oBAAE5K;oBAAQ8J;oBAAUE;gBAAQ;;YAEzC;;;;;;;;;OASC,GACD,IAAI/F,iBAAiB3G,uBAAuBgN,SAAS;gBACnD,OAAO;oBAAElH,MAAM;gBAAW;YAC5B;YACA,qEAAqE;YACrE,+DAA+D;YAC/D,mEAAmE;YACnE,MAAMqF,UAAUhL,kBAAkB6M;YAClC,MAAMO,YAAYpC,UACdA,YAAY,eACV,gDACAA,YAAY,qBACV,0DACA,mBACA,0BACJ;YACJ,mEAAmE;YACnE,oEAAoE;YACpE,qEAAqE;YACrE,QAAQ;YACR,IAAI6B,OAAOQ,IAAI,EAAE;gBACf,MAAM9M,eAAegC;gBACrB,IAAIqK,eAAe;oBACjB,MAAMpL,oBACJ;wBAAEe;wBAAQF,KAAKoC,IAAIpC,GAAG;wBAAES,OAAO2B,IAAI3B,KAAK;oBAAC,GACzC8J,eACA;wBAAEF,SAASD;wBAAc7D,MAAM+D;wBAAWT;oBAAG,GAC7C/F,IAAIlC,EAAE;gBAEV;YACF;YACA,IAAImJ,WAAW,OAAO3G,OAAO2G;QAC/B,OAAO,IAAIhH,KAAKX,IAAI,KAAK,cAAc;gBAEhB;gBAYLW,cAIaA;YAjB7B,MAAMtD,QAAQ,MAAMlC,oBAAoB2B;YACxC,MAAMiJ,QAAQrE,QAAO,kBAAA,AAACZ,QAAgBiF,KAAK,YAAtB,kBAA0B,IAC5ChE,IAAI,GACJ8F,WAAW;YACd,IAAI,CAACxK,SAAS,CAAC0I,SAAS,CAACA,MAAMY,QAAQ,CAAC,MAAM;gBAC5C,OAAO3F,OAAO;YAChB;YACA,MAAM8G,WAAWpN,cACdyE,GAAG,GACHD,SAAS,GACTpB,UAAU,CAAC,QACXO,GAAG,CAAChB,OACJS,UAAU,CAAC;YACd,MAAMiK,UAAUpH,EAAAA,eAAAA,KAAKqH,MAAM,qBAAXrH,aAAaoB,IAAI,MAC7B,MAAM+F,SAASzJ,GAAG,CAACsC,KAAKqH,MAAM,CAACjG,IAAI,IAAI/D,GAAG,KAC1C,AACE,CAAA,MAAM8J,SACHpF,KAAK,CAAC,QAAQ,gBAAM/B,iBAAAA,KAAKsH,QAAQ,qBAAbtH,eAAeoB,IAAI,sBAAM,IAC7ChE,KAAK,CAAC,GACNC,GAAG,EAAC,EACPE,IAAI,CAAC,EAAE;YACb,IAAI,EAAC6J,2BAAAA,QAASlF,MAAM,GAAE;gBACpB,OAAO7B,OAAO,CAAC,cAAc,EAAEL,KAAKsH,QAAQ,IAAItH,KAAKqH,MAAM,CAAC,CAAC,CAAC;YAChE;YACA,mEAAmE;YACnE,mEAAmE;YACnE,qEAAqE;YACrE,MAAMvN,iBAAiB;gBACrByN,SAASH,QAAQvC,GAAG;gBACpB2C,OAAO,MAAMnN,oBAAoB8B;gBACjCiJ;gBACA,oEAAoE;gBACpEqC,QAAQ,GAAG1H,IAAIR,IAAI,CAAC,CAAC,EAAEQ,IAAIlC,EAAE,EAAE;YACjC;QACF,OAAO,IAAImC,KAAKX,IAAI,KAAK,kBAAkB;gBACpB;gBA8BLW,kBAKaA;YAnC7B,MAAMoF,QAAQrE,QAAO,kBAAA,AAACZ,QAAgBiF,KAAK,YAAtB,kBAA0B,IAC5ChE,IAAI,GACJ8F,WAAW;YACd,IAAI,CAAC9B,SAAS,CAACA,MAAMY,QAAQ,CAAC,MAAM;gBAClC,OAAO3F,OAAO;YAChB;YACA,kEAAkE;YAClE,mEAAmE;YACnE,8DAA8D;YAC9D,0DAA0D;YAC1D,MAAM,EAAEwE,KAAK6C,WAAW,EAAE,GAAG,MAAMnN,oBAAoB4B,QAAQ;YAC/D,MAAMwL,UAAU,MAAMjN,mBAAmBgN,aAAatC,OAAO;gBAAEjJ;YAAO;YACtE,IAAI,CAACwL,SAAS,OAAOtH,OAAO,CAAC,eAAe,EAAE+E,OAAO;YACrD;;;;;;;;;;;;;;;OAeC,GACD,MAAMwC,eAAexL,QAAQe,UAAU,CAAC;YACxC,MAAM0K,oBAAU7H,mBAAAA,KAAK8H,UAAU,qBAAf9H,iBAAiBoB,IAAI,sBAAM;YAC3C,MAAM2G,cAAcF,UAChB,MAAMD,aAAalK,GAAG,CAACmK,SAASxK,GAAG,KACnC,AACE,CAAA,MAAMuK,aACH7F,KAAK,CAAC,QAAQ,gBAAM/B,qBAAAA,KAAKgI,YAAY,qBAAjBhI,mBAAmBoB,IAAI,sBAAM,IACjDhE,KAAK,CAAC,GACNC,GAAG,EAAC,EACPE,IAAI,CAAC,EAAE;YACb,IAAI,EAACwK,+BAAAA,YAAa7F,MAAM,GAAE;gBACxB,OAAO7B,OAAO,CAAC,kBAAkB,EAAEL,KAAKgI,YAAY,IAAIhI,KAAK8H,UAAU,CAAC,CAAC,CAAC;YAC5E;YACA;;;;;;;;;;;;;OAaC,GACD,MAAMN,QAAQ,MAAMnN,oBAAoB8B;YACxC,MAAMwL,QAAQ9C,GAAG,CAAC7B,MAAM,CAAC;gBACvB,CAAChK,yBAAyBwO,MAAMS,OAAO,EAAE,EAAErN,WAAWsN,UAAU,CAC9DH,YAAYlK,EAAE;gBAEhB6H,WAAW9K,WAAWoK,eAAe;YACvC;QACF,OAAO,IAAIpM,gBAAgBoH,OAAO;YAChC,iEAAiE;YACjE,iEAAiE;YACjE,kEAAkE;YAClE,kDAAkD;YAClD,IAAI,CAAC3B,IAAI7B,UAAU,EAAE;gBACnB,OAAO6D,OACL,CAAC,sBAAsB,EAAEnI,yBAAyB,OAAO,KAAK,CAAC;YAEnE;YACA,iEAAiE;YACjE,kDAAkD;YAClD,MAAMiQ,UAAU,MAAM7M,iBACpB;gBAAEa;gBAAQF,KAAKoC,IAAIpC,GAAG;gBAAES,OAAO2B,IAAI3B,KAAK;YAAC,GACzCqD,IAAIlC,EAAE,EACNmC,MACAG;YAEF,IAAIgI,QAAQvI,KAAK,EAAE,OAAOS,OAAO8H,QAAQvI,KAAK;YAC9CF,SAASyI,QAAQzI,MAAM;YACvB;;;;;;;OAOC,GACD,IAAIyI,QAAQC,IAAI,EAAE;gBAChB,MAAM,CAACC,eAAeC,YAAY,GAAG,MAAMrL,QAAQC,GAAG,CAAC;oBACrDzB,kBACEU,QACAgM,QAAQC,IAAI,CAAClI,KAAK,EAClBiI,QAAQC,IAAI,CAACjI,OAAO,EACpB1D,QAAQ;oBAEVkF,gBACExF,QACAgM,QAAQC,IAAI,CAAClI,KAAK,EAClBiI,QAAQC,IAAI,CAACjI,OAAO,EACpB1D,QAAQ;iBAEX;gBACDJ,OAAOoD,IAAI,IAAI6I,gBAAgBD;YACjC;QACF;IACF,EAAE,OAAOzI,OAAO;QACd,OAAOS,OAAO,AAACT,MAAgBkB,OAAO;IACxC;IACA,OAAO;QAAEvB,MAAM;QAAQG;IAAO;AAChC;AAEA;;;;;;;CAOC,GACD,eAAe6I,cACblK,GAAiB,EACjBmK,QAAgB,EAChBC,MAAkB,EAClBvI,KAAa,EACbC,OAAyB,EACzBuI,UAAgC,CAAC,CAAC;QAM1BD,cAEcC,wBACPA,MAAAA,gBACgBA,qBAoDqBD;IA5DpD,MAAM,EAAErM,OAAO,EAAE,GAAGiC;IACpB,MAAM0B,MAAqB;QACzBR,MAAM;QACN1B,IAAI2K;QACJ1K,IAAI,GAAE2K,eAAAA,OAAO3K,IAAI,YAAX2K,eAAe;IACvB;IACA,MAAMrI,iBAAgBsI,yBAAAA,QAAQtI,aAAa,YAArBsI,yBAAyB;IAC/C,MAAM/H,QAAQ,EAAC+H,QAAAA,iBAAAA,QAAQ/H,KAAK,YAAb+H,iBAAiBD,OAAO9H,KAAK,YAA7B+H,OAAiC,EAAE,EAAE1H,KAAK,CAAC,GAAGhJ;IAC7D,MAAM2Q,aAAaC,KAAKC,GAAG,CAAC,IAAGH,sBAAAA,QAAQC,UAAU,YAAlBD,sBAAsB;IACrD;;;;;;GAMC,GACD,MAAMtJ,QAAkB;QAAES,QAAQ;QAAOF,QAAQ,EAAE;QAAEH,UAAU,EAAE;IAAC;IAClE,MAAMe,QAAQ;QAAEL;OAAUC;IAC1B,IAAK,IAAIU,QAAQ8H,YAAY9H,QAAQF,MAAMY,MAAM,EAAEV,SAAS,EAAG;QAC7D,MAAMb,OAAOW,KAAK,CAACE,MAAM;QACzB,MAAMvB,UAAU,MAAMQ,cAAczB,KAAK0B,KAAKC,MAAM;YAClDa;YACAF;YACAT;YACAC;YACAI;YACAH;QACF;QACA,IAAIjB,UAAUC,OAAOY,KAAKX,IAAI,EAAEC,UAAU;IAC5C;IACA,MAAM,EAAEO,MAAM,EAAEF,QAAQmJ,UAAU,EAAEtJ,QAAQ,EAAE,GAAGJ;IACjD;;;;;GAKC,GACD,IAAIS,WAAW,YAAY,OAAOA;IAClC,MAAMkJ,UAAUD,WAAWvH,MAAM,GAC7B,CAAC,cAAc,EAAErB,MAAM,cAAc,EAAE4I,WAAWtH,IAAI,CAAC,OAAO,CAACR,KAAK,CAClE,GACA,OAEFnB,WAAW,YACT,CAAC,sBAAsB,EAAEK,OAAO,GAChC,CAAC,cAAc,EAAEA,OAAO;IAC9B,MAAM9D,QACHe,UAAU,CAAC,YACX2H,GAAG,CAAC;QACHkE,SAAS;QACTC,YAAY;QACZ,oEAAoE;QACpE,mEAAmE;QACnE,+BAA+B;QAC/BR,QAAQM;QACR,kEAAkE;QAClE,8CAA8C;QAC9CtC,QAAQqC,WAAWvH,MAAM,GAAG,WAAW;QACvC2H,SAAShJ;QACT6I,SAAS,AAACvJ,CAAAA,SAAS+B,MAAM,GAAG/B,SAASgC,IAAI,CAAC,SAAS,KAAI,EAAGR,KAAK,CAAC,GAAG;QACnEmI,QAAQ;YAAE9J,MAAM;YAAYxB,IAAI2K;YAAU1K,IAAI,GAAE2K,gBAAAA,OAAO3K,IAAI,YAAX2K,gBAAe;QAAG;QAClE1D,WAAWnK,WAAWoK,eAAe;IACvC,GACCkB,KAAK,CAAC,IAAM/D;IACf,OAAOtC;AACT;AA8BA;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,OAAO,eAAeyB,gBACpBjD,GAAiB,EACjB0B,GAAkB,EAClBmB,QAA4B,EAC5BhB,KAAa,EACbC,OAAyB,EACzBuI,UAAkC,CAAC,CAAC;QAEtBA,MAAAA,gBAYQA,wBACSA,qBAqE1BhE;QAXcxD;IAvEnB,MAAMP,SAAQ+H,QAAAA,iBAAAA,QAAQ/H,KAAK,YAAb+H,iBAAiBxH,SAASP,KAAK,YAA/B+H,OAAmC,EAAE;IACnD,MAAMtJ,QAAkB;QAAES,QAAQ;QAAOF,QAAQ,EAAE;QAAEH,UAAU,EAAE;IAAC;IAClE,MAAM4J,UAAqD,CAAC;IAC5D,IAAIzI,MAAMY,MAAM,GAAG/H,oBAAoB;QACrC,OAAO,aACF4F;YACHO,QAAQ;gBAAC,CAAC,wBAAwB,EAAEnG,mBAAmB,MAAM,CAAC;aAAC;YAC/D6P,OAAO;YACPD;;IAEJ;IACA,MAAMnJ,UAAU,MAAM5B,IAAI1B,mBAAmB;IAC7C,MAAMyD,iBAAgBsI,yBAAAA,QAAQtI,aAAa,YAArBsI,yBAAyB;IAC/C,MAAMC,aAAaC,KAAKC,GAAG,CAAC,IAAGH,sBAAAA,QAAQC,UAAU,YAAlBD,sBAAsB;IACrD,uEAAuE,GACvE,MAAMY,cAAc,IAAyB,aAAKnJ,SAAYiJ;IAC9D,IAAK,IAAIvI,QAAQ8H,YAAY9H,QAAQF,MAAMY,MAAM,EAAEV,SAAS,EAAG;QAC7D,MAAMb,OAAOW,KAAK,CAACE,MAAM;QACzB,IAAI,CAACnF,qBAAqBsE,OAAO;gBAEZuJ;YADnB,MAAMA,OAAOvJ;YACb,MAAMwJ,aAAaD,EAAAA,mBAAAA,KAAKC,UAAU,qBAAfD,iBAAiBnI,IAAI,OAAM,CAAC,IAAI,EAAEP,QAAQ,GAAG;YAChE,MAAMY,YAAYlI,YAChB;gBAAEuE,MAAMoD,SAASpD,IAAI;gBAAE6C,OAAO;oBAAC,aAAK4I;wBAAMC;;iBAAa;YAAC,GACxDvJ,QAAQ/B,SAAS,EACjB+B,QAAQ9B,SAAS,EACjB;gBAAE+B;eAAUoJ,gBACZ;gBAAEvL,WAAWkC,QAAQlC,SAAS;YAAC;YAEjC,IAAI0D,UAAUC,EAAE,KAAK,OAAO;gBAC1B,+DAA+D;gBAC/D,2CAA2C;gBAC3CtC,MAAMO,MAAM,CAACF,IAAI,CACfgC,UAAU7B,KAAK,CAAC6J,OAAO,CAAC,aAAa,CAAC,KAAK,EAAE5I,QAAQ,GAAG;gBAE1D;YACF;YACAuI,OAAO,CAACI,WAAW,GAAG/H,UAAU2H,OAAO,CAACI,WAAW;YACnDpK,MAAMI,QAAQ,CAACC,IAAI,CACjB,CAAC,IAAI,EAAEsB,OAAOwI,KAAKG,YAAY,IAAIH,KAAKI,UAAU,IAAI,cAAcvI,IAAI,IAAI;YAE9E;QACF;QACA,MAAMwD,UAAUjJ,0BAA0BqE;QAC1C,IAAI4E,SAAS;YACXxF,MAAMO,MAAM,CAACF,IAAI,CAACmF;YAClB;QACF;QACA,IAAI,CAACvG,IAAI/B,cAAc,EAAE;YACvB8C,MAAMO,MAAM,CAACF,IAAI,CACf,CAAC,CAAC,EAAE5D,sBAAsBmE,KAAKX,IAAI,EAAE,YAAY,CAAC,GAChD,GAAGnH,yBAAyB,WAAW,KAAK,CAAC;YAEjD;QACF;QACA,MAAMoH,UAAU,MAAMQ,cAAczB,KAAK0B,KAAKC,MAAM;YAClDa;YACAF;YACAT;YACAC,SAASmJ;YACT/I,OAAO;gBAAEL;eAAUoJ;YACnBlJ;YACAI,QAAQkI,QAAQlI,MAAM;QACxB;QACA,IAAIrB,UAAUC,OAAOY,KAAKX,IAAI,EAAEC,UAAU;IAC5C;IACA;;;;;GAKC,GACD,MAAMsK,cAAa1I,wBAAAA,SAAS2I,WAAW,qBAApB3I,sBAAsBE,IAAI;IAC7C,MAAMiI,QAAQO,aACV,AAAC,CAAA;QACC,MAAME,QAAQvQ,YACZ;YAAEuE,MAAMoD,SAASpD,IAAI;YAAE6C,OAAO,EAAE;YAAEkJ,aAAaD;QAAW,GAC1D3J,QAAQ/B,SAAS,EACjB+B,QAAQ9B,SAAS,EACjB;YAAE+B;WAAUoJ;QAEd,OAAOQ,MAAMpI,EAAE,KAAK,QAAQ,KAAKoI,MAAMT,KAAK;IAC9C,CAAA,OACC3E,oBAAAA,OAAOH,MAAM,CAAC6E,SAASW,EAAE,CAAC,CAAC,cAA3BrF,oBAAiC;IACtC,OAAO,aAAKtF;QAAOiK;QAAOD;;AAC5B;AAEA;;;;;;;CAOC,GACD,eAAe1I,YACbrC,GAAiB,EACjB0B,GAAkB,EAClBiK,OAOC;cA+CWA;QAFKA;IA3CjB,MAAM,EAAEhK,IAAI,EAAE,GAAGgK;IACjB,MAAMC,UACJjK,KAAKX,IAAI,KAAK,SACVT,OAAOoB,KAAKkK,YAAY,IACxBlK,KAAKX,IAAI,KAAK,iBACZT,OAAOoB,KAAKmK,cAAc,IAC1B;IACR,IAAI,CAACvL,OAAOC,QAAQ,CAACoL,YAAYA,WAAW,GAAG;QAC7C,OAAO;YAAErK,OAAO;QAA2B;IAC7C;IACA,MAAMwK,QAAQxH,KAAKyH,GAAG;IACtB,MAAMC,aAAaF,QAAQH,UAAU;IACrC,MAAMM,gBACJvK,KAAKX,IAAI,KAAK,iBAAiBW,KAAK4B,SAAS,CAACR,IAAI,KAAK;IACzD,MAAM1B,SACJM,KAAKX,IAAI,KAAK,iBACV,GAAGkL,cAAc,QAAQ,EAAEN,QAAQ,EAAE,CAAC,GACtC,GAAGA,QAAQ,CAAC,CAAC;IAEnB,IAAID,QAAQ5J,aAAa,EAAE;QACzB,MAAMvF,sBACJmP,QAAQ5J,aAAa,EACrB;YACEQ,eAAeoJ,QAAQpJ,aAAa;YACpC0J;YACAC;YACApK,SAAS6J,QAAQ7J,OAAO;QAC1B,GACAiK;QAEF,OAAO;YAAE1K;QAAO;IAClB;IAEA,MAAM8K,WAAW,MAAMvP,aAAa;QAClCkB,QAAQkC,IAAIlC,MAAM;QAClBqM,UAAUzI,IAAIlC,EAAE;OAGZkC,IAAIR,IAAI,KAAK,aAAa;QAAEkL,YAAY;IAAoB,IAAI,CAAC;QACrE,0EAA0E;QAC1E,uEAAuE;QACvE,uCAAuC;QACvChC,QAAQ;YAAE3K,MAAMiC,IAAIjC,IAAI;YAAE6C,OAAO;mBAAIqJ,QAAQrJ,KAAK;aAAC;QAAC;QACpDyE,OAAOrE,gBAAQiJ,oBAAAA,QAAQ7J,OAAO,qBAAhB,AAAC6J,iBAAyB,CAAC,QAAQ,mBAAI;QACrD9J,OAAO8J,QAAQ9J,KAAK;QACpBC,OAAO,GAAG6J,mBAAAA,QAAQ7J,OAAO,YAAf6J,mBAAmB,CAAC;QAC9BpJ,eAAeoJ,QAAQpJ,aAAa;QACpC0J;QACAC;QACAH;;IAEF,IAAII,SAASA,QAAQ,KAAK,MAAM,OAAO;QAAE9K;IAAO;IAChD;;;;;GAKC,GACD,OAAO;QACLE,OACE4K,SAASE,MAAM,KAAK,cAChB,oEACA;IACR;AACF;AAEA;;;;;;;;;CASC,GACD,MAAMC,2BAA2B,IAAIC,IAAI;IAAC;CAAW;AAErD;;;;;;;CAOC,GACD,eAAeC,iBACbzO,OAA4C,EAC5CoM,QAAgB,EAChBC,MAAkB,EAClBvI,KAAa;QAqBuCuI;IAnBpD,IAAIkC,yBAAyBG,GAAG,CAAC5K,QAAQ;IACzC,yEAAyE;IACzE,uEAAuE;IACvE,gDAAgD;IAChD,MAAM4J,QAAQzQ,2BAA2BoP,OAAOS,OAAO,EACpDzL,GAAG,CAAC,CAACsN;;eAAchK,eAAOgK,6BAAAA,UAAWC,KAAK,mBAAI,IAAI5J,IAAI;OACtD6J,MAAM,CAACC;IACV,MAAMR,SAASZ,MAAMvI,MAAM,GACvB,CAAC,aAAa,EAAEuI,MAAM9I,KAAK,CAAC,GAAG,GAAGQ,IAAI,CAAC,MAAM,QAAQ,CAAC,GACtD;IACJ,MAAMpF,QACHe,UAAU,CAAC,YACX2H,GAAG,CAAC;QACHkE,SAAS;QACTC,YAAY;QACZR,QAAQ,CAAC,kBAAkB,EAAEvI,OAAO;QACpCuG,QAAQ;QACRyC,SAAShJ;QACT6I,SAAS2B,OAAO1J,KAAK,CAAC,GAAG;QACzBmI,QAAQ;YAAE9J,MAAM;YAAYxB,IAAI2K;YAAU1K,IAAI,GAAE2K,eAAAA,OAAO3K,IAAI,YAAX2K,eAAe;QAAG;QAClE1D,WAAWnK,WAAWoK,eAAe;IACvC,GACCkB,KAAK,CAAC,IAAM/D;AACjB;AAEA;;;;;;CAMC,GACD,OAAO,eAAeR,gBACpBxF,MAAc,EACd+D,KAAa,EACbC,UAA4B,CAAC,CAAC,EAC9B1D,QAAQ,CAAC;IAET,MAAMJ,SAA4B,EAAE;IACpC,IAAII,QAAQ1E,wBAAwB,OAAOsE;IAC3C;;;;;;;GAOC,GACD,MAAM8O,uBAAuBhP,QAAQ+D,OAAOC,SAAS+F,KAAK,CAAC,IAAM/D;IACjE,IAAI;;QACF,MAAM5D,YAAYxE,cAAcyE,GAAG,GAAGD,SAAS;QAC/C,MAAMnC,UAAUmC,UAAUpB,UAAU,CAAC,SAASO,GAAG,CAACvB;QAClD,MAAMiP,YAAY,MAAMhP,QACrBe,UAAU,CAAC,WACX4E,KAAK,CAAC,iBAAiB,MAAM7B,OAC7B9C,KAAK,CAACtB,uBACNuB,GAAG;QACN,MAAMgO,UAAUD,UAAU7N,IAAI,CAAC0N,MAAM,CACnC,CAACvN,MAAQ,CAACA,IAAIL,GAAG,CAAC,gBAAgBK,IAAIL,GAAG,CAAC,eAAe;QAE3D,IAAI,CAACgO,QAAQ9J,MAAM,EAAE,OAAOlF;QAE5B,MAAMiP,WAAW,IAAI1I,OAAOC,WAAW,GAAG7B,KAAK,CAAC,GAAG;QACnD,MAAMuK,gBAAgBnP,QAAQe,UAAU,CAAC,YAAYO,GAAG,CAAC;QACzD,sEAAsE;QACtE,uCAAuC;QACvC,IAAInB,kBAAkB;QACtB,IAAIC,aAAa;QACjB,wEAAwE;QACxE,qEAAqE;QACrE,MAAMN,QAAQ,MAAMjC,cAAckC;QAClC;gBASsBqP;YARpB,MAAMvP,MAAMC,yBAAAA,MAAOD,GAAG;YACtB,IAAI,CAAChE,iBAAiBgE,KAAY,YAAY,OAAOI;YACrDE,kBAAkBtE,iBAAiBgE,KAAY;YAC/CO,aAAavE,iBAAiBgE,KAAY;YAC1C,MAAMmB,QAAQ9D,uBACZ2C,KACAwP,kBAAkB;YACpB,MAAMD,kBAAkB,MAAMD,cAAclO,GAAG;YAC/C,MAAMyB,OAAOF,QAAO4M,uBAAAA,gBAAgBnO,GAAG,CAACiO,qBAApBE,uBAAiC;YACrD,IAAI1M,OAAOuM,QAAQ9J,MAAM,GAAGnE,OAAO,OAAOf;QAC5C;QAEA,MAAMgC,MAAoB;YACxBlC;YACAC;YACAC;YACA,wCAAwC;YACxCC,gBAAgB;YAChBC;YACAC;YACAC;YACAR,GAAG,UAAEC,yBAAAA,MAAOD,GAAG,mBAAI;YACnBS,KAAK,WAAER,yBAAAA,MAAOQ,KAAK,oBAAI;YACvBC,qBAAqBC,0BAA0BR;QACjD;QAEA,IAAIsP,WAAW;QACf,KAAK,MAAMhO,OAAO2N,QAAS;gBAEV5C,wBAAAA;YADf,MAAMA,SAAS/K,IAAIC,IAAI;YACvB,MAAMsN,UAASxC,kBAAAA,OAAOS,OAAO,sBAAdT,yBAAAA,gBAAgBwC,MAAM,qBAAtBxC,uBAAwBrH,IAAI;YAC3C,IAAI6J,QAAQ;gBACV,IAAI;oBACF,IAAI,CAAC5S,mBAAmB4S,QAAQ;wBAAE/K;uBAAUC,WAAY;gBAC1D,EAAE,eAAM;oBACN,UAAS,+BAA+B;gBAC1C;YACF;YACA,oEAAoE;YACpE,iEAAiE;YACjE,0CAA0C;YAC1C,IAAI,CAAC5H,0BAA0BkQ,OAAOS,OAAO,EAAE;gBAAEhJ;eAAUC,WAAY;gBACrE,yDAAyD;gBACzD,6DAA6D;gBAC7D,4DAA4D;gBAC5D,iEAAiE;gBACjE,0CAA0C;gBAC1C,MAAM0K,iBAAiBzO,SAASsB,IAAIG,EAAE,EAAE4K,QAAQvI;gBAChD;YACF;YACAwL,YAAY;YACZ,MAAMnD,cAAclK,KAAKX,IAAIG,EAAE,EAAE4K,QAAQvI,OAAOC;QAClD;QACA,IAAIuL,WAAW,GAAG;YAChB,MAAMH,cACH/F,GAAG,CAAC;gBAAE,CAAC8F,SAAS,EAAE1Q,WAAW+Q,SAAS,CAACD;YAAU,GAAG;gBAAE/F,OAAO;YAAK,GAClEO,KAAK,CAAC,IAAM/D;QACjB;IACF,EAAE,OAAOvC,OAAO;QACdgM,QAAQhM,KAAK,CAAC,0BAA0BzD,QAAQ+D,OAAON;IACzD;IACA,OAAOvD;AACT;AAEA;;;;;;CAMC,GACD,OAAO,eAAewP,gBACpB1P,MAAc,EACdqM,QAAgB,EAChBtI,KAAa,EACbC,UAA4B,CAAC,CAAC;IAE9B,MAAM9D,SAA4B,EAAE;IACpC,IAAI;;YAUSoM;QATX,MAAMlK,YAAYxE,cAAcyE,GAAG,GAAGD,SAAS;QAC/C,MAAMnC,UAAUmC,UAAUpB,UAAU,CAAC,SAASO,GAAG,CAACvB;QAClD,MAAMuB,MAAM,MAAMtB,QAAQe,UAAU,CAAC,WAAWO,GAAG,CAAC8K,UAAUnL,GAAG;QACjE,IAAI,CAACK,IAAIwE,MAAM,IAAIxE,IAAIL,GAAG,CAAC,gBAAgBK,IAAIL,GAAG,CAAC,eAAe,OAAO;YACvE,OAAOhB;QACT;QACA,MAAMoM,SAAS/K,IAAIC,IAAI;QACvB,gEAAgE;QAChE,0CAA0C;QAC1C,IAAIoD,gBAAO0H,kBAAAA,OAAOS,OAAO,qBAAdT,gBAAgBvI,KAAK,mBAAI,QAAQa,OAAOb,QAAQ,OAAO7D;QAClE,oEAAoE;QACpE,sDAAsD;QACtD,+CAA+C;QAC/C,IAAI,CAAC9D,0BAA0BkQ,OAAOS,OAAO,EAAE;YAAEhJ;WAAUC,WAAY;YACrE,OAAO9D;QACT;QAEA,MAAMiP,WAAW,IAAI1I,OAAOC,WAAW,GAAG7B,KAAK,CAAC,GAAG;QACnD,MAAMuK,gBAAgBnP,QAAQe,UAAU,CAAC,YAAYO,GAAG,CAAC;QACzD,IAAInB,kBAAkB;QACtB,IAAIC,aAAa;QACjB,+DAA+D;QAC/D,MAAMN,QAAQ,MAAMjC,cAAckC;QAClC;gBASsBqP;YARpB,MAAMvP,MAAMC,yBAAAA,MAAOD,GAAG;YACtB,IAAI,CAAChE,iBAAiBgE,KAAY,YAAY,OAAOI;YACrDE,kBAAkBtE,iBAAiBgE,KAAY;YAC/CO,aAAavE,iBAAiBgE,KAAY;YAC1C,MAAMmB,QAAQ9D,uBACZ2C,KACAwP,kBAAkB;YACpB,MAAMD,kBAAkB,MAAMD,cAAclO,GAAG;YAC/C,MAAMyB,OAAOF,QAAO4M,uBAAAA,gBAAgBnO,GAAG,CAACiO,qBAApBE,uBAAiC;YACrD,IAAI1M,OAAO,IAAI1B,OAAO,OAAOf;QAC/B;QAEA,MAAMgC,MAAoB;YACxBlC;YACAC;YACAC;YACA,wCAAwC;YACxCC,gBAAgB;YAChBC;YACAC;YACAC,OAAO;YACPR,GAAG,WAAEC,yBAAAA,MAAOD,GAAG,oBAAI;YACnBS,KAAK,WAAER,yBAAAA,MAAOQ,KAAK,oBAAI;YACvBC,qBAAqBC,0BAA0BR;QACjD;QACA,MAAMmM,cAAclK,KAAKX,IAAIG,EAAE,EAAE4K,QAAQvI,OAAOC;QAChD,MAAMoL,cACH/F,GAAG,CAAC;YAAE,CAAC8F,SAAS,EAAE1Q,WAAW+Q,SAAS,CAAC;QAAG,GAAG;YAAEhG,OAAO;QAAK,GAC3DO,KAAK,CAAC,IAAM/D;IACjB,EAAE,OAAOvC,OAAO;QACdgM,QAAQhM,KAAK,CAAC,0BAA0BzD,QAAQqM,UAAU5I;IAC5D;IACA,OAAOvD;AACT;AAEA;;;;;CAKC,GACD,OAAO,eAAeyP,mBACpBC,UAA0B,EAC1BlH,GAAwC,EACxC6F,MAAc;QAoBFqB;IAlBZ,MAAM3P,UAAUrC,cACbyE,GAAG,GACHD,SAAS,GACTpB,UAAU,CAAC,SACXO,GAAG,CAACqO,WAAW5P,MAAM;IACxB,MAAMnB,kBAAkB6J;IACxB,MAAMzI,QACHe,UAAU,CAAC,YACX2H,GAAG,CAAC;QACHkE,SAAS;QACTC,YAAY;QACZR,QAAQ,CAAC,uBAAuB,EAAEiC,QAAQ,CAAC1J,KAAK,CAAC,GAAG;QACpDyF,QAAQ;QACRyC,SAAS6C,WAAW7L,KAAK;QACzB6I,SAAS2B,OAAO1J,KAAK,CAAC,GAAG;QACzBmI,QAAQ;YACN9J,MAAM;YACNxB,IAAIkO,WAAWvD,QAAQ;YACvB1K,IAAI,GAAEiO,yBAAAA,WAAWC,UAAU,YAArBD,yBAAyB;QACjC;QACAhH,WAAWnK,WAAWoK,eAAe;IACvC,GACCkB,KAAK,CAAC,IAAM/D;IACf,OAAO;AACT;AAEA;;;;;;;;;;CAUC,GACD,OAAO,eAAe8J,qBACpBF,UAA0B,EAC1BlH,GAAwC,EACxC6D,OAAgD;4BAwD1CqD;IAtDN,MAAM3B,gBAAQ1B,2BAAAA,QAAS0B,KAAK,mBAAIxH,KAAKyH,GAAG;IACxC,yEAAyE;IACzE,sEAAsE;IACtE,qEAAqE;IACrE,IAAI0B,WAAWtB,UAAU,KAAK,YAAY;QACxC,OAAO,MAAMjP,yBAAyBuQ,YAAYlH,KAAK,aAClD6D;YACH0B;;IAEJ;IACA,MAAMjO,SAAS4P,WAAW5P,MAAM;IAChC,MAAMoC,YAAYxE,cAAcyE,GAAG,GAAGD,SAAS;IAC/C,MAAMnC,UAAUmC,UAAUpB,UAAU,CAAC,SAASO,GAAG,CAACvB;IAElD,MAAM+P,OAAO,CAACxB,SAAmBoB,mBAAmBC,YAAYlH,KAAK6F;IAErE,MAAMhN,MAAM,MAAMtB,QACfe,UAAU,CAAC,WACXO,GAAG,CAACqO,WAAWvD,QAAQ,EACvBnL,GAAG,GACH6I,KAAK,CAAC,IAAM;IACf;;;;;;;;GAQC,GACD,IAAI,EAACxI,uBAAAA,IAAKwE,MAAM,KAAIxE,IAAIL,GAAG,CAAC,gBAAgBK,IAAIL,GAAG,CAAC,eAAe,OAAO;QACxE,OAAO,MAAM6O,KAAK;IACpB;IACA,MAAMzD,SAAS/K,IAAIC,IAAI;IACvB,MAAMzB,QAAQ,MAAMjC,cAAckC,QAAQ+J,KAAK,CAAC,IAAM;IACtD,IAAI,CAACjO,iBAAiBiE,yBAAAA,MAAOD,GAAG,EAAS,YAAY;QACnD,OAAO,MAAMiQ,KAAK;IACpB;IAEA,MAAM7N,MAAoB;QACxBlC;QACAC;QACAC,QAAQ,EAAE;QACV,wCAAwC;QACxCC,gBAAgB;QAChBC,iBAAiBtE,iBAAiBiE,yBAAAA,MAAOD,GAAG,EAAS;QACrDO,YAAYvE,iBAAiBiE,yBAAAA,MAAOD,GAAG,EAAS;QAChDQ,OAAO;QACPR,GAAG,WAAEC,yBAAAA,MAAOD,GAAG,oBAAI;QACnBS,KAAK,WAAER,yBAAAA,MAAOQ,KAAK,oBAAI;QACvBC,qBAAqBC,0BAA0BR;IACjD;IAEA,MAAM+D,UAA4B,cAC5B4L,sBAAAA,WAAW5L,OAAO,YAAlB4L,sBAAsB,CAAC,GAIvBrD,CAAAA,2BAAAA,QAASyD,QAAQ,IAAG;QAAE,CAAC3T,qBAAqB,EAAE;IAAK,IAAI,CAAC;IAG9D,MAAMqH,SAAS,MAAM0I,cACnBlK,KACA0N,WAAWvD,QAAQ,EACnBC,QACAsD,WAAW7L,KAAK,EAChBC,SACA;QACEwI,YAAYoD,WAAWnL,aAAa;QACpC,uEAAuE;QACvE,qBAAqB;QACrBD,OAAOoL,WAAWpL,KAAK;QACvBP,eAAeyE;IACjB;IAGF,IAAIhF,WAAW,YAAY;QACzB;;;;;KAKC,GACD,MAAM9E,oBAAoB8J,KAAKuF,QAAQgC,qBAAqBhC;QAC5D,OAAOvK;IACT;IACA,IAAIA,WAAW,WAAW,MAAM7E,kBAAkB6J;IAElD;;;;;;;;;GASC,GACD,MAAMyG,WAAW,IAAI1I,KAAKwH,OAAOvH,WAAW,GAAG7B,KAAK,CAAC,GAAG;IACxD,MAAM5E,QACHe,UAAU,CAAC,YACXO,GAAG,CAAC,cACJ8H,GAAG,CAAC;QAAE,CAAC8F,SAAS,EAAE1Q,WAAW+Q,SAAS,CAAC;IAAG,GAAG;QAAEhG,OAAO;IAAK,GAC3DO,KAAK,CAAC,IAAM/D;IACf,OAAOtC;AACT;AAEA,kEAAkE,GAClE,OAAO,MAAMuM,sBAAsB,KAAK,MAAM;AAE9C;;;;;;;CAOC,GACD,OAAO,eAAeC,sBACpBjG,IAAuB,EACvBsC,OAIC;IAED,OAAO,MAAMvN,wBAAwBiL,MAAM,aACrCsC,CAAAA,2BAAAA,QAAS0B,KAAK,MAAKjI,YAAY;QAAEiI,OAAO1B,QAAQ0B,KAAK;IAAC,IAAI,CAAC,GAC3D1B,CAAAA,2BAAAA,QAAS4D,UAAU,MAAKnK,YACxB;QAAEmK,YAAY5D,QAAQ4D,UAAU;IAAC,IACjC,CAAC,GACD5D,CAAAA,2BAAAA,QAAS6D,MAAM,IAAG;QAAEA,QAAQ7D,QAAQ6D,MAAM;IAAC,IAAI,CAAC;QACpDC,QAAQ,OAAOT,YAAYlH;YACzB,MAAMoH,qBAAqBF,YAAYlH,KAAK;gBAC1C,oEAAoE;gBACpE,iEAAiE;gBACjE,qEAAqE;gBACrE,wDAAwD;gBACxDsH,UAAUjB,QAAQa,WAAWxB,aAAa;eACtC7B,CAAAA,2BAAAA,QAAS0B,KAAK,MAAKjI,YAAY;gBAAEiI,OAAO1B,QAAQ0B,KAAK;YAAC,IAAI,CAAC;QAEnE;;AAEJ;AAEA;;;;;;;;;;;;CAYC,GACD,eAAee,uBACbhP,MAAc,EACd+D,KAAa,EACbC,OAAyB;;IAEzB,MAAMiF,QAAQrE,eAAQZ,2BAAD,AAACA,OAAiB,CAAC,QAAQ,mBAAI,IAAIiB,IAAI;IAC5D,IAAI,CAACgE,SAAS,CAACA,MAAMY,QAAQ,CAAC,MAAM;IACpC,MAAMyG,UAAU,MAAMvR,4BAA4B;QAAEiB;QAAQ+D;QAAOkF;IAAM;IACzE,KAAK,MAAM1H,OAAO+O,QAAS;QACzB,MAAMC,UAAU,MAAM5R,oBAAoB4C,IAAImH,GAAG,EAAEqB,KAAK,CAAC,IAAM;QAC/D,IAAI,CAACwG,SAAS;QACd,IAAI;gBASiBA;YARnB;;;;OAIC,GACD,MAAMT,qBACJ,aACKS;gBACHvM,SAAS,cAAMuM,mBAAAA,QAAQvM,OAAO,YAAfuM,mBAAmB,CAAC,GAAQvM,kBAAAA,UAAW,CAAC;gBAEzDzC,IAAImH,GAAG,EACP;gBAAEsH,UAAU;YAAM;QAEtB,EAAE,OAAOvM,OAAO;YACdgM,QAAQhM,KAAK,CAAC,4BAA4BlC,IAAImH,GAAG,CAAC8H,IAAI,EAAE/M;YACxD,MAAM7E,oBAAoB2C,IAAImH,GAAG,EAAEjC,KAAKyH,GAAG,KAAK+B;QAClD;IACF;AACF;AAEA,eAAezK,gBAAe"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { HostActionAlert } from '@aglyn/aglyn/server';
18
+ import type { HostEventPayload } from '@aglyn/tenant-runtime/host-event-listeners';
19
+ /**
20
+ * Everything the automation engine runs for one host event: the workflows
21
+ * triggered by it and the actions, side by side, with the site alerts they
22
+ * produced for the visitor — the actions' first, then those a workflow's
23
+ * `siteAlert` step raised. What the runtime's host-event listener calls.
24
+ *
25
+ * Never rejects, because neither runner does: each catches and logs its own
26
+ * failure so the event that has already happened is not refused for it.
27
+ */
28
+ export declare function runEventAutomations(hostId: string, event: string, payload?: HostEventPayload): Promise<HostActionAlert[]>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ import { runEventActions } from "./run-event-actions.js";
17
+ import { runEventWorkflows } from "./run-event-workflows.js";
18
+ /**
19
+ * Everything the automation engine runs for one host event: the workflows
20
+ * triggered by it and the actions, side by side, with the site alerts they
21
+ * produced for the visitor — the actions' first, then those a workflow's
22
+ * `siteAlert` step raised. What the runtime's host-event listener calls.
23
+ *
24
+ * Never rejects, because neither runner does: each catches and logs its own
25
+ * failure so the event that has already happened is not refused for it.
26
+ */ export async function runEventAutomations(hostId, event, payload = {}) {
27
+ const [fromWorkflows, fromActions] = await Promise.all([
28
+ runEventWorkflows(hostId, event, payload),
29
+ runEventActions(hostId, event, payload)
30
+ ]);
31
+ return [
32
+ ...fromActions,
33
+ ...fromWorkflows
34
+ ];
35
+ }
36
+
37
+ //# sourceMappingURL=run-event-automations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/engine/run-event-automations.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { HostActionAlert, HostEventType } from '@aglyn/aglyn/server'\nimport type { HostEventPayload } from '@aglyn/tenant-runtime/host-event-listeners'\nimport { runEventActions } from './run-event-actions'\nimport { runEventWorkflows } from './run-event-workflows'\n\n/**\n * Everything the automation engine runs for one host event: the workflows\n * triggered by it and the actions, side by side, with the site alerts they\n * produced for the visitor — the actions' first, then those a workflow's\n * `siteAlert` step raised. What the runtime's host-event listener calls.\n *\n * Never rejects, because neither runner does: each catches and logs its own\n * failure so the event that has already happened is not refused for it.\n */\nexport async function runEventAutomations(\n hostId: string,\n event: string,\n payload: HostEventPayload = {},\n): Promise<HostActionAlert[]> {\n const [fromWorkflows, fromActions] = await Promise.all([\n runEventWorkflows(hostId, event as HostEventType, payload),\n runEventActions(hostId, event, payload),\n ])\n return [...fromActions, ...fromWorkflows]\n}\n"],"names":["runEventActions","runEventWorkflows","runEventAutomations","hostId","event","payload","fromWorkflows","fromActions","Promise","all"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID,SAASA,eAAe,QAAQ,yBAAqB;AACrD,SAASC,iBAAiB,QAAQ,2BAAuB;AAEzD;;;;;;;;CAQC,GACD,OAAO,eAAeC,oBACpBC,MAAc,EACdC,KAAa,EACbC,UAA4B,CAAC,CAAC;IAE9B,MAAM,CAACC,eAAeC,YAAY,GAAG,MAAMC,QAAQC,GAAG,CAAC;QACrDR,kBAAkBE,QAAQC,OAAwBC;QAClDL,gBAAgBG,QAAQC,OAAOC;KAChC;IACD,OAAO;WAAIE;WAAgBD;KAAc;AAC3C"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { type HostActionAlert, type HostEventType } from '@aglyn/aglyn/server';
18
+ import type { HostEventPayload } from '@aglyn/tenant-runtime/host-event-listeners';
19
+ import { type FlowEnrollment } from './flow-enrollments';
20
+ import { type WorkflowExecution } from './run-event-actions';
21
+ /**
22
+ * The history row for a run of a workflow with Actions steps, in the Actions
23
+ * run's phrasing (AGL-2171) — what each step did, joined — so the run table
24
+ * shows every step's outcome whichever engine the step came from. `status`
25
+ * and `durationMs` ride along as they do on every workflow run, for the
26
+ * dashboard feed that renders them.
27
+ */
28
+ export declare function workflowRunRow(execution: Pick<WorkflowExecution, 'ending' | 'errors' | 'outcomes'>, input: {
29
+ event: string;
30
+ workflowId: string;
31
+ workflowName: string;
32
+ durationMs: number;
33
+ }): Record<string, unknown>;
34
+ /**
35
+ * Event-triggered workflow runner (AGL-128): loads workflows whose
36
+ * `trigger.event` matches, evaluates optional trigger filters over the
37
+ * event payload, runs each, and logs runs to the host activity feed.
38
+ * Fire-and-forget from the tenant's event sites (form submit, pageview
39
+ * collector, membership APIs) — it never throws, and a failure must not
40
+ * break the request that emitted the event.
41
+ *
42
+ * A workflow of function calls runs through the pure `runWorkflow`
43
+ * evaluator exactly as it always has. A workflow with Actions steps runs
44
+ * through `executeWorkflow`, which performs them with the Actions executors;
45
+ * the site alerts its steps raise are what this resolves.
46
+ *
47
+ * ONE RUN, METERED ONCE: either kind counts one run on the workflow meter,
48
+ * `workflowRunsPerMonth`, however many steps it has and whatever they are.
49
+ * Its Actions steps take the Actions tier gates step by step, and none of
50
+ * them counts an action run.
51
+ *
52
+ * `depth` is the chain guard actions run under (`ACTION_MAX_EVENT_DEPTH`):
53
+ * an event a workflow's own step raised — a stage it set — is heard one
54
+ * level deeper, so a workflow that sets the stage it listens for stops.
55
+ */
56
+ export declare function runEventWorkflows(hostId: string, event: HostEventType, payload?: HostEventPayload, depth?: number): Promise<HostActionAlert[]>;
57
+ /**
58
+ * Continues a WORKFLOW's enrollment from where its wait ended — the
59
+ * workflow twin of the action resume, and asked the same questions.
60
+ *
61
+ * - The kill switch is the workflow document: deleted, and the people inside
62
+ * it stop, exactly as they do when an action is deleted or switched off.
63
+ * - The plan is re-asked: a wait is an Actions step, so a workspace whose
64
+ * plan has lost the actions builder stops the flow rather than finishing
65
+ * it on a plan that no longer carries it.
66
+ * - The snapshot runs, not the workflow's current steps, and the results the
67
+ * function calls bound before the wait ride in its payload.
68
+ * - Counted on the workflow meter, never refused by it: the person is already
69
+ * inside, and the gate belongs at enrollment.
70
+ */
71
+ export declare function resumeWorkflowEnrollment(enrollment: FlowEnrollment, ref: FirebaseFirestore.DocumentReference, options: {
72
+ timedOut?: boolean;
73
+ nowMs: number;
74
+ }): Promise<'ran' | 'waiting' | 'exited' | 'deferred' | 'stopped'>;
75
+ export default runEventWorkflows;