@optimizely-opal/opal-tools-sdk 0.1.3-dev → 0.1.6-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/models.js CHANGED
@@ -1,48 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IslandResponse = exports.IslandResponseConfig = exports.IslandConfig = exports.IslandAction = exports.IslandField = exports.Function = exports.AuthRequirement = exports.Parameter = exports.ParameterType = void 0;
3
+ exports.Parameter = exports.IslandResponse = exports.IslandResponseConfig = exports.IslandConfig = exports.IslandField = exports.IslandAction = exports.Function = exports.AuthRequirement = exports.ParameterType = void 0;
4
4
  /**
5
5
  * Types of parameters supported by Opal tools
6
6
  */
7
7
  var ParameterType;
8
8
  (function (ParameterType) {
9
- ParameterType["String"] = "string";
10
- ParameterType["Integer"] = "integer";
11
- ParameterType["Number"] = "number";
12
9
  ParameterType["Boolean"] = "boolean";
13
- ParameterType["List"] = "array";
14
10
  ParameterType["Dictionary"] = "object";
11
+ ParameterType["Integer"] = "integer";
12
+ ParameterType["List"] = "array";
13
+ ParameterType["Number"] = "number";
14
+ ParameterType["String"] = "string";
15
15
  })(ParameterType || (exports.ParameterType = ParameterType = {}));
16
- /**
17
- * Parameter definition for an Opal tool
18
- */
19
- class Parameter {
20
- /**
21
- * Create a new parameter definition
22
- * @param name Parameter name
23
- * @param type Parameter type
24
- * @param description Parameter description
25
- * @param required Whether the parameter is required
26
- */
27
- constructor(name, type, description, required) {
28
- this.name = name;
29
- this.type = type;
30
- this.description = description;
31
- this.required = required;
32
- }
33
- /**
34
- * Convert to JSON for the discovery endpoint
35
- */
36
- toJSON() {
37
- return {
38
- name: this.name,
39
- type: this.type,
40
- description: this.description,
41
- required: this.required
42
- };
43
- }
44
- }
45
- exports.Parameter = Parameter;
46
16
  /**
47
17
  * Authentication requirements for an Opal tool
48
18
  */
@@ -64,8 +34,8 @@ class AuthRequirement {
64
34
  toJSON() {
65
35
  return {
66
36
  provider: this.provider,
37
+ required: this.required,
67
38
  scope_bundle: this.scopeBundle,
68
- required: this.required
69
39
  };
70
40
  }
71
41
  }
@@ -91,95 +61,96 @@ class Function {
91
61
  /**
92
62
  * HTTP method for the endpoint (default: POST)
93
63
  */
94
- this.httpMethod = 'POST';
64
+ this.httpMethod = "POST";
95
65
  }
96
66
  /**
97
67
  * Convert to JSON for the discovery endpoint
98
68
  */
99
69
  toJSON() {
70
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
71
  const result = {
101
- name: this.name,
102
72
  description: this.description,
103
- parameters: this.parameters.map(p => p.toJSON()),
104
73
  endpoint: this.endpoint,
105
- http_method: this.httpMethod
74
+ http_method: this.httpMethod,
75
+ name: this.name,
76
+ parameters: this.parameters.map((p) => p.toJSON()),
106
77
  };
107
78
  if (this.authRequirements && this.authRequirements.length > 0) {
108
- result.auth_requirements = this.authRequirements.map(auth => auth.toJSON());
79
+ result.auth_requirements = this.authRequirements.map((auth) => auth.toJSON());
109
80
  }
110
81
  return result;
111
82
  }
112
83
  }
113
84
  exports.Function = Function;
114
85
  /**
115
- * Island field definition for interactive UI components
86
+ * Island action definition for interactive UI components
116
87
  */
