@aglyn/plugins-workflows 1.0.0-beta.217 → 1.0.0-beta.219
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 +28 -4
- package/package.json +12 -12
- package/src/index.d.ts +1 -0
- package/src/index.js.map +1 -1
- package/src/lib/components/host-run-history-card.component.js +1 -1
- package/src/lib/components/host-run-history-card.component.js.map +1 -1
- package/src/lib/components/host-workflows-card.component.js +2 -1
- package/src/lib/components/host-workflows-card.component.js.map +1 -1
- package/src/lib/declarations.server.d.ts +6 -0
- package/src/lib/declarations.server.js +39 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/engine/run-event-actions.d.ts +2 -1
- package/src/lib/engine/run-event-actions.js +49 -15
- package/src/lib/engine/run-event-actions.js.map +1 -1
- package/src/lib/engine/run-event-workflows.js +2 -1
- package/src/lib/engine/run-event-workflows.js.map +1 -1
- package/src/lib/engine/workflow-steps.d.ts +1 -1
- package/src/lib/engine/workflow-steps.js +1 -1
- package/src/lib/engine/workflow-steps.js.map +1 -1
- package/src/lib/model/org-automations.js.map +1 -1
- package/src/lib/model/workflows-record-routes.d.ts +22 -0
- package/src/lib/model/workflows-record-routes.js +73 -0
- package/src/lib/model/workflows-record-routes.js.map +1 -0
- package/src/lib/model/workflows-search-sources.d.ts +20 -0
- package/src/lib/model/workflows-search-sources.js +35 -0
- package/src/lib/model/workflows-search-sources.js.map +1 -0
- package/src/lib/model/workflows.d.ts +90 -0
- package/src/lib/model/workflows.js +166 -0
- package/src/lib/model/workflows.js.map +1 -0
- package/src/lib/plan-entitlements.d.ts +36 -0
- package/src/lib/plan-entitlements.js +44 -0
- package/src/lib/plan-entitlements.js.map +1 -0
- package/src/lib/plugin.js +5 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/automation-record-index.d.ts +20 -0
- package/src/lib/server/automation-record-index.js +90 -0
- package/src/lib/server/automation-record-index.js.map +1 -0
- package/src/lib/server/workflow-dependents.d.ts +28 -0
- package/src/lib/server/workflow-dependents.js +61 -0
- package/src/lib/server/workflow-dependents.js.map +1 -0
- package/src/lib/server/workflow-variables.d.ts +20 -0
- package/src/lib/server/workflow-variables.js +75 -0
- package/src/lib/server/workflow-variables.js.map +1 -0
- package/src/lib/server.d.ts +1 -0
- package/src/lib/server.js +2 -1
- package/src/lib/server.js.map +1 -1
- package/src/lib/tenant-emails.d.ts +29 -0
- package/src/lib/tenant-emails.js +41 -0
- package/src/lib/tenant-emails.js.map +1 -0
- package/src/lib/usage-axes.d.ts +28 -0
- package/src/lib/usage-axes.js +61 -0
- package/src/lib/usage-axes.js.map +1 -0
|
@@ -0,0 +1,61 @@
|
|
|
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 { workflowRecordIndex } from "./automation-record-index.js";
|
|
17
|
+
/**
|
|
18
|
+
* Which of a site's workflows call a function, for the "Used by" scan.
|
|
19
|
+
*
|
|
20
|
+
* A workflow step calls a site function by its document id, with the name it
|
|
21
|
+
* had when the step was written beside it as a display hint (AGL-261); a step
|
|
22
|
+
* written before ids existed carries the name alone. Either reference is a
|
|
23
|
+
* dependent: the id survives a rename of the function, the name does not.
|
|
24
|
+
*/ /** How many of a site's workflows one scan reads. */ export const FUNCTION_DEPENDENTS_WORKFLOWS_READ = 100;
|
|
25
|
+
const text = (value)=>typeof value === 'string' ? value.trim() : '';
|
|
26
|
+
export async function findFunctionDependents(request) {
|
|
27
|
+
const id = request.id.trim();
|
|
28
|
+
const name = text(request.name);
|
|
29
|
+
const { records, truncated } = await workflowRecordIndex.list({
|
|
30
|
+
hostId: request.hostId,
|
|
31
|
+
limit: FUNCTION_DEPENDENTS_WORKFLOWS_READ
|
|
32
|
+
});
|
|
33
|
+
const dependents = [];
|
|
34
|
+
for (const record of records){
|
|
35
|
+
const steps = Array.isArray(record.facts['steps']) ? record.facts['steps'] : [];
|
|
36
|
+
const via = new Set();
|
|
37
|
+
for (const step of steps){
|
|
38
|
+
if (!step || typeof step !== 'object') continue;
|
|
39
|
+
const { functionId, functionName } = step;
|
|
40
|
+
if (id && text(functionId) === id) via.add('id');
|
|
41
|
+
if (name && text(functionName) === name) via.add('name');
|
|
42
|
+
}
|
|
43
|
+
if (via.size) {
|
|
44
|
+
dependents.push({
|
|
45
|
+
type: 'workflow',
|
|
46
|
+
id: record.id,
|
|
47
|
+
name: record.name,
|
|
48
|
+
via: [
|
|
49
|
+
'id',
|
|
50
|
+
'name'
|
|
51
|
+
].filter((one)=>via.has(one))
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
dependents,
|
|
57
|
+
truncated
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=workflow-dependents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/server/workflow-dependents.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 { BindingRefVia } from '@aglyn/aglyn/app-utils/binding-tokens'\nimport type {\n PluginDependent,\n PluginDependentsAnswer,\n PluginDependentsRequest,\n} from '@aglyn/aglyn/plugin-manager/plugin-dependents'\nimport { workflowRecordIndex } from './automation-record-index'\n\n/**\n * Which of a site's workflows call a function, for the \"Used by\" scan.\n *\n * A workflow step calls a site function by its document id, with the name it\n * had when the step was written beside it as a display hint (AGL-261); a step\n * written before ids existed carries the name alone. Either reference is a\n * dependent: the id survives a rename of the function, the name does not.\n */\n\n/** How many of a site's workflows one scan reads. */\nexport const FUNCTION_DEPENDENTS_WORKFLOWS_READ = 100\n\nconst text = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nexport async function findFunctionDependents(\n request: PluginDependentsRequest,\n): Promise<PluginDependentsAnswer> {\n const id = request.id.trim()\n const name = text(request.name)\n const { records, truncated } = await workflowRecordIndex.list({\n hostId: request.hostId,\n limit: FUNCTION_DEPENDENTS_WORKFLOWS_READ,\n })\n const dependents: PluginDependent[] = []\n for (const record of records) {\n const steps = Array.isArray(record.facts['steps']) ? (record.facts['steps'] as unknown[]) : []\n const via = new Set<BindingRefVia>()\n for (const step of steps) {\n if (!step || typeof step !== 'object') continue\n const { functionId, functionName } = step as Record<string, unknown>\n if (id && text(functionId) === id) via.add('id')\n if (name && text(functionName) === name) via.add('name')\n }\n if (via.size) {\n dependents.push({\n type: 'workflow',\n id: record.id,\n name: record.name,\n via: (['id', 'name'] as const).filter((one) => via.has(one)),\n })\n }\n }\n return { dependents, truncated }\n}\n"],"names":["workflowRecordIndex","FUNCTION_DEPENDENTS_WORKFLOWS_READ","text","value","trim","findFunctionDependents","request","id","name","records","truncated","list","hostId","limit","dependents","record","steps","Array","isArray","facts","via","Set","step","functionId","functionName","add","size","push","type","filter","one","has"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAQD,SAASA,mBAAmB,QAAQ,+BAA2B;AAE/D;;;;;;;CAOC,GAED,mDAAmD,GACnD,OAAO,MAAMC,qCAAqC,IAAG;AAErD,MAAMC,OAAO,CAACC,QAA4B,OAAOA,UAAU,WAAWA,MAAMC,IAAI,KAAK;AAErF,OAAO,eAAeC,uBACpBC,OAAgC;IAEhC,MAAMC,KAAKD,QAAQC,EAAE,CAACH,IAAI;IAC1B,MAAMI,OAAON,KAAKI,QAAQE,IAAI;IAC9B,MAAM,EAAEC,OAAO,EAAEC,SAAS,EAAE,GAAG,MAAMV,oBAAoBW,IAAI,CAAC;QAC5DC,QAAQN,QAAQM,MAAM;QACtBC,OAAOZ;IACT;IACA,MAAMa,aAAgC,EAAE;IACxC,KAAK,MAAMC,UAAUN,QAAS;QAC5B,MAAMO,QAAQC,MAAMC,OAAO,CAACH,OAAOI,KAAK,CAAC,QAAQ,IAAKJ,OAAOI,KAAK,CAAC,QAAQ,GAAiB,EAAE;QAC9F,MAAMC,MAAM,IAAIC;QAChB,KAAK,MAAMC,QAAQN,MAAO;YACxB,IAAI,CAACM,QAAQ,OAAOA,SAAS,UAAU;YACvC,MAAM,EAAEC,UAAU,EAAEC,YAAY,EAAE,GAAGF;YACrC,IAAIf,MAAML,KAAKqB,gBAAgBhB,IAAIa,IAAIK,GAAG,CAAC;YAC3C,IAAIjB,QAAQN,KAAKsB,kBAAkBhB,MAAMY,IAAIK,GAAG,CAAC;QACnD;QACA,IAAIL,IAAIM,IAAI,EAAE;YACZZ,WAAWa,IAAI,CAAC;gBACdC,MAAM;gBACNrB,IAAIQ,OAAOR,EAAE;gBACbC,MAAMO,OAAOP,IAAI;gBACjBY,KAAK,AAAC;oBAAC;oBAAM;iBAAO,CAAWS,MAAM,CAAC,CAACC,MAAQV,IAAIW,GAAG,CAACD;YACzD;QACF;IACF;IACA,OAAO;QAAEhB;QAAYJ;IAAU;AACjC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
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 { VariableComputation } from '@aglyn/aglyn/plugin-manager/computed-variables';
|
|
18
|
+
/** How many of a site's workflows a page reads to compute its variables. */
|
|
19
|
+
export declare const COMPUTED_VARIABLE_WORKFLOWS_READ = 100;
|
|
20
|
+
export declare function prepareWorkflowVariables(hostId: string): Promise<VariableComputation>;
|
|
@@ -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
|
+
*/ import { firebaseAdmin } from "@aglyn/tenant-data-admin/server/firebase-admin";
|
|
17
|
+
import { PUBLISHED_SITE_DATA_TTL_SECONDS, tenantDataTag, withRenderCache } from "@aglyn/tenant-data-admin/render-cache";
|
|
18
|
+
import { resolveComputedVariables } from "../model/workflows.js";
|
|
19
|
+
/**
|
|
20
|
+
* The site variables a workflow computes (AGL-129), as this plugin answers
|
|
21
|
+
* the page's compose pipeline through the core's variable-computer seam.
|
|
22
|
+
*
|
|
23
|
+
* The site's workflows are read beside the page's other reads (`prepare` is
|
|
24
|
+
* issued with them), cached exactly as the page's own variables and
|
|
25
|
+
* functions are, and run once the variables and functions are in hand. Each
|
|
26
|
+
* computed variable that names a workflow — by id first, then by name —
|
|
27
|
+
* takes its result; one whose workflow is gone or fails keeps its stored
|
|
28
|
+
* value.
|
|
29
|
+
*/ /** As long as the page's own data, and dropped by the same publish tag. */ const SITE_WORKFLOWS_TTL_SECONDS = PUBLISHED_SITE_DATA_TTL_SECONDS;
|
|
30
|
+
/** How many of a site's workflows a page reads to compute its variables. */ export const COMPUTED_VARIABLE_WORKFLOWS_READ = 100;
|
|
31
|
+
export async function prepareWorkflowVariables(hostId) {
|
|
32
|
+
const workflows = await siteWorkflows(hostId);
|
|
33
|
+
return ({ variables, functions })=>resolveComputedVariables(variables, functions, workflows);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The site's live workflows, keyed by document id AND by name (AGL-261): an id
|
|
37
|
+
* reference is rename-safe, and a variable picked before ids existed names
|
|
38
|
+
* its workflow. Fails open — a failed read computes nothing, and every
|
|
39
|
+
* variable keeps its stored value.
|
|
40
|
+
*/ async function siteWorkflows(hostId) {
|
|
41
|
+
const read = ()=>readSiteWorkflows(hostId);
|
|
42
|
+
try {
|
|
43
|
+
return await withRenderCache({
|
|
44
|
+
key: [
|
|
45
|
+
'tenant-workflows',
|
|
46
|
+
hostId
|
|
47
|
+
],
|
|
48
|
+
revalidate: SITE_WORKFLOWS_TTL_SECONDS,
|
|
49
|
+
tags: [
|
|
50
|
+
tenantDataTag(hostId)
|
|
51
|
+
],
|
|
52
|
+
read
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(error);
|
|
56
|
+
return read();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function readSiteWorkflows(hostId) {
|
|
60
|
+
const workflows = {};
|
|
61
|
+
try {
|
|
62
|
+
const snapshot = await firebaseAdmin.app().firestore().collection('hosts').doc(hostId).collection('workflows').limit(COMPUTED_VARIABLE_WORKFLOWS_READ).get();
|
|
63
|
+
for (const doc of snapshot.docs){
|
|
64
|
+
const data = doc.data();
|
|
65
|
+
if (data.deletedAt) continue;
|
|
66
|
+
workflows[doc.id] = data;
|
|
67
|
+
if (data.name) workflows[data.name] = data;
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error(error);
|
|
71
|
+
}
|
|
72
|
+
return workflows;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=workflow-variables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/server/workflow-variables.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 { VariableComputation } from '@aglyn/aglyn/plugin-manager/computed-variables'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport {\n PUBLISHED_SITE_DATA_TTL_SECONDS,\n tenantDataTag,\n withRenderCache,\n} from '@aglyn/tenant-data-admin/render-cache'\nimport { type HostWorkflow, resolveComputedVariables } from '../model/workflows'\n\n/**\n * The site variables a workflow computes (AGL-129), as this plugin answers\n * the page's compose pipeline through the core's variable-computer seam.\n *\n * The site's workflows are read beside the page's other reads (`prepare` is\n * issued with them), cached exactly as the page's own variables and\n * functions are, and run once the variables and functions are in hand. Each\n * computed variable that names a workflow — by id first, then by name —\n * takes its result; one whose workflow is gone or fails keeps its stored\n * value.\n */\n\n/** As long as the page's own data, and dropped by the same publish tag. */\nconst SITE_WORKFLOWS_TTL_SECONDS = PUBLISHED_SITE_DATA_TTL_SECONDS\n\n/** How many of a site's workflows a page reads to compute its variables. */\nexport const COMPUTED_VARIABLE_WORKFLOWS_READ = 100\n\nexport async function prepareWorkflowVariables(hostId: string): Promise<VariableComputation> {\n const workflows = await siteWorkflows(hostId)\n return ({ variables, functions }) => resolveComputedVariables(variables, functions, workflows)\n}\n\n/**\n * The site's live workflows, keyed by document id AND by name (AGL-261): an id\n * reference is rename-safe, and a variable picked before ids existed names\n * its workflow. Fails open — a failed read computes nothing, and every\n * variable keeps its stored value.\n */\nasync function siteWorkflows(hostId: string): Promise<Record<string, HostWorkflow>> {\n const read = () => readSiteWorkflows(hostId)\n try {\n return await withRenderCache({\n key: ['tenant-workflows', hostId],\n revalidate: SITE_WORKFLOWS_TTL_SECONDS,\n tags: [tenantDataTag(hostId)],\n read,\n })\n } catch (error) {\n console.error(error)\n return read()\n }\n}\n\nasync function readSiteWorkflows(hostId: string): Promise<Record<string, HostWorkflow>> {\n const workflows: Record<string, HostWorkflow> = {}\n try {\n const snapshot = await firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection('workflows')\n .limit(COMPUTED_VARIABLE_WORKFLOWS_READ)\n .get()\n for (const doc of snapshot.docs) {\n const data = doc.data() as HostWorkflow & { deletedAt?: unknown }\n if (data.deletedAt) continue\n workflows[doc.id] = data\n if (data.name) workflows[data.name] = data\n }\n } catch (error) {\n console.error(error)\n }\n return workflows\n}\n"],"names":["firebaseAdmin","PUBLISHED_SITE_DATA_TTL_SECONDS","tenantDataTag","withRenderCache","resolveComputedVariables","SITE_WORKFLOWS_TTL_SECONDS","COMPUTED_VARIABLE_WORKFLOWS_READ","prepareWorkflowVariables","hostId","workflows","siteWorkflows","variables","functions","read","readSiteWorkflows","key","revalidate","tags","error","console","snapshot","app","firestore","collection","doc","limit","get","docs","data","deletedAt","id","name"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,aAAa,QAAQ,iDAAgD;AAC9E,SACEC,+BAA+B,EAC/BC,aAAa,EACbC,eAAe,QACV,wCAAuC;AAC9C,SAA4BC,wBAAwB,QAAQ,wBAAoB;AAEhF;;;;;;;;;;CAUC,GAED,yEAAyE,GACzE,MAAMC,6BAA6BJ;AAEnC,0EAA0E,GAC1E,OAAO,MAAMK,mCAAmC,IAAG;AAEnD,OAAO,eAAeC,yBAAyBC,MAAc;IAC3D,MAAMC,YAAY,MAAMC,cAAcF;IACtC,OAAO,CAAC,EAAEG,SAAS,EAAEC,SAAS,EAAE,GAAKR,yBAAyBO,WAAWC,WAAWH;AACtF;AAEA;;;;;CAKC,GACD,eAAeC,cAAcF,MAAc;IACzC,MAAMK,OAAO,IAAMC,kBAAkBN;IACrC,IAAI;QACF,OAAO,MAAML,gBAAgB;YAC3BY,KAAK;gBAAC;gBAAoBP;aAAO;YACjCQ,YAAYX;YACZY,MAAM;gBAACf,cAAcM;aAAQ;YAC7BK;QACF;IACF,EAAE,OAAOK,OAAO;QACdC,QAAQD,KAAK,CAACA;QACd,OAAOL;IACT;AACF;AAEA,eAAeC,kBAAkBN,MAAc;IAC7C,MAAMC,YAA0C,CAAC;IACjD,IAAI;QACF,MAAMW,WAAW,MAAMpB,cACpBqB,GAAG,GACHC,SAAS,GACTC,UAAU,CAAC,SACXC,GAAG,CAAChB,QACJe,UAAU,CAAC,aACXE,KAAK,CAACnB,kCACNoB,GAAG;QACN,KAAK,MAAMF,OAAOJ,SAASO,IAAI,CAAE;YAC/B,MAAMC,OAAOJ,IAAII,IAAI;YACrB,IAAIA,KAAKC,SAAS,EAAE;YACpBpB,SAAS,CAACe,IAAIM,EAAE,CAAC,GAAGF;YACpB,IAAIA,KAAKG,IAAI,EAAEtB,SAAS,CAACmB,KAAKG,IAAI,CAAC,GAAGH;QACxC;IACF,EAAE,OAAOV,OAAO;QACdC,QAAQD,KAAK,CAACA;IAChB;IACA,OAAOT;AACT"}
|
package/src/lib/server.d.ts
CHANGED
package/src/lib/server.js
CHANGED
|
@@ -14,13 +14,14 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
14
14
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
|
-
*/ import { checkEntitlement, resolveOrgEntitlements
|
|
17
|
+
*/ import { checkEntitlement, resolveOrgEntitlements } from "@aglyn/aglyn/server";
|
|
18
18
|
import { registerPluginApiRoute, registerPluginJob } from "@aglyn/aglyn/server";
|
|
19
19
|
import { activitySearchTokens } from "@aglyn/aglyn/app-utils/activity-search";
|
|
20
20
|
import { firebaseAdmin, getOrgForHost } from "@aglyn/tenant-data-admin";
|
|
21
21
|
import { timingSafeEqual } from "crypto";
|
|
22
22
|
import { FieldValue } from "firebase-admin/firestore";
|
|
23
23
|
import { runSummaryFields } from "./model/run-history.js";
|
|
24
|
+
import { runWorkflow } from "./model/workflows.js";
|
|
24
25
|
import { eventRunSuspension } from "./engine/site-suspension.js";
|
|
25
26
|
import { BUNDLE_ID as WORKFLOWS_BUNDLE_ID } from "./constants/bundle-common.js";
|
|
26
27
|
import { registerWorkflowsServerDeclarations } from "./declarations.server.js";
|
package/src/lib/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/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\nimport {\n checkEntitlement,\n type HostFunction,\n type HostVariable,\n type HostWebhook,\n type HostWorkflow,\n resolveOrgEntitlements,\n runWorkflow,\n} from '@aglyn/aglyn/server'\nimport {\n registerPluginApiRoute,\n registerPluginJob,\n type PluginApiHandler,\n} from '@aglyn/aglyn/server'\nimport { activitySearchTokens } from '@aglyn/aglyn/app-utils/activity-search'\nimport { firebaseAdmin, getOrgForHost } from '@aglyn/tenant-data-admin'\nimport { timingSafeEqual } from 'crypto'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { runSummaryFields } from './model/run-history'\nimport { eventRunSuspension } from './engine/site-suspension'\nimport { BUNDLE_ID as WORKFLOWS_BUNDLE_ID } from './constants/bundle-common'\nimport { registerWorkflowsServerDeclarations } from './declarations.server'\nimport {\n automationRunEnv,\n executeWorkflow,\n runDueFlowEnrollments,\n workflowContextFromDocs,\n} from './engine/run-event-actions'\nimport {\n type AutomationWorkflow,\n workflowHasActionSteps,\n} from './engine/workflow-steps'\n\n/*==========================================\n * THE BEAT THAT MAKES A WAIT STEP REAL.\n *\n * A `wait` step suspends a run into a `flowEnrollments` row and returns; this\n * is the only thing that ever comes back for it. Without a registration here\n * the wait step would be a delay that never ends — the exact failure\n * `process-abandoned.ts` documents next door, where a Pro entitlement sat\n * behind an HTTP door nothing ever POSTed to.\n *\n * Module scope, like the commerce and bookings registrations: the runner\n * route reaches plugin jobs through `ensureAll(['tenantApi'])`, so a\n * registration inside `registerWorkflowsApi()` would depend on which entry\n * point happened to be loaded.\n *\n * ## Every minute, and why that is cheap\n *\n * `FLOW_WAIT_MIN_MINUTES` is one, so the beat has to be one — a fifteen-minute\n * sweep would make the shortest wait anybody can author wrong by up to\n * fourteen minutes. It costs one collection-group query per minute, which\n * returns nothing at all on a platform with no flow due, because the query\n * asks for `resumeAtMs <= now` rather than for the enrolled population.\n *\n * Bounded and idempotent, the two properties the runner requires: the scan\n * budget bounds one pass, and a transactional claim means an overlapping or\n * repeated beat cannot run the same person's step twice.\n *=========================================*/\nregisterPluginJob({\n pluginId: WORKFLOWS_BUNDLE_ID,\n name: 'resume-flow-waits',\n intervalMinutes: 1,\n description:\n 'Continue automations whose wait step has ended, and time out those ' +\n 'waiting for an event that never arrived.',\n lockdown: { scope: 'per-host' },\n handler: async (gate) => {\n await runDueFlowEnrollments(gate)\n },\n})\n\n// Best-effort per-instance rate limit (mirrors forms/submit).\nconst recentByHook = new Map<string, number[]>()\nconst RATE_WINDOW_MS = 60_000\nconst RATE_MAX = 30\n\nfunction rateLimited(key: string): boolean {\n const now = Date.now()\n const hits = (recentByHook.get(key) ?? []).filter(\n (t) => now - t < RATE_WINDOW_MS,\n )\n hits.push(now)\n recentByHook.set(key, hits)\n return hits.length > RATE_MAX\n}\n\nfunction secretsMatch(expected: string, provided: string): boolean {\n if (!expected || expected.length !== provided.length) return false\n return timingSafeEqual(\n new Uint8Array(Buffer.from(expected)),\n new Uint8Array(Buffer.from(provided)),\n )\n}\n\n/**\n * Inbound webhook endpoint (AGL-149): external systems POST JSON to\n * `/api/hooks/{hostId}/{hookId}` with the hook's secret in\n * `x-aglyn-secret`; a valid call enrolls the configured workflow with the\n * payload's top-level primitives in scope. Business tier (`webhooks`\n * flag); runs bill against the workflow-runs meter like any other run —\n * `workflowRunsPerMonth` is checked before the run and\n * `hosts/{id}/counters/workflowRuns` is incremented after it (AGL-2228).\n * That sentence was here before either half of it was true.\n */\nconst inboundHookHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const hostId = String(req.query['hostId'] ?? '')\n const hookId = String(req.query['hookId'] ?? '')\n if (!hostId || !hookId) {\n return res.status(400).json({ error: 'Invalid webhook address' })\n }\n if (rateLimited(`${hostId}/${hookId}`)) {\n return res.status(429).json({ error: 'Too many requests' })\n }\n\n try {\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hookSnapshot = await hostRef.collection('webhooks').doc(hookId).get()\n const hook = hookSnapshot.data() as HostWebhook | undefined\n if (\n !hook ||\n hookSnapshot.get('deletedAt') ||\n hook.direction !== 'inbound' ||\n hook.enabled === false\n ) {\n return res.status(404).json({ error: 'Unknown webhook' })\n }\n const provided = String(req.headers['x-aglyn-secret'] ?? '')\n if (!secretsMatch(hook.secret ?? '', provided)) {\n return res.status(401).json({ error: 'Bad secret' })\n }\n\n // Plan/quota gates ride the owning org's doc (AGL-238).\n const owner = await getOrgForHost(hostId)\n const org = owner?.org\n /*\n * A suspended site takes no inbound run (AGL-3356). The tenant\n * dispatcher's lockdown gate reads `hostId` from the query or body, and\n * this route carries it in the PATH, so the engine's own question is\n * asked here. A 423 rather than a 200: the caller is an integration, and\n * one told a run happened keeps sending.\n */\n const suspended = await eventRunSuspension(hostRef, org)\n if (suspended) {\n return res.status(423).json({ error: 'locked', scope: suspended.scope })\n }\n if (!checkEntitlement(org as any, 'webhooks')) {\n return res\n .status(403)\n .json({ error: 'Webhooks are not enabled on this site' })\n }\n /*\n * The MONTHLY RUN CAP, which this handler's own docblock claimed for\n * years — \"runs bill against the workflow-runs meter like any other run\"\n * — and did not have. It read neither `workflowRunsPerMonth` nor\n * `hosts/{id}/counters/workflowRuns`, and it wrote neither, so an inbound\n * hook was a workflow execution that no cap could refuse and no meter\n * could see: the customer's usage card, the usage-alerts cron and the\n * COGS rollup all read a counter this path never moved.\n *\n * The refusal is a 402 rather than the silent `return` of\n * `runEventWorkflows`: that path is fired from a visitor request whose\n * success must not depend on the workflow, where this one is an\n * integration calling us on purpose and a caller that gets a 200 for a\n * run that did not happen will keep sending. `Retry-After` is deliberately\n * absent — the month, not a delay, is what clears this.\n *\n * The plan-less/unreadable org resolves as FREE\n * (`workflowRunsPerMonth: 0`), so an org doc we cannot read refuses rather\n * than runs. `checkEntitlement(webhooks)` above already refuses free on\n * today's price list; this is not a second gate on the same fact, because\n * a staff `entitlementOverrides` can grant `webhooks` without granting\n * runs, and because the cap is what bounds a BUSINESS org's spend.\n *\n * Read-then-write, like the event path, and bounded the same way: the\n * per-hook limiter above is 30/60s, so the overshoot a race can buy is\n * bounded by concurrency rather than unbounded by design. Making it\n * atomic means a transaction around the whole run and is the same trade\n * `runEventWorkflows` declined.\n */\n const monthKey = new Date().toISOString().slice(0, 7)\n const runCounterRef = hostRef.collection('counters').doc('workflowRuns')\n const runLimit = resolveOrgEntitlements(org as any).workflowRunsPerMonth\n const runsUsed = Number((await runCounterRef.get()).get(monthKey) ?? 0)\n if (!(runsUsed + 1 <= runLimit)) {\n return res.status(402).json({\n error: `This site has used its ${runLimit} workflow runs for the month`,\n })\n }\n\n // Top-level primitives from the JSON body join the workflow scope.\n const body =\n typeof req.body === 'string'\n ? JSON.parse(req.body || '{}')\n : (req.body ?? {})\n const scope: Record<string, string | number | boolean> = {}\n for (const [key, value] of Object.entries(body)) {\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n scope[key.slice(0, 64)] =\n typeof value === 'string' ? value.slice(0, 2000) : value\n }\n }\n\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 const byName = <T extends { name?: string; deletedAt?: unknown }>(\n docs: 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?.name && !data.deletedAt) map[data.name] = data\n }\n return map\n }\n const workflows = byName<HostWorkflow>(workflowDocs.docs)\n const workflow = workflows[hook.workflowName?.trim() ?? '']\n if (!workflow) {\n return res.status(422).json({ error: 'No workflow bound to this hook' })\n }\n\n const hookEvent = `hook:${hook.name ?? hookId}`\n /*\n * A workflow of function calls is evaluated, as it always has been. One\n * with Actions steps is PERFORMED by the engine's executor — the posted\n * JSON is its payload — under the Actions tier gates, step by step, and\n * it is still one run on the meter below. Its working set is built from\n * the documents read above rather than read again.\n */\n const outcome: { error: string | null; value: unknown; summary: string } =\n workflowHasActionSteps(workflow)\n ? await (async () => {\n const workflowDoc = workflowDocs.docs.find(\n (doc) =>\n !doc.get('deletedAt') &&\n doc.get('name') === hook.workflowName?.trim(),\n )\n const execution = await executeWorkflow(\n automationRunEnv({\n hostId,\n hostRef,\n owner,\n depth: 0,\n loadWorkflowContext: async () =>\n workflowContextFromDocs(\n functionDocs.docs,\n variableDocs.docs,\n workflowDocs.docs,\n ),\n }),\n {\n kind: 'workflow',\n id: workflowDoc?.id ?? '',\n name: workflow.name ?? '',\n },\n workflow as AutomationWorkflow,\n hookEvent,\n scope,\n )\n return {\n error: execution.errors.length\n ? execution.errors.join('; ')\n : null,\n value: execution.value,\n summary: execution.outcomes.length\n ? execution.outcomes.join(' · ')\n : `Ran ${hook.workflowName}`,\n }\n })()\n : (() => {\n const run = runWorkflow(\n workflow,\n byName<HostFunction>(functionDocs.docs),\n byName<HostVariable>(variableDocs.docs),\n { event: hookEvent, ...scope },\n { workflows },\n )\n return run.ok === false\n ? { error: run.error, value: undefined, summary: '' }\n : {\n error: null,\n value: run.value,\n summary: `Ran ${hook.workflowName}`,\n }\n })()\n const failed = outcome.error !== null\n await hostRef\n .collection('activity')\n .add({\n actorId: null,\n actorEmail: null,\n // An outside system called the hook's URL; the hook is who it was.\n triggeredBy: { kind: 'webhook', name: hook.workflowName },\n action: failed\n ? `Inbound webhook run failed: ${outcome.error}`.slice(0, 300)\n : `Inbound webhook ran \"${hook.workflowName}\"`,\n // The run-history shape (AGL-2222) — without `result` this execution\n // is invisible to the Runs table, which reads a verdict and not prose.\n result: failed ? 'failed' : 'succeeded',\n trigger: hookEvent,\n ...runSummaryFields(\n failed\n ? String(outcome.error).slice(0, 300)\n : outcome.summary.slice(0, 300),\n ),\n target: { type: 'workflow', id: hookId, name: hook.name ?? '' },\n // The site log's search finds a run by its hook's name (AGL-3321).\n searchTokens: activitySearchTokens({ target: { name: hook.name } }),\n createdAt: FieldValue.serverTimestamp(),\n })\n .catch(() => undefined)\n\n // The run happened, so it counts — including a FAILED one, exactly as\n // `runEventWorkflows` counts its failures. A run that executed and threw\n // spent the same compute as one that returned a value; only a run that\n // never started (the cap above, a missing workflow) must not count.\n await runCounterRef\n .set({ [monthKey]: FieldValue.increment(1) }, { merge: true })\n .catch(() => undefined)\n\n if (failed) return res.status(422).json({ error: outcome.error })\n return res.status(200).json({ ok: true, value: outcome.value })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Webhook failed' })\n }\n}\n\n/**\n * Registers the workflows plugin's public API routes (AGL-396), and the\n * engine's host-event listener beside them for a process whose boot did not\n * (see `declarations.server.ts`).\n */\nexport function registerWorkflowsApi(): void {\n registerWorkflowsServerDeclarations()\n registerPluginApiRoute('hooks/[hostId]/[hookId]', inboundHookHandler)\n}\n\n/**\n * The console half (AGL-2919), re-exported so one entry point serves both\n * surfaces: the manifest generator resolves every server surface through\n * `@aglyn/plugins-workflows/server`, and a second entry point would be a second\n * thing to keep in step with plugins.config.json.\n */\nexport { registerWorkflowsConsoleApi } from './server-console'\n"],"names":["checkEntitlement","resolveOrgEntitlements","runWorkflow","registerPluginApiRoute","registerPluginJob","activitySearchTokens","firebaseAdmin","getOrgForHost","timingSafeEqual","FieldValue","runSummaryFields","eventRunSuspension","BUNDLE_ID","WORKFLOWS_BUNDLE_ID","registerWorkflowsServerDeclarations","automationRunEnv","executeWorkflow","runDueFlowEnrollments","workflowContextFromDocs","workflowHasActionSteps","pluginId","name","intervalMinutes","description","lockdown","scope","handler","gate","recentByHook","Map","RATE_WINDOW_MS","RATE_MAX","rateLimited","key","now","Date","hits","get","filter","t","push","set","length","secretsMatch","expected","provided","Uint8Array","Buffer","from","inboundHookHandler","req","res","method","status","json","error","hostId","String","query","hookId","hook","firestore","app","hostRef","collection","doc","hookSnapshot","data","direction","enabled","headers","secret","owner","org","suspended","monthKey","toISOString","slice","runCounterRef","runLimit","workflowRunsPerMonth","runsUsed","Number","body","JSON","parse","value","Object","entries","functionDocs","variableDocs","workflowDocs","Promise","all","limit","byName","docs","map","deletedAt","workflows","workflow","workflowName","trim","hookEvent","outcome","workflowDoc","find","execution","depth","loadWorkflowContext","kind","id","errors","join","summary","outcomes","run","event","ok","undefined","failed","add","actorId","actorEmail","triggeredBy","action","result","trigger","target","type","searchTokens","createdAt","serverTimestamp","catch","increment","merge","console","registerWorkflowsApi","registerWorkflowsConsoleApi"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/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\nimport {\n checkEntitlement,\n type HostFunction,\n type HostVariable,\n type HostWebhook,\n resolveOrgEntitlements,\n} from '@aglyn/aglyn/server'\nimport {\n registerPluginApiRoute,\n registerPluginJob,\n type PluginApiHandler,\n} from '@aglyn/aglyn/server'\nimport { activitySearchTokens } from '@aglyn/aglyn/app-utils/activity-search'\nimport { firebaseAdmin, getOrgForHost } from '@aglyn/tenant-data-admin'\nimport { timingSafeEqual } from 'crypto'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { runSummaryFields } from './model/run-history'\nimport { type HostWorkflow, runWorkflow } from './model/workflows'\nimport { eventRunSuspension } from './engine/site-suspension'\nimport { BUNDLE_ID as WORKFLOWS_BUNDLE_ID } from './constants/bundle-common'\nimport { registerWorkflowsServerDeclarations } from './declarations.server'\nimport {\n automationRunEnv,\n executeWorkflow,\n runDueFlowEnrollments,\n workflowContextFromDocs,\n} from './engine/run-event-actions'\nimport {\n type AutomationWorkflow,\n workflowHasActionSteps,\n} from './engine/workflow-steps'\n\n/*==========================================\n * THE BEAT THAT MAKES A WAIT STEP REAL.\n *\n * A `wait` step suspends a run into a `flowEnrollments` row and returns; this\n * is the only thing that ever comes back for it. Without a registration here\n * the wait step would be a delay that never ends — the exact failure\n * `process-abandoned.ts` documents next door, where a Pro entitlement sat\n * behind an HTTP door nothing ever POSTed to.\n *\n * Module scope, like the commerce and bookings registrations: the runner\n * route reaches plugin jobs through `ensureAll(['tenantApi'])`, so a\n * registration inside `registerWorkflowsApi()` would depend on which entry\n * point happened to be loaded.\n *\n * ## Every minute, and why that is cheap\n *\n * `FLOW_WAIT_MIN_MINUTES` is one, so the beat has to be one — a fifteen-minute\n * sweep would make the shortest wait anybody can author wrong by up to\n * fourteen minutes. It costs one collection-group query per minute, which\n * returns nothing at all on a platform with no flow due, because the query\n * asks for `resumeAtMs <= now` rather than for the enrolled population.\n *\n * Bounded and idempotent, the two properties the runner requires: the scan\n * budget bounds one pass, and a transactional claim means an overlapping or\n * repeated beat cannot run the same person's step twice.\n *=========================================*/\nregisterPluginJob({\n pluginId: WORKFLOWS_BUNDLE_ID,\n name: 'resume-flow-waits',\n intervalMinutes: 1,\n description:\n 'Continue automations whose wait step has ended, and time out those ' +\n 'waiting for an event that never arrived.',\n lockdown: { scope: 'per-host' },\n handler: async (gate) => {\n await runDueFlowEnrollments(gate)\n },\n})\n\n// Best-effort per-instance rate limit (mirrors forms/submit).\nconst recentByHook = new Map<string, number[]>()\nconst RATE_WINDOW_MS = 60_000\nconst RATE_MAX = 30\n\nfunction rateLimited(key: string): boolean {\n const now = Date.now()\n const hits = (recentByHook.get(key) ?? []).filter(\n (t) => now - t < RATE_WINDOW_MS,\n )\n hits.push(now)\n recentByHook.set(key, hits)\n return hits.length > RATE_MAX\n}\n\nfunction secretsMatch(expected: string, provided: string): boolean {\n if (!expected || expected.length !== provided.length) return false\n return timingSafeEqual(\n new Uint8Array(Buffer.from(expected)),\n new Uint8Array(Buffer.from(provided)),\n )\n}\n\n/**\n * Inbound webhook endpoint (AGL-149): external systems POST JSON to\n * `/api/hooks/{hostId}/{hookId}` with the hook's secret in\n * `x-aglyn-secret`; a valid call enrolls the configured workflow with the\n * payload's top-level primitives in scope. Business tier (`webhooks`\n * flag); runs bill against the workflow-runs meter like any other run —\n * `workflowRunsPerMonth` is checked before the run and\n * `hosts/{id}/counters/workflowRuns` is incremented after it (AGL-2228).\n * That sentence was here before either half of it was true.\n */\nconst inboundHookHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const hostId = String(req.query['hostId'] ?? '')\n const hookId = String(req.query['hookId'] ?? '')\n if (!hostId || !hookId) {\n return res.status(400).json({ error: 'Invalid webhook address' })\n }\n if (rateLimited(`${hostId}/${hookId}`)) {\n return res.status(429).json({ error: 'Too many requests' })\n }\n\n try {\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hookSnapshot = await hostRef.collection('webhooks').doc(hookId).get()\n const hook = hookSnapshot.data() as HostWebhook | undefined\n if (\n !hook ||\n hookSnapshot.get('deletedAt') ||\n hook.direction !== 'inbound' ||\n hook.enabled === false\n ) {\n return res.status(404).json({ error: 'Unknown webhook' })\n }\n const provided = String(req.headers['x-aglyn-secret'] ?? '')\n if (!secretsMatch(hook.secret ?? '', provided)) {\n return res.status(401).json({ error: 'Bad secret' })\n }\n\n // Plan/quota gates ride the owning org's doc (AGL-238).\n const owner = await getOrgForHost(hostId)\n const org = owner?.org\n /*\n * A suspended site takes no inbound run (AGL-3356). The tenant\n * dispatcher's lockdown gate reads `hostId` from the query or body, and\n * this route carries it in the PATH, so the engine's own question is\n * asked here. A 423 rather than a 200: the caller is an integration, and\n * one told a run happened keeps sending.\n */\n const suspended = await eventRunSuspension(hostRef, org)\n if (suspended) {\n return res.status(423).json({ error: 'locked', scope: suspended.scope })\n }\n if (!checkEntitlement(org as any, 'webhooks')) {\n return res\n .status(403)\n .json({ error: 'Webhooks are not enabled on this site' })\n }\n /*\n * The MONTHLY RUN CAP, which this handler's own docblock claimed for\n * years — \"runs bill against the workflow-runs meter like any other run\"\n * — and did not have. It read neither `workflowRunsPerMonth` nor\n * `hosts/{id}/counters/workflowRuns`, and it wrote neither, so an inbound\n * hook was a workflow execution that no cap could refuse and no meter\n * could see: the customer's usage card, the usage-alerts cron and the\n * COGS rollup all read a counter this path never moved.\n *\n * The refusal is a 402 rather than the silent `return` of\n * `runEventWorkflows`: that path is fired from a visitor request whose\n * success must not depend on the workflow, where this one is an\n * integration calling us on purpose and a caller that gets a 200 for a\n * run that did not happen will keep sending. `Retry-After` is deliberately\n * absent — the month, not a delay, is what clears this.\n *\n * The plan-less/unreadable org resolves as FREE\n * (`workflowRunsPerMonth: 0`), so an org doc we cannot read refuses rather\n * than runs. `checkEntitlement(webhooks)` above already refuses free on\n * today's price list; this is not a second gate on the same fact, because\n * a staff `entitlementOverrides` can grant `webhooks` without granting\n * runs, and because the cap is what bounds a BUSINESS org's spend.\n *\n * Read-then-write, like the event path, and bounded the same way: the\n * per-hook limiter above is 30/60s, so the overshoot a race can buy is\n * bounded by concurrency rather than unbounded by design. Making it\n * atomic means a transaction around the whole run and is the same trade\n * `runEventWorkflows` declined.\n */\n const monthKey = new Date().toISOString().slice(0, 7)\n const runCounterRef = hostRef.collection('counters').doc('workflowRuns')\n const runLimit = resolveOrgEntitlements(org as any).workflowRunsPerMonth\n const runsUsed = Number((await runCounterRef.get()).get(monthKey) ?? 0)\n if (!(runsUsed + 1 <= runLimit)) {\n return res.status(402).json({\n error: `This site has used its ${runLimit} workflow runs for the month`,\n })\n }\n\n // Top-level primitives from the JSON body join the workflow scope.\n const body =\n typeof req.body === 'string'\n ? JSON.parse(req.body || '{}')\n : (req.body ?? {})\n const scope: Record<string, string | number | boolean> = {}\n for (const [key, value] of Object.entries(body)) {\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n scope[key.slice(0, 64)] =\n typeof value === 'string' ? value.slice(0, 2000) : value\n }\n }\n\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 const byName = <T extends { name?: string; deletedAt?: unknown }>(\n docs: 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?.name && !data.deletedAt) map[data.name] = data\n }\n return map\n }\n const workflows = byName<HostWorkflow>(workflowDocs.docs)\n const workflow = workflows[hook.workflowName?.trim() ?? '']\n if (!workflow) {\n return res.status(422).json({ error: 'No workflow bound to this hook' })\n }\n\n const hookEvent = `hook:${hook.name ?? hookId}`\n /*\n * A workflow of function calls is evaluated, as it always has been. One\n * with Actions steps is PERFORMED by the engine's executor — the posted\n * JSON is its payload — under the Actions tier gates, step by step, and\n * it is still one run on the meter below. Its working set is built from\n * the documents read above rather than read again.\n */\n const outcome: { error: string | null; value: unknown; summary: string } =\n workflowHasActionSteps(workflow)\n ? await (async () => {\n const workflowDoc = workflowDocs.docs.find(\n (doc) =>\n !doc.get('deletedAt') &&\n doc.get('name') === hook.workflowName?.trim(),\n )\n const execution = await executeWorkflow(\n automationRunEnv({\n hostId,\n hostRef,\n owner,\n depth: 0,\n loadWorkflowContext: async () =>\n workflowContextFromDocs(\n functionDocs.docs,\n variableDocs.docs,\n workflowDocs.docs,\n ),\n }),\n {\n kind: 'workflow',\n id: workflowDoc?.id ?? '',\n name: workflow.name ?? '',\n },\n workflow as AutomationWorkflow,\n hookEvent,\n scope,\n )\n return {\n error: execution.errors.length\n ? execution.errors.join('; ')\n : null,\n value: execution.value,\n summary: execution.outcomes.length\n ? execution.outcomes.join(' · ')\n : `Ran ${hook.workflowName}`,\n }\n })()\n : (() => {\n const run = runWorkflow(\n workflow,\n byName<HostFunction>(functionDocs.docs),\n byName<HostVariable>(variableDocs.docs),\n { event: hookEvent, ...scope },\n { workflows },\n )\n return run.ok === false\n ? { error: run.error, value: undefined, summary: '' }\n : {\n error: null,\n value: run.value,\n summary: `Ran ${hook.workflowName}`,\n }\n })()\n const failed = outcome.error !== null\n await hostRef\n .collection('activity')\n .add({\n actorId: null,\n actorEmail: null,\n // An outside system called the hook's URL; the hook is who it was.\n triggeredBy: { kind: 'webhook', name: hook.workflowName },\n action: failed\n ? `Inbound webhook run failed: ${outcome.error}`.slice(0, 300)\n : `Inbound webhook ran \"${hook.workflowName}\"`,\n // The run-history shape (AGL-2222) — without `result` this execution\n // is invisible to the Runs table, which reads a verdict and not prose.\n result: failed ? 'failed' : 'succeeded',\n trigger: hookEvent,\n ...runSummaryFields(\n failed\n ? String(outcome.error).slice(0, 300)\n : outcome.summary.slice(0, 300),\n ),\n target: { type: 'workflow', id: hookId, name: hook.name ?? '' },\n // The site log's search finds a run by its hook's name (AGL-3321).\n searchTokens: activitySearchTokens({ target: { name: hook.name } }),\n createdAt: FieldValue.serverTimestamp(),\n })\n .catch(() => undefined)\n\n // The run happened, so it counts — including a FAILED one, exactly as\n // `runEventWorkflows` counts its failures. A run that executed and threw\n // spent the same compute as one that returned a value; only a run that\n // never started (the cap above, a missing workflow) must not count.\n await runCounterRef\n .set({ [monthKey]: FieldValue.increment(1) }, { merge: true })\n .catch(() => undefined)\n\n if (failed) return res.status(422).json({ error: outcome.error })\n return res.status(200).json({ ok: true, value: outcome.value })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Webhook failed' })\n }\n}\n\n/**\n * Registers the workflows plugin's public API routes (AGL-396), and the\n * engine's host-event listener beside them for a process whose boot did not\n * (see `declarations.server.ts`).\n */\nexport function registerWorkflowsApi(): void {\n registerWorkflowsServerDeclarations()\n registerPluginApiRoute('hooks/[hostId]/[hookId]', inboundHookHandler)\n}\n\n/**\n * The console half (AGL-2919), re-exported so one entry point serves both\n * surfaces: the manifest generator resolves every server surface through\n * `@aglyn/plugins-workflows/server`, and a second entry point would be a second\n * thing to keep in step with plugins.config.json.\n */\nexport { registerWorkflowsConsoleApi } from './server-console'\n\n// Type-only (AGL-3080): the plugin's entitlement keys, declared by module\n// augmentation, for every program that loads this entry point.\nexport type { workflowsPlanEntitlements } from './plan-entitlements'\n"],"names":["checkEntitlement","resolveOrgEntitlements","registerPluginApiRoute","registerPluginJob","activitySearchTokens","firebaseAdmin","getOrgForHost","timingSafeEqual","FieldValue","runSummaryFields","runWorkflow","eventRunSuspension","BUNDLE_ID","WORKFLOWS_BUNDLE_ID","registerWorkflowsServerDeclarations","automationRunEnv","executeWorkflow","runDueFlowEnrollments","workflowContextFromDocs","workflowHasActionSteps","pluginId","name","intervalMinutes","description","lockdown","scope","handler","gate","recentByHook","Map","RATE_WINDOW_MS","RATE_MAX","rateLimited","key","now","Date","hits","get","filter","t","push","set","length","secretsMatch","expected","provided","Uint8Array","Buffer","from","inboundHookHandler","req","res","method","status","json","error","hostId","String","query","hookId","hook","firestore","app","hostRef","collection","doc","hookSnapshot","data","direction","enabled","headers","secret","owner","org","suspended","monthKey","toISOString","slice","runCounterRef","runLimit","workflowRunsPerMonth","runsUsed","Number","body","JSON","parse","value","Object","entries","functionDocs","variableDocs","workflowDocs","Promise","all","limit","byName","docs","map","deletedAt","workflows","workflow","workflowName","trim","hookEvent","outcome","workflowDoc","find","execution","depth","loadWorkflowContext","kind","id","errors","join","summary","outcomes","run","event","ok","undefined","failed","add","actorId","actorEmail","triggeredBy","action","result","trigger","target","type","searchTokens","createdAt","serverTimestamp","catch","increment","merge","console","registerWorkflowsApi","registerWorkflowsConsoleApi"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
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 { TenantEmailEntry } from '@aglyn/shared-util-email';
|
|
18
|
+
/**
|
|
19
|
+
* The emails a site sends its own customers through Workflows (AGL-769/770).
|
|
20
|
+
*
|
|
21
|
+
* Declared here, by the plugin that sends them, and compiled into the
|
|
22
|
+
* platform's tenant email catalog (`TENANT_EMAILS` in
|
|
23
|
+
* `@aglyn/shared-util-email`) by `tools/scripts/generate-plugin-manifests.mjs`
|
|
24
|
+
* (AGL-3080): the catalog's readers include the send path, which loads no
|
|
25
|
+
* plugin code, so a runtime registry it had not filled would quietly send
|
|
26
|
+
* the text card in place of the site's designed email. Regenerate after
|
|
27
|
+
* changing an entry; `--check` refuses a stale catalog.
|
|
28
|
+
*/
|
|
29
|
+
export declare function workflowsTenantEmails(): readonly TenantEmailEntry[];
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
* The emails a site sends its own customers through Workflows (AGL-769/770).
|
|
18
|
+
*
|
|
19
|
+
* Declared here, by the plugin that sends them, and compiled into the
|
|
20
|
+
* platform's tenant email catalog (`TENANT_EMAILS` in
|
|
21
|
+
* `@aglyn/shared-util-email`) by `tools/scripts/generate-plugin-manifests.mjs`
|
|
22
|
+
* (AGL-3080): the catalog's readers include the send path, which loads no
|
|
23
|
+
* plugin code, so a runtime registry it had not filled would quietly send
|
|
24
|
+
* the text card in place of the site's designed email. Regenerate after
|
|
25
|
+
* changing an entry; `--check` refuses a stale catalog.
|
|
26
|
+
*/ export function workflowsTenantEmails() {
|
|
27
|
+
return [
|
|
28
|
+
{
|
|
29
|
+
key: 'workflow-email',
|
|
30
|
+
name: 'Workflow email step',
|
|
31
|
+
description: "An email sent by a workflow's send-email action when its trigger " + 'fires.',
|
|
32
|
+
pluginId: 'workflows',
|
|
33
|
+
plugin: 'Workflows',
|
|
34
|
+
control: 'external',
|
|
35
|
+
authoredIn: 'the workflow that sends it',
|
|
36
|
+
footerReason: 'You’re receiving this because you’re in touch with {{host.businessName}}.'
|
|
37
|
+
}
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=tenant-emails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/tenant-emails.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 { TenantEmailEntry } from '@aglyn/shared-util-email'\n\n/**\n * The emails a site sends its own customers through Workflows (AGL-769/770).\n *\n * Declared here, by the plugin that sends them, and compiled into the\n * platform's tenant email catalog (`TENANT_EMAILS` in\n * `@aglyn/shared-util-email`) by `tools/scripts/generate-plugin-manifests.mjs`\n * (AGL-3080): the catalog's readers include the send path, which loads no\n * plugin code, so a runtime registry it had not filled would quietly send\n * the text card in place of the site's designed email. Regenerate after\n * changing an entry; `--check` refuses a stale catalog.\n */\nexport function workflowsTenantEmails(): readonly TenantEmailEntry[] {\n return [\n {\n key: 'workflow-email',\n name: 'Workflow email step',\n description:\n \"An email sent by a workflow's send-email action when its trigger \" +\n 'fires.',\n pluginId: 'workflows',\n plugin: 'Workflows',\n control: 'external',\n authoredIn: 'the workflow that sends it',\n footerReason:\n 'You’re receiving this because you’re in touch with {{host.businessName}}.',\n },\n ]\n}\n"],"names":["workflowsTenantEmails","key","name","description","pluginId","plugin","control","authoredIn","footerReason"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID;;;;;;;;;;CAUC,GACD,OAAO,SAASA;IACd,OAAO;QACL;YACEC,KAAK;YACLC,MAAM;YACNC,aACE,sEACA;YACFC,UAAU;YACVC,QAAQ;YACRC,SAAS;YACTC,YAAY;YACZC,cACE;QACJ;KACD;AACH"}
|
|
@@ -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 { PluginUsageAxesDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-usage-axes';
|
|
18
|
+
/**
|
|
19
|
+
* THE WORKFLOWS PLUGIN'S METERS (AGL-3080): workflow and action runs, in the
|
|
20
|
+
* platform's cost model and utilization table. Compiled into core by the
|
|
21
|
+
* manifest generator (`register.usageAxes`).
|
|
22
|
+
*
|
|
23
|
+
* ONE cost axis for both counters: a run is the same handful of reads, two
|
|
24
|
+
* writes and a moment of compute whichever builder produced it, so both are
|
|
25
|
+
* priced at `perRun` on one line. TWO bands, because each builder sells its
|
|
26
|
+
* own.
|
|
27
|
+
*/
|
|
28
|
+
export declare function workflowsUsageAxes(): PluginUsageAxesDeclaration;
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
* THE WORKFLOWS PLUGIN'S METERS (AGL-3080): workflow and action runs, in the
|
|
18
|
+
* platform's cost model and utilization table. Compiled into core by the
|
|
19
|
+
* manifest generator (`register.usageAxes`).
|
|
20
|
+
*
|
|
21
|
+
* ONE cost axis for both counters: a run is the same handful of reads, two
|
|
22
|
+
* writes and a moment of compute whichever builder produced it, so both are
|
|
23
|
+
* priced at `perRun` on one line. TWO bands, because each builder sells its
|
|
24
|
+
* own.
|
|
25
|
+
*/ export function workflowsUsageAxes() {
|
|
26
|
+
return {
|
|
27
|
+
costAxes: [
|
|
28
|
+
{
|
|
29
|
+
id: 'runs',
|
|
30
|
+
order: 90,
|
|
31
|
+
fields: [
|
|
32
|
+
'workflowRuns',
|
|
33
|
+
'actionRuns'
|
|
34
|
+
],
|
|
35
|
+
rate: 'perRun'
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
bands: [
|
|
39
|
+
{
|
|
40
|
+
id: 'workflowRuns',
|
|
41
|
+
label: 'Workflow runs',
|
|
42
|
+
order: 100,
|
|
43
|
+
fields: [
|
|
44
|
+
'workflowRuns'
|
|
45
|
+
],
|
|
46
|
+
entitlement: 'workflowRunsPerMonth'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'actionRuns',
|
|
50
|
+
label: 'Action runs',
|
|
51
|
+
order: 110,
|
|
52
|
+
fields: [
|
|
53
|
+
'actionRuns'
|
|
54
|
+
],
|
|
55
|
+
entitlement: 'actionRunsPerMonth'
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=usage-axes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/usage-axes.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 { PluginUsageAxesDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-usage-axes'\n\n/**\n * THE WORKFLOWS PLUGIN'S METERS (AGL-3080): workflow and action runs, in the\n * platform's cost model and utilization table. Compiled into core by the\n * manifest generator (`register.usageAxes`).\n *\n * ONE cost axis for both counters: a run is the same handful of reads, two\n * writes and a moment of compute whichever builder produced it, so both are\n * priced at `perRun` on one line. TWO bands, because each builder sells its\n * own.\n */\nexport function workflowsUsageAxes(): PluginUsageAxesDeclaration {\n return {\n costAxes: [\n {\n id: 'runs',\n order: 90,\n fields: ['workflowRuns', 'actionRuns'],\n rate: 'perRun',\n },\n ],\n bands: [\n {\n id: 'workflowRuns',\n label: 'Workflow runs',\n order: 100,\n fields: ['workflowRuns'],\n entitlement: 'workflowRunsPerMonth',\n },\n {\n id: 'actionRuns',\n label: 'Action runs',\n order: 110,\n fields: ['actionRuns'],\n entitlement: 'actionRunsPerMonth',\n },\n ],\n }\n}\n"],"names":["workflowsUsageAxes","costAxes","id","order","fields","rate","bands","label","entitlement"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID;;;;;;;;;CASC,GACD,OAAO,SAASA;IACd,OAAO;QACLC,UAAU;YACR;gBACEC,IAAI;gBACJC,OAAO;gBACPC,QAAQ;oBAAC;oBAAgB;iBAAa;gBACtCC,MAAM;YACR;SACD;QACDC,OAAO;YACL;gBACEJ,IAAI;gBACJK,OAAO;gBACPJ,OAAO;gBACPC,QAAQ;oBAAC;iBAAe;gBACxBI,aAAa;YACf;YACA;gBACEN,IAAI;gBACJK,OAAO;gBACPJ,OAAO;gBACPC,QAAQ;oBAAC;iBAAa;gBACtBI,aAAa;YACf;SACD;IACH;AACF"}
|