@slates/provider 1.0.0-rc.2 → 1.0.0-rc.20

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 (124) hide show
  1. package/dist/index.cjs +2847 -2
  2. package/dist/index.d.cts +1019 -0
  3. package/dist/index.d.ts +1019 -9
  4. package/dist/index.module.js +2737 -2
  5. package/package.json +9 -6
  6. package/src/action/action.ts +92 -4
  7. package/src/action/attachment.ts +87 -0
  8. package/src/action/builder.test.ts +136 -0
  9. package/src/action/builder.ts +113 -15
  10. package/src/action/directUpload.ts +59 -0
  11. package/src/action/getFileUrlTool.ts +41 -0
  12. package/src/action/index.ts +4 -2
  13. package/src/action/publicTool.ts +36 -0
  14. package/src/action/scopes.test.ts +76 -0
  15. package/src/action/scopes.ts +103 -0
  16. package/src/action/tool.ts +22 -6
  17. package/src/action/trigger.ts +19 -11
  18. package/src/adapter/adapter.ts +38 -0
  19. package/src/adapter/index.test.ts +322 -0
  20. package/src/adapter/index.ts +2 -0
  21. package/src/adapter/spec.ts +154 -0
  22. package/src/auth/auth.ts +3 -3
  23. package/src/auth/index.ts +2 -0
  24. package/src/auth/oauth.test.ts +83 -0
  25. package/src/auth/oauth.ts +150 -0
  26. package/src/auth/redact.test.ts +103 -0
  27. package/src/auth/redact.ts +111 -0
  28. package/src/auth/types.ts +72 -13
  29. package/src/axios/http.test.ts +83 -0
  30. package/src/axios/index.ts +116 -10
  31. package/src/axios/trace.test.ts +377 -0
  32. package/src/axios/trace.ts +803 -0
  33. package/src/config/config.ts +14 -3
  34. package/src/context/context.ts +264 -17
  35. package/src/context/hook.test.ts +46 -0
  36. package/src/context/hook.ts +15 -6
  37. package/src/context/index.ts +1 -0
  38. package/src/context/pQueue.ts +4 -0
  39. package/src/crypto/hmac.test.ts +40 -0
  40. package/src/crypto/hmac.ts +32 -0
  41. package/src/crypto/index.ts +1 -0
  42. package/src/docs.ts +22 -0
  43. package/src/error/api.test.ts +114 -0
  44. package/src/error/api.ts +236 -0
  45. package/src/error/axios.ts +220 -0
  46. package/src/error/base.test.ts +178 -0
  47. package/src/error/base.ts +108 -2
  48. package/src/error/declaration.ts +7 -2
  49. package/src/error/defaults.ts +50 -0
  50. package/src/error/index.ts +4 -0
  51. package/src/error/service.ts +170 -0
  52. package/src/error/types.ts +75 -0
  53. package/src/error/utils.ts +157 -0
  54. package/src/file/index.test.ts +36 -0
  55. package/src/file/index.ts +27 -0
  56. package/src/index.ts +8 -0
  57. package/src/logger/logger.test.ts +57 -0
  58. package/src/logger/logger.ts +72 -11
  59. package/src/module/resolveDefaultExport.test.ts +32 -0
  60. package/src/module/resolveDefaultExport.ts +14 -0
  61. package/src/object/index.test.ts +38 -0
  62. package/src/object/index.ts +12 -0
  63. package/src/specification/slate.ts +40 -4
  64. package/src/specification/specification.ts +10 -6
  65. package/src/webhook/graph.test.ts +37 -0
  66. package/src/webhook/graph.ts +22 -0
  67. package/src/webhook/index.ts +2 -0
  68. package/src/webhook/meta.test.ts +52 -0
  69. package/src/webhook/meta.ts +43 -0
  70. package/dist/action/action.d.ts +0 -98
  71. package/dist/action/action.d.ts.map +0 -1
  72. package/dist/action/builder.d.ts +0 -27
  73. package/dist/action/builder.d.ts.map +0 -1
  74. package/dist/action/index.d.ts +0 -5
  75. package/dist/action/index.d.ts.map +0 -1
  76. package/dist/action/tool.d.ts +0 -14
  77. package/dist/action/tool.d.ts.map +0 -1
  78. package/dist/action/trigger.d.ts +0 -32
  79. package/dist/action/trigger.d.ts.map +0 -1
  80. package/dist/auth/auth.d.ts +0 -30
  81. package/dist/auth/auth.d.ts.map +0 -1
  82. package/dist/auth/index.d.ts +0 -3
  83. package/dist/auth/index.d.ts.map +0 -1
  84. package/dist/auth/types.d.ts +0 -148
  85. package/dist/auth/types.d.ts.map +0 -1
  86. package/dist/axios/index.d.ts +0 -4
  87. package/dist/axios/index.d.ts.map +0 -1
  88. package/dist/config/config.d.ts +0 -26
  89. package/dist/config/config.d.ts.map +0 -1
  90. package/dist/config/index.d.ts +0 -2
  91. package/dist/config/index.d.ts.map +0 -1
  92. package/dist/context/context.d.ts +0 -20
  93. package/dist/context/context.d.ts.map +0 -1
  94. package/dist/context/hook.d.ts +0 -4
  95. package/dist/context/hook.d.ts.map +0 -1
  96. package/dist/context/index.d.ts +0 -2
  97. package/dist/context/index.d.ts.map +0 -1
  98. package/dist/error/base.d.ts +0 -5
  99. package/dist/error/base.d.ts.map +0 -1
  100. package/dist/error/declaration.d.ts +0 -6
  101. package/dist/error/declaration.d.ts.map +0 -1
  102. package/dist/error/index.d.ts +0 -3
  103. package/dist/error/index.d.ts.map +0 -1
  104. package/dist/index.cjs.map +0 -1
  105. package/dist/index.d.ts.map +0 -1
  106. package/dist/index.modern.js +0 -2
  107. package/dist/index.modern.js.map +0 -1
  108. package/dist/index.module.js.map +0 -1
  109. package/dist/index.umd.js +0 -2
  110. package/dist/index.umd.js.map +0 -1
  111. package/dist/logger/index.d.ts +0 -2
  112. package/dist/logger/index.d.ts.map +0 -1
  113. package/dist/logger/logger.d.ts +0 -20
  114. package/dist/logger/logger.d.ts.map +0 -1
  115. package/dist/specification/index.d.ts +0 -3
  116. package/dist/specification/index.d.ts.map +0 -1
  117. package/dist/specification/slate.d.ts +0 -15
  118. package/dist/specification/slate.d.ts.map +0 -1
  119. package/dist/specification/specification.d.ts +0 -30
  120. package/dist/specification/specification.d.ts.map +0 -1
  121. package/dist/specification/zero.d.ts +0 -13
  122. package/dist/specification/zero.d.ts.map +0 -1
  123. package/dist/tokens.d.ts +0 -34
  124. package/dist/tokens.d.ts.map +0 -1
