@aglyn/plugins-workflows 1.0.0-beta.221 → 1.0.0-beta.223
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -14
- package/package.json +12 -12
- package/src/lib/components/automation-step-fields.component.d.ts +16 -16
- package/src/lib/components/automation-step-fields.component.js +146 -12
- package/src/lib/components/automation-step-fields.component.js.map +1 -1
- package/src/lib/components/automation-trigger-conditions.component.d.ts +25 -3
- package/src/lib/components/automation-trigger-conditions.component.js +89 -10
- package/src/lib/components/automation-trigger-conditions.component.js.map +1 -1
- package/src/lib/components/host-actions-card.component.js +126 -84
- package/src/lib/components/host-actions-card.component.js.map +1 -1
- package/src/lib/components/host-webhooks-card.component.js +2 -1
- package/src/lib/components/host-webhooks-card.component.js.map +1 -1
- package/src/lib/components/host-workflows-card.component.js +31 -10
- package/src/lib/components/host-workflows-card.component.js.map +1 -1
- package/src/lib/components/org-automation-editor.component.d.ts +2 -1
- package/src/lib/components/org-automation-editor.component.js +20 -11
- package/src/lib/components/org-automation-editor.component.js.map +1 -1
- package/src/lib/components/org-automations-card.component.js +2 -1
- package/src/lib/components/org-automations-card.component.js.map +1 -1
- package/src/lib/components/run-quota-line.component.d.ts +17 -4
- package/src/lib/components/run-quota-line.component.js +16 -7
- package/src/lib/components/run-quota-line.component.js.map +1 -1
- package/src/lib/components/site-org-automations-panel.component.js +2 -1
- package/src/lib/components/site-org-automations-panel.component.js.map +1 -1
- package/src/lib/components/workflows-console-page.js +18 -7
- package/src/lib/components/workflows-console-page.js.map +1 -1
- package/src/lib/declarations.d.ts +28 -0
- package/src/lib/declarations.js +37 -0
- package/src/lib/declarations.js.map +1 -0
- package/src/lib/declarations.server.js +17 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/engine/email-merge.d.ts +69 -0
- package/src/lib/engine/email-merge.js +98 -0
- package/src/lib/engine/email-merge.js.map +1 -0
- package/src/lib/engine/run-event-actions.d.ts +35 -10
- package/src/lib/engine/run-event-actions.js +256 -369
- package/src/lib/engine/run-event-actions.js.map +1 -1
- package/src/lib/engine/run-event-automations.d.ts +1 -1
- package/src/lib/engine/run-event-automations.js.map +1 -1
- package/src/lib/engine/run-event-workflows.d.ts +2 -1
- package/src/lib/engine/run-event-workflows.js +71 -30
- package/src/lib/engine/run-event-workflows.js.map +1 -1
- package/src/lib/engine/run-meter.d.ts +98 -0
- package/src/lib/engine/run-meter.js +121 -0
- package/src/lib/engine/run-meter.js.map +1 -0
- package/src/lib/engine/run-org-automations.d.ts +3 -2
- package/src/lib/engine/run-org-automations.js +17 -12
- package/src/lib/engine/run-org-automations.js.map +1 -1
- package/src/lib/engine/triggered-docs.d.ts +39 -0
- package/src/lib/engine/triggered-docs.js +47 -0
- package/src/lib/engine/triggered-docs.js.map +1 -0
- package/src/lib/engine/workflow-steps.d.ts +1 -1
- package/src/lib/engine/workflow-steps.js +3 -2
- package/src/lib/engine/workflow-steps.js.map +1 -1
- package/src/lib/model/host-actions.d.ts +284 -0
- package/src/lib/model/host-actions.js +316 -0
- package/src/lib/model/host-actions.js.map +1 -0
- package/src/lib/model/org-automations.d.ts +8 -5
- package/src/lib/model/org-automations.js +14 -8
- package/src/lib/model/org-automations.js.map +1 -1
- package/src/lib/model/step-outcomes.d.ts +2 -2
- package/src/lib/model/step-outcomes.js +1 -1
- package/src/lib/model/step-outcomes.js.map +1 -1
- package/src/lib/plugin.js +10 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/action-test-run-route.d.ts +3 -2
- package/src/lib/server/action-test-run-route.js +3 -2
- package/src/lib/server/action-test-run-route.js.map +1 -1
- package/src/lib/server/org-automations-routes.d.ts +42 -1
- package/src/lib/server/org-automations-routes.js +120 -72
- package/src/lib/server/org-automations-routes.js.map +1 -1
- package/src/lib/server-automation-drafts.d.ts +2 -2
- package/src/lib/server-automation-drafts.js +11 -7
- package/src/lib/server-automation-drafts.js.map +1 -1
- package/src/lib/server.js +19 -13
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/stand-in-crm-steps.d.ts +45 -0
- package/src/lib/testing/stand-in-crm-steps.js +97 -0
- package/src/lib/testing/stand-in-crm-steps.js.map +1 -0
- package/src/lib/testing/stand-in-dataset-list.js +11 -3
- package/src/lib/testing/stand-in-dataset-list.js.map +1 -1
- package/src/lib/testing/stand-in-dataset-steps.d.ts +43 -0
- package/src/lib/testing/stand-in-dataset-steps.js +65 -0
- package/src/lib/testing/stand-in-dataset-steps.js.map +1 -0
- package/src/lib/testing/stand-in-person-records.d.ts +35 -0
- package/src/lib/testing/stand-in-person-records.js +54 -0
- package/src/lib/testing/stand-in-person-records.js.map +1 -0
- package/src/lib/testing/stand-in-recipes.d.ts +36 -0
- package/src/lib/testing/stand-in-recipes.js +206 -0
- package/src/lib/testing/stand-in-recipes.js.map +1 -0
- package/src/lib/transfer/org-automations-package.d.ts +54 -0
- package/src/lib/transfer/org-automations-package.js +167 -0
- package/src/lib/transfer/org-automations-package.js.map +1 -0
- package/src/lib/transfer/org-automations-package.server.d.ts +38 -0
- package/src/lib/transfer/org-automations-package.server.js +296 -0
- package/src/lib/transfer/org-automations-package.server.js.map +1 -0
- package/src/lib/usage-axes.js +5 -1
- package/src/lib/usage-axes.js.map +1 -1
- package/src/lib/engine/crm-action-steps.d.ts +0 -142
- package/src/lib/engine/crm-action-steps.js +0 -452
- package/src/lib/engine/crm-action-steps.js.map +0 -1
|
@@ -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
|
+
*/ // The seam from its own module, not the plugin-manager barrel: both apps load
|
|
17
|
+
// this at boot, and the barrel reaches the client contexts.
|
|
18
|
+
import { registerInteractionStepChecks } from "@aglyn/aglyn/plugin-manager/interaction-step-checks";
|
|
19
|
+
import { BUNDLE_ID } from "./constants/bundle-common.js";
|
|
20
|
+
import { hostActionStepProblem, SERVER_ACTION_STEP_TYPES } from "./model/host-actions.js";
|
|
21
|
+
/**
|
|
22
|
+
* The automation editor's DECLARATIONS: what another surface reads of this
|
|
23
|
+
* plugin before any of its own surfaces load, in both apps and in the
|
|
24
|
+
* console's browser.
|
|
25
|
+
*
|
|
26
|
+
* The checks of the server steps an automation holds (AGL-3080), so a plugin
|
|
27
|
+
* that writes an automation it does not edit — a recipe installed into a
|
|
28
|
+
* site, a drafted automation graded before it is handed over — refuses what
|
|
29
|
+
* this editor would (`validateStoredInteraction`). Light by construction: the
|
|
30
|
+
* checks are pure, and nothing here reaches Firestore or React.
|
|
31
|
+
*/ export function registerWorkflowsDeclarations() {
|
|
32
|
+
registerInteractionStepChecks(SERVER_ACTION_STEP_TYPES, hostActionStepProblem, {
|
|
33
|
+
pluginId: BUNDLE_ID
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=declarations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/declarations.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\n// The seam from its own module, not the plugin-manager barrel: both apps load\n// this at boot, and the barrel reaches the client contexts.\nimport { registerInteractionStepChecks } from '@aglyn/aglyn/plugin-manager/interaction-step-checks'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { hostActionStepProblem, SERVER_ACTION_STEP_TYPES } from './model/host-actions'\n\n/**\n * The automation editor's DECLARATIONS: what another surface reads of this\n * plugin before any of its own surfaces load, in both apps and in the\n * console's browser.\n *\n * The checks of the server steps an automation holds (AGL-3080), so a plugin\n * that writes an automation it does not edit — a recipe installed into a\n * site, a drafted automation graded before it is handed over — refuses what\n * this editor would (`validateStoredInteraction`). Light by construction: the\n * checks are pure, and nothing here reaches Firestore or React.\n */\nexport function registerWorkflowsDeclarations(): void {\n registerInteractionStepChecks(SERVER_ACTION_STEP_TYPES, hostActionStepProblem, { pluginId: BUNDLE_ID })\n}\n"],"names":["registerInteractionStepChecks","BUNDLE_ID","hostActionStepProblem","SERVER_ACTION_STEP_TYPES","registerWorkflowsDeclarations","pluginId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,8EAA8E;AAC9E,4DAA4D;AAC5D,SAASA,6BAA6B,QAAQ,sDAAqD;AACnG,SAASC,SAAS,QAAQ,+BAA2B;AACrD,SAASC,qBAAqB,EAAEC,wBAAwB,QAAQ,0BAAsB;AAEtF;;;;;;;;;;CAUC,GACD,OAAO,SAASC;IACdJ,8BAA8BG,0BAA0BD,uBAAuB;QAAEG,UAAUJ;IAAU;AACvG"}
|
|
@@ -19,7 +19,10 @@ import { registerHostEventListener } from "@aglyn/tenant-runtime/host-event-list
|
|
|
19
19
|
import { registerVariableComputer } from "@aglyn/aglyn/plugin-manager/computed-variables";
|
|
20
20
|
import { registerPluginDependentsSource } from "@aglyn/aglyn/plugin-manager/plugin-dependents";
|
|
21
21
|
import { registerPluginRecordIndex } from "@aglyn/aglyn/plugin-manager/plugin-record-index";
|
|
22
|
+
import { registerPluginTransferResource } from "@aglyn/aglyn/plugin-manager/plugin-transfer-resources";
|
|
22
23
|
import { BUNDLE_ID } from "./constants/bundle-common.js";
|
|
24
|
+
import { ORG_AUTOMATION_PACKAGE_RULES, ORG_AUTOMATIONS_TRANSFER_KEY, orgAutomationDependencies, rawOrgAutomationContent, remapOrgAutomationIds } from "./transfer/org-automations-package.js";
|
|
25
|
+
/** The org automations package's server half (AGL-3535), loaded when an import or export first asks. */ const automationsPackage = async ()=>(await import("./transfer/org-automations-package.server.js")).createOrgAutomationsPackage();
|
|
23
26
|
/**
|
|
24
27
|
* The automation engine, as the runtime hears it.
|
|
25
28
|
*
|
|
@@ -68,6 +71,20 @@ import { BUNDLE_ID } from "./constants/bundle-common.js";
|
|
|
68
71
|
* whose boot did not run it still registers the listener the first time a
|
|
69
72
|
* plugin door loads. Registering twice replaces in place.
|
|
70
73
|
*/ export function registerWorkflowsServerDeclarations() {
|
|
74
|
+
// Org automations in a workspace package (AGL-3535).
|
|
75
|
+
registerPluginTransferResource(ORG_AUTOMATIONS_TRANSFER_KEY, {
|
|
76
|
+
items: async (ctx)=>(await automationsPackage()).items(ctx),
|
|
77
|
+
dependencies: (item)=>orgAutomationDependencies(rawOrgAutomationContent(item)),
|
|
78
|
+
remapIds: (item, idMap)=>remapOrgAutomationIds(rawOrgAutomationContent(item), idMap),
|
|
79
|
+
readItems: async (ctx, ids)=>(await automationsPackage()).readItems(ctx, ids),
|
|
80
|
+
writeItems: async (ctx, items, writer)=>(await automationsPackage()).writeItems(ctx, items, writer),
|
|
81
|
+
revertItems: async (ctx, steps)=>(await automationsPackage()).revertItems(ctx, steps),
|
|
82
|
+
problems: async (ctx, write)=>(await automationsPackage()).problems(ctx, write),
|
|
83
|
+
referenceTargets: async (ctx, kinds)=>(await automationsPackage()).referenceTargets(ctx, kinds),
|
|
84
|
+
rules: ORG_AUTOMATION_PACKAGE_RULES
|
|
85
|
+
}, {
|
|
86
|
+
pluginId: BUNDLE_ID
|
|
87
|
+
});
|
|
71
88
|
registerHostEventListener(BUNDLE_ID, workflowsHostEventListener);
|
|
72
89
|
// The readers and the Admin SDK arrive with the first read, not with the boot.
|
|
73
90
|
registerPluginRecordIndex('workflow', lazyIndex('workflowRecordIndex'), {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/declarations.server.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\n// The registry from its own module, not the runtime barrel: boot needs the\n// registry and nothing else, and the barrel reaches the data layer.\nimport {\n type HostEventListener,\n registerHostEventListener,\n} from '@aglyn/tenant-runtime/host-event-listeners'\nimport { registerVariableComputer } from '@aglyn/aglyn/plugin-manager/computed-variables'\nimport { registerPluginDependentsSource } from '@aglyn/aglyn/plugin-manager/plugin-dependents'\nimport {\n registerPluginRecordIndex,\n type PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport { BUNDLE_ID } from './constants/bundle-common'\n\n/**\n * The automation engine, as the runtime hears it.\n *\n * Light by construction: the engine is imported when the first event\n * arrives, not when the process starts, so a process that never sees an\n * event pays for this object and nothing else.\n */\nexport const workflowsHostEventListener: HostEventListener = {\n async onEvent(hostId, event, payload, context) {\n const [{ runEventAutomations }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-automations'),\n import('./engine/run-trigger-actor'),\n ])\n // Every run this event causes records who caused it (AGL-3376).\n return await withRunTriggerActor(context?.actor, () =>\n runEventAutomations(hostId, event, payload),\n )\n },\n async onDispatch(hostId, automationId, event, payload, context) {\n const [{ runSingleAction }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-actions'),\n import('./engine/run-trigger-actor'),\n ])\n // A page-fired automation is always a visitor on the published page.\n return await withRunTriggerActor(context?.actor ?? { kind: 'visitor' }, () =>\n runSingleAction(hostId, automationId, event, payload),\n )\n },\n}\n\n/**\n * The plugin's SERVER declarations: what the server must know at boot,\n * before any door of the plugin has been called.\n *\n * The engine's host-event listener, because the doors that raise the events\n * mostly never load this plugin. A form submission and a page view are core\n * routes; a contact captured by a Stripe webhook is a core capture door. Each\n * calls `emitHostEvent`, and the listener has to be registered before the\n * first of them arrives — which is what running this from both apps'\n * `instrumentation.ts` gives.\n *\n * The indexes of the site's workflows, webhooks and actions, so another\n * surface — an AI job — reads them without reaching for this plugin's\n * collections; the workflows that call a function, which the \"Used by\"\n * scan asks this plugin for; and the site variables a workflow computes,\n * which every published page's compose asks for.\n *\n * Also called from the plugin's own API register functions, so a process\n * whose boot did not run it still registers the listener the first time a\n * plugin door loads. Registering twice replaces in place.\n */\nexport function registerWorkflowsServerDeclarations(): void {\n registerHostEventListener(BUNDLE_ID, workflowsHostEventListener)\n // The readers and the Admin SDK arrive with the first read, not with the boot.\n registerPluginRecordIndex('workflow', lazyIndex('workflowRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('webhook', lazyIndex('webhookRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('action', lazyIndex('actionRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginDependentsSource(\n {\n kinds: ['function'],\n find: async (request) =>\n (await import('./server/workflow-dependents')).findFunctionDependents(request),\n },\n { pluginId: BUNDLE_ID },\n )\n registerVariableComputer(\n {\n prepare: async (hostId) =>\n (await import('./server/workflow-variables')).prepareWorkflowVariables(hostId),\n },\n { pluginId: BUNDLE_ID },\n )\n}\n\ntype IndexName = 'workflowRecordIndex' | 'webhookRecordIndex' | 'actionRecordIndex'\n\nfunction lazyIndex(name: IndexName): PluginRecordIndex {\n const load = async () => (await import('./server/automation-record-index'))[name]\n return {\n list: async (request) => (await load()).list(request),\n get: async (request) => (await load()).get(request),\n }\n}\n"],"names":["registerHostEventListener","registerVariableComputer","registerPluginDependentsSource","registerPluginRecordIndex","BUNDLE_ID","workflowsHostEventListener","onEvent","hostId","event","payload","context","runEventAutomations","withRunTriggerActor","Promise","all","actor","onDispatch","automationId","runSingleAction","kind","registerWorkflowsServerDeclarations","
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/declarations.server.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\n// The registry from its own module, not the runtime barrel: boot needs the\n// registry and nothing else, and the barrel reaches the data layer.\nimport {\n type HostEventListener,\n registerHostEventListener,\n} from '@aglyn/tenant-runtime/host-event-listeners'\nimport { registerVariableComputer } from '@aglyn/aglyn/plugin-manager/computed-variables'\nimport { registerPluginDependentsSource } from '@aglyn/aglyn/plugin-manager/plugin-dependents'\nimport {\n registerPluginRecordIndex,\n type PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport { registerPluginTransferResource } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport {\n ORG_AUTOMATION_PACKAGE_RULES,\n ORG_AUTOMATIONS_TRANSFER_KEY,\n orgAutomationDependencies,\n rawOrgAutomationContent,\n remapOrgAutomationIds,\n} from './transfer/org-automations-package'\n\n/** The org automations package's server half (AGL-3535), loaded when an import or export first asks. */\nconst automationsPackage = async () =>\n (await import('./transfer/org-automations-package.server')).createOrgAutomationsPackage()\n\n/**\n * The automation engine, as the runtime hears it.\n *\n * Light by construction: the engine is imported when the first event\n * arrives, not when the process starts, so a process that never sees an\n * event pays for this object and nothing else.\n */\nexport const workflowsHostEventListener: HostEventListener = {\n async onEvent(hostId, event, payload, context) {\n const [{ runEventAutomations }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-automations'),\n import('./engine/run-trigger-actor'),\n ])\n // Every run this event causes records who caused it (AGL-3376).\n return await withRunTriggerActor(context?.actor, () =>\n runEventAutomations(hostId, event, payload),\n )\n },\n async onDispatch(hostId, automationId, event, payload, context) {\n const [{ runSingleAction }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-actions'),\n import('./engine/run-trigger-actor'),\n ])\n // A page-fired automation is always a visitor on the published page.\n return await withRunTriggerActor(context?.actor ?? { kind: 'visitor' }, () =>\n runSingleAction(hostId, automationId, event, payload),\n )\n },\n}\n\n/**\n * The plugin's SERVER declarations: what the server must know at boot,\n * before any door of the plugin has been called.\n *\n * The engine's host-event listener, because the doors that raise the events\n * mostly never load this plugin. A form submission and a page view are core\n * routes; a contact captured by a Stripe webhook is a core capture door. Each\n * calls `emitHostEvent`, and the listener has to be registered before the\n * first of them arrives — which is what running this from both apps'\n * `instrumentation.ts` gives.\n *\n * The indexes of the site's workflows, webhooks and actions, so another\n * surface — an AI job — reads them without reaching for this plugin's\n * collections; the workflows that call a function, which the \"Used by\"\n * scan asks this plugin for; and the site variables a workflow computes,\n * which every published page's compose asks for.\n *\n * Also called from the plugin's own API register functions, so a process\n * whose boot did not run it still registers the listener the first time a\n * plugin door loads. Registering twice replaces in place.\n */\nexport function registerWorkflowsServerDeclarations(): void {\n // Org automations in a workspace package (AGL-3535).\n registerPluginTransferResource(\n ORG_AUTOMATIONS_TRANSFER_KEY,\n {\n items: async (ctx) => (await automationsPackage()).items(ctx),\n dependencies: (item) => orgAutomationDependencies(rawOrgAutomationContent(item)),\n remapIds: (item, idMap) => remapOrgAutomationIds(rawOrgAutomationContent(item), idMap),\n readItems: async (ctx, ids) => (await automationsPackage()).readItems(ctx, ids),\n writeItems: async (ctx, items, writer) => (await automationsPackage()).writeItems(ctx, items as never, writer),\n revertItems: async (ctx, steps) => (await automationsPackage()).revertItems(ctx, steps as never),\n problems: async (ctx, write) => (await automationsPackage()).problems(ctx, write as never),\n referenceTargets: async (ctx, kinds) => (await automationsPackage()).referenceTargets(ctx, kinds),\n rules: ORG_AUTOMATION_PACKAGE_RULES,\n },\n { pluginId: BUNDLE_ID },\n )\n registerHostEventListener(BUNDLE_ID, workflowsHostEventListener)\n // The readers and the Admin SDK arrive with the first read, not with the boot.\n registerPluginRecordIndex('workflow', lazyIndex('workflowRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('webhook', lazyIndex('webhookRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('action', lazyIndex('actionRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginDependentsSource(\n {\n kinds: ['function'],\n find: async (request) =>\n (await import('./server/workflow-dependents')).findFunctionDependents(request),\n },\n { pluginId: BUNDLE_ID },\n )\n registerVariableComputer(\n {\n prepare: async (hostId) =>\n (await import('./server/workflow-variables')).prepareWorkflowVariables(hostId),\n },\n { pluginId: BUNDLE_ID },\n )\n}\n\ntype IndexName = 'workflowRecordIndex' | 'webhookRecordIndex' | 'actionRecordIndex'\n\nfunction lazyIndex(name: IndexName): PluginRecordIndex {\n const load = async () => (await import('./server/automation-record-index'))[name]\n return {\n list: async (request) => (await load()).list(request),\n get: async (request) => (await load()).get(request),\n }\n}\n"],"names":["registerHostEventListener","registerVariableComputer","registerPluginDependentsSource","registerPluginRecordIndex","registerPluginTransferResource","BUNDLE_ID","ORG_AUTOMATION_PACKAGE_RULES","ORG_AUTOMATIONS_TRANSFER_KEY","orgAutomationDependencies","rawOrgAutomationContent","remapOrgAutomationIds","automationsPackage","createOrgAutomationsPackage","workflowsHostEventListener","onEvent","hostId","event","payload","context","runEventAutomations","withRunTriggerActor","Promise","all","actor","onDispatch","automationId","runSingleAction","kind","registerWorkflowsServerDeclarations","items","ctx","dependencies","item","remapIds","idMap","readItems","ids","writeItems","writer","revertItems","steps","problems","write","referenceTargets","kinds","rules","pluginId","lazyIndex","find","request","findFunctionDependents","prepare","prepareWorkflowVariables","name","load","list","get"],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
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 CrmMergeContext, type CrmMergeResult } from '@aglyn/aglyn/app-utils/crm-email-templates';
|
|
18
|
+
/**
|
|
19
|
+
* The person a step's email is about, as the payload names them when no
|
|
20
|
+
* record does: the `name` and `email` the event carried.
|
|
21
|
+
*/
|
|
22
|
+
export interface EmailMergePerson {
|
|
23
|
+
name?: string | null;
|
|
24
|
+
email?: string | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A `sendEmail` step's subject or body with its merge tags filled from the
|
|
28
|
+
* contact or the lead the run is about (AGL-3458), so a reply greets the
|
|
29
|
+
* person by name rather than as `{{firstName|there}}`.
|
|
30
|
+
*
|
|
31
|
+
* Both spellings a merchant has been shown resolve, because a workflow email
|
|
32
|
+
* is written by the same people who write the other two:
|
|
33
|
+
*
|
|
34
|
+
* - the CRM's fields, `{{contact.firstName}}`, `{{lead.company}}`,
|
|
35
|
+
* `{{site.name}}` — `resolveCrmMergeFields`, the one-to-one email's
|
|
36
|
+
* resolver, run over the record the run resolved;
|
|
37
|
+
* - the campaign's short tags, `{{firstName|there}}`, `{{name}}`,
|
|
38
|
+
* `{{email}}` — `resolveMergeTags`, the campaign sender's own resolver, run
|
|
39
|
+
* over the same person: the contact's name as this site knows it, else
|
|
40
|
+
* the lead's, else the name the event carried.
|
|
41
|
+
*
|
|
42
|
+
* Both honor `|fallback`, and an unknown tag prints its fallback or nothing,
|
|
43
|
+
* never its braces — a typo must not reach a customer as `{{frstName}}`.
|
|
44
|
+
* Pure; the caller hands over what it read.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveStepEmailMerge(text: string, context: CrmMergeContext, person?: EmailMergePerson | null): CrmMergeResult;
|
|
47
|
+
/**
|
|
48
|
+
* The records a step's email is about, for its merge tags: the person the
|
|
49
|
+
* event names, as the plugin that keeps people holds them — a contact, else
|
|
50
|
+
* the lead a lead-routed form filed — and the site's name, in the shape the
|
|
51
|
+
* one-to-one email's resolver takes (`resolveStepEmailMerge`).
|
|
52
|
+
*
|
|
53
|
+
* Asked through the person-records seam (`findPluginPerson`), narrowed to
|
|
54
|
+
* what this site may see, so the engine opens no record system's storage. A
|
|
55
|
+
* workspace with no record system, or a person nobody holds yet, leaves both
|
|
56
|
+
* records out, and the tags fall back to the name and address the event
|
|
57
|
+
* carried.
|
|
58
|
+
*
|
|
59
|
+
* **Never throws.** A lookup that fails is an email with its fallbacks, never
|
|
60
|
+
* an email that did not leave.
|
|
61
|
+
*/
|
|
62
|
+
export declare function stepEmailMergeContext(input: {
|
|
63
|
+
hostId: string;
|
|
64
|
+
/** The address of the person the event is about. */
|
|
65
|
+
email: unknown;
|
|
66
|
+
/** The holder group whose facet names a contact: the sending site's. */
|
|
67
|
+
contactGroupId: string;
|
|
68
|
+
siteName: string;
|
|
69
|
+
}): Promise<CrmMergeContext>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { normalizeContactEmail } from "@aglyn/aglyn/app-utils/contacts";
|
|
18
|
+
import { resolveCrmMergeFields } from "@aglyn/aglyn/app-utils/crm-email-templates";
|
|
19
|
+
import { findPluginPerson } from "@aglyn/aglyn/plugin-manager/plugin-person-records";
|
|
20
|
+
import { resolveMergeTags } from "@aglyn/shared-util-email/email-merge";
|
|
21
|
+
/** One CRM field's value, through the one-to-one email's own resolver. */ const field = (key, context)=>resolveCrmMergeFields(`{{${key}}}`, context).text.trim();
|
|
22
|
+
/**
|
|
23
|
+
* A `sendEmail` step's subject or body with its merge tags filled from the
|
|
24
|
+
* contact or the lead the run is about (AGL-3458), so a reply greets the
|
|
25
|
+
* person by name rather than as `{{firstName|there}}`.
|
|
26
|
+
*
|
|
27
|
+
* Both spellings a merchant has been shown resolve, because a workflow email
|
|
28
|
+
* is written by the same people who write the other two:
|
|
29
|
+
*
|
|
30
|
+
* - the CRM's fields, `{{contact.firstName}}`, `{{lead.company}}`,
|
|
31
|
+
* `{{site.name}}` — `resolveCrmMergeFields`, the one-to-one email's
|
|
32
|
+
* resolver, run over the record the run resolved;
|
|
33
|
+
* - the campaign's short tags, `{{firstName|there}}`, `{{name}}`,
|
|
34
|
+
* `{{email}}` — `resolveMergeTags`, the campaign sender's own resolver, run
|
|
35
|
+
* over the same person: the contact's name as this site knows it, else
|
|
36
|
+
* the lead's, else the name the event carried.
|
|
37
|
+
*
|
|
38
|
+
* Both honor `|fallback`, and an unknown tag prints its fallback or nothing,
|
|
39
|
+
* never its braces — a typo must not reach a customer as `{{frstName}}`.
|
|
40
|
+
* Pure; the caller hands over what it read.
|
|
41
|
+
*/ export function resolveStepEmailMerge(text, context, person = null) {
|
|
42
|
+
var _normalizeContactEmail;
|
|
43
|
+
const crm = resolveCrmMergeFields(text, context);
|
|
44
|
+
const name = field('contact.name', context) || field('lead.name', context) || (typeof (person == null ? void 0 : person.name) === 'string' ? person.name.trim() : '');
|
|
45
|
+
const email = field('contact.email', context) || field('lead.email', context) || ((_normalizeContactEmail = normalizeContactEmail(person == null ? void 0 : person.email)) != null ? _normalizeContactEmail : '');
|
|
46
|
+
return {
|
|
47
|
+
text: resolveMergeTags(crm.text, _extends({
|
|
48
|
+
email
|
|
49
|
+
}, name ? {
|
|
50
|
+
name
|
|
51
|
+
} : {})),
|
|
52
|
+
unresolved: crm.unresolved
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The records a step's email is about, for its merge tags: the person the
|
|
57
|
+
* event names, as the plugin that keeps people holds them — a contact, else
|
|
58
|
+
* the lead a lead-routed form filed — and the site's name, in the shape the
|
|
59
|
+
* one-to-one email's resolver takes (`resolveStepEmailMerge`).
|
|
60
|
+
*
|
|
61
|
+
* Asked through the person-records seam (`findPluginPerson`), narrowed to
|
|
62
|
+
* what this site may see, so the engine opens no record system's storage. A
|
|
63
|
+
* workspace with no record system, or a person nobody holds yet, leaves both
|
|
64
|
+
* records out, and the tags fall back to the name and address the event
|
|
65
|
+
* carried.
|
|
66
|
+
*
|
|
67
|
+
* **Never throws.** A lookup that fails is an email with its fallbacks, never
|
|
68
|
+
* an email that did not leave.
|
|
69
|
+
*/ export async function stepEmailMergeContext(input) {
|
|
70
|
+
const context = {
|
|
71
|
+
site: {
|
|
72
|
+
name: input.siteName
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
const person = await findPluginPerson({
|
|
77
|
+
hostId: input.hostId,
|
|
78
|
+
email: input.email,
|
|
79
|
+
onlyVisibleToSite: true,
|
|
80
|
+
anyKind: true
|
|
81
|
+
});
|
|
82
|
+
const data = person ? _extends({}, person.data) : null;
|
|
83
|
+
if ((person == null ? void 0 : person.kind) === 'contact') {
|
|
84
|
+
return _extends({}, context, {
|
|
85
|
+
contact: data,
|
|
86
|
+
contactGroupId: input.contactGroupId
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if ((person == null ? void 0 : person.kind) === 'lead') return _extends({}, context, {
|
|
90
|
+
lead: data
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('[workflow] email merge context could not be read', input.hostId, error);
|
|
94
|
+
}
|
|
95
|
+
return context;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=email-merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/engine/email-merge.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 { normalizeContactEmail } from '@aglyn/aglyn/app-utils/contacts'\nimport {\n type CrmMergeContext,\n type CrmMergeResult,\n resolveCrmMergeFields,\n} from '@aglyn/aglyn/app-utils/crm-email-templates'\nimport { findPluginPerson } from '@aglyn/aglyn/plugin-manager/plugin-person-records'\nimport { resolveMergeTags } from '@aglyn/shared-util-email/email-merge'\n\n/**\n * The person a step's email is about, as the payload names them when no\n * record does: the `name` and `email` the event carried.\n */\nexport interface EmailMergePerson {\n name?: string | null\n email?: string | null\n}\n\n/** One CRM field's value, through the one-to-one email's own resolver. */\nconst field = (key: string, context: CrmMergeContext): string =>\n resolveCrmMergeFields(`{{${key}}}`, context).text.trim()\n\n/**\n * A `sendEmail` step's subject or body with its merge tags filled from the\n * contact or the lead the run is about (AGL-3458), so a reply greets the\n * person by name rather than as `{{firstName|there}}`.\n *\n * Both spellings a merchant has been shown resolve, because a workflow email\n * is written by the same people who write the other two:\n *\n * - the CRM's fields, `{{contact.firstName}}`, `{{lead.company}}`,\n * `{{site.name}}` — `resolveCrmMergeFields`, the one-to-one email's\n * resolver, run over the record the run resolved;\n * - the campaign's short tags, `{{firstName|there}}`, `{{name}}`,\n * `{{email}}` — `resolveMergeTags`, the campaign sender's own resolver, run\n * over the same person: the contact's name as this site knows it, else\n * the lead's, else the name the event carried.\n *\n * Both honor `|fallback`, and an unknown tag prints its fallback or nothing,\n * never its braces — a typo must not reach a customer as `{{frstName}}`.\n * Pure; the caller hands over what it read.\n */\nexport function resolveStepEmailMerge(\n text: string,\n context: CrmMergeContext,\n person: EmailMergePerson | null = null,\n): CrmMergeResult {\n const crm = resolveCrmMergeFields(text, context)\n const name =\n field('contact.name', context) ||\n field('lead.name', context) ||\n (typeof person?.name === 'string' ? person.name.trim() : '')\n const email =\n field('contact.email', context) ||\n field('lead.email', context) ||\n (normalizeContactEmail(person?.email) ?? '')\n return {\n text: resolveMergeTags(crm.text, { email, ...(name ? { name } : {}) }),\n unresolved: crm.unresolved,\n }\n}\n\n/**\n * The records a step's email is about, for its merge tags: the person the\n * event names, as the plugin that keeps people holds them — a contact, else\n * the lead a lead-routed form filed — and the site's name, in the shape the\n * one-to-one email's resolver takes (`resolveStepEmailMerge`).\n *\n * Asked through the person-records seam (`findPluginPerson`), narrowed to\n * what this site may see, so the engine opens no record system's storage. A\n * workspace with no record system, or a person nobody holds yet, leaves both\n * records out, and the tags fall back to the name and address the event\n * carried.\n *\n * **Never throws.** A lookup that fails is an email with its fallbacks, never\n * an email that did not leave.\n */\nexport async function stepEmailMergeContext(input: {\n hostId: string\n /** The address of the person the event is about. */\n email: unknown\n /** The holder group whose facet names a contact: the sending site's. */\n contactGroupId: string\n siteName: string\n}): Promise<CrmMergeContext> {\n const context: CrmMergeContext = { site: { name: input.siteName } }\n try {\n const person = await findPluginPerson({\n hostId: input.hostId,\n email: input.email,\n onlyVisibleToSite: true,\n anyKind: true,\n })\n const data = person ? { ...person.data } : null\n if (person?.kind === 'contact') {\n return { ...context, contact: data, contactGroupId: input.contactGroupId }\n }\n if (person?.kind === 'lead') return { ...context, lead: data }\n } catch (error) {\n console.error('[workflow] email merge context could not be read', input.hostId, error)\n }\n return context\n}\n"],"names":["normalizeContactEmail","resolveCrmMergeFields","findPluginPerson","resolveMergeTags","field","key","context","text","trim","resolveStepEmailMerge","person","crm","name","email","unresolved","stepEmailMergeContext","input","site","siteName","hostId","onlyVisibleToSite","anyKind","data","kind","contact","contactGroupId","lead","error","console"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,qBAAqB,QAAQ,kCAAiC;AACvE,SAGEC,qBAAqB,QAChB,6CAA4C;AACnD,SAASC,gBAAgB,QAAQ,oDAAmD;AACpF,SAASC,gBAAgB,QAAQ,uCAAsC;AAWvE,wEAAwE,GACxE,MAAMC,QAAQ,CAACC,KAAaC,UAC1BL,sBAAsB,CAAC,EAAE,EAAEI,IAAI,EAAE,CAAC,EAAEC,SAASC,IAAI,CAACC,IAAI;AAExD;;;;;;;;;;;;;;;;;;;CAmBC,GACD,OAAO,SAASC,sBACdF,IAAY,EACZD,OAAwB,EACxBI,SAAkC,IAAI;QAUnCV;IARH,MAAMW,MAAMV,sBAAsBM,MAAMD;IACxC,MAAMM,OACJR,MAAM,gBAAgBE,YACtBF,MAAM,aAAaE,YAClB,CAAA,QAAOI,0BAAAA,OAAQE,IAAI,MAAK,WAAWF,OAAOE,IAAI,CAACJ,IAAI,KAAK,EAAC;IAC5D,MAAMK,QACJT,MAAM,iBAAiBE,YACvBF,MAAM,cAAcE,cACnBN,yBAAAA,sBAAsBU,0BAAAA,OAAQG,KAAK,aAAnCb,yBAAwC;IAC3C,OAAO;QACLO,MAAMJ,iBAAiBQ,IAAIJ,IAAI,EAAE;YAAEM;WAAWD,OAAO;YAAEA;QAAK,IAAI,CAAC;QACjEE,YAAYH,IAAIG,UAAU;IAC5B;AACF;AAEA;;;;;;;;;;;;;;CAcC,GACD,OAAO,eAAeC,sBAAsBC,KAO3C;IACC,MAAMV,UAA2B;QAAEW,MAAM;YAAEL,MAAMI,MAAME,QAAQ;QAAC;IAAE;IAClE,IAAI;QACF,MAAMR,SAAS,MAAMR,iBAAiB;YACpCiB,QAAQH,MAAMG,MAAM;YACpBN,OAAOG,MAAMH,KAAK;YAClBO,mBAAmB;YACnBC,SAAS;QACX;QACA,MAAMC,OAAOZ,SAAS,aAAKA,OAAOY,IAAI,IAAK;QAC3C,IAAIZ,CAAAA,0BAAAA,OAAQa,IAAI,MAAK,WAAW;YAC9B,OAAO,aAAKjB;gBAASkB,SAASF;gBAAMG,gBAAgBT,MAAMS,cAAc;;QAC1E;QACA,IAAIf,CAAAA,0BAAAA,OAAQa,IAAI,MAAK,QAAQ,OAAO,aAAKjB;YAASoB,MAAMJ;;IAC1D,EAAE,OAAOK,OAAO;QACdC,QAAQD,KAAK,CAAC,oDAAoDX,MAAMG,MAAM,EAAEQ;IAClF;IACA,OAAOrB;AACT"}
|
|
@@ -14,11 +14,14 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import { type
|
|
17
|
+
import { type HostFunction, type HostVariable, type PluginJobHostGate } from '@aglyn/aglyn/server';
|
|
18
18
|
import { type HostWorkflow } from '../model/workflows';
|
|
19
19
|
import { type FlowEnrollment, type FlowSweepCursor, type FlowSweepResult } from './flow-enrollments';
|
|
20
20
|
import type { HostEventPayload } from '@aglyn/tenant-runtime/host-event-listeners';
|
|
21
21
|
import { type AutomationWorkflow, type WorkflowStep } from './workflow-steps';
|
|
22
|
+
import { type HostAction, type HostActionAlert, type HostActionStep } from '../model/host-actions';
|
|
23
|
+
/** The live, switched-on actions a site holds for `event`, in document-id order. */
|
|
24
|
+
export declare function liveActionsForEvent(hostRef: FirebaseFirestore.DocumentReference, event: string): Promise<FirebaseFirestore.QueryDocumentSnapshot[]>;
|
|
22
25
|
export interface ActionRunEnv {
|
|
23
26
|
hostId: string;
|
|
24
27
|
hostRef: FirebaseFirestore.DocumentReference;
|
|
@@ -32,20 +35,14 @@ export interface ActionRunEnv {
|
|
|
32
35
|
*/
|
|
33
36
|
actionsAllowed: boolean;
|
|
34
37
|
webhooksAllowed: boolean;
|
|
35
|
-
/**
|
|
36
|
-
* Whether the owning org holds the CRM suite (AGL-2611) — the gate the
|
|
37
|
-
* five CRM steps take, resolved once beside `webhooksAllowed` and for the
|
|
38
|
-
* same reason: a step gated on the org doc it already read costs no
|
|
39
|
-
* second read per step.
|
|
40
|
-
*/
|
|
41
|
-
crmAllowed: boolean;
|
|
42
38
|
depth: number;
|
|
43
39
|
/**
|
|
44
40
|
* The owning org's billing doc, already read by the entitlement gate that
|
|
45
41
|
* admitted this run, and the org's id beside it.
|
|
46
42
|
*
|
|
47
43
|
* Carried rather than re-read: both entry points resolve `getOrgForHost`
|
|
48
|
-
* before they build this, so
|
|
44
|
+
* before they build this, so a step that gates on the plan — another
|
|
45
|
+
* plugin's step, handed both — costs no extra org read.
|
|
49
46
|
* Null only when the host has no resolvable org, which the gate treats as
|
|
50
47
|
* the free plan.
|
|
51
48
|
*/
|
|
@@ -226,6 +223,34 @@ interface ExecuteWorkflowOptions {
|
|
|
226
223
|
* both, once.
|
|
227
224
|
*/
|
|
228
225
|
export declare function executeWorkflow(env: ActionRunEnv, run: AutomationRun, workflow: AutomationWorkflow, event: string, payload: HostEventPayload, options?: ExecuteWorkflowOptions): Promise<WorkflowExecution>;
|
|
226
|
+
/**
|
|
227
|
+
* Events too frequent to log a skip for.
|
|
228
|
+
*
|
|
229
|
+
* `runEventActions` fires on EVERY page view of every published site. A
|
|
230
|
+
* Firestore write per visitor per non-matching action is not a run
|
|
231
|
+
* history, it is an outage — and a page-view condition is one an author
|
|
232
|
+
* tunes by watching the site, not by reading a log. The events people
|
|
233
|
+
* actually debug are the server-emitted ones (a form submission, a
|
|
234
|
+
* booking, a sign-up), and those are low-volume by construction.
|
|
235
|
+
*/
|
|
236
|
+
export declare const SKIP_LOG_EXCLUDED_EVENTS: ReadonlySet<string>;
|
|
237
|
+
/**
|
|
238
|
+
* Whether an action's unmet conditions only say it is ANOTHER FORM'S
|
|
239
|
+
* (AGL-3458) — the case that is noise rather than an answer.
|
|
240
|
+
*
|
|
241
|
+
* A site with one auto-reply per form keyed on `formId equals …` would write
|
|
242
|
+
* a `Skipped` row for every other form's reply on every submission: eight
|
|
243
|
+
* rows a fill on a nine-form site, burying the one row that explains a real
|
|
244
|
+
* miss. A form picked by id cannot be mistyped, so its mismatch against a
|
|
245
|
+
* submission that names a DIFFERENT form says only "not this form", and it
|
|
246
|
+
* is not recorded. Every other unmet condition still is: a hand-typed
|
|
247
|
+
* `formName`, a field condition, a submission that names no form at all.
|
|
248
|
+
*
|
|
249
|
+
* With `and`, one such clause is the whole answer; with `or`, only when every
|
|
250
|
+
* clause is about which form it was — `formId` or `formName` equals — since
|
|
251
|
+
* any other clause could have been the one that was meant to match.
|
|
252
|
+
*/
|
|
253
|
+
export declare function conditionsNameAnotherForm(trigger: HostAction['trigger'] | null | undefined, payload: HostEventPayload): boolean;
|
|
229
254
|
/**
|
|
230
255
|
* Event-triggered action runner (AGL-148): loads enabled actions whose
|
|
231
256
|
* `trigger.event` matches (built-in, site event, or custom), evaluates
|
|
@@ -249,7 +274,7 @@ export declare function runEventActions(hostId: string, event: string, payload?:
|
|
|
249
274
|
export type SingleActionSkip = 'missing' | 'disabled' | 'event' | 'conditions' | 'plan' | 'allowance' | 'failed';
|
|
250
275
|
/** What one dispatched action did, for a caller that has to say so. */
|
|
251
276
|
export interface SingleActionOutcome {
|
|
252
|
-
/** Its steps ran, and the run counted on the site's
|
|
277
|
+
/** Its steps ran, and the run counted on the site's and workspace's meters. */
|
|
253
278
|
ran: boolean;
|
|
254
279
|
/** Why nothing ran; `null` when it ran. */
|
|
255
280
|
skipped: SingleActionSkip | null;
|