@jterrazz/test 6.5.1 → 7.1.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.
@@ -2,26 +2,29 @@ import { n as InterceptResponse, r as InterceptTrigger, t as InterceptEntry } fr
2
2
 
3
3
  //#region src/builder/common/intercept/adapters/anthropic.d.ts
4
4
  interface AnthropicMessagesFilter {
5
- /** Match by model name. */
6
5
  model?: RegExp | string;
7
- /** Match by system message content. */
8
6
  system?: RegExp | string;
9
- /** Match by user message content. */
10
7
  user?: RegExp | string;
11
- /** Match by tool names. */
12
8
  tools?: string[];
13
9
  }
10
+ declare function buildReply(data: unknown): InterceptResponse;
14
11
  /**
15
12
  * Anthropic API intercept helpers.
16
13
  */
17
14
  declare const anthropic: {
18
15
  /**
19
16
  * Trigger: match Anthropic messages API requests.
17
+ *
18
+ * @example
19
+ * anthropic.request()
20
+ * anthropic.request({ system: /classify/ })
20
21
  */
21
- messages(filter?: AnthropicMessagesFilter): InterceptTrigger; /** Response: wrap data in Anthropic messages format. */
22
- response(data: unknown): InterceptResponse; /** Response: return an Anthropic error. */
22
+ request(filter?: AnthropicMessagesFilter): InterceptTrigger; /** Response: wrap data in Anthropic messages format. */
23
+ reply: typeof buildReply; /** Response: return an Anthropic error. */
23
24
  error(status: number, message?: string): InterceptResponse; /** Response: simulate a timeout. */
24
- timeout(): InterceptResponse;
25
+ timeout(): InterceptResponse; /** @deprecated Use anthropic.request() instead. */
26
+ messages(filter?: AnthropicMessagesFilter): InterceptTrigger; /** @deprecated Use anthropic.reply() instead. */
27
+ response: typeof buildReply;
25
28
  };
26
29
  //#endregion
27
30
  //#region src/builder/common/intercept/adapters/http.d.ts