@@ -0,0 +1,57 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { type SlateLogEntry, SlateLogger } from './logger';
3
+
4
+ let flushLogs = async () => {
5
+ await new Promise(resolve => setTimeout(resolve, 20));
6
+ };
7
+
8
+ describe('SlateLogger', () => {
9
+ it('keeps a readable message and structured data for object logs', async () => {
10
+ let entries: SlateLogEntry[] = [];
11
+ let logger = new SlateLogger([
12
+ batch => {
13
+ entries.push(...batch);
14
+ }
15
+ ]);
16
+
17
+ logger.info({
18
+ message: 'Invoking tool action',
19
+ providerId: 'demo-slate',
20
+ actionId: 'echo',
21
+ phase: 'start'
22
+ });
23
+
24
+ await flushLogs();
25
+
26
+ expect(entries[0]).toMatchObject({
27
+ type: 'info',
28
+ message: 'Invoking tool action',
29
+ data: {
30
+ providerId: 'demo-slate',
31
+ actionId: 'echo',
32
+ phase: 'start'
33
+ }
34
+ });
35
+ });
36
+
37
+ it('captures error details as structured metadata', async () => {
38
+ let entries: SlateLogEntry[] = [];
39
+ let logger = new SlateLogger([
40
+ batch => {
41
+ entries.push(...batch);
42
+ }
43
+ ]);
44
+
45
+ logger.error(new Error('boom'));
46
+
47
+ await flushLogs();
48
+
49
+ expect(entries[0]?.message).toBe('Error: boom');
50
+ expect(entries[0]?.data).toMatchObject({
51
+ error: {
52
+ name: 'Error',
53
+ message: 'boom'
54
+ }
55
+ });
56
+ });
57
+ });
@@ -1,6 +1,7 @@
1
1
  import { encode } from '@toon-format/toon';