117
- class IslandField {
88
+ class IslandAction {
118
89
  /**
119
- * Create a new island field
120
- * @param name Programmatic field identifier
121
- * @param label Human-readable label
122
- * @param type Field type
123
- * @param value Current field value
124
- * @param hidden Whether to hide from user
125
- * @param options Available options for selection
90
+ * Create a new island action
91
+ * @param name Programmatic action identifier
92
+ * @param label Human-readable button label
93
+ * @param type UI element type
94
+ * @param endpoint API endpoint to call
95
+ * @param operation Operation type
126
96
  */
127
- constructor(name, label, type, value = "", hidden = false, options = []) {
97
+ constructor(name, label, type, endpoint, operation = "create") {
128
98
  this.name = name;
129
99
  this.label = label;
130
100
  this.type = type;
131
- this.value = value;
132
- this.hidden = hidden;
133
- this.options = options;
101
+ this.endpoint = endpoint;
102
+ this.operation = operation;
134
103
  }
135
104
  /**
136
105
  * Convert to JSON for the discovery endpoint
137
106
  */
138
107
  toJSON() {
139
108
  return {
140
- name: this.name,
109
+ endpoint: this.endpoint,
141
110
  label: this.label,
111
+ name: this.name,
112
+ operation: this.operation,
142
113
  type: this.type,
143
- value: this.value,
144
- hidden: this.hidden,
145
- options: this.options,
146
114
  };
147
115
  }
148
116
  }
149
- exports.IslandField = IslandField;
117
+ exports.IslandAction = IslandAction;
150
118
  /**
151
- * Island action definition for interactive UI components
119
+ * Island field definition for interactive UI components
152
120
  */
153
- class IslandAction {
121
+ class IslandField {
154
122
  /**
155
- * Create a new island action
156
- * @param name Programmatic action identifier
157
- * @param label Human-readable button label
158
- * @param type UI element type
159
- * @param endpoint API endpoint to call
160
- * @param operation Operation type
123
+ * Create a new island field
124
+ * @param name Programmatic field identifier
125
+ * @param label Human-readable label
126
+ * @param type Field type
127
+ * @param value Current field value
128
+ * @param hidden Whether to hide from user
129
+ * @param options Available options for selection
161
130
  */
162
- constructor(name, label, type, endpoint, operation = "create") {
131
+ constructor(name, label, type, value = "", hidden = false, options = []) {
163
132
  this.name = name;
164
133
  this.label = label;
165
134
  this.type = type;
166
- this.endpoint = endpoint;
167
- this.operation = operation;
135
+ this.value = value;
136
+ this.hidden = hidden;
137
+ this.options = options;
168
138
  }
169
139
  /**
170
140
  * Convert to JSON for the discovery endpoint
171
141
  */
172
142
  toJSON() {
173
143
  return {
174
- name: this.name,
144
+ hidden: this.hidden,
175
145
  label: this.label,
146
+ name: this.name,
147
+ options: this.options,
176
148
  type: this.type,
177
- endpoint: this.endpoint,
178
- operation: this.operation,
149
+ value: this.value,
179
150
  };
180
151
  }
181
152
  }
182
- exports.IslandAction = IslandAction;
153
+ exports.IslandField = IslandField;
183
154
  /**
184
155
  * Island configuration for interactive UI components
185
156
  */
@@ -188,24 +159,28 @@ class IslandConfig {
188
159
  * Create a new island configuration
189
160
  * @param fields List of island fields
190
161
  * @param actions List of island actions
162
+ * @param type Optional island type
163
+ * @param icon Optional island icon
191
164
  */
192
- constructor(fields, actions) {
165
+ constructor(fields, actions, type, icon) {
193
166
  this.fields = fields;
194
167
  this.actions = actions;
168
+ this.type = type;
169
+ this.icon = icon;
195
170
  }
196
171
  /**
197
172
  * Convert to JSON for the discovery endpoint
198
173
  */
199
174
  toJSON() {
200
175
  return {
201
- fields: this.fields.map((field) => field.toJSON()),
202
176
  actions: this.actions.map((action) => action.toJSON()),
177
+ fields: this.fields.map((field) => field.toJSON()),
203
178
  };
204
179
  }
205
180
  }
206
181
  exports.IslandConfig = IslandConfig;
207
- IslandConfig.Field = IslandField;
208
182
  IslandConfig.Action = IslandAction;
183
+ IslandConfig.Field = IslandField;
209
184
  /**
210
185
  * Island response configuration
211
186
  */
@@ -234,18 +209,21 @@ class IslandResponse {
234
209
  /**
235
210
  * Create a new island response
236
211
  * @param config Response configuration
212
+ * @param message Optional message for the island response
237
213
  */
238
- constructor(config) {
214
+ constructor(config, message) {
239
215
  this.config = config;
216
+ this.message = message;
240
217
  this.type = "island";
241
218
  }
242
219
  /**
243
220
  * Create an island response with a list of islands
244
221
  * @param islands List of island configurations
222
+ * @param message Optional message for the island response
245
223
  * @returns New IslandResponse instance
246
224
  */
247
- static create(islands) {
248
- return new IslandResponse(new IslandResponseConfig(islands));
225
+ static create(islands, message) {
226
+ return new IslandResponse(new IslandResponseConfig(islands), message);
249
227
  }
250
228
  /**
251
229
  * Convert to JSON for the discovery endpoint
@@ -258,3 +236,33 @@ class IslandResponse {
258
236
  }
259
237
  exports.IslandResponse = IslandResponse;
260
238
  IslandResponse.ResponseConfig = IslandResponseConfig;
239
+ /**
240
+ * Parameter definition for an Opal tool
241
+ */
242
+ class Parameter {
243
+ /**
244
+ * Create a new parameter definition
245
+ * @param name Parameter name
246
+ * @param type Parameter type
247
+ * @param description Parameter description
248
+ * @param required Whether the parameter is required
249
+ */
250
+ constructor(name, type, description, required) {
251
+ this.name = name;
252
+ this.type = type;
253
+ this.description = description;
254
+ this.required = required;
255
+ }
256
+ /**
257
+ * Convert to JSON for the discovery endpoint
258
+ */
259
+ toJSON() {
260
+ return {
261
+ description: this.description,
262
+ name: this.name,
263
+ required: this.required,
264
+ type: this.type,
265
+ };
266
+ }
267
+ }
268
+ exports.Parameter = Parameter;
@@ -0,0 +1,68 @@
1
+ import { z } from "zod/v4";
2
+ import type { BlockResponse } from "./block";
3
+ import { Credentials } from "./models";
4
+ /**
5
+ * Extra context passed to tool handlers
6
+ */
7
+ export type RequestHandlerExtra = {
8
+ /** Authentication data if provided */
9
+ auth?: {
10
+ credentials: Credentials;
11
+ provider: string;
12
+ };
13
+ /** Execution mode: 'headless' for non-interactive, 'interactive' for user interaction (defaults to 'headless') */
14
+ mode: "headless" | "interactive";
15
+ };
16
+ type ToolOptions<TSchema extends Record<string, z.ZodTypeAny>, TType extends "block" | "json" = "json"> = {
17
+ authRequirements?: {
18
+ provider: string;
19
+ required?: boolean;
20
+ scopeBundle: string;
21
+ };
22
+ description: string;
23
+ inputSchema: TSchema;
24
+ type?: TType;
25
+ };
26
+ /**
27
+ * Register a function as an Opal tool
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const greetTool = registerTool('greet', {
32
+ * description: 'Greet a user',
33
+ * inputSchema: {
34
+ * name: z.string().describe('The name to greet')
35
+ * }
36
+ * }, async (params) => {
37
+ * // params is automatically typed as { name: string }
38
+ * return `Hello, ${params.name}!`;
39
+ * });
40
+ * ```
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * // With auth and execution mode
45
+ * const fetchTool = registerTool('fetch_data', {
46
+ * description: 'Fetch data from API',
47
+ * inputSchema: {
48
+ * url: z.string().describe('URL to fetch')
49
+ * },
50
+ * authRequirements: {
51
+ * provider: 'api-service',
52
+ * scopeBundle: 'read'
53
+ * }
54
+ * }, async (params, extra) => {
55
+ * // extra.mode: 'headless' | 'interactive'
56
+ * // extra.auth: { provider, credentials }
57
+ * const headers = extra?.auth ? { Authorization: extra.auth.credentials.token } : {};
58
+ * return fetch(params.url, { headers });
59
+ * });
60
+ * ```
61
+ */
62
+ export declare function registerTool<TSchema extends Record<string, z.ZodTypeAny>>(name: string, options: ToolOptions<TSchema, "block">, handler: (params: {
63
+ [K in keyof TSchema]: z.infer<TSchema[K]>;
64
+ }, extra?: RequestHandlerExtra) => BlockResponse | Promise<BlockResponse>): typeof handler;
65
+ export declare function registerTool<TSchema extends Record<string, z.ZodTypeAny>>(name: string, options: Omit<ToolOptions<TSchema>, "type"> | ToolOptions<TSchema, "json">, handler: (params: {
66
+ [K in keyof TSchema]: z.infer<TSchema[K]>;
67
+ }, extra?: RequestHandlerExtra) => Promise<unknown> | unknown): typeof handler;
68
+ export {};
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerTool = registerTool;
4
+ const v4_1 = require("zod/v4");
5
+ const models_1 = require("./models");
6
+ const registry_1 = require("./registry");
7
+ // Implementation
8
+ function registerTool(name, options, handler) {
9
+ // Register the tool with all services
10
+ const responseType = options.type || "json";
11
+ for (const service of registry_1.registry.services) {
12
+ service.registerTool(name, options.description, handler, jsonSchemaToParameters(v4_1.z.toJSONSchema(v4_1.z.object(options.inputSchema))), `/tools/${name.replace(/_/g, "-")}`, options.authRequirements
13
+ ? [
14
+ new models_1.AuthRequirement(options.authRequirements.provider, options.authRequirements.scopeBundle, options.authRequirements.required ?? true),
15
+ ]
16
+ : undefined, responseType, true);
17
+ }
18
+ return handler;
19
+ }
20
+ /**
21
+ * Convert JSON Schema to Parameter definitions
22
+ * This converts the output of z.toJSONSchema() back to the Parameter[] format
23
+ * expected by the legacy discovery endpoint
24
+ */
25
+ function jsonSchemaToParameters(jsonSchema) {
26
+ const parameters = [];
27
+ if (!jsonSchema.properties) {
28
+ return parameters;
29
+ }
30
+ const required = jsonSchema.required || [];
31
+ for (const [key, value] of Object.entries(jsonSchema.properties)) {
32
+ const prop = value;
33
+ parameters.push(new models_1.Parameter(key, jsonSchemaTypeToParameterType(prop.type || "string"), prop.description || "", required.includes(key)));
34
+ }
35
+ return parameters;
36
+ }
37
+ /**
38
+ * Map JSON Schema type to ParameterType
39
+ */
40
+ function jsonSchemaTypeToParameterType(jsonType) {
41
+ switch (jsonType) {
42
+ case "array":
43
+ return models_1.ParameterType.List;
44
+ case "boolean":
45
+ return models_1.ParameterType.Boolean;
46
+ case "integer":
47
+ return models_1.ParameterType.Integer;
48
+ case "number":
49
+ return models_1.ParameterType.Number;
50
+ case "object":
51
+ return models_1.ParameterType.Dictionary;
52
+ case "string":
53
+ return models_1.ParameterType.String;
54
+ default:
55
+ return models_1.ParameterType.String;
56
+ }
57
+ }
@@ -1,4 +1,4 @@
1
- import { ToolsService } from './service';
1
+ import { ToolsService } from "./service";
2
2
  /**
3
3
  * Internal registry for ToolsService instances
4
4
  */
package/dist/registry.js CHANGED
@@ -5,5 +5,5 @@ exports.registry = void 0;
5
5
  * Internal registry for ToolsService instances
6
6
  */
7
7
  exports.registry = {
8
- services: []
8
+ services: [],
9
9
  };
package/dist/service.d.ts CHANGED
@@ -1,18 +1,14 @@
1
- import { Express } from 'express';
2
- import { AuthRequirement, Parameter } from './models';
1
+ import { Express } from "express";
2
+ import { AuthRequirement, Parameter } from "./models";
3
3
  export declare class ToolsService {
4
4
  private app;
5
- private router;
6
5
  private functions;
6
+ private router;
7
7
  /**
8
8
  * Initialize a new tools service
9
9
  * @param app Express application
10
10
  */
11
11
  constructor(app: Express);
12
- /**
13
- * Initialize the discovery endpoint
14
- */
15
- private initRoutes;
16
12
  /**
17
13
  * Register a tool function
18
14
  * @param name Tool name
@@ -21,7 +17,13 @@ export declare class ToolsService {
21
17
  * @param parameters List of parameters for the tool
22
18
  * @param endpoint API endpoint for the tool
23
19
  * @param authRequirements Authentication requirements (optional)
20
+ * @param responseType Response type - 'json' (default) or 'block'
21
+ * @param isNewStyle Whether this is a new-style tool (registerTool) vs legacy decorator
24
22
  */
25
23
  registerTool(name: string, description: string, handler: any, // Changed from Function to any to avoid confusion with built-in Function type
26
- parameters: Parameter[], endpoint: string, authRequirements?: AuthRequirement[]): void;
24
+ parameters: Parameter[], endpoint: string, authRequirements?: AuthRequirement[], responseType?: "block" | "json", isNewStyle?: boolean): void;
25
+ /**
26
+ * Initialize the discovery endpoint
27
+ */
28
+ private initRoutes;
27
29
  }
package/dist/service.js CHANGED
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ToolsService = void 0;
7
7
  const express_1 = __importDefault(require("express"));
8
+ const block_1 = require("./block");
8
9
  const models_1 = require("./models");
9
10
  const registry_1 = require("./registry");
10
11
  class ToolsService {
@@ -20,15 +21,6 @@ class ToolsService {
20
21
  // Register this service in the global registry
21
22
  registry_1.registry.services.push(this);
22
23
  }
23
- /**
24
- * Initialize the discovery endpoint
25
- */
26
- initRoutes() {
27
- this.router.get('/discovery', (req, res) => {
28
- res.json({ functions: this.functions.map(f => f.toJSON()) });
29
- });
30
- this.app.use(this.router);
31
- }
32
24
  /**
33
25
  * Register a tool function
34
26
  * @param name Tool name
@@ -37,11 +29,17 @@ class ToolsService {
37
29
  * @param parameters List of parameters for the tool
38
30
  * @param endpoint API endpoint for the tool
39
31
  * @param authRequirements Authentication requirements (optional)
32
+ * @param responseType Response type - 'json' (default) or 'block'
33
+ * @param isNewStyle Whether this is a new-style tool (registerTool) vs legacy decorator
40
34
  */
41
- registerTool(name, description, handler, // Changed from Function to any to avoid confusion with built-in Function type
42
- parameters, endpoint, authRequirements) {
35
+ registerTool(name, description,
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ handler, // Changed from Function to any to avoid confusion with built-in Function type
38
+ parameters, endpoint, authRequirements, responseType = "json", isNewStyle = false) {
43
39
  const func = new models_1.Function(name, description, parameters, endpoint, authRequirements);
44
40
  this.functions.push(func);
41
+ // Determine if this is a block tool
42
+ const isBlockTool = responseType === "block";
45
43
  // Register the actual endpoint
46
44
  this.router.post(endpoint, async (req, res) => {
47
45
  try {
@@ -61,28 +59,58 @@ class ToolsService {
61
59
  // Extract auth data if available
62
60
  const authData = req.body && req.body.auth;
63
61
  if (authData) {
64
- console.log(`Auth data provided for provider: ${authData.provider || 'unknown'}`);
62
+ console.log(`Auth data provided for provider: ${authData.provider || "unknown"}`);
65
63
  }
66
- // Call the handler with extracted parameters and auth data
67
- // Check if handler accepts auth as third parameter
68
- const handlerParamCount = handler.length;
64
+ // Call the handler with extracted parameters
69
65
  let result;
70
- if (handlerParamCount >= 2) {
71
- // Handler accepts auth data
72
- result = await handler(params, authData);
66
+ if (isNewStyle) {
67
+ result = await handler(params, {
68
+ mode: (req.body && req.body.execution_mode) || "headless",
69
+ ...(authData && { auth: authData }),
70
+ });
73
71
  }
74
72
  else {
75
- // Handler doesn't accept auth data
76
- result = await handler(params);
73
+ // Check if handler accepts auth as third parameter
74
+ const handlerParamCount = handler.length;
75
+ if (handlerParamCount >= 2) {
76
+ // Handler accepts auth data
77
+ result = await handler(params, authData);
78
+ }
79
+ else {
80
+ // Handler doesn't accept auth data
81
+ result = await handler(params);
82
+ }
77
83
  }
78
84
  console.log(`Tool ${name} returned:`, result);
79
- res.json(result);
85
+ // Return with appropriate content-type header
86
+ if (isBlockTool) {
87
+ // Validate that block tools return a BlockResponse
88
+ if (!(0, block_1.isBlockResponse)(result)) {
89
+ throw new Error(`Block tool '${name}' must return a BlockResponse object, but returned ${typeof result}`);
90
+ }
91
+ res.set("Content-Type", "application/vnd.opal.block+json");
92
+ res.json(result);
93
+ }
94
+ else {
95
+ res.json(result);
96
+ }
80
97
  }
81
98
  catch (error) {
82
99
  console.error(`Error in tool ${name}:`, error);
83
- res.status(500).json({ error: error.message || 'Unknown error' });
100
+ res.status(500).json({
101
+ error: error instanceof Error ? error.message : "Unknown error",
102
+ });
84
103
  }
85
104
  });
86
105
  }
106
+ /**
107
+ * Initialize the discovery endpoint
108
+ */
109
+ initRoutes() {
110
+ this.router.get("/discovery", (req, res) => {
111
+ res.json({ functions: this.functions.map((f) => f.toJSON()) });
112
+ });
113
+ this.app.use(this.router);
114
+ }
87
115
  }
88
116
  exports.ToolsService = ToolsService;
@@ -0,0 +1,20 @@
1
+ const typescript = require("typescript-eslint");
2
+ const perfectionist = require("eslint-plugin-perfectionist");
3
+
4
+ module.exports = typescript.config(
5
+ {
6
+ ignores: ["dist/**", "node_modules/**", "*.config.js"],
7
+ },
8
+ ...typescript.configs.recommended,
9
+ perfectionist.configs["recommended-natural"],
10
+ {
11
+ files: ["**/*.ts"],
12
+ ignores: ["*.config.ts"],
13
+ languageOptions: {
14
+ parserOptions: {
15
+ projectService: true,
16
+ tsconfigRootDir: __dirname,
17
+ },
18
+ },
19
+ },
20
+ );
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "@optimizely-opal/opal-tools-sdk",
3
- "version": "0.1.3-dev",
3
+ "version": "0.1.6-dev",
4
4
  "description": "SDK for creating Opal-compatible tools services",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
- "build": "tsc",
9
- "test": "jest",
10
- "prepublishOnly": "npm run build"
8
+ "build": "tsc -p tsconfig.build.json",
9
+ "generate:block": "ts-node scripts/generate-block.ts",
10
+ "lint": "bash scripts/lint.sh",
11
+ "prepublishOnly": "npm run build",
12
+ "test": "vitest run"
11
13
  },
12
14
  "keywords": [
13
15
  "opal",
@@ -19,20 +21,27 @@
19
21
  "author": "Optimizely",
20
22
  "license": "MIT",
21
23
  "dependencies": {
22
- "express": "^4.18.2",
23
- "axios": "^1.6.0",
24
24
  "reflect-metadata": "^0.1.13"
25
25
  },
26
26
  "devDependencies": {
27
- "@types/express": "^4.17.17",
28
- "@types/jest": "^29.5.3",
29
27
  "@types/node": "^20.4.5",
30
- "jest": "^29.6.2",
31
- "ts-jest": "^29.1.1",
32
- "typescript": "^5.1.6"
28
+ "@types/supertest": "^6.0.2",
29
+ "eslint": "^9.39.2",
30
+ "eslint-plugin-perfectionist": "^5.4.0",
31
+ "json-schema-to-typescript": "^13.1.1",
32
+ "prettier": "^3.8.1",
33
+ "supertest": "^7.0.0",
34
+ "ts-node": "^10.9.2",
35
+ "typescript-eslint": "^8.54.0",
36
+ "vitest": "^1.2.0"
33
37
  },
34
38
  "peerDependencies": {
35
- "express": "^4.18.2"
39
+ "@types/express": "^4.17.17",
40
+ "@types/node": "^20.4.5",
41
+ "axios": "^1.6.0",
42
+ "express": "^4.18.2",
43
+ "typescript": "^5.1.6",
44
+ "zod": "^3.25.0 || ^4.0.0"
36
45
  },
37
46
  "repository": {
38
47
  "type": "git",