@@ -29,14 +32,13 @@ declare const anthropic: {
29
32
  * Generic HTTP intercept helpers for any URL.
30
33
  *
31
34
  * @example
32
- * .intercept(http.get('https://api.example.com/data'), { body: [...] })
33
- * .intercept(http.post('https://api.stripe.com/v1/charges'), { body: { id: 'ch_...' } })
35
+ * .intercept(http.get('https://api.example.com/data'), 'http/response.json')
34
36
  */
35
37
  declare const http: {
36
- /** Trigger: match GET requests to a URL pattern. */get(url: RegExp | string): InterceptTrigger; /** Trigger: match POST requests to a URL pattern. */
37
- post(url: RegExp | string): InterceptTrigger; /** Trigger: match PUT requests to a URL pattern. */
38
- put(url: RegExp | string): InterceptTrigger; /** Trigger: match DELETE requests to a URL pattern. */
39
- delete(url: RegExp | string): InterceptTrigger; /** Trigger: match any method to a URL pattern. */
38
+ get(url: RegExp | string): InterceptTrigger;
39
+ post(url: RegExp | string): InterceptTrigger;
40
+ put(url: RegExp | string): InterceptTrigger;
41
+ delete(url: RegExp | string): InterceptTrigger;
40
42
  any(url: RegExp | string): InterceptTrigger; /** Response: simple JSON success. */
41
43
  json(data: unknown, status?: number): InterceptResponse; /** Response: error with message. */
42
44
  error(status: number, message?: string): InterceptResponse;
@@ -44,75 +46,59 @@ declare const http: {
44
46
  //#endregion
45
47
  //#region src/builder/common/intercept/adapters/openai.d.ts
46
48
  interface OpenAIChatFilter {
47
- /** Match by model name (exact string or regex). */
48
49
  model?: RegExp | string;
49
- /** Match by system message content (regex or substring). */
50
50
  system?: RegExp | string;
51
- /** Match by user message content (regex or substring). */
52
51
  user?: RegExp | string;
53
- /** Match by tool/function names present in the request. */
54
52
  tools?: string[];
55
- /** Match by temperature value. */
56
53
  temperature?: number;
57
54
  }
58
- /** Responses API filter — uses `input` array instead of `messages`. */
59
55
  interface OpenAIResponsesFilter {
60
- /** Match by model name. */
61
56
  model?: RegExp | string;
62
- /** Match by system instruction content. */
63
57
  system?: RegExp | string;
64
- /** Match by user input content. */
65
58
  user?: RegExp | string;
66
- /** Match by tool names. */
67
59
  tools?: string[];
68
60
  }
61
+ declare function buildChatReply(data: unknown): InterceptResponse;
62
+ declare function buildResponsesReply(data: unknown): InterceptResponse;
69
63
  /**
70
64
  * OpenAI API intercept helpers.
71
65
  */
72
66
  declare const openai: {
73
67
  /**
74
- * Trigger: match OpenAI chat completion requests.
75
- *
76
- * @param filter - Optional filters to narrow which requests match.
68
+ * Trigger: match Chat Completions API requests.
77
69
  *
78
70
  * @example
79
- * openai.chat() // any chat call
80
- * openai.chat({ model: 'gpt-4o' }) // specific model
81
- * openai.chat({ system: /classify/ }) // system prompt contains "classify"
82
- * openai.chat({ tools: ['extract_facts'] }) // function calling
71
+ * openai.request() // any chat call
72
+ * openai.request({ model: 'gpt-4o' }) // specific model
73
+ * openai.request({ system: /classify/ }) // system prompt match
83
74
  */
84
- chat(filter?: OpenAIChatFilter): InterceptTrigger;
75
+ request(filter?: OpenAIChatFilter): InterceptTrigger;
85
76
  /**
86
- * Trigger: match OpenAI Responses API requests (AI SDK v5+).
87
- *
88
- * @param filter - Optional filters. Supports custom gateway URLs.
89
- * @param url - Override URL for custom gateways (e.g. 'https://gateway.example.com/v1/responses').
77
+ * Trigger: match Responses API requests (AI SDK v5+) with auto-wrapping.
78
+ * When used with a JSON file, the data is automatically wrapped in the
79
+ * Responses API envelope.
90
80
  *
91
- * @example
92
- * openai.responses() // default URL
93
- * openai.responses({ user: /Report Ingestion/ }) // match by prompt content
94
- * openai.responses({ model: 'gpt-4o' }, 'https://my-gateway/v1/responses') // custom gateway
95
- */
96
- responses(filter?: OpenAIResponsesFilter, url?: string): InterceptTrigger;
97
- /**
98
- * Response: wrap data in OpenAI Responses API format (AI SDK v5+).
81
+ * @param filter - Optional body filters.
82
+ * @param url - Custom gateway URL (default: api.openai.com).
99
83
  *
100
84
  * @example
101
- * openai.responsesResponse({ categories: ['TECH'] })
85
+ * openai.agent({ user: /Report Ingestion/ }, GATEWAY)
102
86
  */
103
- responsesResponse(data: unknown): InterceptResponse;
87
+ agent(filter?: OpenAIResponsesFilter, url?: string): InterceptTrigger;
104
88
  /**
105
- * Response: wrap data in OpenAI chat completion format.
106
- *
107
- * @param data - The content to return (will be JSON.stringified if not a string).
89
+ * Response: wrap data in Chat Completions format.
108
90
  *
109
91
  * @example
110
- * openai.response({ categories: ['TECH'] })
92
+ * openai.reply({ categories: ['TECH'] })
111
93
  */
112
- response(data: unknown): InterceptResponse; /** Response: return an OpenAI error. */
113
- error(status: number, message?: string): InterceptResponse; /** Response: return malformed (non-JSON) content. */
114
- malformed(content: string): InterceptResponse; /** Response: simulate a timeout (30s delay). */
115
- timeout(): InterceptResponse;
94
+ reply: typeof buildChatReply; /** Response: return an OpenAI error. */
95
+ error(status: number, message?: string): InterceptResponse; /** Response: return malformed content. */
96
+ malformed(content: string): InterceptResponse; /** Response: simulate a timeout. */
97
+ timeout(): InterceptResponse; /** @deprecated Use openai.request() instead. */
98
+ chat(filter?: OpenAIChatFilter): InterceptTrigger; /** @deprecated Use openai.reply() instead. */
99
+ response: typeof buildChatReply; /** @deprecated Use openai.agent() instead. */
100
+ responses(filter?: OpenAIResponsesFilter, url?: string): InterceptTrigger; /** @deprecated Use openai.reply() or .intercept(trigger, 'file.json') instead. */
101
+ responsesResponse: typeof buildResponsesReply;
116
102
  };
117
103
  //#endregion
118
104
  export { type InterceptEntry, type InterceptResponse, type InterceptTrigger, anthropic, http, openai };
package/dist/intercept.js CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region src/builder/common/intercept/adapters/anthropic.ts
2
2
  const ANTHROPIC_MESSAGES_URL = "https://api.anthropic.com/v1/messages";
3
- function matchesFilter$1(body, filter) {
3
+ function matchesFilter(body, filter) {
4
4
  if (filter.model) {
5
5
  const model = body?.model;
6
6
  if (typeof filter.model === "string" && model !== filter.model) return false;
@@ -18,42 +18,45 @@ function matchesFilter$1(body, filter) {
18
18
  if (filter.user instanceof RegExp && !filter.user.test(text)) return false;
19
19
  }
20
20
  if (filter.tools) {
21
- const requestTools = body?.tools?.map((t) => t.name).filter(Boolean) ?? [];
22
- if (!filter.tools.every((t) => requestTools.includes(t))) return false;
21
+ const names = body?.tools?.map((t) => t.name).filter(Boolean) ?? [];
22
+ if (!filter.tools.every((t) => names.includes(t))) return false;
23
23
  }
24
24
  return true;
25
25
  }
26
+ function buildReply(data) {
27
+ return {
28
+ status: 200,
29
+ body: {
30
+ id: "msg-test",
31
+ type: "message",
32
+ role: "assistant",
33
+ content: [{
34
+ type: "text",
35
+ text: typeof data === "string" ? data : JSON.stringify(data)
36
+ }],
37
+ model: "claude-sonnet-4-20250514",
38
+ stop_reason: "end_turn",
39
+ usage: {
40
+ input_tokens: 10,
41
+ output_tokens: 10
42
+ }
43
+ }
44
+ };
45
+ }
26
46
  /**
27
47
  * Anthropic API intercept helpers.
28
48
  */
29
49
  const anthropic = {
30
- messages(filter) {
50
+ request(filter) {
31
51
  return {
52
+ adapter: "anthropic",
32
53
  method: "POST",
33
54
  url: ANTHROPIC_MESSAGES_URL,
34
- match: filter ? (body) => matchesFilter$1(body, filter) : void 0
35
- };
36
- },
37
- response(data) {
38
- return {
39
- status: 200,
40
- body: {
41
- id: "msg-test",
42
- type: "message",
43
- role: "assistant",
44
- content: [{
45
- type: "text",
46
- text: typeof data === "string" ? data : JSON.stringify(data)
47
- }],
48
- model: "claude-sonnet-4-20250514",
49
- stop_reason: "end_turn",
50
- usage: {
51
- input_tokens: 10,
52
- output_tokens: 10
53
- }
54
- }
55
+ match: filter ? (body) => matchesFilter(body, filter) : void 0,
56
+ wrap: buildReply
55
57
  };
56
58
  },
59
+ reply: buildReply,
57
60
  error(status, message) {
58
61
  return {
59
62
  status,
@@ -72,46 +75,65 @@ const anthropic = {
72
75
  body: {},
73
76
  delay: 3e4
74
77
  };
75
- }
78
+ },
79
+ messages(filter) {
80
+ return anthropic.request(filter);
81
+ },
82
+ response: buildReply
76
83
  };
77
84
  //#endregion
78
85
  //#region src/builder/common/intercept/adapters/http.ts
86
+ function wrapJson(data) {
87
+ return {
88
+ status: 200,
89
+ body: data
90
+ };
91
+ }
79
92
  /**
80
93
  * Generic HTTP intercept helpers for any URL.
81
94
  *
82
95
  * @example
83
- * .intercept(http.get('https://api.example.com/data'), { body: [...] })
84
- * .intercept(http.post('https://api.stripe.com/v1/charges'), { body: { id: 'ch_...' } })
96
+ * .intercept(http.get('https://api.example.com/data'), 'http/response.json')
85
97
  */
86
98
  const http = {
87
99
  get(url) {
88
100
  return {
101
+ adapter: "http",
89
102
  method: "GET",
90
- url
103
+ url,
104
+ wrap: wrapJson
91
105
  };
92
106
  },
93
107
  post(url) {
94
108
  return {
109
+ adapter: "http",
95
110
  method: "POST",
96
- url
111
+ url,
112
+ wrap: wrapJson
97
113
  };
98
114
  },
99
115
  put(url) {
100
116
  return {
117
+ adapter: "http",
101
118
  method: "PUT",
102
- url
119
+ url,
120
+ wrap: wrapJson
103
121
  };
104
122
  },
105
123
  delete(url) {
106
124
  return {
125
+ adapter: "http",
107
126
  method: "DELETE",
108
- url
127
+ url,
128
+ wrap: wrapJson
109
129
  };
110
130
  },
111
131
  any(url) {
112
132
  return {
133
+ adapter: "http",
113
134
  method: "*",
114
- url
135
+ url,
136
+ wrap: wrapJson
115
137
  };
116
138
  },
117
139
  json(data, status = 200) {
@@ -131,25 +153,25 @@ const http = {
131
153
  //#region src/builder/common/intercept/adapters/openai.ts
132
154
  const OPENAI_CHAT_URL = "https://api.openai.com/v1/chat/completions";
133
155
  const OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses";
134
- function matchesFilter(body, filter) {
156
+ function matchesChatFilter(body, filter) {
135
157
  if (filter.model) {
136
158
  const model = body?.model;
137
159
  if (typeof filter.model === "string" && model !== filter.model) return false;
138
160
  if (filter.model instanceof RegExp && !filter.model.test(model ?? "")) return false;
139
161
  }
140
162
  if (filter.system) {
141
- const systemMsg = body?.messages?.find((m) => m.role === "system")?.content ?? "";
142
- if (typeof filter.system === "string" && !systemMsg.includes(filter.system)) return false;
143
- if (filter.system instanceof RegExp && !filter.system.test(systemMsg)) return false;
163
+ const msg = body?.messages?.find((m) => m.role === "system")?.content ?? "";
164
+ if (typeof filter.system === "string" && !msg.includes(filter.system)) return false;
165
+ if (filter.system instanceof RegExp && !filter.system.test(msg)) return false;
144
166
  }
145
167
  if (filter.user) {
146
- const userMsg = body?.messages?.find((m) => m.role === "user")?.content ?? "";
147
- if (typeof filter.user === "string" && !userMsg.includes(filter.user)) return false;
148
- if (filter.user instanceof RegExp && !filter.user.test(userMsg)) return false;
168
+ const msg = body?.messages?.find((m) => m.role === "user")?.content ?? "";
169
+ if (typeof filter.user === "string" && !msg.includes(filter.user)) return false;
170
+ if (filter.user instanceof RegExp && !filter.user.test(msg)) return false;
149
171
  }
150
172
  if (filter.tools) {
151
- const requestTools = body?.tools?.map((t) => t.function?.name).filter(Boolean) ?? [];
152
- if (!filter.tools.every((t) => requestTools.includes(t))) return false;
173
+ const names = body?.tools?.map((t) => t.function?.name).filter(Boolean) ?? [];
174
+ if (!filter.tools.every((t) => names.includes(t))) return false;
153
175
  }
154
176
  if (filter.temperature !== void 0 && body?.temperature !== filter.temperature) return false;
155
177
  return true;
@@ -163,91 +185,96 @@ function matchesResponsesFilter(body, filter) {
163
185
  if (filter.system) {
164
186
  const instructions = body?.instructions ?? "";
165
187
  const systemInput = body?.input?.find?.((m) => m.role === "system")?.content ?? "";
166
- const systemText = instructions || systemInput;
167
- if (typeof filter.system === "string" && !systemText.includes(filter.system)) return false;
168
- if (filter.system instanceof RegExp && !filter.system.test(systemText)) return false;
188
+ const text = instructions || systemInput;
189
+ if (typeof filter.system === "string" && !text.includes(filter.system)) return false;
190
+ if (filter.system instanceof RegExp && !filter.system.test(text)) return false;
169
191
  }
170
192
  if (filter.user) {
171
- const userInputs = (body?.input ?? []).filter((m) => m.role === "user").map((m) => typeof m.content === "string" ? m.content : JSON.stringify(m.content)).join(" ");
172
- if (typeof filter.user === "string" && !userInputs.includes(filter.user)) return false;
173
- if (filter.user instanceof RegExp && !filter.user.test(userInputs)) return false;
193
+ const msgs = (body?.input ?? []).filter((m) => m.role === "user").map((m) => typeof m.content === "string" ? m.content : JSON.stringify(m.content)).join(" ");
194
+ if (typeof filter.user === "string" && !msgs.includes(filter.user)) return false;
195
+ if (filter.user instanceof RegExp && !filter.user.test(msgs)) return false;
174
196
  }
175
197
  if (filter.tools) {
176
- const requestTools = body?.tools?.map((t) => t.name ?? t.function?.name).filter(Boolean) ?? [];
177
- if (!filter.tools.every((t) => requestTools.includes(t))) return false;
198
+ const names = body?.tools?.map((t) => t.name ?? t.function?.name).filter(Boolean) ?? [];
199
+ if (!filter.tools.every((t) => names.includes(t))) return false;
178
200
  }
179
201
  return true;
180
202
  }
203
+ function buildChatReply(data) {
204
+ const content = typeof data === "string" ? data : JSON.stringify(data);
205
+ return {
206
+ status: 200,
207
+ body: {
208
+ id: "chatcmpl-test",
209
+ object: "chat.completion",
210
+ created: Math.floor(Date.now() / 1e3),
211
+ model: "gpt-4o-test",
212
+ choices: [{
213
+ index: 0,
214
+ message: {
215
+ role: "assistant",
216
+ content
217
+ },
218
+ finish_reason: "stop"
219
+ }],
220
+ usage: {
221
+ prompt_tokens: 10,
222
+ completion_tokens: 10,
223
+ total_tokens: 20
224
+ }
225
+ }
226
+ };
227
+ }
228
+ function buildResponsesReply(data) {
229
+ const text = typeof data === "string" ? data : JSON.stringify(data);
230
+ return {
231
+ status: 200,
232
+ body: {
233
+ id: "resp-test",
234
+ object: "response",
235
+ created_at: Math.floor(Date.now() / 1e3),
236
+ model: "gpt-4o-test",
237
+ output: [{
238
+ type: "message",
239
+ id: "msg-test",
240
+ role: "assistant",
241
+ content: [{
242
+ type: "output_text",
243
+ text,
244
+ annotations: []
245
+ }]
246
+ }],
247
+ usage: {
248
+ input_tokens: 10,
249
+ output_tokens: 10,
250
+ total_tokens: 20
251
+ }
252
+ }
253
+ };
254
+ }
181
255
  /**
182
256
  * OpenAI API intercept helpers.
183
257
  */
184
258
  const openai = {
185
- chat(filter) {
259
+ request(filter) {
186
260
  return {
261
+ adapter: "openai",
187
262
  method: "POST",
188
263
  url: OPENAI_CHAT_URL,
189
- match: filter ? (body) => matchesFilter(body, filter) : void 0
264
+ match: filter ? (body) => matchesChatFilter(body, filter) : void 0,
265
+ wrap: buildChatReply
190
266
  };
191
267
  },
192
- responses(filter, url) {
268
+ agent(filter, url) {
193
269
  return {
270
+ adapter: "openai",
194
271
  method: "POST",
195
272
  url: url ?? OPENAI_RESPONSES_URL,
196
- match: filter ? (body) => matchesResponsesFilter(body, filter) : void 0
197
- };
198
- },
199
- responsesResponse(data) {
200
- const text = typeof data === "string" ? data : JSON.stringify(data);
201
- return {
202
- status: 200,
203
- body: {
204
- id: "resp-test",
205
- object: "response",
206
- created_at: Math.floor(Date.now() / 1e3),
207
- model: "gpt-4o-test",
208
- output: [{
209
- type: "message",
210
- id: "msg-test",
211
- role: "assistant",
212
- content: [{
213
- type: "output_text",
214
- text,
215
- annotations: []
216
- }]
217
- }],
218
- usage: {
219
- input_tokens: 10,
220
- output_tokens: 10,
221
- total_tokens: 20
222
- }
223
- }
224
- };
225
- },
226
- response(data) {
227
- const content = typeof data === "string" ? data : JSON.stringify(data);
228
- return {
229
- status: 200,
230
- body: {
231
- id: "chatcmpl-test",
232
- object: "chat.completion",
233
- created: Math.floor(Date.now() / 1e3),
234
- model: "gpt-4o-test",
235
- choices: [{
236
- index: 0,
237
- message: {
238
- role: "assistant",
239
- content
240
- },
241
- finish_reason: "stop"
242
- }],
243
- usage: {
244
- prompt_tokens: 10,
245
- completion_tokens: 10,
246
- total_tokens: 20
247
- }
248
- }
273
+ match: filter ? (body) => matchesResponsesFilter(body, filter) : void 0,
274
+ wrap: buildResponsesReply
249
275
  };
250
276
  },
277
+ reply: buildChatReply,
251
278
  error(status, message) {
252
279
  return {
253
280
  status,
@@ -259,28 +286,7 @@ const openai = {
259
286
  };
260
287
  },
261
288
  malformed(content) {
262
- return {
263
- status: 200,
264
- body: {
265
- id: "chatcmpl-test",
266
- object: "chat.completion",
267
- created: Math.floor(Date.now() / 1e3),
268
- model: "gpt-4o-test",
269
- choices: [{
270
- index: 0,
271
- message: {
272
- role: "assistant",
273
- content
274
- },
275
- finish_reason: "stop"
276
- }],
277
- usage: {
278
- prompt_tokens: 10,
279
- completion_tokens: 10,
280
- total_tokens: 20
281
- }
282
- }
283
- };
289
+ return buildChatReply(content);
284
290
  },
285
291
  timeout() {
286
292
  return {
@@ -288,7 +294,15 @@ const openai = {
288
294
  body: {},
289
295
  delay: 3e4
290
296
  };
291
- }
297
+ },
298
+ chat(filter) {
299
+ return openai.request(filter);
300
+ },
301
+ response: buildChatReply,
302
+ responses(filter, url) {
303
+ return openai.agent(filter, url);
304
+ },
305
+ responsesResponse: buildResponsesReply
292
306
  };
293
307
  //#endregion
294
308
  export { anthropic, http, openai };
@@ -1 +1 @@
1
- {"version":3,"file":"intercept.js","names":["matchesFilter"],"sources":["../src/builder/common/intercept/adapters/anthropic.ts","../src/builder/common/intercept/adapters/http.ts","../src/builder/common/intercept/adapters/openai.ts"],"sourcesContent":["import type { InterceptResponse, InterceptTrigger } from '../types.js';\n\nconst ANTHROPIC_MESSAGES_URL = 'https://api.anthropic.com/v1/messages';\n\nexport interface AnthropicMessagesFilter {\n /** Match by model name. */\n model?: RegExp | string;\n /** Match by system message content. */\n system?: RegExp | string;\n /** Match by user message content. */\n user?: RegExp | string;\n /** Match by tool names. */\n tools?: string[];\n}\n\nfunction matchesFilter(body: any, filter: AnthropicMessagesFilter): boolean {\n if (filter.model) {\n const model = body?.model;\n if (typeof filter.model === 'string' && model !== filter.model) {\n return false;\n }\n if (filter.model instanceof RegExp && !filter.model.test(model ?? '')) {\n return false;\n }\n }\n\n if (filter.system) {\n const system = typeof body?.system === 'string' ? body.system : '';\n if (typeof filter.system === 'string' && !system.includes(filter.system)) {\n return false;\n }\n if (filter.system instanceof RegExp && !filter.system.test(system)) {\n return false;\n }\n }\n\n if (filter.user) {\n const userMsg = body?.messages?.find((m: any) => m.role === 'user')?.content ?? '';\n const text = typeof userMsg === 'string' ? userMsg : JSON.stringify(userMsg);\n if (typeof filter.user === 'string' && !text.includes(filter.user)) {\n return false;\n }\n if (filter.user instanceof RegExp && !filter.user.test(text)) {\n return false;\n }\n }\n\n if (filter.tools) {\n const requestTools = body?.tools?.map((t: any) => t.name).filter(Boolean) ?? [];\n if (!filter.tools.every((t) => requestTools.includes(t))) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Anthropic API intercept helpers.\n */\nexport const anthropic = {\n /**\n * Trigger: match Anthropic messages API requests.\n */\n messages(filter?: AnthropicMessagesFilter): InterceptTrigger {\n return {\n method: 'POST',\n url: ANTHROPIC_MESSAGES_URL,\n match: filter ? (body: unknown) => matchesFilter(body, filter) : undefined,\n };\n },\n\n /** Response: wrap data in Anthropic messages format. */\n response(data: unknown): InterceptResponse {\n const content = typeof data === 'string' ? data : JSON.stringify(data);\n return {\n status: 200,\n body: {\n id: 'msg-test',\n type: 'message',\n role: 'assistant',\n content: [{ type: 'text', text: content }],\n model: 'claude-sonnet-4-20250514',\n stop_reason: 'end_turn',\n usage: { input_tokens: 10, output_tokens: 10 },\n },\n };\n },\n\n /** Response: return an Anthropic error. */\n error(status: number, message?: string): InterceptResponse {\n return {\n status,\n body: {\n type: 'error',\n error: {\n type: status === 429 ? 'rate_limit_error' : 'api_error',\n message: message ?? `Anthropic error (${status})`,\n },\n },\n };\n },\n\n /** Response: simulate a timeout. */\n timeout(): InterceptResponse {\n return { status: 200, body: {}, delay: 30_000 };\n },\n};\n","import type { InterceptResponse, InterceptTrigger } from '../types.js';\n\n/**\n * Generic HTTP intercept helpers for any URL.\n *\n * @example\n * .intercept(http.get('https://api.example.com/data'), { body: [...] })\n * .intercept(http.post('https://api.stripe.com/v1/charges'), { body: { id: 'ch_...' } })\n */\nexport const http = {\n /** Trigger: match GET requests to a URL pattern. */\n get(url: RegExp | string): InterceptTrigger {\n return { method: 'GET', url };\n },\n\n /** Trigger: match POST requests to a URL pattern. */\n post(url: RegExp | string): InterceptTrigger {\n return { method: 'POST', url };\n },\n\n /** Trigger: match PUT requests to a URL pattern. */\n put(url: RegExp | string): InterceptTrigger {\n return { method: 'PUT', url };\n },\n\n /** Trigger: match DELETE requests to a URL pattern. */\n delete(url: RegExp | string): InterceptTrigger {\n return { method: 'DELETE', url };\n },\n\n /** Trigger: match any method to a URL pattern. */\n any(url: RegExp | string): InterceptTrigger {\n return { method: '*', url };\n },\n\n /** Response: simple JSON success. */\n json(data: unknown, status = 200): InterceptResponse {\n return { status, body: data };\n },\n\n /** Response: error with message. */\n error(status: number, message?: string): InterceptResponse {\n return { status, body: { error: message ?? `HTTP ${status}` } };\n },\n};\n","import type { InterceptResponse, InterceptTrigger } from '../types.js';\n\nconst OPENAI_CHAT_URL = 'https://api.openai.com/v1/chat/completions';\nconst OPENAI_RESPONSES_URL = 'https://api.openai.com/v1/responses';\n\nexport interface OpenAIChatFilter {\n /** Match by model name (exact string or regex). */\n model?: RegExp | string;\n /** Match by system message content (regex or substring). */\n system?: RegExp | string;\n /** Match by user message content (regex or substring). */\n user?: RegExp | string;\n /** Match by tool/function names present in the request. */\n tools?: string[];\n /** Match by temperature value. */\n temperature?: number;\n}\n\nfunction matchesFilter(body: any, filter: OpenAIChatFilter): boolean {\n if (filter.model) {\n const model = body?.model;\n if (typeof filter.model === 'string' && model !== filter.model) {\n return false;\n }\n if (filter.model instanceof RegExp && !filter.model.test(model ?? '')) {\n return false;\n }\n }\n\n if (filter.system) {\n const systemMsg = body?.messages?.find((m: any) => m.role === 'system')?.content ?? '';\n if (typeof filter.system === 'string' && !systemMsg.includes(filter.system)) {\n return false;\n }\n if (filter.system instanceof RegExp && !filter.system.test(systemMsg)) {\n return false;\n }\n }\n\n if (filter.user) {\n const userMsg = body?.messages?.find((m: any) => m.role === 'user')?.content ?? '';\n if (typeof filter.user === 'string' && !userMsg.includes(filter.user)) {\n return false;\n }\n if (filter.user instanceof RegExp && !filter.user.test(userMsg)) {\n return false;\n }\n }\n\n if (filter.tools) {\n const requestTools = body?.tools?.map((t: any) => t.function?.name).filter(Boolean) ?? [];\n if (!filter.tools.every((t) => requestTools.includes(t))) {\n return false;\n }\n }\n\n if (filter.temperature !== undefined && body?.temperature !== filter.temperature) {\n return false;\n }\n\n return true;\n}\n\n/** Responses API filter — uses `input` array instead of `messages`. */\nexport interface OpenAIResponsesFilter {\n /** Match by model name. */\n model?: RegExp | string;\n /** Match by system instruction content. */\n system?: RegExp | string;\n /** Match by user input content. */\n user?: RegExp | string;\n /** Match by tool names. */\n tools?: string[];\n}\n\nfunction matchesResponsesFilter(body: any, filter: OpenAIResponsesFilter): boolean {\n if (filter.model) {\n const model = body?.model;\n if (typeof filter.model === 'string' && model !== filter.model) {\n return false;\n }\n if (filter.model instanceof RegExp && !filter.model.test(model ?? '')) {\n return false;\n }\n }\n\n if (filter.system) {\n const instructions = body?.instructions ?? '';\n const systemInput = body?.input?.find?.((m: any) => m.role === 'system')?.content ?? '';\n const systemText = instructions || systemInput;\n if (typeof filter.system === 'string' && !systemText.includes(filter.system)) {\n return false;\n }\n if (filter.system instanceof RegExp && !filter.system.test(systemText)) {\n return false;\n }\n }\n\n if (filter.user) {\n const userInputs = (body?.input ?? [])\n .filter((m: any) => m.role === 'user')\n .map((m: any) =>\n typeof m.content === 'string' ? m.content : JSON.stringify(m.content),\n )\n .join(' ');\n if (typeof filter.user === 'string' && !userInputs.includes(filter.user)) {\n return false;\n }\n if (filter.user instanceof RegExp && !filter.user.test(userInputs)) {\n return false;\n }\n }\n\n if (filter.tools) {\n const requestTools =\n body?.tools?.map((t: any) => t.name ?? t.function?.name).filter(Boolean) ?? [];\n if (!filter.tools.every((t) => requestTools.includes(t))) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * OpenAI API intercept helpers.\n */\nexport const openai = {\n /**\n * Trigger: match OpenAI chat completion requests.\n *\n * @param filter - Optional filters to narrow which requests match.\n *\n * @example\n * openai.chat() // any chat call\n * openai.chat({ model: 'gpt-4o' }) // specific model\n * openai.chat({ system: /classify/ }) // system prompt contains \"classify\"\n * openai.chat({ tools: ['extract_facts'] }) // function calling\n */\n chat(filter?: OpenAIChatFilter): InterceptTrigger {\n return {\n method: 'POST',\n url: OPENAI_CHAT_URL,\n match: filter ? (body: unknown) => matchesFilter(body, filter) : undefined,\n };\n },\n\n /**\n * Trigger: match OpenAI Responses API requests (AI SDK v5+).\n *\n * @param filter - Optional filters. Supports custom gateway URLs.\n * @param url - Override URL for custom gateways (e.g. 'https://gateway.example.com/v1/responses').\n *\n * @example\n * openai.responses() // default URL\n * openai.responses({ user: /Report Ingestion/ }) // match by prompt content\n * openai.responses({ model: 'gpt-4o' }, 'https://my-gateway/v1/responses') // custom gateway\n */\n responses(filter?: OpenAIResponsesFilter, url?: string): InterceptTrigger {\n return {\n method: 'POST',\n url: url ?? OPENAI_RESPONSES_URL,\n match: filter ? (body: unknown) => matchesResponsesFilter(body, filter) : undefined,\n };\n },\n\n /**\n * Response: wrap data in OpenAI Responses API format (AI SDK v5+).\n *\n * @example\n * openai.responsesResponse({ categories: ['TECH'] })\n */\n responsesResponse(data: unknown): InterceptResponse {\n const text = typeof data === 'string' ? data : JSON.stringify(data);\n return {\n status: 200,\n body: {\n id: 'resp-test',\n object: 'response',\n created_at: Math.floor(Date.now() / 1000),\n model: 'gpt-4o-test',\n output: [\n {\n type: 'message',\n id: 'msg-test',\n role: 'assistant',\n content: [{ type: 'output_text', text, annotations: [] }],\n },\n ],\n usage: {\n input_tokens: 10,\n output_tokens: 10,\n total_tokens: 20,\n },\n },\n };\n },\n\n /**\n * Response: wrap data in OpenAI chat completion format.\n *\n * @param data - The content to return (will be JSON.stringified if not a string).\n *\n * @example\n * openai.response({ categories: ['TECH'] })\n */\n response(data: unknown): InterceptResponse {\n const content = typeof data === 'string' ? data : JSON.stringify(data);\n return {\n status: 200,\n body: {\n id: 'chatcmpl-test',\n object: 'chat.completion',\n created: Math.floor(Date.now() / 1000),\n model: 'gpt-4o-test',\n choices: [\n {\n index: 0,\n message: { role: 'assistant', content },\n finish_reason: 'stop',\n },\n ],\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n },\n };\n },\n\n /** Response: return an OpenAI error. */\n error(status: number, message?: string): InterceptResponse {\n return {\n status,\n body: {\n error: {\n message: message ?? `OpenAI error (${status})`,\n type: status === 429 ? 'rate_limit_error' : 'api_error',\n code: status === 429 ? 'rate_limit_exceeded' : null,\n },\n },\n };\n },\n\n /** Response: return malformed (non-JSON) content. */\n malformed(content: string): InterceptResponse {\n return {\n status: 200,\n body: {\n id: 'chatcmpl-test',\n object: 'chat.completion',\n created: Math.floor(Date.now() / 1000),\n model: 'gpt-4o-test',\n choices: [\n {\n index: 0,\n message: { role: 'assistant', content },\n finish_reason: 'stop',\n },\n ],\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n },\n };\n },\n\n /** Response: simulate a timeout (30s delay). */\n timeout(): InterceptResponse {\n return {\n status: 200,\n body: {},\n delay: 30_000,\n };\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"intercept.js","names":[],"sources":["../src/builder/common/intercept/adapters/anthropic.ts","../src/builder/common/intercept/adapters/http.ts","../src/builder/common/intercept/adapters/openai.ts"],"sourcesContent":["import type { InterceptResponse, InterceptTrigger } from '../types.js';\n\nconst ANTHROPIC_MESSAGES_URL = 'https://api.anthropic.com/v1/messages';\n\nexport interface AnthropicMessagesFilter {\n model?: RegExp | string;\n system?: RegExp | string;\n user?: RegExp | string;\n tools?: string[];\n}\n\nfunction matchesFilter(body: any, filter: AnthropicMessagesFilter): boolean {\n if (filter.model) {\n const model = body?.model;\n if (typeof filter.model === 'string' && model !== filter.model) {\n return false;\n }\n if (filter.model instanceof RegExp && !filter.model.test(model ?? '')) {\n return false;\n }\n }\n if (filter.system) {\n const system = typeof body?.system === 'string' ? body.system : '';\n if (typeof filter.system === 'string' && !system.includes(filter.system)) {\n return false;\n }\n if (filter.system instanceof RegExp && !filter.system.test(system)) {\n return false;\n }\n }\n if (filter.user) {\n const userMsg = body?.messages?.find((m: any) => m.role === 'user')?.content ?? '';\n const text = typeof userMsg === 'string' ? userMsg : JSON.stringify(userMsg);\n if (typeof filter.user === 'string' && !text.includes(filter.user)) {\n return false;\n }\n if (filter.user instanceof RegExp && !filter.user.test(text)) {\n return false;\n }\n }\n if (filter.tools) {\n const names = body?.tools?.map((t: any) => t.name).filter(Boolean) ?? [];\n if (!filter.tools.every((t) => names.includes(t))) {\n return false;\n }\n }\n return true;\n}\n\nfunction buildReply(data: unknown): InterceptResponse {\n const content = typeof data === 'string' ? data : JSON.stringify(data);\n return {\n status: 200,\n body: {\n id: 'msg-test',\n type: 'message',\n role: 'assistant',\n content: [{ type: 'text', text: content }],\n model: 'claude-sonnet-4-20250514',\n stop_reason: 'end_turn',\n usage: { input_tokens: 10, output_tokens: 10 },\n },\n };\n}\n\n/**\n * Anthropic API intercept helpers.\n */\nexport const anthropic = {\n /**\n * Trigger: match Anthropic messages API requests.\n *\n * @example\n * anthropic.request()\n * anthropic.request({ system: /classify/ })\n */\n request(filter?: AnthropicMessagesFilter): InterceptTrigger {\n return {\n adapter: 'anthropic',\n method: 'POST',\n url: ANTHROPIC_MESSAGES_URL,\n match: filter ? (body: unknown) => matchesFilter(body, filter) : undefined,\n wrap: buildReply,\n };\n },\n\n /** Response: wrap data in Anthropic messages format. */\n reply: buildReply,\n\n /** Response: return an Anthropic error. */\n error(status: number, message?: string): InterceptResponse {\n return {\n status,\n body: {\n type: 'error',\n error: {\n type: status === 429 ? 'rate_limit_error' : 'api_error',\n message: message ?? `Anthropic error (${status})`,\n },\n },\n };\n },\n\n /** Response: simulate a timeout. */\n timeout(): InterceptResponse {\n return { status: 200, body: {}, delay: 30_000 };\n },\n\n // Legacy aliases\n /** @deprecated Use anthropic.request() instead. */\n messages(filter?: AnthropicMessagesFilter): InterceptTrigger {\n return anthropic.request(filter);\n },\n /** @deprecated Use anthropic.reply() instead. */\n response: buildReply,\n};\n","import type { InterceptResponse, InterceptTrigger } from '../types.js';\n\nfunction wrapJson(data: unknown): InterceptResponse {\n return { status: 200, body: data };\n}\n\n/**\n * Generic HTTP intercept helpers for any URL.\n *\n * @example\n * .intercept(http.get('https://api.example.com/data'), 'http/response.json')\n */\nexport const http = {\n get(url: RegExp | string): InterceptTrigger {\n return { adapter: 'http', method: 'GET', url, wrap: wrapJson };\n },\n\n post(url: RegExp | string): InterceptTrigger {\n return { adapter: 'http', method: 'POST', url, wrap: wrapJson };\n },\n\n put(url: RegExp | string): InterceptTrigger {\n return { adapter: 'http', method: 'PUT', url, wrap: wrapJson };\n },\n\n delete(url: RegExp | string): InterceptTrigger {\n return { adapter: 'http', method: 'DELETE', url, wrap: wrapJson };\n },\n\n any(url: RegExp | string): InterceptTrigger {\n return { adapter: 'http', method: '*', url, wrap: wrapJson };\n },\n\n /** Response: simple JSON success. */\n json(data: unknown, status = 200): InterceptResponse {\n return { status, body: data };\n },\n\n /** Response: error with message. */\n error(status: number, message?: string): InterceptResponse {\n return { status, body: { error: message ?? `HTTP ${status}` } };\n },\n};\n","import type { InterceptResponse, InterceptTrigger } from '../types.js';\n\nconst OPENAI_CHAT_URL = 'https://api.openai.com/v1/chat/completions';\nconst OPENAI_RESPONSES_URL = 'https://api.openai.com/v1/responses';\n\n// ── Chat Completions filters ──\n\nexport interface OpenAIChatFilter {\n model?: RegExp | string;\n system?: RegExp | string;\n user?: RegExp | string;\n tools?: string[];\n temperature?: number;\n}\n\nfunction matchesChatFilter(body: any, filter: OpenAIChatFilter): boolean {\n if (filter.model) {\n const model = body?.model;\n if (typeof filter.model === 'string' && model !== filter.model) {\n return false;\n }\n if (filter.model instanceof RegExp && !filter.model.test(model ?? '')) {\n return false;\n }\n }\n if (filter.system) {\n const msg = body?.messages?.find((m: any) => m.role === 'system')?.content ?? '';\n if (typeof filter.system === 'string' && !msg.includes(filter.system)) {\n return false;\n }\n if (filter.system instanceof RegExp && !filter.system.test(msg)) {\n return false;\n }\n }\n if (filter.user) {\n const msg = body?.messages?.find((m: any) => m.role === 'user')?.content ?? '';\n if (typeof filter.user === 'string' && !msg.includes(filter.user)) {\n return false;\n }\n if (filter.user instanceof RegExp && !filter.user.test(msg)) {\n return false;\n }\n }\n if (filter.tools) {\n const names = body?.tools?.map((t: any) => t.function?.name).filter(Boolean) ?? [];\n if (!filter.tools.every((t) => names.includes(t))) {\n return false;\n }\n }\n if (filter.temperature !== undefined && body?.temperature !== filter.temperature) {\n return false;\n }\n return true;\n}\n\n// ── Responses API filters ──\n\nexport interface OpenAIResponsesFilter {\n model?: RegExp | string;\n system?: RegExp | string;\n user?: RegExp | string;\n tools?: string[];\n}\n\nfunction matchesResponsesFilter(body: any, filter: OpenAIResponsesFilter): boolean {\n if (filter.model) {\n const model = body?.model;\n if (typeof filter.model === 'string' && model !== filter.model) {\n return false;\n }\n if (filter.model instanceof RegExp && !filter.model.test(model ?? '')) {\n return false;\n }\n }\n if (filter.system) {\n const instructions = body?.instructions ?? '';\n const systemInput = body?.input?.find?.((m: any) => m.role === 'system')?.content ?? '';\n const text = instructions || systemInput;\n if (typeof filter.system === 'string' && !text.includes(filter.system)) {\n return false;\n }\n if (filter.system instanceof RegExp && !filter.system.test(text)) {\n return false;\n }\n }\n if (filter.user) {\n const msgs = (body?.input ?? [])\n .filter((m: any) => m.role === 'user')\n .map((m: any) =>\n typeof m.content === 'string' ? m.content : JSON.stringify(m.content),\n )\n .join(' ');\n if (typeof filter.user === 'string' && !msgs.includes(filter.user)) {\n return false;\n }\n if (filter.user instanceof RegExp && !filter.user.test(msgs)) {\n return false;\n }\n }\n if (filter.tools) {\n const names =\n body?.tools?.map((t: any) => t.name ?? t.function?.name).filter(Boolean) ?? [];\n if (!filter.tools.every((t) => names.includes(t))) {\n return false;\n }\n }\n return true;\n}\n\n// ── Response builders ──\n\nfunction buildChatReply(data: unknown): InterceptResponse {\n const content = typeof data === 'string' ? data : JSON.stringify(data);\n return {\n status: 200,\n body: {\n id: 'chatcmpl-test',\n object: 'chat.completion',\n created: Math.floor(Date.now() / 1000),\n model: 'gpt-4o-test',\n choices: [{ index: 0, message: { role: 'assistant', content }, finish_reason: 'stop' }],\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n },\n };\n}\n\nfunction buildResponsesReply(data: unknown): InterceptResponse {\n const text = typeof data === 'string' ? data : JSON.stringify(data);\n return {\n status: 200,\n body: {\n id: 'resp-test',\n object: 'response',\n created_at: Math.floor(Date.now() / 1000),\n model: 'gpt-4o-test',\n output: [\n {\n type: 'message',\n id: 'msg-test',\n role: 'assistant',\n content: [{ type: 'output_text', text, annotations: [] }],\n },\n ],\n usage: { input_tokens: 10, output_tokens: 10, total_tokens: 20 },\n },\n };\n}\n\n// ── Public API ──\n\n/**\n * OpenAI API intercept helpers.\n */\nexport const openai = {\n /**\n * Trigger: match Chat Completions API requests.\n *\n * @example\n * openai.request() // any chat call\n * openai.request({ model: 'gpt-4o' }) // specific model\n * openai.request({ system: /classify/ }) // system prompt match\n */\n request(filter?: OpenAIChatFilter): InterceptTrigger {\n return {\n adapter: 'openai',\n method: 'POST',\n url: OPENAI_CHAT_URL,\n match: filter ? (body: unknown) => matchesChatFilter(body, filter) : undefined,\n wrap: buildChatReply,\n };\n },\n\n /**\n * Trigger: match Responses API requests (AI SDK v5+) with auto-wrapping.\n * When used with a JSON file, the data is automatically wrapped in the\n * Responses API envelope.\n *\n * @param filter - Optional body filters.\n * @param url - Custom gateway URL (default: api.openai.com).\n *\n * @example\n * openai.agent({ user: /Report Ingestion/ }, GATEWAY)\n */\n agent(filter?: OpenAIResponsesFilter, url?: string): InterceptTrigger {\n return {\n adapter: 'openai',\n method: 'POST',\n url: url ?? OPENAI_RESPONSES_URL,\n match: filter ? (body: unknown) => matchesResponsesFilter(body, filter) : undefined,\n wrap: buildResponsesReply,\n };\n },\n\n /**\n * Response: wrap data in Chat Completions format.\n *\n * @example\n * openai.reply({ categories: ['TECH'] })\n */\n reply: buildChatReply,\n\n /** Response: return an OpenAI error. */\n error(status: number, message?: string): InterceptResponse {\n return {\n status,\n body: {\n error: {\n message: message ?? `OpenAI error (${status})`,\n type: status === 429 ? 'rate_limit_error' : 'api_error',\n code: status === 429 ? 'rate_limit_exceeded' : null,\n },\n },\n };\n },\n\n /** Response: return malformed content. */\n malformed(content: string): InterceptResponse {\n return buildChatReply(content);\n },\n\n /** Response: simulate a timeout. */\n timeout(): InterceptResponse {\n return { status: 200, body: {}, delay: 30_000 };\n },\n\n // ── Legacy aliases ──\n\n /** @deprecated Use openai.request() instead. */\n chat(filter?: OpenAIChatFilter): InterceptTrigger {\n return openai.request(filter);\n },\n\n /** @deprecated Use openai.reply() instead. */\n response: buildChatReply,\n\n /** @deprecated Use openai.agent() instead. */\n responses(filter?: OpenAIResponsesFilter, url?: string): InterceptTrigger {\n return openai.agent(filter, url);\n },\n\n /** @deprecated Use openai.reply() or .intercept(trigger, 'file.json') instead. */\n responsesResponse: buildResponsesReply,\n};\n"],"mappings":"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"}
package/dist/types.d.cts CHANGED
@@ -3,12 +3,19 @@
3
3
  * An intercept trigger describes which HTTP request to match.
4
4
  */
5
5
  interface InterceptTrigger {
6
+ /** Adapter name - must match the folder prefix in file-based intercepts. */
7
+ adapter: string;
6
8
  /** HTTP method to match. */
7
9
  method: string;
8
10
  /** URL pattern to match (string for exact prefix, RegExp for pattern). */
9
11
  url: RegExp | string;
10
- /** Optional request body matcher the handler only fires if this returns true. */
12
+ /** Optional request body matcher - the handler only fires if this returns true. */
11
13
  match?: (body: unknown) => boolean;
14
+ /**
15
+ * Transform raw JSON data into a provider-specific response envelope.
16
+ * Called when .intercept(trigger, 'adapter/file.json') loads a file.
17
+ */
18
+ wrap: (data: unknown) => InterceptResponse;
12
19
  }
13
20
  /**
14
21
  * An intercept response describes what to return when the trigger matches.