@aglyn/plugins-workflows 1.0.0-beta.143

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +52 -0
  3. package/package.json +53 -0
  4. package/src/index.d.ts +18 -0
  5. package/src/index.js +19 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/automation-step-fields.component.d.ts +74 -0
  8. package/src/lib/components/automation-step-fields.component.js +1210 -0
  9. package/src/lib/components/automation-step-fields.component.js.map +1 -0
  10. package/src/lib/components/host-actions-card.component.d.ts +15 -0
  11. package/src/lib/components/host-actions-card.component.js +1229 -0
  12. package/src/lib/components/host-actions-card.component.js.map +1 -0
  13. package/src/lib/components/host-activity-card.component.d.ts +84 -0
  14. package/src/lib/components/host-activity-card.component.js +280 -0
  15. package/src/lib/components/host-activity-card.component.js.map +1 -0
  16. package/src/lib/components/host-run-history-card.component.d.ts +40 -0
  17. package/src/lib/components/host-run-history-card.component.js +284 -0
  18. package/src/lib/components/host-run-history-card.component.js.map +1 -0
  19. package/src/lib/components/host-webhooks-card.component.d.ts +15 -0
  20. package/src/lib/components/host-webhooks-card.component.js +421 -0
  21. package/src/lib/components/host-webhooks-card.component.js.map +1 -0
  22. package/src/lib/components/host-workflows-card.component.d.ts +18 -0
  23. package/src/lib/components/host-workflows-card.component.js +952 -0
  24. package/src/lib/components/host-workflows-card.component.js.map +1 -0
  25. package/src/lib/components/run-quota-line.component.d.ts +27 -0
  26. package/src/lib/components/run-quota-line.component.js +72 -0
  27. package/src/lib/components/run-quota-line.component.js.map +1 -0
  28. package/src/lib/components/use-automation-step-pickers.d.ts +27 -0
  29. package/src/lib/components/use-automation-step-pickers.js +205 -0
  30. package/src/lib/components/use-automation-step-pickers.js.map +1 -0
  31. package/src/lib/components/workflow-zones.d.ts +36 -0
  32. package/src/lib/components/workflow-zones.js +19 -0
  33. package/src/lib/components/workflow-zones.js.map +1 -0
  34. package/src/lib/components/workflows-console-page.d.ts +25 -0
  35. package/src/lib/components/workflows-console-page.js +112 -0
  36. package/src/lib/components/workflows-console-page.js.map +1 -0
  37. package/src/lib/components/workflows-console-sections.d.ts +34 -0
  38. package/src/lib/components/workflows-console-sections.js +50 -0
  39. package/src/lib/components/workflows-console-sections.js.map +1 -0
  40. package/src/lib/constants/bundle-common.d.ts +8 -0
  41. package/src/lib/constants/bundle-common.js +9 -0
  42. package/src/lib/constants/bundle-common.js.map +1 -0
  43. package/src/lib/declarations.server.d.ts +41 -0
  44. package/src/lib/declarations.server.js +54 -0
  45. package/src/lib/declarations.server.js.map +1 -0
  46. package/src/lib/engine/crm-action-steps.d.ts +129 -0
  47. package/src/lib/engine/crm-action-steps.js +432 -0
  48. package/src/lib/engine/crm-action-steps.js.map +1 -0
  49. package/src/lib/engine/flow-enrollments.d.ts +264 -0
  50. package/src/lib/engine/flow-enrollments.js +386 -0
  51. package/src/lib/engine/flow-enrollments.js.map +1 -0
  52. package/src/lib/engine/run-event-actions.d.ts +232 -0
  53. package/src/lib/engine/run-event-actions.js +1392 -0
  54. package/src/lib/engine/run-event-actions.js.map +1 -0
  55. package/src/lib/engine/run-event-automations.d.ts +28 -0
  56. package/src/lib/engine/run-event-automations.js +37 -0
  57. package/src/lib/engine/run-event-automations.js.map +1 -0
  58. package/src/lib/engine/run-event-workflows.d.ts +75 -0
  59. package/src/lib/engine/run-event-workflows.js +311 -0
  60. package/src/lib/engine/run-event-workflows.js.map +1 -0
  61. package/src/lib/engine/workflow-steps.d.ts +107 -0
  62. package/src/lib/engine/workflow-steps.js +124 -0
  63. package/src/lib/engine/workflow-steps.js.map +1 -0
  64. package/src/lib/plugin.d.ts +33 -0
  65. package/src/lib/plugin.js +94 -0
  66. package/src/lib/plugin.js.map +1 -0
  67. package/src/lib/server-automation-drafts.d.ts +90 -0
  68. package/src/lib/server-automation-drafts.js +429 -0
  69. package/src/lib/server-automation-drafts.js.map +1 -0
  70. package/src/lib/server-console.d.ts +27 -0
  71. package/src/lib/server-console.js +35 -0
  72. package/src/lib/server-console.js.map +1 -0
  73. package/src/lib/server.d.ts +29 -0
  74. package/src/lib/server.js +298 -0
  75. package/src/lib/server.js.map +1 -0
