@forumone/throughline-integrations 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +94 -28
  4. package/dist/collection.d.ts +16 -0
  5. package/dist/collection.d.ts.map +1 -0
  6. package/dist/collection.js +119 -0
  7. package/dist/collection.js.map +1 -0
  8. package/dist/index.d.ts +9 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +6 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/integrations/index.d.ts +3 -0
  13. package/dist/integrations/index.d.ts.map +1 -0
  14. package/dist/integrations/index.js +2 -0
  15. package/dist/integrations/index.js.map +1 -0
  16. package/dist/integrations/webhook/config-fields.d.ts +33 -0
  17. package/dist/integrations/webhook/config-fields.d.ts.map +1 -0
  18. package/dist/integrations/webhook/config-fields.js +54 -0
  19. package/dist/integrations/webhook/config-fields.js.map +1 -0
  20. package/dist/integrations/webhook/functions.d.ts +21 -0
  21. package/dist/integrations/webhook/functions.d.ts.map +1 -0
  22. package/dist/integrations/webhook/functions.js +124 -0
  23. package/dist/integrations/webhook/functions.js.map +1 -0
  24. package/dist/integrations/webhook/healthcheck.d.ts +10 -0
  25. package/dist/integrations/webhook/healthcheck.d.ts.map +1 -0
  26. package/dist/integrations/webhook/healthcheck.js +26 -0
  27. package/dist/integrations/webhook/healthcheck.js.map +1 -0
  28. package/dist/integrations/webhook/hmac.d.ts +11 -0
  29. package/dist/integrations/webhook/hmac.d.ts.map +1 -0
  30. package/dist/integrations/webhook/hmac.js +25 -0
  31. package/dist/integrations/webhook/hmac.js.map +1 -0
  32. package/dist/integrations/webhook/index.d.ts +6 -0
  33. package/dist/integrations/webhook/index.d.ts.map +1 -0
  34. package/dist/integrations/webhook/index.js +46 -0
  35. package/dist/integrations/webhook/index.js.map +1 -0
  36. package/dist/integrations/webhook/payload.d.ts +9 -0
  37. package/dist/integrations/webhook/payload.d.ts.map +1 -0
  38. package/dist/integrations/webhook/payload.js +19 -0
  39. package/dist/integrations/webhook/payload.js.map +1 -0
  40. package/dist/options.d.ts +22 -0
  41. package/dist/options.d.ts.map +1 -0
  42. package/dist/options.js +8 -0
  43. package/dist/options.js.map +1 -0
  44. package/dist/plugin.d.ts +19 -0
  45. package/dist/plugin.d.ts.map +1 -0
  46. package/dist/plugin.js +162 -0
  47. package/dist/plugin.js.map +1 -0
  48. package/dist/registry.d.ts +15 -0
  49. package/dist/registry.d.ts.map +1 -0
  50. package/dist/registry.js +27 -0
  51. package/dist/registry.js.map +1 -0
  52. package/dist/tools/access.d.ts +7 -0
  53. package/dist/tools/access.d.ts.map +1 -0
  54. package/dist/tools/access.js +15 -0
  55. package/dist/tools/access.js.map +1 -0
  56. package/dist/tools/get-integration-status.d.ts +17 -0
  57. package/dist/tools/get-integration-status.d.ts.map +1 -0
  58. package/dist/tools/get-integration-status.js +41 -0
  59. package/dist/tools/get-integration-status.js.map +1 -0
  60. package/dist/tools/index.d.ts +12 -0
  61. package/dist/tools/index.d.ts.map +1 -0
  62. package/dist/tools/index.js +7 -0
  63. package/dist/tools/index.js.map +1 -0
  64. package/dist/tools/list-integration-types.d.ts +10 -0
  65. package/dist/tools/list-integration-types.d.ts.map +1 -0
  66. package/dist/tools/list-integration-types.js +22 -0
  67. package/dist/tools/list-integration-types.js.map +1 -0
  68. package/dist/tools/list-integrations.d.ts +20 -0
  69. package/dist/tools/list-integrations.d.ts.map +1 -0
  70. package/dist/tools/list-integrations.js +49 -0
  71. package/dist/tools/list-integrations.js.map +1 -0
  72. package/dist/tools/test-integration.d.ts +19 -0
  73. package/dist/tools/test-integration.d.ts.map +1 -0
  74. package/dist/tools/test-integration.js +50 -0
  75. package/dist/tools/test-integration.js.map +1 -0
  76. package/dist/tools/trigger-sync.d.ts +22 -0
  77. package/dist/tools/trigger-sync.d.ts.map +1 -0
  78. package/dist/tools/trigger-sync.js +60 -0
  79. package/dist/tools/trigger-sync.js.map +1 -0
  80. package/dist/types.d.ts +116 -0
  81. package/dist/types.d.ts.map +1 -0
  82. package/dist/types.js +2 -0
  83. package/dist/types.js.map +1 -0
  84. package/package.json +63 -7
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healthcheck.d.ts","sourceRoot":"","sources":["../../../src/integrations/webhook/healthcheck.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIvD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgBnF"}
@@ -0,0 +1,26 @@
1
+ const HEALTHCHECK_TIMEOUT_MS = 5000;
2
+ /**
3
+ * Light reachability check for the webhook target. Sends a HEAD request and
4
+ * accepts any 2xx, plus 405 (Method Not Allowed) since plenty of webhook
5
+ * receivers only allow POST and reject HEAD with 405. That's healthy enough
6
+ * for a "your endpoint is up" probe.
7
+ */
8
+ export async function healthcheck(config) {
9
+ try {
10
+ const response = await fetch(config.targetUrl, {
11
+ method: 'HEAD',
12
+ signal: AbortSignal.timeout(HEALTHCHECK_TIMEOUT_MS),
13
+ });
14
+ if (response.ok || response.status === 405) {
15
+ return { ok: true, details: `Reachable (HTTP ${response.status})` };
16
+ }
17
+ return { ok: false, details: `Endpoint returned HTTP ${response.status}` };
18
+ }
19
+ catch (error) {
20
+ return {
21
+ ok: false,
22
+ details: error instanceof Error ? error.message : 'Unknown error',
23
+ };
24
+ }
25
+ }
26
+ //# sourceMappingURL=healthcheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healthcheck.js","sourceRoot":"","sources":["../../../src/integrations/webhook/healthcheck.ts"],"names":[],"mappings":"AAGA,MAAM,sBAAsB,GAAG,IAAI,CAAA;AAEnC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAqB;IACrD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE;YAC7C,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,sBAAsB,CAAC;SACpD,CAAC,CAAA;QACF,IAAI,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC3C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,mBAAmB,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAA;QACrE,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,0BAA0B,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAA;IAC5E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;SAClE,CAAA;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * HMAC-SHA256 utility used by the webhook integration to sign delivery
3
+ * payloads. The hex-encoded digest is sent in the `x-throughline-signature`
4
+ * header (prefixed with `sha256=`); receivers verify by recomputing with
5
+ * the shared secret.
6
+ *
7
+ * Uses Web Crypto so the same code runs in Node 20+, edge runtimes, and
8
+ * the browser (for tests). No Node-specific imports.
9
+ */
10
+ export declare function hmacSha256Hex(payload: string, secret: string): Promise<string>;
11
+ //# sourceMappingURL=hmac.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hmac.d.ts","sourceRoot":"","sources":["../../../src/integrations/webhook/hmac.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWpF"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * HMAC-SHA256 utility used by the webhook integration to sign delivery
3
+ * payloads. The hex-encoded digest is sent in the `x-throughline-signature`
4
+ * header (prefixed with `sha256=`); receivers verify by recomputing with
5
+ * the shared secret.
6
+ *
7
+ * Uses Web Crypto so the same code runs in Node 20+, edge runtimes, and
8
+ * the browser (for tests). No Node-specific imports.
9
+ */
10
+ const HEX_CHARS = '0123456789abcdef';
11
+ export async function hmacSha256Hex(payload, secret) {
12
+ const encoder = new TextEncoder();
13
+ const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
14
+ const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(payload));
15
+ return bytesToHex(new Uint8Array(signature));
16
+ }
17
+ function bytesToHex(bytes) {
18
+ let out = '';
19
+ for (let i = 0; i < bytes.length; i += 1) {
20
+ const byte = bytes[i];
21
+ out += HEX_CHARS[byte >> 4] + HEX_CHARS[byte & 0xf];
22
+ }
23
+ return out;
24
+ }
25
+ //# sourceMappingURL=hmac.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hmac.js","sourceRoot":"","sources":["../../../src/integrations/webhook/hmac.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,SAAS,GAAG,kBAAkB,CAAA;AAEpC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,MAAc;IACjE,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;IACjC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACvC,KAAK,EACL,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EACtB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAA;IACD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;IAChF,OAAO,UAAU,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC,CAAA;AAC9C,CAAC;AAED,SAAS,UAAU,CAAC,KAAiB;IACnC,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;QACtB,GAAG,IAAI,SAAS,CAAC,IAAI,IAAI,CAAC,CAAE,GAAG,SAAS,CAAC,IAAI,GAAG,GAAG,CAAE,CAAA;IACvD,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Integration } from '../../types.js';
2
+ import { type WebhookConfig } from './config-fields.js';
3
+ export declare const webhookIntegration: Integration<WebhookConfig>;
4
+ export type { WebhookConfig } from './config-fields.js';
5
+ export { WEBHOOK_INTEGRATION_ID } from './functions.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/integrations/webhook/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,oBAAoB,CAAA;AAI3B,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,aAAa,CAyCzD,CAAA;AAED,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,46 @@
1
+ import { configFields, MIN_SIGNING_SECRET_LENGTH, } from './config-fields.js';
2
+ import { createWebhookFunctions, WEBHOOK_INTEGRATION_ID } from './functions.js';
3
+ import { healthcheck } from './healthcheck.js';
4
+ export const webhookIntegration = {
5
+ id: WEBHOOK_INTEGRATION_ID,
6
+ name: 'Generic Webhook',
7
+ description: 'POST system events to an external HTTPS endpoint with HMAC-SHA256 signed payloads. Configurable event filter, retries, and timeout.',
8
+ category: 'webhook',
9
+ configFields,
10
+ async validateConfig(config) {
11
+ if (!config?.targetUrl)
12
+ return { ok: false, reason: 'targetUrl is required' };
13
+ let url;
14
+ try {
15
+ url = new URL(config.targetUrl);
16
+ }
17
+ catch {
18
+ return { ok: false, reason: 'targetUrl is not a valid URL' };
19
+ }
20
+ if (url.protocol !== 'https:') {
21
+ return { ok: false, reason: 'targetUrl must use https://' };
22
+ }
23
+ if (!config.signingSecret || config.signingSecret.length < MIN_SIGNING_SECRET_LENGTH) {
24
+ return {
25
+ ok: false,
26
+ reason: `signingSecret must be at least ${MIN_SIGNING_SECRET_LENGTH} characters`,
27
+ };
28
+ }
29
+ if (config.timeoutSeconds !== undefined &&
30
+ (typeof config.timeoutSeconds !== 'number' || config.timeoutSeconds <= 0)) {
31
+ return { ok: false, reason: 'timeoutSeconds must be a positive number' };
32
+ }
33
+ return { ok: true };
34
+ },
35
+ subscribes: [
36
+ { event: 'content/page.published', purpose: 'Notify external systems when content goes live' },
37
+ { event: 'content/page.unpublished', purpose: 'Notify when content is taken down' },
38
+ { event: 'content/page.rolled_back', purpose: 'Notify when content reverts to a prior version' },
39
+ { event: 'form/submission.received', purpose: 'Forward form submissions to downstream systems' },
40
+ { event: 'approval/decided', purpose: 'Notify external workflow tools of approval outcomes' },
41
+ ],
42
+ createFunctions: createWebhookFunctions,
43
+ healthcheck,
44
+ };
45
+ export { WEBHOOK_INTEGRATION_ID } from './functions.js';
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/integrations/webhook/index.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,YAAY,EACZ,yBAAyB,GAC1B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,MAAM,CAAC,MAAM,kBAAkB,GAA+B;IAC5D,EAAE,EAAE,sBAAsB;IAC1B,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,qIAAqI;IACvI,QAAQ,EAAE,SAAS;IACnB,YAAY;IACZ,KAAK,CAAC,cAAc,CAAC,MAAM;QACzB,IAAI,CAAC,MAAM,EAAE,SAAS;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAA;QAC7E,IAAI,GAAQ,CAAA;QACZ,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,8BAA8B,EAAE,CAAA;QAC9D,CAAC;QACD,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,6BAA6B,EAAE,CAAA;QAC7D,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,yBAAyB,EAAE,CAAC;YACrF,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,kCAAkC,yBAAyB,aAAa;aACjF,CAAA;QACH,CAAC;QACD,IACE,MAAM,CAAC,cAAc,KAAK,SAAS;YACnC,CAAC,OAAO,MAAM,CAAC,cAAc,KAAK,QAAQ,IAAI,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC,EACzE,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,0CAA0C,EAAE,CAAA;QAC1E,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;IACrB,CAAC;IACD,UAAU,EAAE;QACV,EAAE,KAAK,EAAE,wBAAwB,EAAE,OAAO,EAAE,gDAAgD,EAAE;QAC9F,EAAE,KAAK,EAAE,0BAA0B,EAAE,OAAO,EAAE,mCAAmC,EAAE;QACnF,EAAE,KAAK,EAAE,0BAA0B,EAAE,OAAO,EAAE,gDAAgD,EAAE;QAChG,EAAE,KAAK,EAAE,0BAA0B,EAAE,OAAO,EAAE,gDAAgD,EAAE;QAChG,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,qDAAqD,EAAE;KAC9F;IACD,eAAe,EAAE,sBAAsB;IACvC,WAAW;CACZ,CAAA;AAGD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * When a webhook instance is configured with `includeFullPayload=false`,
3
+ * we still want to include enough context to make the event useful: IDs,
4
+ * slugs, and any other field whose name ends in `Id`. Keeps payloads small
5
+ * without requiring the receiver to make a follow-up API call for the
6
+ * common case.
7
+ */
8
+ export declare function extractIds(data: unknown): Record<string, unknown>;
9
+ //# sourceMappingURL=payload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../../src/integrations/webhook/payload.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASjE"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * When a webhook instance is configured with `includeFullPayload=false`,
3
+ * we still want to include enough context to make the event useful: IDs,
4
+ * slugs, and any other field whose name ends in `Id`. Keeps payloads small
5
+ * without requiring the receiver to make a follow-up API call for the
6
+ * common case.
7
+ */
8
+ export function extractIds(data) {
9
+ if (!data || typeof data !== 'object')
10
+ return {};
11
+ const result = {};
12
+ for (const [key, value] of Object.entries(data)) {
13
+ if (key === 'id' || key === 'slug' || key.endsWith('Id')) {
14
+ result[key] = value;
15
+ }
16
+ }
17
+ return result;
18
+ }
19
+ //# sourceMappingURL=payload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../../src/integrations/webhook/payload.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAA;IAChD,MAAM,MAAM,GAA4B,EAAE,CAAA;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACzD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACrB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Inngest } from 'inngest';
2
+ import type { BaseCorePluginOptions } from '@forumone/throughline-plugin-contract';
3
+ import type { Integration } from './types.js';
4
+ export declare const DEFAULT_INTEGRATIONS_SLUG = "integrations";
5
+ export interface IntegrationsPluginOptions extends BaseCorePluginOptions {
6
+ /**
7
+ * Inngest client used to register integration functions and to fire
8
+ * manual-sync trigger events. Required: integrations are an
9
+ * event-driven feature and there is no useful behaviour without one.
10
+ */
11
+ inngest: Inngest;
12
+ /**
13
+ * Integration modules to register, in addition to the built-in webhook
14
+ * integration. Order matters only for tie-breaking in lists; the registry
15
+ * rejects duplicate ids.
16
+ */
17
+ integrations?: Integration[];
18
+ /** Override the Integrations collection slug. Default: 'integrations'. */
19
+ collectionSlug?: string;
20
+ }
21
+ export declare function validateOptions(options: IntegrationsPluginOptions): IntegrationsPluginOptions;
22
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,eAAO,MAAM,yBAAyB,iBAAiB,CAAA;AAEvD,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;IACtE;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,YAAY,CAAC,EAAE,WAAW,EAAE,CAAA;IAC5B,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,yBAAyB,GAAG,yBAAyB,CAK7F"}
@@ -0,0 +1,8 @@
1
+ export const DEFAULT_INTEGRATIONS_SLUG = 'integrations';
2
+ export function validateOptions(options) {
3
+ if (!options.inngest) {
4
+ throw new Error('integrationsPlugin requires an Inngest client (`options.inngest`).');
5
+ }
6
+ return options;
7
+ }
8
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,yBAAyB,GAAG,cAAc,CAAA;AAmBvD,MAAM,UAAU,eAAe,CAAC,OAAkC;IAChE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;IACvF,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { CorePlugin } from '@forumone/throughline-plugin-contract';
2
+ import { type IntegrationsPluginOptions } from './options.js';
3
+ import { IntegrationRegistry } from './registry.js';
4
+ import type { IntegrationContext } from './types.js';
5
+ /**
6
+ * Integrations server. Registers the Integrations collection, the
7
+ * five-tool MCP server, and a process-local registry of integration
8
+ * modules. The webhook integration is registered automatically; clients
9
+ * extend the registry by passing additional integrations via options.
10
+ *
11
+ * Integration Inngest functions are not served here — they are exposed
12
+ * via `getIntegrationRegistry(payload)` so the client app's Inngest
13
+ * endpoint can merge them with its own functions. See
14
+ * `docs/integrations-wiring.md` in the repository root.
15
+ */
16
+ export declare const integrationsPlugin: CorePlugin<IntegrationsPluginOptions>;
17
+ export declare function getIntegrationRegistry(payload: unknown): IntegrationRegistry | undefined;
18
+ export declare function getIntegrationContext(payload: unknown): IntegrationContext | undefined;
19
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,uCAAuC,CAAA;AAQ1F,OAAO,EAAE,KAAK,yBAAyB,EAA8C,MAAM,cAAc,CAAA;AACzG,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAGnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAkBpD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,UAAU,CAAC,yBAAyB,CAoJlE,CAAA;AAEH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,GAAG,SAAS,CAExF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,kBAAkB,GAAG,SAAS,CAEtF"}
package/dist/plugin.js ADDED
@@ -0,0 +1,162 @@
1
+ import { getPluginRegistry } from '@forumone/throughline-plugin-contract';
2
+ import { createMcpHandler, createNamedLogger, defaultLogger, getAuditWriter, } from '@forumone/throughline-core';
3
+ import { validateOptions, DEFAULT_INTEGRATIONS_SLUG } from './options.js';
4
+ import { IntegrationRegistry } from './registry.js';
5
+ import { createIntegrationsCollection } from './collection.js';
6
+ import { webhookIntegration } from './integrations/index.js';
7
+ import { createGetIntegrationStatusTool, createListIntegrationsTool, createListIntegrationTypesTool, createTestIntegrationTool, createTriggerSyncTool, } from './tools/index.js';
8
+ const PLUGIN_ID = '@forumone/throughline-integrations';
9
+ const PLUGIN_VERSION = '0.1.0';
10
+ const MCP_HANDLER_SYMBOL = Symbol.for('@forumone/throughline/integrations-mcp-handler');
11
+ const REGISTRY_SYMBOL = Symbol.for('@forumone/throughline/integrations-registry');
12
+ const CONTEXT_SYMBOL = Symbol.for('@forumone/throughline/integrations-context');
13
+ /**
14
+ * Integrations server. Registers the Integrations collection, the
15
+ * five-tool MCP server, and a process-local registry of integration
16
+ * modules. The webhook integration is registered automatically; clients
17
+ * extend the registry by passing additional integrations via options.
18
+ *
19
+ * Integration Inngest functions are not served here — they are exposed
20
+ * via `getIntegrationRegistry(payload)` so the client app's Inngest
21
+ * endpoint can merge them with its own functions. See
22
+ * `docs/integrations-wiring.md` in the repository root.
23
+ */
24
+ export const integrationsPlugin = (rawOptions) => (incomingConfig) => {
25
+ if (rawOptions.enabled === false)
26
+ return incomingConfig;
27
+ const options = validateOptions(rawOptions);
28
+ const collectionSlug = options.collectionSlug ?? DEFAULT_INTEGRATIONS_SLUG;
29
+ const routePrefix = options.routePrefix ?? '/integrations';
30
+ const logger = createNamedLogger('integrations', options.logger ?? defaultLogger);
31
+ const registry = new IntegrationRegistry();
32
+ registry.register(webhookIntegration);
33
+ for (const integration of options.integrations ?? []) {
34
+ registry.register(integration);
35
+ }
36
+ const collection = createIntegrationsCollection({ slug: collectionSlug, registry });
37
+ return {
38
+ ...incomingConfig,
39
+ collections: [...(incomingConfig.collections ?? []), collection],
40
+ endpoints: [
41
+ ...(incomingConfig.endpoints ?? []),
42
+ {
43
+ path: `${routePrefix}/mcp`,
44
+ method: 'post',
45
+ handler: async (req) => {
46
+ const handler = req.payload[MCP_HANDLER_SYMBOL];
47
+ if (!handler) {
48
+ return new Response(JSON.stringify({ error: 'Integrations MCP not initialized' }), { status: 503, headers: { 'content-type': 'application/json' } });
49
+ }
50
+ return handler(req);
51
+ },
52
+ },
53
+ ],
54
+ onInit: async (payload) => {
55
+ if (incomingConfig.onInit)
56
+ await incomingConfig.onInit(payload);
57
+ const pluginRegistry = getPluginRegistry(payload);
58
+ pluginRegistry.requireCapability('audit-log', PLUGIN_ID);
59
+ const auditWriter = getAuditWriter(payload);
60
+ const context = {
61
+ inngest: options.inngest,
62
+ integrationsCollectionSlug: collectionSlug,
63
+ async loadInstances(integrationId) {
64
+ const result = await payload.find({
65
+ collection: collectionSlug,
66
+ where: {
67
+ and: [
68
+ { integrationType: { equals: integrationId } },
69
+ { enabled: { equals: true } },
70
+ ],
71
+ },
72
+ limit: 100,
73
+ });
74
+ return result.docs.map((doc) => ({
75
+ id: String(doc['id']),
76
+ name: String(doc['name']),
77
+ config: (doc['config'] ?? {}),
78
+ }));
79
+ },
80
+ async updateStatus(instanceId, status, error) {
81
+ await payload.update({
82
+ collection: collectionSlug,
83
+ id: instanceId,
84
+ data: {
85
+ lastSyncAt: new Date().toISOString(),
86
+ lastSyncStatus: status,
87
+ ...(error !== undefined ? { lastError: error } : { lastError: null }),
88
+ },
89
+ });
90
+ },
91
+ async recordAudit(event) {
92
+ await auditWriter({
93
+ actor: { type: 'system', apiKeyName: `integration:${event.integrationId}` },
94
+ action: event.action,
95
+ mcpServer: 'integrations',
96
+ mcpTool: `integration:${event.integrationId}`,
97
+ integrationId: event.instanceName,
98
+ summary: event.summary,
99
+ ...(event.errorMessage ? { errorMessage: event.errorMessage } : {}),
100
+ success: event.action === 'integration.synced',
101
+ });
102
+ },
103
+ };
104
+ for (const integration of registry.list()) {
105
+ const fnCount = integration.createFunctions(context).length;
106
+ logger.info('Integration registered', {
107
+ id: integration.id,
108
+ inngestFunctions: fnCount,
109
+ });
110
+ }
111
+ const deps = { payload, collectionSlug };
112
+ const tools = [
113
+ createListIntegrationsTool(deps),
114
+ createGetIntegrationStatusTool(deps),
115
+ createTriggerSyncTool({ ...deps, inngest: options.inngest }),
116
+ createTestIntegrationTool({ ...deps, registry }),
117
+ createListIntegrationTypesTool({ registry }),
118
+ ];
119
+ const handler = createMcpHandler({
120
+ payload,
121
+ serverName: 'integrations',
122
+ tools,
123
+ logger,
124
+ });
125
+ Object.defineProperty(payload, MCP_HANDLER_SYMBOL, {
126
+ value: handler,
127
+ enumerable: false,
128
+ writable: false,
129
+ configurable: false,
130
+ });
131
+ Object.defineProperty(payload, REGISTRY_SYMBOL, {
132
+ value: registry,
133
+ enumerable: false,
134
+ writable: false,
135
+ configurable: false,
136
+ });
137
+ Object.defineProperty(payload, CONTEXT_SYMBOL, {
138
+ value: context,
139
+ enumerable: false,
140
+ writable: false,
141
+ configurable: false,
142
+ });
143
+ pluginRegistry.register({
144
+ id: PLUGIN_ID,
145
+ version: PLUGIN_VERSION,
146
+ capabilities: ['integrations', 'integration-registry'],
147
+ });
148
+ logger.info('Integrations server ready', {
149
+ collectionSlug,
150
+ routePrefix,
151
+ integrationCount: registry.size,
152
+ });
153
+ },
154
+ };
155
+ };
156
+ export function getIntegrationRegistry(payload) {
157
+ return payload[REGISTRY_SYMBOL];
158
+ }
159
+ export function getIntegrationContext(payload) {
160
+ return payload[CONTEXT_SYMBOL];
161
+ }
162
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Integration } from './types.js';
2
+ /**
3
+ * Tiny registry keyed by integration id. Construction is per-plugin-init
4
+ * (not a module-level singleton) so two Payload configs in the same process
5
+ * don't share state. Keeps duplicate-id detection synchronous and simple.
6
+ */
7
+ export declare class IntegrationRegistry {
8
+ private readonly byId;
9
+ register(integration: Integration): void;
10
+ get(id: string): Integration | undefined;
11
+ list(): Integration[];
12
+ has(id: string): boolean;
13
+ get size(): number;
14
+ }
15
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C;;;;GAIG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IAEtD,QAAQ,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAOxC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIxC,IAAI,IAAI,WAAW,EAAE;IAIrB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Tiny registry keyed by integration id. Construction is per-plugin-init
3
+ * (not a module-level singleton) so two Payload configs in the same process
4
+ * don't share state. Keeps duplicate-id detection synchronous and simple.
5
+ */
6
+ export class IntegrationRegistry {
7
+ byId = new Map();
8
+ register(integration) {
9
+ if (this.byId.has(integration.id)) {
10
+ throw new Error(`Integration "${integration.id}" is already registered`);
11
+ }
12
+ this.byId.set(integration.id, integration);
13
+ }
14
+ get(id) {
15
+ return this.byId.get(id);
16
+ }
17
+ list() {
18
+ return Array.from(this.byId.values());
19
+ }
20
+ has(id) {
21
+ return this.byId.has(id);
22
+ }
23
+ get size() {
24
+ return this.byId.size;
25
+ }
26
+ }
27
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,OAAO,mBAAmB;IACb,IAAI,GAAG,IAAI,GAAG,EAAuB,CAAA;IAEtD,QAAQ,CAAC,WAAwB;QAC/B,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,gBAAgB,WAAW,CAAC,EAAE,yBAAyB,CAAC,CAAA;QAC1E,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,EAAE,WAAW,CAAC,CAAA;IAC5C,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC1B,CAAC;IAED,IAAI;QACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;IACvC,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC1B,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;IACvB,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ import type { McpToolContext } from '@forumone/throughline-plugin-contract';
2
+ export declare function isIntegrationsReader(ctx: McpToolContext): boolean;
3
+ export declare function isIntegrationsAdmin(ctx: McpToolContext): boolean;
4
+ export declare function deniedEnvelope(reason: string): {
5
+ error: string;
6
+ };
7
+ //# sourceMappingURL=access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/tools/access.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AAE3E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAIjE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAGhE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAEhE"}
@@ -0,0 +1,15 @@
1
+ export function isIntegrationsReader(ctx) {
2
+ if (!ctx.user)
3
+ return false;
4
+ const roles = ctx.user.roles;
5
+ return roles.includes('admin') || roles.includes('editor');
6
+ }
7
+ export function isIntegrationsAdmin(ctx) {
8
+ if (!ctx.user)
9
+ return false;
10
+ return ctx.user.roles.includes('admin');
11
+ }
12
+ export function deniedEnvelope(reason) {
13
+ return { error: reason };
14
+ }
15
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/tools/access.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,oBAAoB,CAAC,GAAmB;IACtD,IAAI,CAAC,GAAG,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAA;IAC5B,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AAC5D,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAmB;IACrD,IAAI,CAAC,GAAG,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IAC3B,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AAC1B,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ import type { Payload } from 'payload';
3
+ import type { McpToolDefinition } from '@forumone/throughline-plugin-contract';
4
+ export interface GetIntegrationStatusDeps {
5
+ payload: Payload;
6
+ collectionSlug: string;
7
+ }
8
+ declare const inputSchema: z.ZodObject<{
9
+ integrationId: z.ZodString;
10
+ }, "strip", z.ZodTypeAny, {
11
+ integrationId: string;
12
+ }, {
13
+ integrationId: string;
14
+ }>;
15
+ export declare function createGetIntegrationStatusTool(deps: GetIntegrationStatusDeps): McpToolDefinition<typeof inputSchema>;
16
+ export {};
17
+ //# sourceMappingURL=get-integration-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-integration-status.d.ts","sourceRoot":"","sources":["../../src/tools/get-integration-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAG9E,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,QAAA,MAAM,WAAW;;;;;;EAIf,CAAA;AAEF,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,wBAAwB,GAC7B,iBAAiB,CAAC,OAAO,WAAW,CAAC,CAkCvC"}
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ import { deniedEnvelope, isIntegrationsReader } from './access.js';
3
+ const inputSchema = z.object({
4
+ integrationId: z
5
+ .string()
6
+ .describe('The ID of the integration instance (the row id from list_integrations).'),
7
+ });
8
+ export function createGetIntegrationStatusTool(deps) {
9
+ return {
10
+ name: 'get_integration_status',
11
+ description: 'Detailed status for a single integration instance — including its last sync time, last error, and current configuration metadata (config values themselves are admin-only and not returned).',
12
+ inputSchema,
13
+ handler: async (input, ctx) => {
14
+ if (!isIntegrationsReader(ctx)) {
15
+ return deniedEnvelope('Only admins and editors can view integration status.');
16
+ }
17
+ let doc = null;
18
+ try {
19
+ doc = (await deps.payload.findByID({
20
+ collection: deps.collectionSlug,
21
+ id: input.integrationId,
22
+ }));
23
+ }
24
+ catch {
25
+ doc = null;
26
+ }
27
+ if (!doc)
28
+ return { error: `No integration instance with id "${input.integrationId}".` };
29
+ return {
30
+ id: String(doc['id']),
31
+ name: String(doc['name']),
32
+ type: String(doc['integrationType']),
33
+ enabled: doc['enabled'] === true,
34
+ lastSyncAt: typeof doc['lastSyncAt'] === 'string' ? doc['lastSyncAt'] : null,
35
+ lastSyncStatus: typeof doc['lastSyncStatus'] === 'string' ? doc['lastSyncStatus'] : 'never-run',
36
+ lastError: typeof doc['lastError'] === 'string' ? doc['lastError'] : null,
37
+ };
38
+ },
39
+ };
40
+ }
41
+ //# sourceMappingURL=get-integration-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-integration-status.js","sourceRoot":"","sources":["../../src/tools/get-integration-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAOlE,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,aAAa,EAAE,CAAC;SACb,MAAM,EAAE;SACR,QAAQ,CAAC,yEAAyE,CAAC;CACvF,CAAC,CAAA;AAEF,MAAM,UAAU,8BAA8B,CAC5C,IAA8B;IAE9B,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EACT,8LAA8L;QAChM,WAAW;QACX,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC5B,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/B,OAAO,cAAc,CAAC,sDAAsD,CAAC,CAAA;YAC/E,CAAC;YAED,IAAI,GAAG,GAAmC,IAAI,CAAA;YAC9C,IAAI,CAAC;gBACH,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;oBACjC,UAAU,EAAE,IAAI,CAAC,cAAc;oBAC/B,EAAE,EAAE,KAAK,CAAC,aAAa;iBACxB,CAAC,CAAmC,CAAA;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,GAAG,GAAG,IAAI,CAAA;YACZ,CAAC;YAED,IAAI,CAAC,GAAG;gBAAE,OAAO,EAAE,KAAK,EAAE,oCAAoC,KAAK,CAAC,aAAa,IAAI,EAAE,CAAA;YAEvF,OAAO;gBACL,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACrB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACzB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;gBACpC,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,KAAK,IAAI;gBAChC,UAAU,EAAE,OAAO,GAAG,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;gBAC5E,cAAc,EAAE,OAAO,GAAG,CAAC,gBAAgB,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,WAAW;gBAC/F,SAAS,EAAE,OAAO,GAAG,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI;aAC1E,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ export { createListIntegrationsTool } from './list-integrations.js';
2
+ export type { ListIntegrationsDeps } from './list-integrations.js';
3
+ export { createGetIntegrationStatusTool } from './get-integration-status.js';
4
+ export type { GetIntegrationStatusDeps } from './get-integration-status.js';
5
+ export { createTriggerSyncTool } from './trigger-sync.js';
6
+ export type { TriggerSyncDeps } from './trigger-sync.js';
7
+ export { createTestIntegrationTool } from './test-integration.js';
8
+ export type { TestIntegrationDeps } from './test-integration.js';
9
+ export { createListIntegrationTypesTool } from './list-integration-types.js';
10
+ export type { ListIntegrationTypesDeps } from './list-integration-types.js';
11
+ export { isIntegrationsReader, isIntegrationsAdmin } from './access.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAA;AACnE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAA;AAC5E,YAAY,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA;AACjE,YAAY,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAA;AAC5E,YAAY,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AAC3E,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,7 @@
1
+ export { createListIntegrationsTool } from './list-integrations.js';
2
+ export { createGetIntegrationStatusTool } from './get-integration-status.js';
3
+ export { createTriggerSyncTool } from './trigger-sync.js';
4
+ export { createTestIntegrationTool } from './test-integration.js';
5
+ export { createListIntegrationTypesTool } from './list-integration-types.js';
6
+ export { isIntegrationsReader, isIntegrationsAdmin } from './access.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAA;AAEnE,OAAO,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAA;AAE5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAEzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA;AAEjE,OAAO,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAA;AAE5E,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,10 @@
1
+ import { z } from 'zod';
2
+ import type { McpToolDefinition } from '@forumone/throughline-plugin-contract';
3
+ import type { IntegrationRegistry } from '../registry.js';
4
+ export interface ListIntegrationTypesDeps {
5
+ registry: IntegrationRegistry;
6
+ }
7
+ declare const inputSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
8
+ export declare function createListIntegrationTypesTool(deps: ListIntegrationTypesDeps): McpToolDefinition<typeof inputSchema>;
9
+ export {};
10
+ //# sourceMappingURL=list-integration-types.d.ts.map