@slates/provider 1.0.0-rc.33 → 1.0.0-rc.35

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.
@@ -1,6 +1,12 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { z } from 'zod';
3
- import { SlateTool, SlateTrigger, SlateTriggerGroup, toolCategory } from '../action';
3
+ import {
4
+ optionList,
5
+ SlateTool,
6
+ SlateTrigger,
7
+ SlateTriggerGroup,
8
+ toolCategory
9
+ } from '../action';
4
10
  import { SlateAuth } from '../auth';
5
11
  import { SlateConfig } from '../config';
6
12
  import { Slate } from './slate';
@@ -37,12 +43,28 @@ let createManualWebhookTriggerGroup = (spec: ReturnType<typeof createTestSpec>,
37
43
  .routingMatchers(async () => [])
38
44
  .build();
39
45
 
40
- let createAutoWebhookTriggerGroup = (spec: ReturnType<typeof createTestSpec>, key: string) =>
46
+ let createAutoWebhookTriggerGroup = (
47
+ spec: ReturnType<typeof createTestSpec>,
48
+ key: string,
49
+ options: { deliveryConfig?: boolean } = {}
50
+ ) =>
41
51
  SlateTriggerGroup.create(spec, {
42
52
  key,
43
53
  name: key
44
54
  })
45
55
  .webhook({
56
+ deliveryConfig: options.deliveryConfig
57
+ ? {
58
+ userConfigSchema: z.object({}),
59
+ fullConfigSchema: z.object({}),
60
+ setup: async () => ({
61
+ webhookSetupDocument: '',
62
+ partialWebhookRegistrationPayload: {}
63
+ }),
64
+ finish: async () => ({ webhookRegistrationPayload: {} }),
65
+ authenticate: async () => ({ routingKeys: [] })
66
+ }
67
+ : undefined,
46
68
  autoRegistration: {
47
69
  webhookTargetList: async () => ({ targets: [], nextPageToken: null }),
48
70
  webhookRegister: async () => ({
@@ -70,10 +92,45 @@ describe('Slate.create', () => {
70
92
  triggerGroups: [first, second]
71
93
  })
72
94
  ).toThrow(
73
- 'Only one trigger group may use manual webhook registration, but found: manual_one, manual_two'
95
+ 'Only one trigger group may use manual webhook registration or a delivery config, but found: manual_one, manual_two'
74
96
  );
75
97
  });
76
98
 
99
+ it('rejects more than one trigger group with a manual registration or delivery config', () => {
100
+ let spec = createTestSpec();
101
+ let manual = createManualWebhookTriggerGroup(spec, 'manual_group');
102
+ let first = createAutoWebhookTriggerGroup(spec, 'config_one', { deliveryConfig: true });
103
+ let second = createAutoWebhookTriggerGroup(spec, 'config_two', { deliveryConfig: true });
104
+ let create = (triggerGroups: (typeof manual)[]) =>
105
+ Slate.create({ spec, tools: [], triggers: [], triggerGroups });
106
+
107
+ expect(() => create([first, second])).toThrow(
108
+ 'Only one trigger group may use manual webhook registration or a delivery config, but found: config_one, config_two'
109
+ );
110
+ expect(() => create([manual, first])).toThrow('but found: manual_group, config_one');
111
+ });
112
+
113
+ it('allows a single delivery config alongside plain automatic trigger groups', () => {
114
+ let spec = createTestSpec();
115
+ let configured = createAutoWebhookTriggerGroup(spec, 'config_group', {
116
+ deliveryConfig: true
117
+ });
118
+ let auto = createAutoWebhookTriggerGroup(spec, 'auto_group');
119
+
120
+ let slate = Slate.create({
121
+ spec,
122
+ tools: [],
123
+ triggers: [],
124
+ triggerGroups: [auto, configured, createAutoWebhookTriggerGroup(spec, 'other_auto')]
125
+ });
126
+
127
+ expect(slate.triggerGroups.map(group => group.key)).toEqual([
128
+ 'auto_group',
129
+ 'config_group',
130
+ 'other_auto'
131
+ ]);
132
+ });
133
+
77
134
  it('allows a single manual webhook trigger group alongside other trigger groups', () => {
78
135
  let spec = createTestSpec();
79
136
  let manual = createManualWebhookTriggerGroup(spec, 'manual_group');
@@ -269,3 +326,66 @@ describe('Slate.create tool categories', () => {
269
326
  ).toThrow('Tool category "crm" lists tool "stray", which is not registered in this slate');
270
327
  });
271
328
  });
329
+
330
+ describe('Slate.create option lists', () => {
331
+ it('keeps option lists in declared order with their params schema', () => {
332
+ let spec = createTestSpec();
333
+ let files = optionList(spec, {
334
+ key: 'files',
335
+ paramsSchema: z.object({ kind: z.string().optional() }),
336
+ list: async ctx => ({
337
+ options: [{ value: ctx.input.params.kind ?? 'any', label: 'Any' }]
338
+ })
339
+ });
340
+ let folders = optionList(spec, {
341
+ key: 'folders',
342
+ list: async () => ({ options: [] })
343
+ });
344
+
345
+ let slate = Slate.create({ spec, tools: [], triggers: [], optionLists: [files, folders] });
346
+
347
+ expect(slate.optionLists.map(list => list.key)).toEqual(['files', 'folders']);
348
+ expect(folders.paramsSchema.parse({})).toEqual({});
349
+ expect(files.paramsSchema.parse({ kind: 'sheet' })).toEqual({ kind: 'sheet' });
350
+ expect(Slate.create({ spec, tools: [], triggers: [] }).optionLists).toEqual([]);
351
+ });
352
+
353
+ it('rejects params with unknown keys for the default and custom params schemas', () => {
354
+ let spec = createTestSpec();
355
+ let list = async () => ({ options: [] });
356
+ let folders = optionList(spec, { key: 'folders', list });
357
+ let files = optionList(spec, {
358
+ key: 'files',
359
+ paramsSchema: z.object({ kind: z.string().optional() }),
360
+ list
361
+ });
362
+ let loose = optionList(spec, {
363
+ key: 'loose',
364
+ paramsSchema: z.object({ kind: z.string() }).passthrough(),
365
+ list
366
+ });
367
+
368
+ expect(folders.paramsSchema.safeParse({ kind: 'sheet' }).success).toBe(false);
369
+ expect(files.paramsSchema.safeParse({ knd: 'sheet' }).success).toBe(false);
370
+ expect(loose.paramsSchema.safeParse({ kind: 'sheet', extra: 1 }).success).toBe(false);
371
+ });
372
+
373
+ it('rejects a params schema that is not a zod object', () => {
374
+ expect(() =>
375
+ optionList(createTestSpec(), {
376
+ key: 'files',
377
+ paramsSchema: z.record(z.string(), z.string()) as any,
378
+ list: async () => ({ options: [] })
379
+ })
380
+ ).toThrow('Option list "files" params schema must be a zod object');
381
+ });
382
+
383
+ it('rejects duplicate option list keys', () => {
384
+ let spec = createTestSpec();
385
+ let list = () => optionList(spec, { key: 'files', list: async () => ({ options: [] }) });
386
+
387
+ expect(() =>
388
+ Slate.create({ spec, tools: [], triggers: [], optionLists: [list(), list()] })
389
+ ).toThrow('Option list "files" is registered more than once');
390
+ });
391
+ });
@@ -1,4 +1,10 @@
1
- import type { SlateTool, SlateToolCategory, SlateTrigger, SlateTriggerGroup } from '../action';
1
+ import type {
2
+ SlateOptionList,
3
+ SlateTool,
4
+ SlateToolCategory,
5
+ SlateTrigger,
6
+ SlateTriggerGroup
7
+ } from '../action';
2
8
  import type { SlateAdapter } from '../adapter';
3
9
  import { SlateDeclarationError } from '../error';
4
10
  import type { SlateSpecification } from './specification';
@@ -13,7 +19,8 @@ export class Slate<ConfigType extends {}, AuthType extends {}> {
13
19
  private readonly _adapters: SlateAdapter<ConfigType, AuthType>[],
14
20
  private readonly _triggerGroups: SlateTriggerGroup<ConfigType, AuthType>[],
15
21
  private readonly _toolCategories: SlateToolCategory[],
16
- private readonly _toolCategoryKeysByActionKey: Map<string, string[]>
22
+ private readonly _toolCategoryKeysByActionKey: Map<string, string[]>,
23
+ private readonly _optionLists: SlateOptionList<ConfigType, AuthType, any>[]
17
24
  ) {}
18
25
 
19
26
  static create<ConfigType extends {}, AuthType extends {}>(params: {
@@ -23,6 +30,7 @@ export class Slate<ConfigType extends {}, AuthType extends {}> {
23
30
  adapters?: SlateAdapter<ConfigType, AuthType>[];
24
31
  triggerGroups?: SlateTriggerGroup<ConfigType, AuthType>[];
25
32
  toolCategories?: SlateToolCategory[];
33
+ optionLists?: SlateOptionList<ConfigType, AuthType, any>[];
26
34
  }) {
27
35
  let adapters = params.adapters ?? [];
28
36
  let actions = [...params.triggers, ...params.tools];
@@ -63,17 +71,30 @@ export class Slate<ConfigType extends {}, AuthType extends {}> {
63
71
  seenTriggerGroupKeys.add(group.key);
64
72
  }
65
73
 
66
- let manualWebhookTriggerGroups = triggerGroups.filter(
67
- group => group.source === 'webhook' && group.webhook?.manualRegistration
74
+ let configuredWebhookTriggerGroups = triggerGroups.filter(
75
+ group =>
76
+ group.source === 'webhook' &&
77
+ (group.webhook?.manualRegistration || group.webhook?.deliveryConfig)
68
78
  );
69
- if (manualWebhookTriggerGroups.length > 1) {
79
+ if (configuredWebhookTriggerGroups.length > 1) {
70
80
  throw new SlateDeclarationError(
71
- `Only one trigger group may use manual webhook registration, but found: ${manualWebhookTriggerGroups
81
+ `Only one trigger group may use manual webhook registration or a delivery config, but found: ${configuredWebhookTriggerGroups
72
82
  .map(group => group.key)
73
83
  .join(', ')}`
74
84
  );
75
85
  }
76
86
 
87
+ let optionLists = params.optionLists ?? [];
88
+ let seenOptionListKeys = new Set<string>();
89
+ for (let list of optionLists) {
90
+ if (seenOptionListKeys.has(list.key)) {
91
+ throw new SlateDeclarationError(
92
+ `Option list "${list.key}" is registered more than once`
93
+ );
94
+ }
95
+ seenOptionListKeys.add(list.key);
96
+ }
97
+
77
98
  let toolCategories = params.toolCategories ?? [];
78
99
  let toolCategoryKeysByActionKey = resolveToolCategoryMembership(toolCategories, actions);
79
100
 
@@ -83,7 +104,8 @@ export class Slate<ConfigType extends {}, AuthType extends {}> {
83
104
  adapters,
84
105
  triggerGroups,
85
106
  toolCategories,
86
- toolCategoryKeysByActionKey
107
+ toolCategoryKeysByActionKey,
108
+ optionLists
87
109
  );
88
110
  }
89
111
 
@@ -107,6 +129,10 @@ export class Slate<ConfigType extends {}, AuthType extends {}> {
107
129
  return this._toolCategories ?? [];
108
130
  }
109
131
 
132
+ get optionLists() {
133
+ return this._optionLists ?? [];
134
+ }
135
+
110
136
  // Keys of the tool categories an action belongs to, in declared category order.
111
137
  getToolCategoryKeys(actionKey: string) {
112
138
  return [...(this._toolCategoryKeysByActionKey?.get(actionKey) ?? [])];