@jterrazz/test 6.4.2 → 6.5.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.
- package/dist/intercept.cjs +58 -0
- package/dist/intercept.cjs.map +1 -1
- package/dist/intercept.d.cts +30 -0
- package/dist/intercept.d.ts +30 -0
- package/dist/intercept.js +58 -0
- package/dist/intercept.js.map +1 -1
- package/dist/intercept2.cjs +40 -38
- package/dist/intercept2.cjs.map +1 -1
- package/dist/intercept2.js +40 -38
- package/dist/intercept2.js.map +1 -1
- package/package.json +1 -1
package/dist/intercept.cjs
CHANGED
|
@@ -131,6 +131,7 @@ const http = {
|
|
|
131
131
|
//#endregion
|
|
132
132
|
//#region src/builder/common/intercept/adapters/openai.ts
|
|
133
133
|
const OPENAI_CHAT_URL = "https://api.openai.com/v1/chat/completions";
|
|
134
|
+
const OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses";
|
|
134
135
|
function matchesFilter(body, filter) {
|
|
135
136
|
if (filter.model) {
|
|
136
137
|
const model = body?.model;
|
|
@@ -154,6 +155,30 @@ function matchesFilter(body, filter) {
|
|
|
154
155
|
if (filter.temperature !== void 0 && body?.temperature !== filter.temperature) return false;
|
|
155
156
|
return true;
|
|
156
157
|
}
|
|
158
|
+
function matchesResponsesFilter(body, filter) {
|
|
159
|
+
if (filter.model) {
|
|
160
|
+
const model = body?.model;
|
|
161
|
+
if (typeof filter.model === "string" && model !== filter.model) return false;
|
|
162
|
+
if (filter.model instanceof RegExp && !filter.model.test(model ?? "")) return false;
|
|
163
|
+
}
|
|
164
|
+
if (filter.system) {
|
|
165
|
+
const instructions = body?.instructions ?? "";
|
|
166
|
+
const systemInput = body?.input?.find?.((m) => m.role === "system")?.content ?? "";
|
|
167
|
+
const systemText = instructions || systemInput;
|
|
168
|
+
if (typeof filter.system === "string" && !systemText.includes(filter.system)) return false;
|
|
169
|
+
if (filter.system instanceof RegExp && !filter.system.test(systemText)) return false;
|
|
170
|
+
}
|
|
171
|
+
if (filter.user) {
|
|
172
|
+
const userInputs = (body?.input ?? []).filter((m) => m.role === "user").map((m) => typeof m.content === "string" ? m.content : JSON.stringify(m.content)).join(" ");
|
|
173
|
+
if (typeof filter.user === "string" && !userInputs.includes(filter.user)) return false;
|
|
174
|
+
if (filter.user instanceof RegExp && !filter.user.test(userInputs)) return false;
|
|
175
|
+
}
|
|
176
|
+
if (filter.tools) {
|
|
177
|
+
const requestTools = body?.tools?.map((t) => t.name ?? t.function?.name).filter(Boolean) ?? [];
|
|
178
|
+
if (!filter.tools.every((t) => requestTools.includes(t))) return false;
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
157
182
|
/**
|
|
158
183
|
* OpenAI API intercept helpers.
|
|
159
184
|
*/
|
|
@@ -165,6 +190,39 @@ const openai = {
|
|
|
165
190
|
match: filter ? (body) => matchesFilter(body, filter) : void 0
|
|
166
191
|
};
|
|
167
192
|
},
|
|
193
|
+
responses(filter, url) {
|
|
194
|
+
return {
|
|
195
|
+
method: "POST",
|
|
196
|
+
url: url ?? OPENAI_RESPONSES_URL,
|
|
197
|
+
match: filter ? (body) => matchesResponsesFilter(body, filter) : void 0
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
responsesResponse(data) {
|
|
201
|
+
const text = typeof data === "string" ? data : JSON.stringify(data);
|
|
202
|
+
return {
|
|
203
|
+
status: 200,
|
|
204
|
+
body: {
|
|
205
|
+
id: "resp-test",
|
|
206
|
+
object: "response",
|
|
207
|
+
created_at: Math.floor(Date.now() / 1e3),
|
|
208
|
+
model: "gpt-4o-test",
|
|
209
|
+
output: [{
|
|
210
|
+
type: "message",
|
|
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
|
+
},
|
|
168
226
|
response(data) {
|
|
169
227
|
const content = typeof data === "string" ? data : JSON.stringify(data);
|
|
170
228
|
return {
|
package/dist/intercept.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intercept.cjs","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';\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/**\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 * 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.cjs","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 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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"}
|
package/dist/intercept.d.cts
CHANGED
|
@@ -55,6 +55,17 @@ interface OpenAIChatFilter {
|
|
|
55
55
|
/** Match by temperature value. */
|
|
56
56
|
temperature?: number;
|
|
57
57
|
}
|
|
58
|
+
/** Responses API filter — uses `input` array instead of `messages`. */
|
|
59
|
+
interface OpenAIResponsesFilter {
|
|
60
|
+
/** Match by model name. */
|
|
61
|
+
model?: RegExp | string;
|
|
62
|
+
/** Match by system instruction content. */
|
|
63
|
+
system?: RegExp | string;
|
|
64
|
+
/** Match by user input content. */
|
|
65
|
+
user?: RegExp | string;
|
|
66
|
+
/** Match by tool names. */
|
|
67
|
+
tools?: string[];
|
|
68
|
+
}
|
|
58
69
|
/**
|
|
59
70
|
* OpenAI API intercept helpers.
|
|
60
71
|
*/
|
|
@@ -71,6 +82,25 @@ declare const openai: {
|
|
|
71
82
|
* openai.chat({ tools: ['extract_facts'] }) // function calling
|
|
72
83
|
*/
|
|
73
84
|
chat(filter?: OpenAIChatFilter): InterceptTrigger;
|
|
85
|
+
/**
|
|
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').
|
|
90
|
+
*
|
|
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+).
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* openai.responsesResponse({ categories: ['TECH'] })
|
|
102
|
+
*/
|
|
103
|
+
responsesResponse(data: unknown): InterceptResponse;
|
|
74
104
|
/**
|
|
75
105
|
* Response: wrap data in OpenAI chat completion format.
|
|
76
106
|
*
|
package/dist/intercept.d.ts
CHANGED
|
@@ -55,6 +55,17 @@ interface OpenAIChatFilter {
|
|
|
55
55
|
/** Match by temperature value. */
|
|
56
56
|
temperature?: number;
|
|
57
57
|
}
|
|
58
|
+
/** Responses API filter — uses `input` array instead of `messages`. */
|
|
59
|
+
interface OpenAIResponsesFilter {
|
|
60
|
+
/** Match by model name. */
|
|
61
|
+
model?: RegExp | string;
|
|
62
|
+
/** Match by system instruction content. */
|
|
63
|
+
system?: RegExp | string;
|
|
64
|
+
/** Match by user input content. */
|
|
65
|
+
user?: RegExp | string;
|
|
66
|
+
/** Match by tool names. */
|
|
67
|
+
tools?: string[];
|
|
68
|
+
}
|
|
58
69
|
/**
|
|
59
70
|
* OpenAI API intercept helpers.
|
|
60
71
|
*/
|
|
@@ -71,6 +82,25 @@ declare const openai: {
|
|
|
71
82
|
* openai.chat({ tools: ['extract_facts'] }) // function calling
|
|
72
83
|
*/
|
|
73
84
|
chat(filter?: OpenAIChatFilter): InterceptTrigger;
|
|
85
|
+
/**
|
|
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').
|
|
90
|
+
*
|
|
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+).
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* openai.responsesResponse({ categories: ['TECH'] })
|
|
102
|
+
*/
|
|
103
|
+
responsesResponse(data: unknown): InterceptResponse;
|
|
74
104
|
/**
|
|
75
105
|
* Response: wrap data in OpenAI chat completion format.
|
|
76
106
|
*
|
package/dist/intercept.js
CHANGED
|
@@ -130,6 +130,7 @@ const http = {
|
|
|
130
130
|
//#endregion
|
|
131
131
|
//#region src/builder/common/intercept/adapters/openai.ts
|
|
132
132
|
const OPENAI_CHAT_URL = "https://api.openai.com/v1/chat/completions";
|
|
133
|
+
const OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses";
|
|
133
134
|
function matchesFilter(body, filter) {
|
|
134
135
|
if (filter.model) {
|
|
135
136
|
const model = body?.model;
|
|
@@ -153,6 +154,30 @@ function matchesFilter(body, filter) {
|
|
|
153
154
|
if (filter.temperature !== void 0 && body?.temperature !== filter.temperature) return false;
|
|
154
155
|
return true;
|
|
155
156
|
}
|
|
157
|
+
function matchesResponsesFilter(body, filter) {
|
|
158
|
+
if (filter.model) {
|
|
159
|
+
const model = body?.model;
|
|
160
|
+
if (typeof filter.model === "string" && model !== filter.model) return false;
|
|
161
|
+
if (filter.model instanceof RegExp && !filter.model.test(model ?? "")) return false;
|
|
162
|
+
}
|
|
163
|
+
if (filter.system) {
|
|
164
|
+
const instructions = body?.instructions ?? "";
|
|
165
|
+
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;
|
|
169
|
+
}
|
|
170
|
+
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;
|
|
174
|
+
}
|
|
175
|
+
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;
|
|
178
|
+
}
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
156
181
|
/**
|
|
157
182
|
* OpenAI API intercept helpers.
|
|
158
183
|
*/
|
|
@@ -164,6 +189,39 @@ const openai = {
|
|
|
164
189
|
match: filter ? (body) => matchesFilter(body, filter) : void 0
|
|
165
190
|
};
|
|
166
191
|
},
|
|
192
|
+
responses(filter, url) {
|
|
193
|
+
return {
|
|
194
|
+
method: "POST",
|
|
195
|
+
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
|
+
role: "assistant",
|
|
211
|
+
content: [{
|
|
212
|
+
type: "output_text",
|
|
213
|
+
text,
|
|
214
|
+
annotations: []
|
|
215
|
+
}]
|
|
216
|
+
}],
|
|
217
|
+
usage: {
|
|
218
|
+
input_tokens: 10,
|
|
219
|
+
output_tokens: 10,
|
|
220
|
+
total_tokens: 20
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
},
|
|
167
225
|
response(data) {
|
|
168
226
|
const content = typeof data === "string" ? data : JSON.stringify(data);
|
|
169
227
|
return {
|
package/dist/intercept.js.map
CHANGED
|
@@ -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';\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/**\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 * 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":["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 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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"}
|
package/dist/intercept2.cjs
CHANGED
|
@@ -23,55 +23,57 @@ async function ensureInterceptServer() {
|
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Register intercept entries as MSW handlers. Returns a cleanup function.
|
|
26
|
-
*
|
|
26
|
+
*
|
|
27
|
+
* Each incoming request is matched against all unconsumed entries in order.
|
|
28
|
+
* The first entry whose URL and optional body filter match is consumed.
|
|
29
|
+
* This supports multiple entries for the same URL with different body matchers.
|
|
27
30
|
*/
|
|
28
31
|
async function registerIntercepts(entries) {
|
|
29
32
|
if (entries.length === 0) return () => {};
|
|
30
33
|
await ensureInterceptServer();
|
|
31
34
|
const { msw } = await loadMsw();
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const existing = queues.get(key) ?? [];
|
|
36
|
-
existing.push(entry);
|
|
37
|
-
queues.set(key, existing);
|
|
38
|
-
}
|
|
35
|
+
const consumed = Array.from({ length: entries.length }, () => false);
|
|
36
|
+
const urls = /* @__PURE__ */ new Set();
|
|
37
|
+
for (const entry of entries) urls.add(entry.trigger.url);
|
|
39
38
|
const handlers = [];
|
|
40
|
-
for (const
|
|
41
|
-
|
|
42
|
-
const trigger
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
for (const url of urls) {
|
|
40
|
+
const methods = /* @__PURE__ */ new Set();
|
|
41
|
+
for (const entry of entries) if (entry.trigger.url === url || String(entry.trigger.url) === String(url)) methods.add(entry.trigger.method);
|
|
42
|
+
for (const method of methods) {
|
|
43
|
+
const handlerFn = method === "*" ? msw.http.all : msw.http[method.toLowerCase()];
|
|
44
|
+
if (!handlerFn) continue;
|
|
45
|
+
const handler = handlerFn(url, async ({ request }) => {
|
|
46
|
+
let body = null;
|
|
47
|
+
let bodyParsed = false;
|
|
48
|
+
for (let i = 0; i < entries.length; i++) {
|
|
49
|
+
if (consumed[i]) continue;
|
|
50
|
+
const entry = entries[i];
|
|
51
|
+
if (entry.trigger.url !== url && String(entry.trigger.url) !== String(url)) continue;
|
|
52
|
+
if (entry.trigger.method !== method && entry.trigger.method !== "*") continue;
|
|
53
|
+
if (entry.trigger.match) {
|
|
54
|
+
if (!bodyParsed) {
|
|
55
|
+
try {
|
|
56
|
+
body = await request.clone().json();
|
|
57
|
+
} catch {}
|
|
58
|
+
bodyParsed = true;
|
|
59
|
+
}
|
|
60
|
+
if (!entry.trigger.match(body)) continue;
|
|
61
|
+
}
|
|
62
|
+
consumed[i] = true;
|
|
63
|
+
if (entry.response.delay) await new Promise((r) => setTimeout(r, entry.response.delay));
|
|
64
|
+
return msw.HttpResponse.json(entry.response.body, {
|
|
65
|
+
status: entry.response.status ?? 200,
|
|
66
|
+
headers: entry.response.headers
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
handlers.push(handler);
|
|
71
|
+
}
|
|
49
72
|
}
|
|
50
73
|
serverInstance.use(...handlers);
|
|
51
74
|
return () => {
|
|
52
75
|
serverInstance.resetHandlers();
|
|
53
76
|
};
|
|
54
|
-
function createResolver(queue, getIndex, advance) {
|
|
55
|
-
return async ({ request }) => {
|
|
56
|
-
const idx = getIndex();
|
|
57
|
-
if (idx >= queue.length) return;
|
|
58
|
-
const entry = queue[idx];
|
|
59
|
-
if (entry.trigger.match) {
|
|
60
|
-
let body = null;
|
|
61
|
-
try {
|
|
62
|
-
body = await request.clone().json();
|
|
63
|
-
} catch {}
|
|
64
|
-
if (!entry.trigger.match(body)) return;
|
|
65
|
-
}
|
|
66
|
-
advance();
|
|
67
|
-
if (entry.response.delay) await new Promise((r) => setTimeout(r, entry.response.delay));
|
|
68
|
-
const { HttpResponse } = await loadMsw().then((m) => m.msw);
|
|
69
|
-
return HttpResponse.json(entry.response.body, {
|
|
70
|
-
status: entry.response.status ?? 200,
|
|
71
|
-
headers: entry.response.headers
|
|
72
|
-
});
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
77
|
}
|
|
76
78
|
//#endregion
|
|
77
79
|
exports.registerIntercepts = registerIntercepts;
|
package/dist/intercept2.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intercept2.cjs","names":[],"sources":["../src/builder/common/intercept/intercept.ts"],"sourcesContent":["/**\n * MSW-based intercept server. Manages HTTP interception for spec.run().\n *\n * Intercepts are
|
|
1
|
+
{"version":3,"file":"intercept2.cjs","names":[],"sources":["../src/builder/common/intercept/intercept.ts"],"sourcesContent":["/**\n * MSW-based intercept server. Manages HTTP interception for spec.run().\n *\n * Intercepts are matched in order — for each incoming request, the first\n * unconsumed entry whose trigger matches (URL + optional body filter) is\n * used and consumed. This supports body-based routing to the same URL.\n */\nimport type { InterceptEntry } from './types.js';\n\nlet mswModule: any = null;\nlet mswHttp: any = null;\n\nasync function loadMsw() {\n if (!mswModule) {\n mswModule = await import('msw/node');\n mswHttp = await import('msw');\n }\n return { node: mswModule, msw: mswHttp! };\n}\n\nlet serverInstance: any = null;\n\n/**\n * Start the MSW server (once per process).\n */\nexport async function ensureInterceptServer(): Promise<void> {\n if (serverInstance) {\n return;\n }\n const { node } = await loadMsw();\n serverInstance = node.setupServer();\n serverInstance.listen({ onUnhandledRequest: 'bypass' });\n}\n\n/**\n * Register intercept entries as MSW handlers. Returns a cleanup function.\n *\n * Each incoming request is matched against all unconsumed entries in order.\n * The first entry whose URL and optional body filter match is consumed.\n * This supports multiple entries for the same URL with different body matchers.\n */\nexport async function registerIntercepts(entries: InterceptEntry[]): Promise<() => void> {\n if (entries.length === 0) {\n return () => {};\n }\n\n await ensureInterceptServer();\n const { msw } = await loadMsw();\n\n // Track which entries have been consumed\n const consumed = Array.from({ length: entries.length }, () => false);\n\n // Collect unique URL patterns to register handlers for\n const urls = new Set<RegExp | string>();\n for (const entry of entries) {\n urls.add(entry.trigger.url);\n }\n\n const handlers: any[] = [];\n\n for (const url of urls) {\n // Collect all methods for this URL\n const methods = new Set<string>();\n for (const entry of entries) {\n if (entry.trigger.url === url || String(entry.trigger.url) === String(url)) {\n methods.add(entry.trigger.method);\n }\n }\n\n for (const method of methods) {\n const handlerFn =\n method === '*' ? msw.http.all : (msw.http as any)[method.toLowerCase()];\n if (!handlerFn) {\n continue;\n }\n\n const handler = handlerFn(url, async ({ request }: { request: Request }) => {\n // Clone body once for all match checks\n let body: unknown = null;\n let bodyParsed = false;\n\n for (let i = 0; i < entries.length; i++) {\n if (consumed[i]) {\n continue;\n }\n\n const entry = entries[i];\n\n // Check URL matches\n if (entry.trigger.url !== url && String(entry.trigger.url) !== String(url)) {\n continue;\n }\n\n // Check method matches\n if (entry.trigger.method !== method && entry.trigger.method !== '*') {\n continue;\n }\n\n // Check body matcher if present\n if (entry.trigger.match) {\n if (!bodyParsed) {\n try {\n body = await request.clone().json();\n } catch {\n // Not JSON\n }\n bodyParsed = true;\n }\n if (!entry.trigger.match(body)) {\n continue;\n }\n }\n\n // Match found — consume and respond\n consumed[i] = true;\n\n if (entry.response.delay) {\n await new Promise((r) => setTimeout(r, entry.response.delay));\n }\n\n return msw.HttpResponse.json(entry.response.body, {\n status: entry.response.status ?? 200,\n headers: entry.response.headers,\n });\n }\n\n // No match — passthrough\n return undefined;\n });\n\n handlers.push(handler);\n }\n }\n\n serverInstance.use(...handlers);\n\n return () => {\n serverInstance.resetHandlers();\n };\n}\n\n/**\n * Stop the MSW server (call in afterAll).\n */\nexport async function stopInterceptServer(): Promise<void> {\n if (serverInstance) {\n serverInstance.close();\n serverInstance = null;\n }\n}\n"],"mappings":";AASA,IAAI,YAAiB;AACrB,IAAI,UAAe;AAEnB,eAAe,UAAU;AACrB,KAAI,CAAC,WAAW;AACZ,cAAY,MAAM,OAAO;AACzB,YAAU,MAAM,OAAO;;AAE3B,QAAO;EAAE,MAAM;EAAW,KAAK;EAAU;;AAG7C,IAAI,iBAAsB;;;;AAK1B,eAAsB,wBAAuC;AACzD,KAAI,eACA;CAEJ,MAAM,EAAE,SAAS,MAAM,SAAS;AAChC,kBAAiB,KAAK,aAAa;AACnC,gBAAe,OAAO,EAAE,oBAAoB,UAAU,CAAC;;;;;;;;;AAU3D,eAAsB,mBAAmB,SAAgD;AACrF,KAAI,QAAQ,WAAW,EACnB,cAAa;AAGjB,OAAM,uBAAuB;CAC7B,MAAM,EAAE,QAAQ,MAAM,SAAS;CAG/B,MAAM,WAAW,MAAM,KAAK,EAAE,QAAQ,QAAQ,QAAQ,QAAQ,MAAM;CAGpE,MAAM,uBAAO,IAAI,KAAsB;AACvC,MAAK,MAAM,SAAS,QAChB,MAAK,IAAI,MAAM,QAAQ,IAAI;CAG/B,MAAM,WAAkB,EAAE;AAE1B,MAAK,MAAM,OAAO,MAAM;EAEpB,MAAM,0BAAU,IAAI,KAAa;AACjC,OAAK,MAAM,SAAS,QAChB,KAAI,MAAM,QAAQ,QAAQ,OAAO,OAAO,MAAM,QAAQ,IAAI,KAAK,OAAO,IAAI,CACtE,SAAQ,IAAI,MAAM,QAAQ,OAAO;AAIzC,OAAK,MAAM,UAAU,SAAS;GAC1B,MAAM,YACF,WAAW,MAAM,IAAI,KAAK,MAAO,IAAI,KAAa,OAAO,aAAa;AAC1E,OAAI,CAAC,UACD;GAGJ,MAAM,UAAU,UAAU,KAAK,OAAO,EAAE,cAAoC;IAExE,IAAI,OAAgB;IACpB,IAAI,aAAa;AAEjB,SAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACrC,SAAI,SAAS,GACT;KAGJ,MAAM,QAAQ,QAAQ;AAGtB,SAAI,MAAM,QAAQ,QAAQ,OAAO,OAAO,MAAM,QAAQ,IAAI,KAAK,OAAO,IAAI,CACtE;AAIJ,SAAI,MAAM,QAAQ,WAAW,UAAU,MAAM,QAAQ,WAAW,IAC5D;AAIJ,SAAI,MAAM,QAAQ,OAAO;AACrB,UAAI,CAAC,YAAY;AACb,WAAI;AACA,eAAO,MAAM,QAAQ,OAAO,CAAC,MAAM;eAC/B;AAGR,oBAAa;;AAEjB,UAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,CAC1B;;AAKR,cAAS,KAAK;AAEd,SAAI,MAAM,SAAS,MACf,OAAM,IAAI,SAAS,MAAM,WAAW,GAAG,MAAM,SAAS,MAAM,CAAC;AAGjE,YAAO,IAAI,aAAa,KAAK,MAAM,SAAS,MAAM;MAC9C,QAAQ,MAAM,SAAS,UAAU;MACjC,SAAS,MAAM,SAAS;MAC3B,CAAC;;KAKR;AAEF,YAAS,KAAK,QAAQ;;;AAI9B,gBAAe,IAAI,GAAG,SAAS;AAE/B,cAAa;AACT,iBAAe,eAAe"}
|
package/dist/intercept2.js
CHANGED
|
@@ -23,55 +23,57 @@ async function ensureInterceptServer() {
|
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Register intercept entries as MSW handlers. Returns a cleanup function.
|
|
26
|
-
*
|
|
26
|
+
*
|
|
27
|
+
* Each incoming request is matched against all unconsumed entries in order.
|
|
28
|
+
* The first entry whose URL and optional body filter match is consumed.
|
|
29
|
+
* This supports multiple entries for the same URL with different body matchers.
|
|
27
30
|
*/
|
|
28
31
|
async function registerIntercepts(entries) {
|
|
29
32
|
if (entries.length === 0) return () => {};
|
|
30
33
|
await ensureInterceptServer();
|
|
31
34
|
const { msw } = await loadMsw();
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const existing = queues.get(key) ?? [];
|
|
36
|
-
existing.push(entry);
|
|
37
|
-
queues.set(key, existing);
|
|
38
|
-
}
|
|
35
|
+
const consumed = Array.from({ length: entries.length }, () => false);
|
|
36
|
+
const urls = /* @__PURE__ */ new Set();
|
|
37
|
+
for (const entry of entries) urls.add(entry.trigger.url);
|
|
39
38
|
const handlers = [];
|
|
40
|
-
for (const
|
|
41
|
-
|
|
42
|
-
const trigger
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
for (const url of urls) {
|
|
40
|
+
const methods = /* @__PURE__ */ new Set();
|
|
41
|
+
for (const entry of entries) if (entry.trigger.url === url || String(entry.trigger.url) === String(url)) methods.add(entry.trigger.method);
|
|
42
|
+
for (const method of methods) {
|
|
43
|
+
const handlerFn = method === "*" ? msw.http.all : msw.http[method.toLowerCase()];
|
|
44
|
+
if (!handlerFn) continue;
|
|
45
|
+
const handler = handlerFn(url, async ({ request }) => {
|
|
46
|
+
let body = null;
|
|
47
|
+
let bodyParsed = false;
|
|
48
|
+
for (let i = 0; i < entries.length; i++) {
|
|
49
|
+
if (consumed[i]) continue;
|
|
50
|
+
const entry = entries[i];
|
|
51
|
+
if (entry.trigger.url !== url && String(entry.trigger.url) !== String(url)) continue;
|
|
52
|
+
if (entry.trigger.method !== method && entry.trigger.method !== "*") continue;
|
|
53
|
+
if (entry.trigger.match) {
|
|
54
|
+
if (!bodyParsed) {
|
|
55
|
+
try {
|
|
56
|
+
body = await request.clone().json();
|
|
57
|
+
} catch {}
|
|
58
|
+
bodyParsed = true;
|
|
59
|
+
}
|
|
60
|
+
if (!entry.trigger.match(body)) continue;
|
|
61
|
+
}
|
|
62
|
+
consumed[i] = true;
|
|
63
|
+
if (entry.response.delay) await new Promise((r) => setTimeout(r, entry.response.delay));
|
|
64
|
+
return msw.HttpResponse.json(entry.response.body, {
|
|
65
|
+
status: entry.response.status ?? 200,
|
|
66
|
+
headers: entry.response.headers
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
handlers.push(handler);
|
|
71
|
+
}
|
|
49
72
|
}
|
|
50
73
|
serverInstance.use(...handlers);
|
|
51
74
|
return () => {
|
|
52
75
|
serverInstance.resetHandlers();
|
|
53
76
|
};
|
|
54
|
-
function createResolver(queue, getIndex, advance) {
|
|
55
|
-
return async ({ request }) => {
|
|
56
|
-
const idx = getIndex();
|
|
57
|
-
if (idx >= queue.length) return;
|
|
58
|
-
const entry = queue[idx];
|
|
59
|
-
if (entry.trigger.match) {
|
|
60
|
-
let body = null;
|
|
61
|
-
try {
|
|
62
|
-
body = await request.clone().json();
|
|
63
|
-
} catch {}
|
|
64
|
-
if (!entry.trigger.match(body)) return;
|
|
65
|
-
}
|
|
66
|
-
advance();
|
|
67
|
-
if (entry.response.delay) await new Promise((r) => setTimeout(r, entry.response.delay));
|
|
68
|
-
const { HttpResponse } = await loadMsw().then((m) => m.msw);
|
|
69
|
-
return HttpResponse.json(entry.response.body, {
|
|
70
|
-
status: entry.response.status ?? 200,
|
|
71
|
-
headers: entry.response.headers
|
|
72
|
-
});
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
77
|
}
|
|
76
78
|
//#endregion
|
|
77
79
|
export { registerIntercepts };
|
package/dist/intercept2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intercept2.js","names":[],"sources":["../src/builder/common/intercept/intercept.ts"],"sourcesContent":["/**\n * MSW-based intercept server. Manages HTTP interception for spec.run().\n *\n * Intercepts are
|
|
1
|
+
{"version":3,"file":"intercept2.js","names":[],"sources":["../src/builder/common/intercept/intercept.ts"],"sourcesContent":["/**\n * MSW-based intercept server. Manages HTTP interception for spec.run().\n *\n * Intercepts are matched in order — for each incoming request, the first\n * unconsumed entry whose trigger matches (URL + optional body filter) is\n * used and consumed. This supports body-based routing to the same URL.\n */\nimport type { InterceptEntry } from './types.js';\n\nlet mswModule: any = null;\nlet mswHttp: any = null;\n\nasync function loadMsw() {\n if (!mswModule) {\n mswModule = await import('msw/node');\n mswHttp = await import('msw');\n }\n return { node: mswModule, msw: mswHttp! };\n}\n\nlet serverInstance: any = null;\n\n/**\n * Start the MSW server (once per process).\n */\nexport async function ensureInterceptServer(): Promise<void> {\n if (serverInstance) {\n return;\n }\n const { node } = await loadMsw();\n serverInstance = node.setupServer();\n serverInstance.listen({ onUnhandledRequest: 'bypass' });\n}\n\n/**\n * Register intercept entries as MSW handlers. Returns a cleanup function.\n *\n * Each incoming request is matched against all unconsumed entries in order.\n * The first entry whose URL and optional body filter match is consumed.\n * This supports multiple entries for the same URL with different body matchers.\n */\nexport async function registerIntercepts(entries: InterceptEntry[]): Promise<() => void> {\n if (entries.length === 0) {\n return () => {};\n }\n\n await ensureInterceptServer();\n const { msw } = await loadMsw();\n\n // Track which entries have been consumed\n const consumed = Array.from({ length: entries.length }, () => false);\n\n // Collect unique URL patterns to register handlers for\n const urls = new Set<RegExp | string>();\n for (const entry of entries) {\n urls.add(entry.trigger.url);\n }\n\n const handlers: any[] = [];\n\n for (const url of urls) {\n // Collect all methods for this URL\n const methods = new Set<string>();\n for (const entry of entries) {\n if (entry.trigger.url === url || String(entry.trigger.url) === String(url)) {\n methods.add(entry.trigger.method);\n }\n }\n\n for (const method of methods) {\n const handlerFn =\n method === '*' ? msw.http.all : (msw.http as any)[method.toLowerCase()];\n if (!handlerFn) {\n continue;\n }\n\n const handler = handlerFn(url, async ({ request }: { request: Request }) => {\n // Clone body once for all match checks\n let body: unknown = null;\n let bodyParsed = false;\n\n for (let i = 0; i < entries.length; i++) {\n if (consumed[i]) {\n continue;\n }\n\n const entry = entries[i];\n\n // Check URL matches\n if (entry.trigger.url !== url && String(entry.trigger.url) !== String(url)) {\n continue;\n }\n\n // Check method matches\n if (entry.trigger.method !== method && entry.trigger.method !== '*') {\n continue;\n }\n\n // Check body matcher if present\n if (entry.trigger.match) {\n if (!bodyParsed) {\n try {\n body = await request.clone().json();\n } catch {\n // Not JSON\n }\n bodyParsed = true;\n }\n if (!entry.trigger.match(body)) {\n continue;\n }\n }\n\n // Match found — consume and respond\n consumed[i] = true;\n\n if (entry.response.delay) {\n await new Promise((r) => setTimeout(r, entry.response.delay));\n }\n\n return msw.HttpResponse.json(entry.response.body, {\n status: entry.response.status ?? 200,\n headers: entry.response.headers,\n });\n }\n\n // No match — passthrough\n return undefined;\n });\n\n handlers.push(handler);\n }\n }\n\n serverInstance.use(...handlers);\n\n return () => {\n serverInstance.resetHandlers();\n };\n}\n\n/**\n * Stop the MSW server (call in afterAll).\n */\nexport async function stopInterceptServer(): Promise<void> {\n if (serverInstance) {\n serverInstance.close();\n serverInstance = null;\n }\n}\n"],"mappings":";AASA,IAAI,YAAiB;AACrB,IAAI,UAAe;AAEnB,eAAe,UAAU;AACrB,KAAI,CAAC,WAAW;AACZ,cAAY,MAAM,OAAO;AACzB,YAAU,MAAM,OAAO;;AAE3B,QAAO;EAAE,MAAM;EAAW,KAAK;EAAU;;AAG7C,IAAI,iBAAsB;;;;AAK1B,eAAsB,wBAAuC;AACzD,KAAI,eACA;CAEJ,MAAM,EAAE,SAAS,MAAM,SAAS;AAChC,kBAAiB,KAAK,aAAa;AACnC,gBAAe,OAAO,EAAE,oBAAoB,UAAU,CAAC;;;;;;;;;AAU3D,eAAsB,mBAAmB,SAAgD;AACrF,KAAI,QAAQ,WAAW,EACnB,cAAa;AAGjB,OAAM,uBAAuB;CAC7B,MAAM,EAAE,QAAQ,MAAM,SAAS;CAG/B,MAAM,WAAW,MAAM,KAAK,EAAE,QAAQ,QAAQ,QAAQ,QAAQ,MAAM;CAGpE,MAAM,uBAAO,IAAI,KAAsB;AACvC,MAAK,MAAM,SAAS,QAChB,MAAK,IAAI,MAAM,QAAQ,IAAI;CAG/B,MAAM,WAAkB,EAAE;AAE1B,MAAK,MAAM,OAAO,MAAM;EAEpB,MAAM,0BAAU,IAAI,KAAa;AACjC,OAAK,MAAM,SAAS,QAChB,KAAI,MAAM,QAAQ,QAAQ,OAAO,OAAO,MAAM,QAAQ,IAAI,KAAK,OAAO,IAAI,CACtE,SAAQ,IAAI,MAAM,QAAQ,OAAO;AAIzC,OAAK,MAAM,UAAU,SAAS;GAC1B,MAAM,YACF,WAAW,MAAM,IAAI,KAAK,MAAO,IAAI,KAAa,OAAO,aAAa;AAC1E,OAAI,CAAC,UACD;GAGJ,MAAM,UAAU,UAAU,KAAK,OAAO,EAAE,cAAoC;IAExE,IAAI,OAAgB;IACpB,IAAI,aAAa;AAEjB,SAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACrC,SAAI,SAAS,GACT;KAGJ,MAAM,QAAQ,QAAQ;AAGtB,SAAI,MAAM,QAAQ,QAAQ,OAAO,OAAO,MAAM,QAAQ,IAAI,KAAK,OAAO,IAAI,CACtE;AAIJ,SAAI,MAAM,QAAQ,WAAW,UAAU,MAAM,QAAQ,WAAW,IAC5D;AAIJ,SAAI,MAAM,QAAQ,OAAO;AACrB,UAAI,CAAC,YAAY;AACb,WAAI;AACA,eAAO,MAAM,QAAQ,OAAO,CAAC,MAAM;eAC/B;AAGR,oBAAa;;AAEjB,UAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,CAC1B;;AAKR,cAAS,KAAK;AAEd,SAAI,MAAM,SAAS,MACf,OAAM,IAAI,SAAS,MAAM,WAAW,GAAG,MAAM,SAAS,MAAM,CAAC;AAGjE,YAAO,IAAI,aAAa,KAAK,MAAM,SAAS,MAAM;MAC9C,QAAQ,MAAM,SAAS,UAAU;MACjC,SAAS,MAAM,SAAS;MAC3B,CAAC;;KAKR;AAEF,YAAS,KAAK,QAAQ;;;AAI9B,gBAAe,IAAI,GAAG,SAAS;AAE/B,cAAa;AACT,iBAAe,eAAe"}
|