@@ -0,0 +1,421 @@
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
+ */ 'use client';
17
+ import { _ as _extends } from "@swc/helpers/_/_extends";
18
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
+ import { checkEntitlement, createResourceUid, pluginDocsHelp, WEBHOOK_MAX_PER_HOST, WEBHOOK_URL_PATTERN } from "@aglyn/aglyn";
20
+ import { CardDisplay, useConfirmationContext } from "@aglyn/shared-ui-jsx";
21
+ import { hostPublicOrigin } from "@aglyn/aglyn/app-utils/host-naming";
22
+ import { useSnackbar } from "@aglyn/shared-ui-snackstack";
23
+ import { Timestamp } from "@aglyn/shared-util-timestamp";
24
+ import { Alert, Button, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack, TextField, Typography } from "@mui/material";
25
+ import { collection, doc, updateDoc } from "firebase/firestore";
26
+ import { useCallback, useState } from "react";
27
+ import { useFirestore, useFirestoreCollection, useFirestoreDoc, useHostResourceApi } from "@aglyn/tenant-feature-instance";
28
+ /*
29
+ * The MODULE, not the barrel, for the two PURE helpers — the specs that render
30
+ * this card mock `@aglyn/tenant-feature-instance` wholesale to stage their
31
+ * Firestore hooks, and a query builder imported through that barrel disappears
32
+ * under the mock. Neither of these is a hook.
33
+ */ import { ceilingedWindow, collectionCeiling } from "@aglyn/tenant-feature-instance/hooks/host-collection-queries";
34
+ /**
35
+ * How many webhook documents the card reads.
36
+ *
37
+ * Above `WEBHOOK_MAX_PER_HOST` on purpose. The cap counts LIVE hooks and the
38
+ * collection keeps soft-deleted ones, so a site that has created and deleted
39
+ * its five several times holds more documents than it may have hooks.
40
+ */ const WEBHOOK_CEILING = 20;
41
+ /**
42
+ * How many workflows the inbound-endpoint picker offers.
43
+ *
44
+ * Paid for only while the editor is open, which is what makes a ceiling this
45
+ * size affordable: it is one operator mid-edit rather than every visitor to
46
+ * the page.
47
+ */ const WORKFLOW_OPTION_CEILING = 100;
48
+ function generateSecret() {
49
+ const bytes = new Uint8Array(24);
50
+ crypto.getRandomValues(bytes);
51
+ return Array.from(bytes, (byte)=>byte.toString(16).padStart(2, '0')).join('');
52
+ }
53
+ /**
54
+ * Webhooks manager (AGL-149): outbound targets (used by the actions
55
+ * builder's "Send a webhook" step, HMAC-signed) and inbound endpoints
56
+ * (`/api/hooks/{hostId}/{hookId}`, secret-verified, run a workflow with
57
+ * the payload in scope). Business tier.
58
+ */ export function HostWebhooksCard(props) {
59
+ var _hostPublicOrigin, _ref, _ref1, _ref2, _ref3, _ref4;
60
+ const { hostId, org } = props;
61
+ const firestore = useFirestore();
62
+ const { enqueueSnackbar } = useSnackbar();
63
+ const { confirm } = useConfirmationContext();
64
+ const createResource = useHostResourceApi();
65
+ const [draft, setDraft] = useState(null);
66
+ /**
67
+ * The editor has been opened at least once in this session.
68
+ *
69
+ * A LATCH rather than `draft` itself, because the workflow picker below keys its
70
+ * listeners on it. Tracking the dialog would tear that subscription down
71
+ * on Cancel and pay for them again on the next Edit, so a merchant working
72
+ * through ten actions would buy the same window ten times — worse than
73
+ * the mount-time read this replaces. Latched, a reader who never edits pays
74
+ * nothing and a reader who edits pays once.
75
+ */ const [editorOpened, setEditorOpened] = useState(false);
76
+ if (draft && !editorOpened) setEditorOpened(true);
77
+ /*
78
+ * ORDERED AND CEILINGED (AGL-2501). A bare `limit` is answered in
79
+ * DOCUMENT-ID order, so an unnamed window is an arbitrary twenty that the
80
+ * `localeCompare` below would arrange alphabetically. `collectionCeiling`
81
+ * does not change WHICH twenty; it NAMES the order, and the probe row it
82
+ * adds is what lets the card say when the ceiling bites.
83
+ */ const { data: webhookRead } = useFirestoreCollection(()=>collectionCeiling(collection(firestore, 'hosts', hostId, 'webhooks'), WEBHOOK_CEILING), [
84
+ firestore,
85
+ hostId
86
+ ], {
87
+ idField: '$id'
88
+ });
89
+ const { rows: readWebhooks, truncated: webhooksTruncated } = ceilingedWindow(webhookRead, WEBHOOK_CEILING);
90
+ /*
91
+ * READ WHEN THE EDITOR OPENS, not when the card mounts. The only thing that
92
+ * consumes these rows is the inbound-endpoint select inside the dialog — the
93
+ * table renders a hook's stored `workflowName` — so an unconditional
94
+ * listener would charge every visitor to this page a hundred workflows to
95
+ * fill a control most of them never see, and one that only appears for the
96
+ * inbound half of the form at that.
97
+ */ const { data: workflowRead } = useFirestoreCollection(()=>editorOpened ? collectionCeiling(collection(firestore, 'hosts', hostId, 'workflows'), WORKFLOW_OPTION_CEILING) : null, [
98
+ firestore,
99
+ hostId,
100
+ editorOpened
101
+ ], {
102
+ idField: '$id'
103
+ });
104
+ const { rows: readWorkflows, truncated: workflowOptionsTruncated } = ceilingedWindow(workflowRead, WORKFLOW_OPTION_CEILING);
105
+ const { data: host } = useFirestoreDoc(()=>doc(firestore, 'hosts', hostId), [
106
+ firestore,
107
+ hostId
108
+ ], {
109
+ idField: '$id'
110
+ });
111
+ // Sorting the whole ceiling, not a page of it.
112
+ const webhooks = [
113
+ ...readWebhooks
114
+ ].filter((hook)=>!hook.deletedAt).sort((a, b)=>{
115
+ var _a_name, _b_name;
116
+ return String((_a_name = a.name) != null ? _a_name : '').localeCompare(String((_b_name = b.name) != null ? _b_name : ''));
117
+ });
118
+ const workflowNames = readWorkflows.filter((workflow)=>!workflow.deletedAt && workflow.name).map((workflow)=>workflow.name).sort();
119
+ // `hostPublicOrigin` (AGL-2195) — this base is printed into the webhook
120
+ // sample payload an operator copies into a third-party system.
121
+ const siteBase = (_hostPublicOrigin = hostPublicOrigin(host)) != null ? _hostPublicOrigin : '';
122
+ const handleAdd = useCallback(()=>{
123
+ if (!checkEntitlement(org, 'webhooks')) {
124
+ return void enqueueSnackbar('Webhooks require a Business plan — see Billing to upgrade', {
125
+ variant: 'warning',
126
+ persist: false
127
+ });
128
+ }
129
+ // An affordance, NOT the limit (AGL-1360). `webhooks` comes from a
130
+ // Firestore listener and the console runs `persistentLocalCache`, so this
131
+ // count can be arbitrarily stale — it is here to fail fast when it
132
+ // happens to be right, never to authorise the create. The cap that holds
133
+ // is counted server-side in /api/hosts/resources.
134
+ if (webhooks.length >= WEBHOOK_MAX_PER_HOST) {
135
+ return void enqueueSnackbar(`Webhooks are capped at ${WEBHOOK_MAX_PER_HOST} per site`, {
136
+ variant: 'warning',
137
+ persist: false
138
+ });
139
+ }
140
+ setDraft({
141
+ id: null,
142
+ name: '',
143
+ direction: 'outbound',
144
+ url: '',
145
+ workflowName: '',
146
+ secret: generateSecret()
147
+ });
148
+ }, [
149
+ org,
150
+ webhooks.length,
151
+ enqueueSnackbar
152
+ ]);
153
+ const handleSave = useCallback(async ()=>{
154
+ if (!draft || !draft.name.trim()) return;
155
+ if (draft.direction === 'outbound') {
156
+ if (!WEBHOOK_URL_PATTERN.test(draft.url.trim())) {
157
+ return void enqueueSnackbar('Outbound URLs must be public https addresses', {
158
+ variant: 'warning',
159
+ persist: false
160
+ });
161
+ }
162
+ } else if (!draft.workflowName.trim()) {
163
+ return void enqueueSnackbar('Pick the workflow this endpoint runs', {
164
+ variant: 'warning',
165
+ persist: false
166
+ });
167
+ }
168
+ try {
169
+ // Creates go through /api/hosts/resources (AGL-1360), which counts the
170
+ // live webhooks with the Admin SDK before allowing one more; Firestore
171
+ // rules deny client `create` on this collection. The card has no edit
172
+ // path — every save is a create — so there is no update branch here.
173
+ await createResource({
174
+ hostId,
175
+ resource: 'webhook',
176
+ id: createResourceUid(),
177
+ data: _extends({
178
+ name: draft.name.trim().slice(0, 60),
179
+ direction: draft.direction
180
+ }, draft.direction === 'outbound' ? {
181
+ url: draft.url.trim()
182
+ } : {
183
+ workflowName: draft.workflowName.trim()
184
+ }, {
185
+ secret: draft.secret,
186
+ enabled: true
187
+ })
188
+ });
189
+ setDraft(null);
190
+ enqueueSnackbar('Webhook saved', {
191
+ variant: 'success',
192
+ persist: false
193
+ });
194
+ } catch (error) {
195
+ console.error(error);
196
+ // The route's message is the useful one — it names the cap, the
197
+ // entitlement or the role that refused.
198
+ enqueueSnackbar((error == null ? void 0 : error.message) || 'An error has occurred', {
199
+ variant: 'error',
200
+ allowDuplicate: true
201
+ });
202
+ }
203
+ }, [
204
+ draft,
205
+ createResource,
206
+ hostId,
207
+ enqueueSnackbar
208
+ ]);
209
+ const handleDelete = useCallback((hook)=>async ()=>{
210
+ const confirmed = await confirm({
211
+ title: 'Delete this webhook?',
212
+ description: `"${hook.name}" stops ${hook.direction === 'outbound' ? 'delivering' : 'accepting calls'} immediately.`,
213
+ confirmationText: 'Delete',
214
+ confirmationButtonProps: {
215
+ color: 'error'
216
+ }
217
+ }).then(()=>true).catch(()=>false);
218
+ if (!confirmed) return;
219
+ await updateDoc(doc(firestore, 'hosts', hostId, 'webhooks', hook.$id), {
220
+ deletedAt: Timestamp.now()
221
+ });
222
+ }, [
223
+ confirm,
224
+ firestore,
225
+ hostId
226
+ ]);
227
+ return /*#__PURE__*/ _jsxs(CardDisplay, {
228
+ header: 'Webhooks',
229
+ help: pluginDocsHelp('webhooks', {
230
+ anchor: '#outbound-webhooks'
231
+ }),
232
+ contentGutterX: true,
233
+ contentGutterY: true,
234
+ children: [
235
+ /*#__PURE__*/ _jsxs(Stack, {
236
+ spacing: 1,
237
+ children: [
238
+ /*#__PURE__*/ _jsx(Typography, {
239
+ variant: "body2",
240
+ color: "text.secondary",
241
+ children: 'Send signed JSON to outside systems from the actions builder, ' + 'or accept calls that run a workflow. Business plans.'
242
+ }),
243
+ webhooks.map((hook)=>/*#__PURE__*/ _jsxs(Stack, {
244
+ direction: "row",
245
+ spacing: 1,
246
+ sx: {
247
+ alignItems: 'center'
248
+ },
249
+ children: [
250
+ /*#__PURE__*/ _jsxs(Stack, {
251
+ sx: {
252
+ flex: 1,
253
+ minWidth: 0
254
+ },
255
+ children: [
256
+ /*#__PURE__*/ _jsx(Typography, {
257
+ variant: "body2",
258
+ noWrap: true,
259
+ children: hook.name
260
+ }),
261
+ /*#__PURE__*/ _jsx(Typography, {
262
+ variant: "caption",
263
+ color: "text.secondary",
264
+ noWrap: true,
265
+ children: hook.direction === 'outbound' ? `outbound · ${hook.url}` : `inbound · ${siteBase}/api/hooks/${hostId}/${hook.$id}` + ` → ${hook.workflowName}`
266
+ })
267
+ ]
268
+ }),
269
+ hook.direction === 'inbound' ? /*#__PURE__*/ _jsx(Button, {
270
+ size: "small",
271
+ onClick: ()=>{
272
+ void navigator.clipboard.writeText(`${siteBase}/api/hooks/${hostId}/${hook.$id}`);
273
+ enqueueSnackbar('Endpoint URL copied — send the secret in x-aglyn-secret', {
274
+ variant: 'success',
275
+ persist: false
276
+ });
277
+ },
278
+ children: 'Copy URL'
279
+ }) : null,
280
+ /*#__PURE__*/ _jsx(Button, {
281
+ size: "small",
282
+ onClick: ()=>{
283
+ var _hook_secret;
284
+ void navigator.clipboard.writeText((_hook_secret = hook.secret) != null ? _hook_secret : '');
285
+ enqueueSnackbar('Secret copied', {
286
+ variant: 'success',
287
+ persist: false
288
+ });
289
+ },
290
+ children: 'Secret'
291
+ }),
292
+ /*#__PURE__*/ _jsx(Button, {
293
+ size: "small",
294
+ color: "error",
295
+ onClick: handleDelete(hook),
296
+ children: 'Delete'
297
+ })
298
+ ]
299
+ }, hook.$id)),
300
+ webhooksTruncated ? /*#__PURE__*/ _jsx(Alert, {
301
+ severity: "info",
302
+ children: `Showing the first ${WEBHOOK_CEILING} webhook documents on this ` + 'site, ordered by id. There are more, so a hook may be missing ' + 'from this list.'
303
+ }) : null,
304
+ /*#__PURE__*/ _jsx(Button, {
305
+ size: "small",
306
+ color: "primary",
307
+ sx: {
308
+ alignSelf: 'flex-start'
309
+ },
310
+ onClick: handleAdd,
311
+ children: 'Add webhook'
312
+ })
313
+ ]
314
+ }),
315
+ /*#__PURE__*/ _jsxs(Dialog, {
316
+ open: Boolean(draft),
317
+ onClose: ()=>setDraft(null),
318
+ maxWidth: "xs",
319
+ fullWidth: true,
320
+ children: [
321
+ /*#__PURE__*/ _jsx(DialogTitle, {
322
+ children: 'Add webhook'
323
+ }),
324
+ /*#__PURE__*/ _jsxs(DialogContent, {
325
+ sx: {
326
+ display: 'flex',
327
+ flexDirection: 'column',
328
+ gap: 1.5,
329
+ pt: 1
330
+ },
331
+ children: [
332
+ /*#__PURE__*/ _jsx(TextField, {
333
+ label: "Name",
334
+ value: (_ref = draft == null ? void 0 : draft.name) != null ? _ref : '',
335
+ onChange: (event)=>setDraft((prev)=>prev ? _extends({}, prev, {
336
+ name: event.target.value
337
+ }) : prev),
338
+ size: "small",
339
+ autoFocus: true,
340
+ sx: {
341
+ mt: 1
342
+ }
343
+ }),
344
+ /*#__PURE__*/ _jsxs(TextField, {
345
+ select: true,
346
+ label: "Direction",
347
+ value: (_ref1 = draft == null ? void 0 : draft.direction) != null ? _ref1 : 'outbound',
348
+ onChange: (event)=>setDraft((prev)=>prev ? _extends({}, prev, {
349
+ direction: event.target.value
350
+ }) : prev),
351
+ size: "small",
352
+ children: [
353
+ /*#__PURE__*/ _jsx(MenuItem, {
354
+ value: "outbound",
355
+ children: 'Outbound — send data to a URL'
356
+ }),
357
+ /*#__PURE__*/ _jsx(MenuItem, {
358
+ value: "inbound",
359
+ children: 'Inbound — receive data, run a workflow'
360
+ })
361
+ ]
362
+ }),
363
+ (draft == null ? void 0 : draft.direction) === 'outbound' ? /*#__PURE__*/ _jsx(TextField, {
364
+ label: "Delivery URL",
365
+ placeholder: "https://example.com/hooks/aglyn",
366
+ value: (_ref2 = draft == null ? void 0 : draft.url) != null ? _ref2 : '',
367
+ onChange: (event)=>setDraft((prev)=>prev ? _extends({}, prev, {
368
+ url: event.target.value
369
+ }) : prev),
370
+ size: "small"
371
+ }) : /*#__PURE__*/ _jsx(TextField, {
372
+ select: true,
373
+ label: "Workflow to run",
374
+ helperText: workflowOptionsTruncated ? `Showing ${WORKFLOW_OPTION_CEILING} workflows, ordered ` + 'by id. This site has more, so one of them is not ' + 'offered here.' : undefined,
375
+ value: (_ref3 = draft == null ? void 0 : draft.workflowName) != null ? _ref3 : '',
376
+ onChange: (event)=>setDraft((prev)=>prev ? _extends({}, prev, {
377
+ workflowName: event.target.value
378
+ }) : prev),
379
+ size: "small",
380
+ children: workflowNames.map((name)=>/*#__PURE__*/ _jsx(MenuItem, {
381
+ value: name,
382
+ children: name
383
+ }, name))
384
+ }),
385
+ /*#__PURE__*/ _jsx(TextField, {
386
+ label: "Secret",
387
+ helperText: (draft == null ? void 0 : draft.direction) === 'outbound' ? 'Signs deliveries (X-Aglyn-Signature, HMAC-SHA256)' : 'Callers send this in the x-aglyn-secret header',
388
+ value: (_ref4 = draft == null ? void 0 : draft.secret) != null ? _ref4 : '',
389
+ size: "small",
390
+ slotProps: {
391
+ input: {
392
+ readOnly: true
393
+ }
394
+ }
395
+ })
396
+ ]
397
+ }),
398
+ /*#__PURE__*/ _jsxs(DialogActions, {
399
+ children: [
400
+ /*#__PURE__*/ _jsx(Button, {
401
+ onClick: ()=>setDraft(null),
402
+ children: 'Cancel'
403
+ }),
404
+ /*#__PURE__*/ _jsx(Button, {
405
+ variant: "contained",
406
+ color: "primary",
407
+ disabled: !(draft == null ? void 0 : draft.name.trim()),
408
+ onClick: handleSave,
409
+ children: 'Save webhook'
410
+ })
411
+ ]
412
+ })
413
+ ]
414
+ })
415
+ ]
416
+ });
417
+ }
418
+ HostWebhooksCard.displayName = 'HostWebhooksCard';
419
+ export default HostWebhooksCard;
420
+
421
+ //# sourceMappingURL=host-webhooks-card.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/host-webhooks-card.component.tsx"],"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'use client'\n\nimport {\n type AglynOrgBilling,\n checkEntitlement,\n createResourceUid,\n pluginDocsHelp,\n WEBHOOK_MAX_PER_HOST,\n WEBHOOK_URL_PATTERN,\n} from '@aglyn/aglyn'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { hostPublicOrigin } from '@aglyn/aglyn/app-utils/host-naming'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Timestamp } from '@aglyn/shared-util-timestamp'\nimport {\n Alert,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, doc, updateDoc } from 'firebase/firestore'\nimport { useCallback, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useFirestoreDoc,\n useHostResourceApi,\n} from '@aglyn/tenant-feature-instance'\n/*\n * The MODULE, not the barrel, for the two PURE helpers — the specs that render\n * this card mock `@aglyn/tenant-feature-instance` wholesale to stage their\n * Firestore hooks, and a query builder imported through that barrel disappears\n * under the mock. Neither of these is a hook.\n */\nimport {\n ceilingedWindow,\n collectionCeiling,\n} from '@aglyn/tenant-feature-instance/hooks/host-collection-queries'\n\n/**\n * How many webhook documents the card reads.\n *\n * Above `WEBHOOK_MAX_PER_HOST` on purpose. The cap counts LIVE hooks and the\n * collection keeps soft-deleted ones, so a site that has created and deleted\n * its five several times holds more documents than it may have hooks.\n */\nconst WEBHOOK_CEILING = 20\n\n/**\n * How many workflows the inbound-endpoint picker offers.\n *\n * Paid for only while the editor is open, which is what makes a ceiling this\n * size affordable: it is one operator mid-edit rather than every visitor to\n * the page.\n */\nconst WORKFLOW_OPTION_CEILING = 100\n\nfunction generateSecret(): string {\n const bytes = new Uint8Array(24)\n crypto.getRandomValues(bytes)\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join(\n '',\n )\n}\n\ninterface WebhookDraft {\n id: string | null\n name: string\n direction: 'outbound' | 'inbound'\n url: string\n workflowName: string\n secret: string\n}\n\n/**\n * Webhooks manager (AGL-149): outbound targets (used by the actions\n * builder's \"Send a webhook\" step, HMAC-signed) and inbound endpoints\n * (`/api/hooks/{hostId}/{hookId}`, secret-verified, run a workflow with\n * the payload in scope). Business tier.\n */\nexport function HostWebhooksCard(props: {\n hostId: string\n org?: Partial<AglynOrgBilling>\n}) {\n const { hostId, org } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const createResource = useHostResourceApi()\n\n\n const [draft, setDraft] = useState<WebhookDraft | null>(null)\n /**\n * The editor has been opened at least once in this session.\n *\n * A LATCH rather than `draft` itself, because the workflow picker below keys its\n * listeners on it. Tracking the dialog would tear that subscription down\n * on Cancel and pay for them again on the next Edit, so a merchant working\n * through ten actions would buy the same window ten times — worse than\n * the mount-time read this replaces. Latched, a reader who never edits pays\n * nothing and a reader who edits pays once.\n */\n const [editorOpened, setEditorOpened] = useState(false)\n if (draft && !editorOpened) setEditorOpened(true)\n /*\n * ORDERED AND CEILINGED (AGL-2501). A bare `limit` is answered in\n * DOCUMENT-ID order, so an unnamed window is an arbitrary twenty that the\n * `localeCompare` below would arrange alphabetically. `collectionCeiling`\n * does not change WHICH twenty; it NAMES the order, and the probe row it\n * adds is what lets the card say when the ceiling bites.\n */\n const { data: webhookRead } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'webhooks'),\n WEBHOOK_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { rows: readWebhooks, truncated: webhooksTruncated } =\n ceilingedWindow<any>(webhookRead, WEBHOOK_CEILING)\n /*\n * READ WHEN THE EDITOR OPENS, not when the card mounts. The only thing that\n * consumes these rows is the inbound-endpoint select inside the dialog — the\n * table renders a hook's stored `workflowName` — so an unconditional\n * listener would charge every visitor to this page a hundred workflows to\n * fill a control most of them never see, and one that only appears for the\n * inbound half of the form at that.\n */\n const { data: workflowRead } = useFirestoreCollection<any>(\n () =>\n editorOpened\n ? collectionCeiling(\n collection(firestore, 'hosts', hostId, 'workflows'),\n WORKFLOW_OPTION_CEILING,\n )\n : null,\n [firestore, hostId, editorOpened],\n { idField: '$id' },\n )\n const { rows: readWorkflows, truncated: workflowOptionsTruncated } =\n ceilingedWindow<any>(workflowRead, WORKFLOW_OPTION_CEILING)\n const { data: host } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId),\n [firestore, hostId],\n { idField: '$id' },\n )\n // Sorting the whole ceiling, not a page of it.\n const webhooks = [...readWebhooks]\n .filter((hook: any) => !hook.deletedAt)\n .sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n const workflowNames = readWorkflows\n .filter((workflow: any) => !workflow.deletedAt && workflow.name)\n .map((workflow: any) => workflow.name as string)\n .sort()\n // `hostPublicOrigin` (AGL-2195) — this base is printed into the webhook\n // sample payload an operator copies into a third-party system.\n const siteBase = hostPublicOrigin(host) ?? ''\n\n const handleAdd = useCallback(() => {\n if (!checkEntitlement(org, 'webhooks')) {\n return void enqueueSnackbar(\n 'Webhooks require a Business plan — see Billing to upgrade',\n { variant: 'warning', persist: false },\n )\n }\n // An affordance, NOT the limit (AGL-1360). `webhooks` comes from a\n // Firestore listener and the console runs `persistentLocalCache`, so this\n // count can be arbitrarily stale — it is here to fail fast when it\n // happens to be right, never to authorise the create. The cap that holds\n // is counted server-side in /api/hosts/resources.\n if (webhooks.length >= WEBHOOK_MAX_PER_HOST) {\n return void enqueueSnackbar(\n `Webhooks are capped at ${WEBHOOK_MAX_PER_HOST} per site`,\n { variant: 'warning', persist: false },\n )\n }\n setDraft({\n id: null,\n name: '',\n direction: 'outbound',\n url: '',\n workflowName: '',\n secret: generateSecret(),\n })\n }, [org, webhooks.length, enqueueSnackbar])\n\n const handleSave = useCallback(async () => {\n if (!draft || !draft.name.trim()) return\n if (draft.direction === 'outbound') {\n if (!WEBHOOK_URL_PATTERN.test(draft.url.trim())) {\n return void enqueueSnackbar(\n 'Outbound URLs must be public https addresses',\n { variant: 'warning', persist: false },\n )\n }\n } else if (!draft.workflowName.trim()) {\n return void enqueueSnackbar('Pick the workflow this endpoint runs', {\n variant: 'warning',\n persist: false,\n })\n }\n try {\n // Creates go through /api/hosts/resources (AGL-1360), which counts the\n // live webhooks with the Admin SDK before allowing one more; Firestore\n // rules deny client `create` on this collection. The card has no edit\n // path — every save is a create — so there is no update branch here.\n await createResource({\n hostId,\n resource: 'webhook',\n id: createResourceUid(),\n data: {\n name: draft.name.trim().slice(0, 60),\n direction: draft.direction,\n ...(draft.direction === 'outbound'\n ? { url: draft.url.trim() }\n : { workflowName: draft.workflowName.trim() }),\n secret: draft.secret,\n enabled: true,\n },\n })\n setDraft(null)\n enqueueSnackbar('Webhook saved', { variant: 'success', persist: false })\n } catch (error: any) {\n console.error(error)\n // The route's message is the useful one — it names the cap, the\n // entitlement or the role that refused.\n enqueueSnackbar(error?.message || 'An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n }, [draft, createResource, hostId, enqueueSnackbar])\n\n const handleDelete = useCallback(\n (hook: any) => async () => {\n const confirmed = await confirm({\n title: 'Delete this webhook?',\n description: `\"${hook.name}\" stops ${\n hook.direction === 'outbound' ? 'delivering' : 'accepting calls'\n } immediately.`,\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await updateDoc(doc(firestore, 'hosts', hostId, 'webhooks', hook.$id), {\n deletedAt: Timestamp.now(),\n })\n },\n [confirm, firestore, hostId],\n )\n\n return (\n <CardDisplay\n header={'Webhooks'}\n help={pluginDocsHelp('webhooks', { anchor: '#outbound-webhooks' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Send signed JSON to outside systems from the actions builder, ' +\n 'or accept calls that run a workflow. Business plans.'}\n </Typography>\n {webhooks.map((hook: any) => (\n <Stack\n key={hook.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Stack sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {hook.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {hook.direction === 'outbound'\n ? `outbound · ${hook.url}`\n : `inbound · ${siteBase}/api/hooks/${hostId}/${hook.$id}` +\n ` → ${hook.workflowName}`}\n </Typography>\n </Stack>\n {hook.direction === 'inbound' ? (\n <Button\n size=\"small\"\n onClick={() => {\n void navigator.clipboard.writeText(\n `${siteBase}/api/hooks/${hostId}/${hook.$id}`,\n )\n enqueueSnackbar(\n 'Endpoint URL copied — send the secret in x-aglyn-secret',\n { variant: 'success', persist: false },\n )\n }}\n >\n {'Copy URL'}\n </Button>\n ) : null}\n <Button\n size=\"small\"\n onClick={() => {\n void navigator.clipboard.writeText(hook.secret ?? '')\n enqueueSnackbar('Secret copied', {\n variant: 'success',\n persist: false,\n })\n }}\n >\n {'Secret'}\n </Button>\n <Button size=\"small\" color=\"error\" onClick={handleDelete(hook)}>\n {'Delete'}\n </Button>\n </Stack>\n ))}\n {webhooksTruncated ? (\n <Alert severity=\"info\">\n {`Showing the first ${WEBHOOK_CEILING} webhook documents on this ` +\n 'site, ordered by id. There are more, so a hook may be missing ' +\n 'from this list.'}\n </Alert>\n ) : null}\n <Button\n size=\"small\"\n color=\"primary\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={handleAdd}\n >\n {'Add webhook'}\n </Button>\n </Stack>\n\n <Dialog\n open={Boolean(draft)}\n onClose={() => setDraft(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>{'Add webhook'}</DialogTitle>\n <DialogContent\n sx={{ display: 'flex', flexDirection: 'column', gap: 1.5, pt: 1 }}\n >\n <TextField\n label=\"Name\"\n value={draft?.name ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, name: event.target.value } : prev,\n )\n }\n size=\"small\"\n autoFocus\n sx={{ mt: 1 }}\n />\n <TextField\n select\n label=\"Direction\"\n value={draft?.direction ?? 'outbound'}\n onChange={(event) =>\n setDraft((prev) =>\n prev\n ? { ...prev, direction: event.target.value as any }\n : prev,\n )\n }\n size=\"small\"\n >\n <MenuItem value=\"outbound\">\n {'Outbound — send data to a URL'}\n </MenuItem>\n <MenuItem value=\"inbound\">\n {'Inbound — receive data, run a workflow'}\n </MenuItem>\n </TextField>\n {draft?.direction === 'outbound' ? (\n <TextField\n label=\"Delivery URL\"\n placeholder=\"https://example.com/hooks/aglyn\"\n value={draft?.url ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, url: event.target.value } : prev,\n )\n }\n size=\"small\"\n />\n ) : (\n <TextField\n select\n label=\"Workflow to run\"\n helperText={\n workflowOptionsTruncated\n ? `Showing ${WORKFLOW_OPTION_CEILING} workflows, ordered ` +\n 'by id. This site has more, so one of them is not ' +\n 'offered here.'\n : undefined\n }\n value={draft?.workflowName ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, workflowName: event.target.value } : prev,\n )\n }\n size=\"small\"\n >\n {workflowNames.map((name) => (\n <MenuItem key={name} value={name}>\n {name}\n </MenuItem>\n ))}\n </TextField>\n )}\n <TextField\n label=\"Secret\"\n helperText={\n draft?.direction === 'outbound'\n ? 'Signs deliveries (X-Aglyn-Signature, HMAC-SHA256)'\n : 'Callers send this in the x-aglyn-secret header'\n }\n value={draft?.secret ?? ''}\n size=\"small\"\n slotProps={{ input: { readOnly: true } }}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setDraft(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!draft?.name.trim()}\n onClick={handleSave}\n >\n {'Save webhook'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nHostWebhooksCard.displayName = 'HostWebhooksCard'\n\nexport default HostWebhooksCard\n"],"names":["checkEntitlement","createResourceUid","pluginDocsHelp","WEBHOOK_MAX_PER_HOST","WEBHOOK_URL_PATTERN","CardDisplay","useConfirmationContext","hostPublicOrigin","useSnackbar","Timestamp","Alert","Button","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","collection","doc","updateDoc","useCallback","useState","useFirestore","useFirestoreCollection","useFirestoreDoc","useHostResourceApi","ceilingedWindow","collectionCeiling","WEBHOOK_CEILING","WORKFLOW_OPTION_CEILING","generateSecret","bytes","Uint8Array","crypto","getRandomValues","Array","from","byte","toString","padStart","join","HostWebhooksCard","props","hostId","org","firestore","enqueueSnackbar","confirm","createResource","draft","setDraft","editorOpened","setEditorOpened","data","webhookRead","idField","rows","readWebhooks","truncated","webhooksTruncated","workflowRead","readWorkflows","workflowOptionsTruncated","host","webhooks","filter","hook","deletedAt","sort","a","b","String","name","localeCompare","workflowNames","workflow","map","siteBase","handleAdd","variant","persist","length","id","direction","url","workflowName","secret","handleSave","trim","test","resource","slice","enabled","error","console","message","allowDuplicate","handleDelete","confirmed","title","description","confirmationText","confirmationButtonProps","color","then","catch","$id","now","header","help","anchor","contentGutterX","contentGutterY","spacing","sx","alignItems","flex","minWidth","noWrap","size","onClick","navigator","clipboard","writeText","severity","alignSelf","open","Boolean","onClose","maxWidth","fullWidth","display","flexDirection","gap","pt","label","value","onChange","event","prev","target","autoFocus","mt","select","placeholder","helperText","undefined","slotProps","input","readOnly","disabled","displayName"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { type AglynOrgBilling } from '@aglyn/aglyn';
2
+ export interface HostWorkflowsCardProps {
3
+ hostId: string;
4
+ /** Resolved entitlement source for quota checks (AGL-395). */
5
+ org?: Partial<AglynOrgBilling>;
6
+ }
7
+ /**
8
+ * Workflow builder v1 (AGL-101): pipelines of host-function calls — each
9
+ * step's argument expressions see host variables and previous step results
10
+ * (`step1`, or a custom result name). Runs through the pure `runWorkflow`
11
+ * evaluator; test-run panel included. Plan-gated (`workflows` flag +
12
+ * `workflowsPerHost` cap, AGL-99); site-event triggers are v2.
13
+ */
14
+ export declare function HostWorkflowsCard(props: HostWorkflowsCardProps): import("react").JSX.Element;
15
+ export declare namespace HostWorkflowsCard {
16
+ var displayName: string;
17
+ }
18
+ export default HostWorkflowsCard;