2
2
 
3
3
  export type SlateLogType = 'info' | 'warn' | 'error' | 'progress';
4
+ export type SlateLogFields = Record<string, unknown>;
4
5
 
5
6
  export type SlateLogMessageInput =
6
7
  | string
@@ -12,22 +13,79 @@ export type SlateLogMessageInput =
12
13
  | object
13
14
  | SlateLogMessageInput[];
14
15
 
15
- let formatMessage = (message: SlateLogMessageInput): string => {
16
+ let isRecord = (value: unknown): value is Record<string, unknown> =>
17
+ typeof value === 'object' && value !== null && !Array.isArray(value);
18
+
19
+ let normalizeMessage = (
20
+ message: SlateLogMessageInput
21
+ ): {
22
+ message: string;
23
+ data?: SlateLogFields;
24
+ } => {
25
+ if (Array.isArray(message)) {
26
+ let parts = message.map(part => normalizeMessage(part));
27
+ let combinedMessage = parts
28
+ .map(part => part.message)
29
+ .filter(Boolean)
30
+ .join(' ')
31
+ .trim();
32
+
33
+ let dataParts = parts
34
+ .map(part => {
35
+ if (part.data) {
36
+ return {
37
+ message: part.message,
38
+ ...part.data
39
+ };
40
+ }
41
+
42
+ return part.message;
43
+ })
44
+ .filter(Boolean);
45
+
46
+ return {
47
+ message: combinedMessage || 'Log entry',
48
+ data: dataParts.length > 0 ? { parts: dataParts } : undefined
49
+ };
50
+ }
51
+
16
52
  if (typeof message === 'object') {
17
- if (message === null) return 'null';
53
+ if (message === null) return { message: 'null' };
18
54
  if (message instanceof Error) {
19
- return `[ERROR]: ${message.name}: ${message.message}\n${message.stack}`;
55
+ return {
56
+ message: `${message.name}: ${message.message}`,
57
+ data: {
58
+ error: {
59
+ name: message.name,
60
+ message: message.message,
61
+ stack: message.stack
62
+ }
63
+ }
64
+ };
20
65
  }
21
- return encode(message);
66
+
67
+ if (isRecord(message) && typeof message.message === 'string') {
68
+ let { message: text, ...data } = message;
69
+ return {
70
+ message: text || 'Log entry',
71
+ data: Object.keys(data).length > 0 ? data : undefined
72
+ };
73
+ }
74
+
75
+ return {
76
+ message: encode(message),
77
+ data: isRecord(message) ? message : undefined
78
+ };
22
79
  }
23
80
 
24
- return String(message);
81
+ return { message: String(message) };
25
82
  };
26
83
 
27
84
  export interface SlateLogEntry {
28
85
  type: SlateLogType;
29
86
  message: string;
30
87
  timestamp: Date;
88
+ data?: SlateLogFields;
31
89
  }
32
90
 
33
91
  export type SlateLogListener = (entries: SlateLogEntry[]) => void;
@@ -44,9 +102,10 @@ export class SlateLogger {
44
102
  private flush() {
45
103
  if (this.#timer) clearTimeout(this.#timer);
46
104
  if (this.#logs.length === 0) return;
105
+ let logs = this.#logs.splice(0);
47
106
 
48
107
  for (let listener of this.#listeners) {
49
- listener(this.#logs);
108
+ listener(logs);
50
109
  }
51
110
  }
52
111
 
@@ -59,27 +118,29 @@ export class SlateLogger {
59
118
  }
60
119
 
61
120
  log(type: SlateLogType, message: SlateLogMessageInput) {
121
+ let normalized = normalizeMessage(message);
62
122
  this.#logs.push({
63
123
  type,
64
124
  timestamp: new Date(),
65
- message: formatMessage(message)
125
+ message: normalized.message,
126
+ data: normalized.data
66
127
  });
67
128
  this.scheduleFlush();
68
129
  }
69
130
 
70
131
  info(...message: SlateLogMessageInput[]) {
71
- this.log('info', message.flat());
132
+ this.log('info', message.length === 1 ? message[0]! : message.flat());
72
133
  }
73
134
 
74
135
  warn(...message: SlateLogMessageInput[]) {
75
- this.log('warn', message.flat());
136
+ this.log('warn', message.length === 1 ? message[0]! : message.flat());
76
137
  }
77
138
 
78
139
  error(...message: SlateLogMessageInput[]) {
79
- this.log('error', message.flat());
140
+ this.log('error', message.length === 1 ? message[0]! : message.flat());
80
141
  }
81
142
 
82
143
  progress(...message: SlateLogMessageInput[]) {
83
- this.log('progress', message.flat());
144
+ this.log('progress', message.length === 1 ? message[0]! : message.flat());
84
145
  }
85
146
  }
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { resolveDefaultExport } from './resolveDefaultExport';
3
+
4
+ class Example {
5
+ value: number;
6
+ constructor(value: number) {
7
+ this.value = value;
8
+ }
9
+ }
10
+
11
+ describe('resolveDefaultExport', () => {
12
+ it('returns a constructor export as-is', () => {
13
+ expect(resolveDefaultExport<typeof Example>(Example)).toBe(Example);
14
+ expect(new (resolveDefaultExport<typeof Example>(Example))(1).value).toBe(1);
15
+ });
16
+
17
+ it('unwraps CJS default-export objects produced by bundlers', () => {
18
+ expect(resolveDefaultExport<typeof Example>({ default: Example })).toBe(Example);
19
+ });
20
+
21
+ it('unwraps nested default exports from double-bundling', () => {
22
+ expect(
23
+ resolveDefaultExport<typeof Example>({ default: { default: Example } })
24
+ ).toBe(Example);
25
+ });
26
+
27
+ it('throws when the export is not a constructor', () => {
28
+ expect(() => resolveDefaultExport({})).toThrow(TypeError);
29
+ expect(() => resolveDefaultExport({ default: { foo: 1 } })).toThrow(TypeError);
30
+ expect(() => resolveDefaultExport(undefined)).toThrow(TypeError);
31
+ });
32
+ });
@@ -0,0 +1,14 @@
1
+ export let resolveDefaultExport = <T>(value: unknown): T => {
2
+ let current = value;
3
+
4
+ for (let i = 0; i < 4; i++) {
5
+ if (typeof current === 'function') return current as T;
6
+ if (current && typeof current === 'object' && 'default' in current) {
7
+ current = (current as { default: unknown }).default;
8
+ continue;
9
+ }
10
+ break;
11
+ }
12
+
13
+ throw new TypeError('Module export is not a constructor');
14
+ };
@@ -0,0 +1,38 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { pickDefined, setIfDefined } from './index';
3
+
4
+ describe('object helpers', () => {
5
+ it('omits only undefined values', () => {
6
+ expect(
7
+ pickDefined({
8
+ keepNull: null,
9
+ keepFalse: false,
10
+ keepZero: 0,
11
+ keepEmpty: '',
12
+ dropUndefined: undefined
13
+ })
14
+ ).toEqual({
15
+ keepNull: null,
16
+ keepFalse: false,
17
+ keepZero: 0,
18
+ keepEmpty: ''
19
+ });
20
+ });
21
+
22
+ it('sets only defined values on an existing target', () => {
23
+ let target: Record<string, unknown> = {};
24
+
25
+ setIfDefined(target, 'nullValue', null);
26
+ setIfDefined(target, 'falseValue', false);
27
+ setIfDefined(target, 'zeroValue', 0);
28
+ setIfDefined(target, 'emptyValue', '');
29
+ setIfDefined(target, 'undefinedValue', undefined);
30
+
31
+ expect(target).toEqual({
32
+ nullValue: null,
33
+ falseValue: false,
34
+ zeroValue: 0,
35
+ emptyValue: ''
36
+ });
37
+ });
38
+ });
@@ -0,0 +1,12 @@
1
+ export let pickDefined = <T extends object>(value: T) =>
2
+ Object.fromEntries(
3
+ Object.entries(value).filter(([, child]) => child !== undefined)
4
+ ) as Partial<T> & Record<string, unknown>;
5
+
6
+ export let setIfDefined = (target: Record<string, unknown>, key: string, value: unknown) => {
7
+ if (value !== undefined) {
8
+ target[key] = value;
9
+ }
10
+
11
+ return target;
12
+ };
@@ -1,5 +1,7 @@
1
- import { SlateTool, SlateTrigger } from '../action';
2
- import { SlateSpecification } from './specification';
1
+ import type { SlateTool, SlateTrigger } from '../action';
2
+ import type { SlateAdapter } from '../adapter';
3
+ import { SlateDeclarationError } from '../error';
4
+ import type { SlateSpecification } from './specification';
3
5
 
4
6
  export class Slate<ConfigType extends {}, AuthType extends {}> {
5
7
  private constructor(
@@ -7,15 +9,45 @@ export class Slate<ConfigType extends {}, AuthType extends {}> {
7
9
  private readonly _actions: (
8
10
  | SlateTrigger<ConfigType, AuthType, any, any>
9
11
  | SlateTool<ConfigType, AuthType, any, any>
10
- )[]
12
+ )[],
13
+ private readonly _adapters: SlateAdapter<ConfigType, AuthType>[]
11
14
  ) {}
12
15
 
13
16
  static create<ConfigType extends {}, AuthType extends {}>(params: {
14
17
  spec: SlateSpecification<ConfigType, AuthType>;
15
18
  triggers: SlateTrigger<ConfigType, AuthType, any, any>[];
16
19
  tools: SlateTool<ConfigType, AuthType, any, any>[];
20
+ adapters?: SlateAdapter<ConfigType, AuthType>[];
17
21
  }) {
18
- return new Slate(params.spec, [...params.triggers, ...params.tools]);
22
+ let adapters = params.adapters ?? [];
23
+ let actions = [...params.triggers, ...params.tools];
24
+ let seenAdapterIds = new Set<string>();
25
+ let seenActionKeys = new Map(actions.map(action => [action.key, action]));
26
+
27
+ for (let adapter of adapters) {
28
+ if (seenAdapterIds.has(adapter.id)) {
29
+ throw new SlateDeclarationError(
30
+ `Adapter "${adapter.id}" is registered more than once`
31
+ );
32
+ }
33
+ seenAdapterIds.add(adapter.id);
34
+
35
+ for (let action of adapter.actions) {
36
+ let existing = seenActionKeys.get(action.key);
37
+ if (existing && existing !== action) {
38
+ throw new SlateDeclarationError(
39
+ `Adapter "${adapter.id}" action "${action.key}" conflicts with another action`
40
+ );
41
+ }
42
+
43
+ if (!existing) {
44
+ actions.push(action);
45
+ seenActionKeys.set(action.key, action);
46
+ }
47
+ }
48
+ }
49
+
50
+ return new Slate(params.spec, actions, adapters);
19
51
  }
20
52
 
21
53
  get spec() {
@@ -25,4 +57,8 @@ export class Slate<ConfigType extends {}, AuthType extends {}> {
25
57
  get actions() {
26
58
  return this._actions;
27
59
  }
60
+
61
+ get adapters() {
62
+ return this._adapters;
63
+ }
28
64
  }
@@ -1,18 +1,18 @@
1
1
  import z from 'zod';
2
- import { SlateAuth } from '../auth';
3
- import { SlateConfig } from '../config';
2
+ import type { SlateAuth } from '../auth';
3
+ import type { SlateConfig } from '../config';
4
+ import type { SlateProviderDocsReference } from '../docs';
4
5
 
5
6
  export interface SlateSpecificationParameters {
6
7
  key: string;
7
8
  name: string;
8
9
  description?: string;
10
+ docs?: SlateProviderDocsReference[];
9
11
  metadata?: Record<string, any>;
10
12
  }
11
13
 
12
- export interface SlateSpecificationCreateParameters<
13
- ConfigType extends {},
14
- AuthType extends {}
15
- > extends SlateSpecificationParameters {
14
+ export interface SlateSpecificationCreateParameters<ConfigType extends {}, AuthType extends {}>
15
+ extends SlateSpecificationParameters {
16
16
  config: SlateConfig<ConfigType>;
17
17
  auth: SlateAuth<AuthType>;
18
18
  }
@@ -63,6 +63,10 @@ export class SlateSpecification<ConfigType extends {}, AuthType extends {}> {
63
63
  get description() {
64
64
  return this._params.description;
65
65
  }
66
+
67
+ get docs() {
68
+ return this._params.docs;
69
+ }
66
70
  }
67
71
 
68
72
  export let spec = <ConfigType extends {}, AuthType extends {}>(
@@ -0,0 +1,37 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getGraphWebhookValidationResponse, graphWebhookHttp } from './graph';
3
+
4
+ describe('Microsoft Graph webhook validation', () => {
5
+ it('makes only validation requests synchronous', () => {
6
+ expect(graphWebhookHttp).toEqual({
7
+ methods: ['POST'],
8
+ sync: {
9
+ mode: 'match',
10
+ match: [{ hasQueryParam: 'validationToken' }]
11
+ }
12
+ });
13
+ });
14
+
15
+ it('echoes the URL-decoded validation token as plain text', () => {
16
+ let response = getGraphWebhookValidationResponse(
17
+ new Request(
18
+ 'https://example.com/webhook?validationToken=Validation%3a+Testing+client+application+reachability',
19
+ { method: 'POST' }
20
+ )
21
+ );
22
+
23
+ expect(response).toEqual({
24
+ status: 200,
25
+ headers: { 'content-type': 'text/plain' },
26
+ body: 'Validation: Testing client application reachability'
27
+ });
28
+ });
29
+
30
+ it('ignores ordinary change notifications', () => {
31
+ expect(
32
+ getGraphWebhookValidationResponse(
33
+ new Request('https://example.com/webhook', { method: 'POST' })
34
+ )
35
+ ).toBeNull();
36
+ });
37
+ });
@@ -0,0 +1,22 @@
1
+ import type { SlateWebhookHttpOptions, SlateWebhookHttpResponseInit } from '../action';
2
+
3
+ export const graphWebhookHttp = {
4
+ methods: ['POST'],
5
+ sync: {
6
+ mode: 'match',
7
+ match: [{ hasQueryParam: 'validationToken' }]
8
+ }
9
+ } satisfies SlateWebhookHttpOptions;
10
+
11
+ export let getGraphWebhookValidationResponse = (
12
+ request: Request
13
+ ): SlateWebhookHttpResponseInit | null => {
14
+ let validationToken = new URL(request.url).searchParams.get('validationToken');
15
+ if (validationToken === null) return null;
16
+
17
+ return {
18
+ status: 200,
19
+ headers: { 'content-type': 'text/plain' },
20
+ body: validationToken
21
+ };
22
+ };
@@ -0,0 +1,2 @@
1
+ export * from './graph';
2
+ export * from './meta';
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getMetaWebhookVerificationResponse, metaWebhookHttp } from './meta';
3
+
4
+ let verificationRequest = (verifyToken = 'verify-token', challenge = 'challenge-value') =>
5
+ new Request(
6
+ `https://example.com/webhook?hub.mode=subscribe&hub.verify_token=${verifyToken}&hub.challenge=${challenge}`
7
+ );
8
+
9
+ describe('Meta webhook verification', () => {
10
+ it('makes only verification GET requests synchronous', () => {
11
+ expect(metaWebhookHttp).toEqual({
12
+ methods: ['GET', 'POST'],
13
+ sync: {
14
+ mode: 'match',
15
+ match: [{ method: 'GET', hasQueryParam: 'hub.mode' }]
16
+ }
17
+ });
18
+ });
19
+
20
+ it('returns the challenge for a matching configured token', () => {
21
+ expect(getMetaWebhookVerificationResponse(verificationRequest(), 'verify-token')).toEqual({
22
+ status: 200,
23
+ headers: { 'content-type': 'text/plain' },
24
+ body: 'challenge-value'
25
+ });
26
+ });
27
+
28
+ it('rejects a mismatched configured token', () => {
29
+ expect(
30
+ getMetaWebhookVerificationResponse(verificationRequest('wrong-token'), 'verify-token')
31
+ ).toMatchObject({ status: 403 });
32
+ expect(
33
+ getMetaWebhookVerificationResponse(verificationRequest('wrong-token'), '')
34
+ ).toMatchObject({ status: 403 });
35
+ });
36
+
37
+ it('preserves legacy verification when no token is configured', () => {
38
+ expect(
39
+ getMetaWebhookVerificationResponse(verificationRequest('legacy-token'), undefined)
40
+ ).toMatchObject({ status: 200, body: 'challenge-value' });
41
+ });
42
+
43
+ it('rejects malformed verification and ignores POST events', () => {
44
+ let malformed = new Request('https://example.com/webhook?hub.mode=subscribe');
45
+ let post = new Request('https://example.com/webhook', { method: 'POST' });
46
+
47
+ expect(getMetaWebhookVerificationResponse(malformed, undefined)).toMatchObject({
48
+ status: 400
49
+ });
50
+ expect(getMetaWebhookVerificationResponse(post, undefined)).toBeNull();
51
+ });
52
+ });
@@ -0,0 +1,43 @@
1
+ import type { SlateWebhookHttpOptions, SlateWebhookHttpResponseInit } from '../action';
2
+
3
+ export const metaWebhookHttp = {
4
+ methods: ['GET', 'POST'],
5
+ sync: {
6
+ mode: 'match',
7
+ match: [{ method: 'GET', hasQueryParam: 'hub.mode' }]
8
+ }
9
+ } satisfies SlateWebhookHttpOptions;
10
+
11
+ export let getMetaWebhookVerificationResponse = (
12
+ request: Request,
13
+ expectedVerifyToken: string | undefined
14
+ ): SlateWebhookHttpResponseInit | null => {
15
+ if (request.method !== 'GET') return null;
16
+
17
+ let url = new URL(request.url);
18
+ let mode = url.searchParams.get('hub.mode');
19
+ let verifyToken = url.searchParams.get('hub.verify_token');
20
+ let challenge = url.searchParams.get('hub.challenge');
21
+
22
+ if (mode !== 'subscribe' || challenge === null) {
23
+ return {
24
+ status: 400,
25
+ headers: { 'content-type': 'text/plain' },
26
+ body: 'invalid webhook verification request'
27
+ };
28
+ }
29
+
30
+ if (expectedVerifyToken !== undefined && verifyToken !== expectedVerifyToken) {
31
+ return {
32
+ status: 403,
33
+ headers: { 'content-type': 'text/plain' },
34
+ body: 'invalid verify token'
35
+ };
36
+ }
37
+
38
+ return {
39
+ status: 200,
40
+ headers: { 'content-type': 'text/plain' },
41
+ body: challenge
42
+ };
43
+ };
@@ -1,98 +0,0 @@
1
- import { z } from 'zod';
2
- import { SlateContext } from '../context';
3
- import { SlateSpecification } from '../specification/specification';
4
- export type SlateActionType = 'tool' | 'trigger';
5
- export interface SlateActionParameters {
6
- key: string;
7
- name: string;
8
- description?: string;
9
- instructions?: string[];
10
- constraints?: string[];
11
- tags?: {
12
- destructive?: boolean;
13
- readOnly?: boolean;
14
- [key: string]: boolean | undefined;
15
- };
16
- metadata?: Record<string, any>;
17
- }
18
- export type SlateToolInvocationHandler<ConfigType extends {}, AuthType extends {}, InputType extends {}, OutputType extends {}> = (context: SlateContext<ConfigType, AuthType, InputType>) => Promise<{
19
- output: OutputType;
20
- message: string;
21
- }>;
22
- export type SlateTriggerMappingHandler<ConfigType extends {}, AuthType extends {}, InputType extends {}, OutputType extends {}> = (context: SlateContext<ConfigType, AuthType, InputType>) => Promise<{
23
- type: string;
24
- id: string;
25
- output: OutputType;
26
- }>;
27
- export type SlateTriggerPollingHandler<ConfigType extends {}, AuthType extends {}, InputType extends {}> = (context: SlateContext<ConfigType, AuthType, {
28
- state: any | null;
29
- }>) => Promise<{
30
- inputs: InputType[];
31
- updatedState?: any;
32
- }>;
33
- export type SlateTriggerWebhookRequestHandler<ConfigType extends {}, AuthType extends {}, InputType extends {}> = (context: SlateContext<ConfigType, AuthType, {
34
- request: Request;
35
- state: any | null;
36
- }>) => Promise<{
37
- inputs: InputType[];
38
- updatedState?: any;
39
- }>;
40
- export type SlateTriggerWebhookAutoRegistrationHandler<ConfigType extends {}, AuthType extends {}> = (context: SlateContext<ConfigType, AuthType, {
41
- webhookBaseUrl: string;
42
- }>) => Promise<{
43
- registrationDetails: any;
44
- state?: any;
45
- }>;
46
- export type SlateTriggerWebhookAutoUnregistrationHandler<ConfigType extends {}, AuthType extends {}> = (context: SlateContext<ConfigType, AuthType, {
47
- webhookBaseUrl: string;
48
- registrationDetails: any;
49
- state: any | null;
50
- }>) => Promise<unknown>;
51
- export interface SlateActionParametersTool<ConfigType extends {}, AuthType extends {}, InputType extends {}, OutputType extends {}> {
52
- type: 'tool';
53
- handleInvocation: SlateToolInvocationHandler<ConfigType, AuthType, InputType, OutputType>;
54
- }
55
- export interface SlatePollingOptions {
56
- intervalInSeconds?: number;
57
- }
58
- export interface SlateActionParametersTrigger<ConfigType extends {}, AuthType extends {}, InputType extends {}, OutputType extends {}> {
59
- type: 'trigger';
60
- source: 'polling' | 'webhook';
61
- polling?: SlatePollingOptions;
62
- handleEvent: SlateTriggerMappingHandler<ConfigType, AuthType, InputType, OutputType>;
63
- handleRequest?: SlateTriggerWebhookRequestHandler<ConfigType, AuthType, InputType>;
64
- pollEvents?: SlateTriggerPollingHandler<ConfigType, AuthType, InputType>;
65
- autoRegisterWebhook?: SlateTriggerWebhookAutoRegistrationHandler<ConfigType, AuthType>;
66
- autoUnregisterWebhook?: SlateTriggerWebhookAutoUnregistrationHandler<ConfigType, AuthType>;
67
- }
68
- export type SlateActionParametersAny<ConfigType extends {}, AuthType extends {}, InputType extends {}, OutputType extends {}> = SlateActionParametersTool<ConfigType, AuthType, InputType, OutputType> | SlateActionParametersTrigger<ConfigType, AuthType, InputType, OutputType>;
69
- export type SlateActionCreateParameters<ConfigType extends {}, AuthType extends {}, InputType extends {}, OutputType extends {}> = SlateActionParametersAny<ConfigType, AuthType, InputType, OutputType> & SlateActionParameters & {
70
- configSchema: z.ZodType<ConfigType>;
71
- authSchema: z.ZodType<AuthType>;
72
- inputSchema: z.ZodType<InputType>;
73
- outputSchema: z.ZodType<OutputType>;
74
- };
75
- export declare abstract class SlateAction<Type extends SlateActionType, ConfigType extends {}, AuthType extends {}, InputType extends {}, OutputType extends {}> {
76
- readonly type: Type;
77
- protected readonly _spec: SlateSpecification<ConfigType, AuthType>;
78
- protected readonly _inputSchema: z.ZodType<InputType>;
79
- protected readonly _outputSchema: z.ZodType<OutputType>;
80
- protected readonly _params: SlateActionParameters;
81
- constructor(type: Type, _spec: SlateSpecification<ConfigType, AuthType>, _inputSchema: z.ZodType<InputType>, _outputSchema: z.ZodType<OutputType>, _params: SlateActionParameters);
82
- get configSchema(): z.ZodType<ConfigType, unknown, z.core.$ZodTypeInternals<ConfigType, unknown>>;
83
- get inputSchema(): z.ZodType<InputType, unknown, z.core.$ZodTypeInternals<InputType, unknown>>;
84
- get outputSchema(): z.ZodType<OutputType, unknown, z.core.$ZodTypeInternals<OutputType, unknown>>;
85
- get parameters(): SlateActionParameters;
86
- get key(): string;
87
- get name(): string;
88
- get description(): string | undefined;
89
- get tags(): {
90
- [key: string]: boolean | undefined;
91
- destructive?: boolean;
92
- readOnly?: boolean;
93
- } | undefined;
94
- get instructions(): string[] | undefined;
95
- get constraints(): string[] | undefined;
96
- get metadata(): Record<string, any> | undefined;
97
- }
98
- //# sourceMappingURL=action.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/action/action.ts"],"names":[],"mappings":"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"}