@copilotkit/aimock 1.40.0 → 1.42.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +28 -1
- package/README.md +5 -3
- package/dist/a2a-mock.cjs +18 -11
- package/dist/a2a-mock.cjs.map +1 -1
- package/dist/a2a-mock.d.cts +1 -0
- package/dist/a2a-mock.d.cts.map +1 -1
- package/dist/a2a-mock.d.ts +1 -0
- package/dist/a2a-mock.d.ts.map +1 -1
- package/dist/a2a-mock.js +18 -11
- package/dist/a2a-mock.js.map +1 -1
- package/dist/agui-handler.d.cts.map +1 -1
- package/dist/agui-handler.d.ts.map +1 -1
- package/dist/agui-mock.cjs +2 -0
- package/dist/agui-mock.cjs.map +1 -1
- package/dist/agui-mock.d.cts.map +1 -1
- package/dist/agui-mock.d.ts.map +1 -1
- package/dist/agui-mock.js +2 -0
- package/dist/agui-mock.js.map +1 -1
- package/dist/bedrock-converse.cjs +36 -2
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +37 -3
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +50 -13
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +51 -14
- package/dist/bedrock.js.map +1 -1
- package/dist/byteplus-video.cjs +954 -0
- package/dist/byteplus-video.cjs.map +1 -0
- package/dist/byteplus-video.d.cts +176 -0
- package/dist/byteplus-video.d.cts.map +1 -0
- package/dist/byteplus-video.d.ts +176 -0
- package/dist/byteplus-video.d.ts.map +1 -0
- package/dist/byteplus-video.js +947 -0
- package/dist/byteplus-video.js.map +1 -0
- package/dist/chaos.cjs +36 -7
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts +8 -3
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts +8 -3
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +36 -8
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +5 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +5 -1
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +58 -5
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +59 -6
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/elevenlabs-audio.cjs +36 -2
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.d.cts.map +1 -1
- package/dist/elevenlabs-audio.d.ts.map +1 -1
- package/dist/elevenlabs-audio.js +37 -3
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/embeddings.cjs +58 -3
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +59 -4
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +38 -2
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.d.cts.map +1 -1
- package/dist/fal-audio.d.ts.map +1 -1
- package/dist/fal-audio.js +39 -3
- package/dist/fal-audio.js.map +1 -1
- package/dist/fixture-loader.cjs +0 -0
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +9 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +9 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +0 -0
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +19 -1
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +20 -2
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +15 -1
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +16 -2
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +70 -6
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +71 -7
- package/dist/gemini.js.map +1 -1
- package/dist/grok-video.cjs +2 -2
- package/dist/grok-video.cjs.map +1 -1
- package/dist/grok-video.d.cts.map +1 -1
- package/dist/grok-video.d.ts.map +1 -1
- package/dist/grok-video.js +2 -2
- package/dist/grok-video.js.map +1 -1
- package/dist/helpers.cjs +160 -5
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +152 -6
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +24 -6
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts +11 -3
- package/dist/images.d.cts.map +1 -1
- package/dist/images.d.ts +11 -3
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +25 -7
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +7 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/llmock.cjs +15 -0
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +12 -0
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +12 -0
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +16 -1
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-mock.cjs +11 -0
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +11 -0
- package/dist/mcp-mock.js.map +1 -1
- package/dist/messages.cjs +18 -1
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +19 -2
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +20 -0
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +19 -1
- package/dist/metrics.js.map +1 -1
- package/dist/moderation.cjs +67 -3
- package/dist/moderation.cjs.map +1 -1
- package/dist/moderation.d.cts +1 -0
- package/dist/moderation.d.cts.map +1 -1
- package/dist/moderation.d.ts +1 -0
- package/dist/moderation.d.ts.map +1 -1
- package/dist/moderation.js +68 -4
- package/dist/moderation.js.map +1 -1
- package/dist/ollama.cjs +75 -5
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +76 -6
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-video.cjs +4 -4
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.d.cts.map +1 -1
- package/dist/openrouter-video.d.ts.map +1 -1
- package/dist/openrouter-video.js +4 -4
- package/dist/openrouter-video.js.map +1 -1
- package/dist/provider-auth.cjs +2 -0
- package/dist/provider-auth.cjs.map +1 -1
- package/dist/provider-auth.js +2 -0
- package/dist/provider-auth.js.map +1 -1
- package/dist/rerank.cjs +65 -2
- package/dist/rerank.cjs.map +1 -1
- package/dist/rerank.d.cts +1 -1
- package/dist/rerank.d.cts.map +1 -1
- package/dist/rerank.d.ts +1 -1
- package/dist/rerank.d.ts.map +1 -1
- package/dist/rerank.js +66 -3
- package/dist/rerank.js.map +1 -1
- package/dist/responses.cjs +18 -1
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +19 -2
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +1 -0
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +1 -0
- package/dist/router.js.map +1 -1
- package/dist/search.cjs +67 -3
- package/dist/search.cjs.map +1 -1
- package/dist/search.d.cts +1 -0
- package/dist/search.d.cts.map +1 -1
- package/dist/search.d.ts +1 -0
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +68 -4
- package/dist/search.js.map +1 -1
- package/dist/server.cjs +406 -17
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -0
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +410 -21
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +18 -1
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.d.cts.map +1 -1
- package/dist/speech.d.ts.map +1 -1
- package/dist/speech.js +19 -2
- package/dist/speech.js.map +1 -1
- package/dist/stream-collapse.cjs +2 -1
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.js +2 -1
- package/dist/stream-collapse.js.map +1 -1
- package/dist/transcription.cjs +21 -2
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +21 -2
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +58 -5
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +58 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-mock.cjs +88 -0
- package/dist/vector-mock.cjs.map +1 -1
- package/dist/vector-mock.d.cts.map +1 -1
- package/dist/vector-mock.d.ts.map +1 -1
- package/dist/vector-mock.js +89 -1
- package/dist/vector-mock.js.map +1 -1
- package/dist/vector-types.d.cts.map +1 -1
- package/dist/veo-video.cjs +2 -2
- package/dist/veo-video.cjs.map +1 -1
- package/dist/veo-video.d.cts.map +1 -1
- package/dist/veo-video.d.ts.map +1 -1
- package/dist/veo-video.js +2 -2
- package/dist/veo-video.js.map +1 -1
- package/dist/video.cjs +19 -2
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +20 -3
- package/dist/video.js.map +1 -1
- package/package.json +2 -2
package/dist/vector-mock.cjs
CHANGED
|
@@ -60,10 +60,40 @@ var VectorMock = class {
|
|
|
60
60
|
if (body) parsed = JSON.parse(body);
|
|
61
61
|
} catch (parseErr) {
|
|
62
62
|
const detail = parseErr instanceof Error ? parseErr.message : "unknown";
|
|
63
|
+
if (this.journal) this.journal.add({
|
|
64
|
+
method: req.method ?? "GET",
|
|
65
|
+
path: req.url ?? "/",
|
|
66
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
67
|
+
body: null,
|
|
68
|
+
service: "vector",
|
|
69
|
+
response: {
|
|
70
|
+
status: 400,
|
|
71
|
+
fixture: null
|
|
72
|
+
}
|
|
73
|
+
});
|
|
63
74
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
64
75
|
res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));
|
|
65
76
|
return true;
|
|
66
77
|
}
|
|
78
|
+
if (!require_helpers.isJsonObject(parsed)) {
|
|
79
|
+
if (this.journal) this.journal.add({
|
|
80
|
+
method: req.method ?? "GET",
|
|
81
|
+
path: req.url ?? "/",
|
|
82
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
83
|
+
body: null,
|
|
84
|
+
service: "vector",
|
|
85
|
+
response: {
|
|
86
|
+
status: 400,
|
|
87
|
+
fixture: null
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
91
|
+
res.end(JSON.stringify({ error: {
|
|
92
|
+
message: "Request body must be a JSON object",
|
|
93
|
+
type: "invalid_request_error"
|
|
94
|
+
} }));
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
67
97
|
}
|
|
68
98
|
const handled = this.requestHandler(req, res, pathname, parsed);
|
|
69
99
|
if (handled && this.registry) {
|
|
@@ -84,6 +114,23 @@ var VectorMock = class {
|
|
|
84
114
|
fixture: null
|
|
85
115
|
}
|
|
86
116
|
});
|
|
117
|
+
if (!handled && this.journal) {
|
|
118
|
+
const journal = this.journal;
|
|
119
|
+
res.once("finish", () => {
|
|
120
|
+
if (res.statusCode !== 404) return;
|
|
121
|
+
journal.add({
|
|
122
|
+
method: req.method ?? "GET",
|
|
123
|
+
path: req.url ?? "/",
|
|
124
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
125
|
+
body: null,
|
|
126
|
+
service: "vector",
|
|
127
|
+
response: {
|
|
128
|
+
status: 404,
|
|
129
|
+
fixture: null
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
87
134
|
return handled;
|
|
88
135
|
}
|
|
89
136
|
health() {
|
|
@@ -114,11 +161,41 @@ var VectorMock = class {
|
|
|
114
161
|
} catch (parseErr) {
|
|
115
162
|
if (req.method !== "GET") {
|
|
116
163
|
const detail = parseErr instanceof Error ? parseErr.message : "unknown";
|
|
164
|
+
if (this.journal) this.journal.add({
|
|
165
|
+
method: req.method ?? "GET",
|
|
166
|
+
path: req.url ?? "/",
|
|
167
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
168
|
+
body: null,
|
|
169
|
+
service: "vector",
|
|
170
|
+
response: {
|
|
171
|
+
status: 400,
|
|
172
|
+
fixture: null
|
|
173
|
+
}
|
|
174
|
+
});
|
|
117
175
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
118
176
|
res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));
|
|
119
177
|
return;
|
|
120
178
|
}
|
|
121
179
|
}
|
|
180
|
+
if (req.method !== "GET" && !require_helpers.isJsonObject(parsed)) {
|
|
181
|
+
if (this.journal) this.journal.add({
|
|
182
|
+
method: req.method ?? "GET",
|
|
183
|
+
path: req.url ?? "/",
|
|
184
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
185
|
+
body: null,
|
|
186
|
+
service: "vector",
|
|
187
|
+
response: {
|
|
188
|
+
status: 400,
|
|
189
|
+
fixture: null
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
193
|
+
res.end(JSON.stringify({ error: {
|
|
194
|
+
message: "Request body must be a JSON object",
|
|
195
|
+
type: "invalid_request_error"
|
|
196
|
+
} }));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
122
199
|
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
123
200
|
const handled = this.requestHandler(req, res, url.pathname, parsed);
|
|
124
201
|
if (handled && this.journal) this.journal.add({
|
|
@@ -133,6 +210,17 @@ var VectorMock = class {
|
|
|
133
210
|
}
|
|
134
211
|
});
|
|
135
212
|
if (!handled) {
|
|
213
|
+
if (this.journal) this.journal.add({
|
|
214
|
+
method: req.method ?? "GET",
|
|
215
|
+
path: req.url ?? "/",
|
|
216
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
217
|
+
body: null,
|
|
218
|
+
service: "vector",
|
|
219
|
+
response: {
|
|
220
|
+
status: 404,
|
|
221
|
+
fixture: null
|
|
222
|
+
}
|
|
223
|
+
});
|
|
136
224
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
137
225
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
138
226
|
}
|
package/dist/vector-mock.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector-mock.cjs","names":["readBody","flattenHeaders","http","createVectorRequestHandler"],"sources":["../src/vector-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable, JournalEntry } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n VectorMockOptions,\n VectorCollection,\n VectorEntry,\n QueryResult,\n VectorQuery,\n QueryHandler,\n} from \"./vector-types.js\";\nimport { createVectorRequestHandler, type VectorState } from \"./vector-handler.js\";\nimport { flattenHeaders, readBody } from \"./helpers.js\";\n\nexport class VectorMock implements Mountable {\n private collections: Map<string, VectorCollection> = new Map();\n private queryHandlers: Map<string, QueryHandler> = new Map();\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: VectorMockOptions;\n private requestHandler: ReturnType<typeof createVectorRequestHandler>;\n\n constructor(options?: VectorMockOptions) {\n this.options = options ?? {};\n this.requestHandler = this.buildHandler();\n }\n\n // ---- Configuration ----\n\n addCollection(name: string, opts: { dimension: number }): this {\n const collection: VectorCollection = {\n name,\n dimension: opts.dimension,\n vectors: new Map(),\n };\n this.collections.set(name, collection);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n upsert(collection: string, vectors: VectorEntry[]): this {\n let col = this.collections.get(collection);\n if (!col) {\n const dim = vectors.length > 0 ? vectors[0].values.length : 0;\n col = { name: collection, dimension: dim, vectors: new Map() };\n this.collections.set(collection, col);\n }\n for (const v of vectors) {\n col.vectors.set(v.id, v);\n }\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n onQuery(\n collection: string,\n results: QueryResult[] | ((query: VectorQuery) => QueryResult[]),\n ): this {\n this.queryHandlers.set(collection, results);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n deleteCollection(name: string): this {\n this.collections.delete(name);\n this.queryHandlers.delete(name);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n // Only consume the request body for methods that carry a body,\n // avoiding double-consumption when the route doesn't match.\n let parsed: Record<string, unknown> = {};\n if (req.method !== \"GET\" && req.method !== \"DELETE\" && req.method !== \"HEAD\") {\n const body = await readBody(req);\n try {\n if (body) parsed = JSON.parse(body);\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));\n return true;\n }\n }\n\n const handled = this.requestHandler(req, res, pathname, parsed);\n\n // Record vector operation metric\n if (handled && this.registry) {\n const { operation, provider } = classifyVectorRequest(req.method ?? \"GET\", pathname);\n this.registry.incrementCounter(\"aimock_vector_requests_total\", { operation, provider });\n }\n\n // Journal the request after the handler completes\n if (handled && this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n\n return handled;\n }\n\n health(): { status: string; collections: number } {\n return {\n status: \"ok\",\n collections: this.collections.size,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"Server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise((resolve, reject) => {\n const srv = http.createServer((req, res) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => {\n const body = Buffer.concat(chunks).toString();\n let parsed: Record<string, unknown> = {};\n try {\n if (body) parsed = JSON.parse(body);\n } catch (parseErr) {\n if (req.method !== \"GET\") {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));\n return;\n }\n }\n\n const url = new URL(req.url ?? \"/\", `http://${host}`);\n\n const handled = this.requestHandler(req, res, url.pathname, parsed);\n\n if (handled && this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n if (!handled) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n }\n });\n });\n\n srv.listen(port, host, () => {\n this.server = srv;\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n resolve(`http://${host}:${addr.port}`);\n } else {\n resolve(`http://${host}:${port}`);\n }\n });\n\n srv.on(\"error\", reject);\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"Server not started\");\n }\n const srv = this.server;\n this.server = null;\n await new Promise<void>((resolve, reject) => {\n srv.close((err) => (err ? reject(err) : resolve()));\n });\n }\n\n // ---- Inspection ----\n\n getRequests(): JournalEntry[] {\n if (!this.journal) return [];\n return this.journal.getAll().filter((e) => e.service === \"vector\");\n }\n\n reset(): this {\n this.collections.clear();\n this.queryHandlers.clear();\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n // ---- Internal ----\n\n private buildHandler() {\n const state: VectorState = {\n collections: this.collections,\n queryHandlers: this.queryHandlers,\n };\n return createVectorRequestHandler(state);\n }\n}\n\n// ---- Helpers ----\n\n/**\n * Classify a vector request by operation and provider based on HTTP method and pathname.\n */\nfunction classifyVectorRequest(\n method: string,\n pathname: string,\n): { operation: string; provider: string } {\n // Pinecone paths\n if (pathname === \"/query\" && method === \"POST\") {\n return { operation: \"query\", provider: \"pinecone\" };\n }\n if (pathname === \"/vectors/upsert\" && method === \"POST\") {\n return { operation: \"upsert\", provider: \"pinecone\" };\n }\n if (pathname === \"/vectors/delete\" && method === \"POST\") {\n return { operation: \"delete\", provider: \"pinecone\" };\n }\n if (pathname === \"/describe-index-stats\" && method === \"GET\") {\n return { operation: \"describe\", provider: \"pinecone\" };\n }\n\n // Qdrant paths\n if (/^\\/collections\\/[^/]+\\/points\\/search$/.test(pathname) && method === \"POST\") {\n return { operation: \"query\", provider: \"qdrant\" };\n }\n if (/^\\/collections\\/[^/]+\\/points$/.test(pathname) && method === \"PUT\") {\n return { operation: \"upsert\", provider: \"qdrant\" };\n }\n if (/^\\/collections\\/[^/]+\\/points\\/delete$/.test(pathname) && method === \"POST\") {\n return { operation: \"delete\", provider: \"qdrant\" };\n }\n\n // ChromaDB paths\n if (/^\\/api\\/v1\\/collections\\/[^/]+\\/query$/.test(pathname) && method === \"POST\") {\n return { operation: \"query\", provider: \"chromadb\" };\n }\n if (/^\\/api\\/v1\\/collections\\/[^/]+\\/add$/.test(pathname) && method === \"POST\") {\n return { operation: \"upsert\", provider: \"chromadb\" };\n }\n if (pathname === \"/api/v1/collections\" && method === \"GET\") {\n return { operation: \"list\", provider: \"chromadb\" };\n }\n if (/^\\/api\\/v1\\/collections\\/[^/]+$/.test(pathname) && method === \"DELETE\") {\n return { operation: \"delete\", provider: \"chromadb\" };\n }\n\n return { operation: \"unknown\", provider: \"unknown\" };\n}\n"],"mappings":";;;;;;;AAeA,IAAa,aAAb,MAA6C;CAC3C,AAAQ,8BAA6C,IAAI,KAAK;CAC9D,AAAQ,gCAA2C,IAAI,KAAK;CAC5D,AAAQ,SAA6B;CACrC,AAAQ,UAA0B;CAClC,AAAQ,WAAmC;CAC3C,AAAQ;CACR,AAAQ;CAER,YAAY,SAA6B;AACvC,OAAK,UAAU,WAAW,EAAE;AAC5B,OAAK,iBAAiB,KAAK,cAAc;;CAK3C,cAAc,MAAc,MAAmC;EAC7D,MAAM,aAA+B;GACnC;GACA,WAAW,KAAK;GAChB,yBAAS,IAAI,KAAK;GACnB;AACD,OAAK,YAAY,IAAI,MAAM,WAAW;AACtC,OAAK,iBAAiB,KAAK,cAAc;AACzC,SAAO;;CAGT,OAAO,YAAoB,SAA8B;EACvD,IAAI,MAAM,KAAK,YAAY,IAAI,WAAW;AAC1C,MAAI,CAAC,KAAK;AAER,SAAM;IAAE,MAAM;IAAY,WADd,QAAQ,SAAS,IAAI,QAAQ,GAAG,OAAO,SAAS;IAClB,yBAAS,IAAI,KAAK;IAAE;AAC9D,QAAK,YAAY,IAAI,YAAY,IAAI;;AAEvC,OAAK,MAAM,KAAK,QACd,KAAI,QAAQ,IAAI,EAAE,IAAI,EAAE;AAE1B,OAAK,iBAAiB,KAAK,cAAc;AACzC,SAAO;;CAGT,QACE,YACA,SACM;AACN,OAAK,cAAc,IAAI,YAAY,QAAQ;AAC3C,OAAK,iBAAiB,KAAK,cAAc;AACzC,SAAO;;CAGT,iBAAiB,MAAoB;AACnC,OAAK,YAAY,OAAO,KAAK;AAC7B,OAAK,cAAc,OAAO,KAAK;AAC/B,OAAK,iBAAiB,KAAK,cAAc;AACzC,SAAO;;CAKT,MAAM,cACJ,KACA,KACA,UACkB;EAGlB,IAAI,SAAkC,EAAE;AACxC,MAAI,IAAI,WAAW,SAAS,IAAI,WAAW,YAAY,IAAI,WAAW,QAAQ;GAC5E,MAAM,OAAO,MAAMA,yBAAS,IAAI;AAChC,OAAI;AACF,QAAI,KAAM,UAAS,KAAK,MAAM,KAAK;YAC5B,UAAU;IACjB,MAAM,SAAS,oBAAoB,QAAQ,SAAS,UAAU;AAC9D,QAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,QAAI,IAAI,KAAK,UAAU,EAAE,OAAO,wBAAwB,UAAU,CAAC,CAAC;AACpE,WAAO;;;EAIX,MAAM,UAAU,KAAK,eAAe,KAAK,KAAK,UAAU,OAAO;AAG/D,MAAI,WAAW,KAAK,UAAU;GAC5B,MAAM,EAAE,WAAW,aAAa,sBAAsB,IAAI,UAAU,OAAO,SAAS;AACpF,QAAK,SAAS,iBAAiB,gCAAgC;IAAE;IAAW;IAAU,CAAC;;AAIzF,MAAI,WAAW,KAAK,QAClB,MAAK,QAAQ,IAAI;GACf,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAASC,+BAAe,IAAI,QAAQ;GACpC,MAAM;GACN,SAAS;GACT,UAAU;IAAE,QAAQ,IAAI;IAAY,SAAS;IAAM;GACpD,CAAC;AAGJ,SAAO;;CAGT,SAAkD;AAChD,SAAO;GACL,QAAQ;GACR,aAAa,KAAK,YAAY;GAC/B;;CAGH,WAAW,SAAwB;AACjC,OAAK,UAAU;;CAGjB,YAAY,UAAiC;AAC3C,OAAK,WAAW;;CAKlB,MAAM,QAAyB;AAC7B,MAAI,KAAK,OACP,OAAM,IAAI,MAAM,yBAAyB;EAG3C,MAAM,OAAO,KAAK,QAAQ,QAAQ;EAClC,MAAM,OAAO,KAAK,QAAQ,QAAQ;AAElC,SAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,MAAMC,UAAK,cAAc,KAAK,QAAQ;IAC1C,MAAM,SAAmB,EAAE;AAC3B,QAAI,GAAG,SAAS,UAAkB,OAAO,KAAK,MAAM,CAAC;AACrD,QAAI,GAAG,aAAa;KAClB,MAAM,OAAO,OAAO,OAAO,OAAO,CAAC,UAAU;KAC7C,IAAI,SAAkC,EAAE;AACxC,SAAI;AACF,UAAI,KAAM,UAAS,KAAK,MAAM,KAAK;cAC5B,UAAU;AACjB,UAAI,IAAI,WAAW,OAAO;OACxB,MAAM,SAAS,oBAAoB,QAAQ,SAAS,UAAU;AAC9D,WAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,WAAI,IAAI,KAAK,UAAU,EAAE,OAAO,wBAAwB,UAAU,CAAC,CAAC;AACpE;;;KAIJ,MAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,OAAO;KAErD,MAAM,UAAU,KAAK,eAAe,KAAK,KAAK,IAAI,UAAU,OAAO;AAEnE,SAAI,WAAW,KAAK,QAClB,MAAK,QAAQ,IAAI;MACf,QAAQ,IAAI,UAAU;MACtB,MAAM,IAAI,OAAO;MACjB,SAASD,+BAAe,IAAI,QAAQ;MACpC,MAAM;MACN,SAAS;MACT,UAAU;OAAE,QAAQ,IAAI;OAAY,SAAS;OAAM;MACpD,CAAC;AAEJ,SAAI,CAAC,SAAS;AACZ,UAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,aAAa,CAAC,CAAC;;MAEjD;KACF;AAEF,OAAI,OAAO,MAAM,YAAY;AAC3B,SAAK,SAAS;IACd,MAAM,OAAO,IAAI,SAAS;AAC1B,QAAI,OAAO,SAAS,YAAY,SAAS,KACvC,SAAQ,UAAU,KAAK,GAAG,KAAK,OAAO;QAEtC,SAAQ,UAAU,KAAK,GAAG,OAAO;KAEnC;AAEF,OAAI,GAAG,SAAS,OAAO;IACvB;;CAGJ,MAAM,OAAsB;AAC1B,MAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,qBAAqB;EAEvC,MAAM,MAAM,KAAK;AACjB,OAAK,SAAS;AACd,QAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,OAAI,OAAO,QAAS,MAAM,OAAO,IAAI,GAAG,SAAS,CAAE;IACnD;;CAKJ,cAA8B;AAC5B,MAAI,CAAC,KAAK,QAAS,QAAO,EAAE;AAC5B,SAAO,KAAK,QAAQ,QAAQ,CAAC,QAAQ,MAAM,EAAE,YAAY,SAAS;;CAGpE,QAAc;AACZ,OAAK,YAAY,OAAO;AACxB,OAAK,cAAc,OAAO;AAC1B,OAAK,iBAAiB,KAAK,cAAc;AACzC,SAAO;;CAKT,AAAQ,eAAe;AAKrB,SAAOE,kDAJoB;GACzB,aAAa,KAAK;GAClB,eAAe,KAAK;GACrB,CACuC;;;;;;AAS5C,SAAS,sBACP,QACA,UACyC;AAEzC,KAAI,aAAa,YAAY,WAAW,OACtC,QAAO;EAAE,WAAW;EAAS,UAAU;EAAY;AAErD,KAAI,aAAa,qBAAqB,WAAW,OAC/C,QAAO;EAAE,WAAW;EAAU,UAAU;EAAY;AAEtD,KAAI,aAAa,qBAAqB,WAAW,OAC/C,QAAO;EAAE,WAAW;EAAU,UAAU;EAAY;AAEtD,KAAI,aAAa,2BAA2B,WAAW,MACrD,QAAO;EAAE,WAAW;EAAY,UAAU;EAAY;AAIxD,KAAI,yCAAyC,KAAK,SAAS,IAAI,WAAW,OACxE,QAAO;EAAE,WAAW;EAAS,UAAU;EAAU;AAEnD,KAAI,iCAAiC,KAAK,SAAS,IAAI,WAAW,MAChE,QAAO;EAAE,WAAW;EAAU,UAAU;EAAU;AAEpD,KAAI,yCAAyC,KAAK,SAAS,IAAI,WAAW,OACxE,QAAO;EAAE,WAAW;EAAU,UAAU;EAAU;AAIpD,KAAI,yCAAyC,KAAK,SAAS,IAAI,WAAW,OACxE,QAAO;EAAE,WAAW;EAAS,UAAU;EAAY;AAErD,KAAI,uCAAuC,KAAK,SAAS,IAAI,WAAW,OACtE,QAAO;EAAE,WAAW;EAAU,UAAU;EAAY;AAEtD,KAAI,aAAa,yBAAyB,WAAW,MACnD,QAAO;EAAE,WAAW;EAAQ,UAAU;EAAY;AAEpD,KAAI,kCAAkC,KAAK,SAAS,IAAI,WAAW,SACjE,QAAO;EAAE,WAAW;EAAU,UAAU;EAAY;AAGtD,QAAO;EAAE,WAAW;EAAW,UAAU;EAAW"}
|
|
1
|
+
{"version":3,"file":"vector-mock.cjs","names":["readBody","flattenHeaders","isJsonObject","http","createVectorRequestHandler"],"sources":["../src/vector-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable, JournalEntry } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n VectorMockOptions,\n VectorCollection,\n VectorEntry,\n QueryResult,\n VectorQuery,\n QueryHandler,\n} from \"./vector-types.js\";\nimport { createVectorRequestHandler, type VectorState } from \"./vector-handler.js\";\nimport { flattenHeaders, isJsonObject, readBody } from \"./helpers.js\";\n\nexport class VectorMock implements Mountable {\n private collections: Map<string, VectorCollection> = new Map();\n private queryHandlers: Map<string, QueryHandler> = new Map();\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: VectorMockOptions;\n private requestHandler: ReturnType<typeof createVectorRequestHandler>;\n\n constructor(options?: VectorMockOptions) {\n this.options = options ?? {};\n this.requestHandler = this.buildHandler();\n }\n\n // ---- Configuration ----\n\n addCollection(name: string, opts: { dimension: number }): this {\n const collection: VectorCollection = {\n name,\n dimension: opts.dimension,\n vectors: new Map(),\n };\n this.collections.set(name, collection);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n upsert(collection: string, vectors: VectorEntry[]): this {\n let col = this.collections.get(collection);\n if (!col) {\n const dim = vectors.length > 0 ? vectors[0].values.length : 0;\n col = { name: collection, dimension: dim, vectors: new Map() };\n this.collections.set(collection, col);\n }\n for (const v of vectors) {\n col.vectors.set(v.id, v);\n }\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n onQuery(\n collection: string,\n results: QueryResult[] | ((query: VectorQuery) => QueryResult[]),\n ): this {\n this.queryHandlers.set(collection, results);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n deleteCollection(name: string): this {\n this.collections.delete(name);\n this.queryHandlers.delete(name);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n // Only consume the request body for methods that carry a body,\n // avoiding double-consumption when the route doesn't match.\n let parsed: Record<string, unknown> = {};\n if (req.method !== \"GET\" && req.method !== \"DELETE\" && req.method !== \"HEAD\") {\n const body = await readBody(req);\n try {\n if (body) parsed = JSON.parse(body);\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 400, fixture: null },\n });\n }\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));\n return true;\n }\n\n // Reject bodies that parsed but are not a JSON object (e.g. `null`)\n // before the route handlers touch fields — `body.ids`, `body.vectors`,\n // `body.points` and friends throw a TypeError on `null` that nothing\n // catches, killing the process rather than answering the request.\n // Arrays and scalars are rejected for the same reason the JSON handlers\n // reject them: every field reads as `undefined` and the handler answers\n // a misleading `200` on a degenerate empty result.\n if (!isJsonObject(parsed)) {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 400, fixture: null },\n });\n }\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Request body must be a JSON object\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return true;\n }\n }\n\n const handled = this.requestHandler(req, res, pathname, parsed);\n\n // Record vector operation metric\n if (handled && this.registry) {\n const { operation, provider } = classifyVectorRequest(req.method ?? \"GET\", pathname);\n this.registry.incrementCounter(\"aimock_vector_requests_total\", { operation, provider });\n }\n\n // Journal the request after the handler completes\n if (handled && this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n\n // When mounted, an unmatched path is answered by the parent server, not\n // here, so journal it once the response is out and only if it really was\n // a 404 — otherwise a path this wrapper declines but the server rewrites\n // and serves would be journaled twice. Keeps mounted mode in step with\n // the standalone 404 journaling below.\n if (!handled && this.journal) {\n const journal = this.journal;\n res.once(\"finish\", () => {\n if (res.statusCode !== 404) return;\n journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 404, fixture: null },\n });\n });\n }\n\n return handled;\n }\n\n health(): { status: string; collections: number } {\n return {\n status: \"ok\",\n collections: this.collections.size,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"Server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise((resolve, reject) => {\n const srv = http.createServer((req, res) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => {\n const body = Buffer.concat(chunks).toString();\n let parsed: Record<string, unknown> = {};\n try {\n if (body) parsed = JSON.parse(body);\n } catch (parseErr) {\n if (req.method !== \"GET\") {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 400, fixture: null },\n });\n }\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));\n return;\n }\n }\n\n // Same non-object rejection as the mounted path above — a `null`\n // body otherwise reaches the route handlers and throws an uncaught\n // TypeError that takes the standalone server's process down.\n if (req.method !== \"GET\" && !isJsonObject(parsed)) {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 400, fixture: null },\n });\n }\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Request body must be a JSON object\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const url = new URL(req.url ?? \"/\", `http://${host}`);\n\n const handled = this.requestHandler(req, res, url.pathname, parsed);\n\n if (handled && this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n if (!handled) {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 404, fixture: null },\n });\n }\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n }\n });\n });\n\n srv.listen(port, host, () => {\n this.server = srv;\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n resolve(`http://${host}:${addr.port}`);\n } else {\n resolve(`http://${host}:${port}`);\n }\n });\n\n srv.on(\"error\", reject);\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"Server not started\");\n }\n const srv = this.server;\n this.server = null;\n await new Promise<void>((resolve, reject) => {\n srv.close((err) => (err ? reject(err) : resolve()));\n });\n }\n\n // ---- Inspection ----\n\n getRequests(): JournalEntry[] {\n if (!this.journal) return [];\n return this.journal.getAll().filter((e) => e.service === \"vector\");\n }\n\n reset(): this {\n this.collections.clear();\n this.queryHandlers.clear();\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n // ---- Internal ----\n\n private buildHandler() {\n const state: VectorState = {\n collections: this.collections,\n queryHandlers: this.queryHandlers,\n };\n return createVectorRequestHandler(state);\n }\n}\n\n// ---- Helpers ----\n\n/**\n * Classify a vector request by operation and provider based on HTTP method and pathname.\n */\nfunction classifyVectorRequest(\n method: string,\n pathname: string,\n): { operation: string; provider: string } {\n // Pinecone paths\n if (pathname === \"/query\" && method === \"POST\") {\n return { operation: \"query\", provider: \"pinecone\" };\n }\n if (pathname === \"/vectors/upsert\" && method === \"POST\") {\n return { operation: \"upsert\", provider: \"pinecone\" };\n }\n if (pathname === \"/vectors/delete\" && method === \"POST\") {\n return { operation: \"delete\", provider: \"pinecone\" };\n }\n if (pathname === \"/describe-index-stats\" && method === \"GET\") {\n return { operation: \"describe\", provider: \"pinecone\" };\n }\n\n // Qdrant paths\n if (/^\\/collections\\/[^/]+\\/points\\/search$/.test(pathname) && method === \"POST\") {\n return { operation: \"query\", provider: \"qdrant\" };\n }\n if (/^\\/collections\\/[^/]+\\/points$/.test(pathname) && method === \"PUT\") {\n return { operation: \"upsert\", provider: \"qdrant\" };\n }\n if (/^\\/collections\\/[^/]+\\/points\\/delete$/.test(pathname) && method === \"POST\") {\n return { operation: \"delete\", provider: \"qdrant\" };\n }\n\n // ChromaDB paths\n if (/^\\/api\\/v1\\/collections\\/[^/]+\\/query$/.test(pathname) && method === \"POST\") {\n return { operation: \"query\", provider: \"chromadb\" };\n }\n if (/^\\/api\\/v1\\/collections\\/[^/]+\\/add$/.test(pathname) && method === \"POST\") {\n return { operation: \"upsert\", provider: \"chromadb\" };\n }\n if (pathname === \"/api/v1/collections\" && method === \"GET\") {\n return { operation: \"list\", provider: \"chromadb\" };\n }\n if (/^\\/api\\/v1\\/collections\\/[^/]+$/.test(pathname) && method === \"DELETE\") {\n return { operation: \"delete\", provider: \"chromadb\" };\n }\n\n return { operation: \"unknown\", provider: \"unknown\" };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector-mock.d.cts","names":[],"sources":["../src/vector-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAea,UAAA,YAAsB;;EAAtB,QAAA,aAAW;EAAA,QAAA,MAAA;UASA,OAAA;UAkBc,QAAA;UAgBzB,OAAA;UAAyB,cAAA;aAAgB,CAAA,OAAA,CAAA,EAlC9B,iBAkC8B;eAiBxC,CAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAA;IACL,SAAK,EAAA,MAAA;MAET,IAAA;
|
|
1
|
+
{"version":3,"file":"vector-mock.d.cts","names":[],"sources":["../src/vector-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAea,UAAA,YAAsB;;EAAtB,QAAA,aAAW;EAAA,QAAA,MAAA;UASA,OAAA;UAkBc,QAAA;UAgBzB,OAAA;UAAyB,cAAA;aAAgB,CAAA,OAAA,CAAA,EAlC9B,iBAkC8B;eAiBxC,CAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAA;IACL,SAAK,EAAA,MAAA;MAET,IAAA;QA0GiB,CAAA,UAAA,EAAA,MAAA,EAAA,OAAA,EA9IgB,WA8IhB,EAAA,CAAA,EAAA,IAAA;SAIE,CAAA,UAAA,EAAA,MAAA,EAAA,OAAA,EAlIX,WAkIW,EAAA,GAAA,CAAA,CAAA,KAAA,EAlIc,WAkId,EAAA,GAlI8B,WAkI9B,EAAA,CAAA,CAAA,EAAA,IAAA;kBAMP,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,IAAA;eA2GD,CAAA,GAAA,EAlOP,MAAA,CAAK,eAkOE,EAAA,GAAA,EAjOP,MAAA,CAAK,cAiOE,EAAA,QAAA,EAAA,MAAA,CAAA,EA/NX,OA+NW,CAAA,OAAA,CAAA;QAaC,CAAA,CAAA,EAAA;IA3SkB,MAAA,EAAA,MAAA;IAAS,WAAA,EAAA,MAAA;;sBAyKtB;wBAIE;WAMP;UA2GD;iBAaC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector-mock.d.ts","names":[],"sources":["../src/vector-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAea,UAAA,YAAsB;;EAAtB,QAAA,aAAW;EAAA,QAAA,MAAA;UASA,OAAA;UAkBc,QAAA;UAgBzB,OAAA;UAAyB,cAAA;aAAgB,CAAA,OAAA,CAAA,EAlC9B,iBAkC8B;eAiBxC,CAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAA;IACL,SAAK,EAAA,MAAA;MAET,IAAA;
|
|
1
|
+
{"version":3,"file":"vector-mock.d.ts","names":[],"sources":["../src/vector-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAea,UAAA,YAAsB;;EAAtB,QAAA,aAAW;EAAA,QAAA,MAAA;UASA,OAAA;UAkBc,QAAA;UAgBzB,OAAA;UAAyB,cAAA;aAAgB,CAAA,OAAA,CAAA,EAlC9B,iBAkC8B;eAiBxC,CAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAA;IACL,SAAK,EAAA,MAAA;MAET,IAAA;QA0GiB,CAAA,UAAA,EAAA,MAAA,EAAA,OAAA,EA9IgB,WA8IhB,EAAA,CAAA,EAAA,IAAA;SAIE,CAAA,UAAA,EAAA,MAAA,EAAA,OAAA,EAlIX,WAkIW,EAAA,GAAA,CAAA,CAAA,KAAA,EAlIc,WAkId,EAAA,GAlI8B,WAkI9B,EAAA,CAAA,CAAA,EAAA,IAAA;kBAMP,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,IAAA;eA2GD,CAAA,GAAA,EAlOP,MAAA,CAAK,eAkOE,EAAA,GAAA,EAjOP,MAAA,CAAK,cAiOE,EAAA,QAAA,EAAA,MAAA,CAAA,EA/NX,OA+NW,CAAA,OAAA,CAAA;QAaC,CAAA,CAAA,EAAA;IA3SkB,MAAA,EAAA,MAAA;IAAS,WAAA,EAAA,MAAA;;sBAyKtB;wBAIE;WAMP;UA2GD;iBAaC"}
|
package/dist/vector-mock.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { flattenHeaders, readBody } from "./helpers.js";
|
|
1
|
+
import { flattenHeaders, isJsonObject, readBody } from "./helpers.js";
|
|
2
2
|
import { createVectorRequestHandler } from "./vector-handler.js";
|
|
3
3
|
import * as http$1 from "node:http";
|
|
4
4
|
|
|
@@ -58,10 +58,40 @@ var VectorMock = class {
|
|
|
58
58
|
if (body) parsed = JSON.parse(body);
|
|
59
59
|
} catch (parseErr) {
|
|
60
60
|
const detail = parseErr instanceof Error ? parseErr.message : "unknown";
|
|
61
|
+
if (this.journal) this.journal.add({
|
|
62
|
+
method: req.method ?? "GET",
|
|
63
|
+
path: req.url ?? "/",
|
|
64
|
+
headers: flattenHeaders(req.headers),
|
|
65
|
+
body: null,
|
|
66
|
+
service: "vector",
|
|
67
|
+
response: {
|
|
68
|
+
status: 400,
|
|
69
|
+
fixture: null
|
|
70
|
+
}
|
|
71
|
+
});
|
|
61
72
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
62
73
|
res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));
|
|
63
74
|
return true;
|
|
64
75
|
}
|
|
76
|
+
if (!isJsonObject(parsed)) {
|
|
77
|
+
if (this.journal) this.journal.add({
|
|
78
|
+
method: req.method ?? "GET",
|
|
79
|
+
path: req.url ?? "/",
|
|
80
|
+
headers: flattenHeaders(req.headers),
|
|
81
|
+
body: null,
|
|
82
|
+
service: "vector",
|
|
83
|
+
response: {
|
|
84
|
+
status: 400,
|
|
85
|
+
fixture: null
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
89
|
+
res.end(JSON.stringify({ error: {
|
|
90
|
+
message: "Request body must be a JSON object",
|
|
91
|
+
type: "invalid_request_error"
|
|
92
|
+
} }));
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
65
95
|
}
|
|
66
96
|
const handled = this.requestHandler(req, res, pathname, parsed);
|
|
67
97
|
if (handled && this.registry) {
|
|
@@ -82,6 +112,23 @@ var VectorMock = class {
|
|
|
82
112
|
fixture: null
|
|
83
113
|
}
|
|
84
114
|
});
|
|
115
|
+
if (!handled && this.journal) {
|
|
116
|
+
const journal = this.journal;
|
|
117
|
+
res.once("finish", () => {
|
|
118
|
+
if (res.statusCode !== 404) return;
|
|
119
|
+
journal.add({
|
|
120
|
+
method: req.method ?? "GET",
|
|
121
|
+
path: req.url ?? "/",
|
|
122
|
+
headers: flattenHeaders(req.headers),
|
|
123
|
+
body: null,
|
|
124
|
+
service: "vector",
|
|
125
|
+
response: {
|
|
126
|
+
status: 404,
|
|
127
|
+
fixture: null
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
85
132
|
return handled;
|
|
86
133
|
}
|
|
87
134
|
health() {
|
|
@@ -112,11 +159,41 @@ var VectorMock = class {
|
|
|
112
159
|
} catch (parseErr) {
|
|
113
160
|
if (req.method !== "GET") {
|
|
114
161
|
const detail = parseErr instanceof Error ? parseErr.message : "unknown";
|
|
162
|
+
if (this.journal) this.journal.add({
|
|
163
|
+
method: req.method ?? "GET",
|
|
164
|
+
path: req.url ?? "/",
|
|
165
|
+
headers: flattenHeaders(req.headers),
|
|
166
|
+
body: null,
|
|
167
|
+
service: "vector",
|
|
168
|
+
response: {
|
|
169
|
+
status: 400,
|
|
170
|
+
fixture: null
|
|
171
|
+
}
|
|
172
|
+
});
|
|
115
173
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
116
174
|
res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));
|
|
117
175
|
return;
|
|
118
176
|
}
|
|
119
177
|
}
|
|
178
|
+
if (req.method !== "GET" && !isJsonObject(parsed)) {
|
|
179
|
+
if (this.journal) this.journal.add({
|
|
180
|
+
method: req.method ?? "GET",
|
|
181
|
+
path: req.url ?? "/",
|
|
182
|
+
headers: flattenHeaders(req.headers),
|
|
183
|
+
body: null,
|
|
184
|
+
service: "vector",
|
|
185
|
+
response: {
|
|
186
|
+
status: 400,
|
|
187
|
+
fixture: null
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
191
|
+
res.end(JSON.stringify({ error: {
|
|
192
|
+
message: "Request body must be a JSON object",
|
|
193
|
+
type: "invalid_request_error"
|
|
194
|
+
} }));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
120
197
|
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
121
198
|
const handled = this.requestHandler(req, res, url.pathname, parsed);
|
|
122
199
|
if (handled && this.journal) this.journal.add({
|
|
@@ -131,6 +208,17 @@ var VectorMock = class {
|
|
|
131
208
|
}
|
|
132
209
|
});
|
|
133
210
|
if (!handled) {
|
|
211
|
+
if (this.journal) this.journal.add({
|
|
212
|
+
method: req.method ?? "GET",
|
|
213
|
+
path: req.url ?? "/",
|
|
214
|
+
headers: flattenHeaders(req.headers),
|
|
215
|
+
body: null,
|
|
216
|
+
service: "vector",
|
|
217
|
+
response: {
|
|
218
|
+
status: 404,
|
|
219
|
+
fixture: null
|
|
220
|
+
}
|
|
221
|
+
});
|
|
134
222
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
135
223
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
136
224
|
}
|
package/dist/vector-mock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector-mock.js","names":["http"],"sources":["../src/vector-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable, JournalEntry } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n VectorMockOptions,\n VectorCollection,\n VectorEntry,\n QueryResult,\n VectorQuery,\n QueryHandler,\n} from \"./vector-types.js\";\nimport { createVectorRequestHandler, type VectorState } from \"./vector-handler.js\";\nimport { flattenHeaders, readBody } from \"./helpers.js\";\n\nexport class VectorMock implements Mountable {\n private collections: Map<string, VectorCollection> = new Map();\n private queryHandlers: Map<string, QueryHandler> = new Map();\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: VectorMockOptions;\n private requestHandler: ReturnType<typeof createVectorRequestHandler>;\n\n constructor(options?: VectorMockOptions) {\n this.options = options ?? {};\n this.requestHandler = this.buildHandler();\n }\n\n // ---- Configuration ----\n\n addCollection(name: string, opts: { dimension: number }): this {\n const collection: VectorCollection = {\n name,\n dimension: opts.dimension,\n vectors: new Map(),\n };\n this.collections.set(name, collection);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n upsert(collection: string, vectors: VectorEntry[]): this {\n let col = this.collections.get(collection);\n if (!col) {\n const dim = vectors.length > 0 ? vectors[0].values.length : 0;\n col = { name: collection, dimension: dim, vectors: new Map() };\n this.collections.set(collection, col);\n }\n for (const v of vectors) {\n col.vectors.set(v.id, v);\n }\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n onQuery(\n collection: string,\n results: QueryResult[] | ((query: VectorQuery) => QueryResult[]),\n ): this {\n this.queryHandlers.set(collection, results);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n deleteCollection(name: string): this {\n this.collections.delete(name);\n this.queryHandlers.delete(name);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n // Only consume the request body for methods that carry a body,\n // avoiding double-consumption when the route doesn't match.\n let parsed: Record<string, unknown> = {};\n if (req.method !== \"GET\" && req.method !== \"DELETE\" && req.method !== \"HEAD\") {\n const body = await readBody(req);\n try {\n if (body) parsed = JSON.parse(body);\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));\n return true;\n }\n }\n\n const handled = this.requestHandler(req, res, pathname, parsed);\n\n // Record vector operation metric\n if (handled && this.registry) {\n const { operation, provider } = classifyVectorRequest(req.method ?? \"GET\", pathname);\n this.registry.incrementCounter(\"aimock_vector_requests_total\", { operation, provider });\n }\n\n // Journal the request after the handler completes\n if (handled && this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n\n return handled;\n }\n\n health(): { status: string; collections: number } {\n return {\n status: \"ok\",\n collections: this.collections.size,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"Server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise((resolve, reject) => {\n const srv = http.createServer((req, res) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => {\n const body = Buffer.concat(chunks).toString();\n let parsed: Record<string, unknown> = {};\n try {\n if (body) parsed = JSON.parse(body);\n } catch (parseErr) {\n if (req.method !== \"GET\") {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));\n return;\n }\n }\n\n const url = new URL(req.url ?? \"/\", `http://${host}`);\n\n const handled = this.requestHandler(req, res, url.pathname, parsed);\n\n if (handled && this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n if (!handled) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n }\n });\n });\n\n srv.listen(port, host, () => {\n this.server = srv;\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n resolve(`http://${host}:${addr.port}`);\n } else {\n resolve(`http://${host}:${port}`);\n }\n });\n\n srv.on(\"error\", reject);\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"Server not started\");\n }\n const srv = this.server;\n this.server = null;\n await new Promise<void>((resolve, reject) => {\n srv.close((err) => (err ? reject(err) : resolve()));\n });\n }\n\n // ---- Inspection ----\n\n getRequests(): JournalEntry[] {\n if (!this.journal) return [];\n return this.journal.getAll().filter((e) => e.service === \"vector\");\n }\n\n reset(): this {\n this.collections.clear();\n this.queryHandlers.clear();\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n // ---- Internal ----\n\n private buildHandler() {\n const state: VectorState = {\n collections: this.collections,\n queryHandlers: this.queryHandlers,\n };\n return createVectorRequestHandler(state);\n }\n}\n\n// ---- Helpers ----\n\n/**\n * Classify a vector request by operation and provider based on HTTP method and pathname.\n */\nfunction classifyVectorRequest(\n method: string,\n pathname: string,\n): { operation: string; provider: string } {\n // Pinecone paths\n if (pathname === \"/query\" && method === \"POST\") {\n return { operation: \"query\", provider: \"pinecone\" };\n }\n if (pathname === \"/vectors/upsert\" && method === \"POST\") {\n return { operation: \"upsert\", provider: \"pinecone\" };\n }\n if (pathname === \"/vectors/delete\" && method === \"POST\") {\n return { operation: \"delete\", provider: \"pinecone\" };\n }\n if (pathname === \"/describe-index-stats\" && method === \"GET\") {\n return { operation: \"describe\", provider: \"pinecone\" };\n }\n\n // Qdrant paths\n if (/^\\/collections\\/[^/]+\\/points\\/search$/.test(pathname) && method === \"POST\") {\n return { operation: \"query\", provider: \"qdrant\" };\n }\n if (/^\\/collections\\/[^/]+\\/points$/.test(pathname) && method === \"PUT\") {\n return { operation: \"upsert\", provider: \"qdrant\" };\n }\n if (/^\\/collections\\/[^/]+\\/points\\/delete$/.test(pathname) && method === \"POST\") {\n return { operation: \"delete\", provider: \"qdrant\" };\n }\n\n // ChromaDB paths\n if (/^\\/api\\/v1\\/collections\\/[^/]+\\/query$/.test(pathname) && method === \"POST\") {\n return { operation: \"query\", provider: \"chromadb\" };\n }\n if (/^\\/api\\/v1\\/collections\\/[^/]+\\/add$/.test(pathname) && method === \"POST\") {\n return { operation: \"upsert\", provider: \"chromadb\" };\n }\n if (pathname === \"/api/v1/collections\" && method === \"GET\") {\n return { operation: \"list\", provider: \"chromadb\" };\n }\n if (/^\\/api\\/v1\\/collections\\/[^/]+$/.test(pathname) && method === \"DELETE\") {\n return { operation: \"delete\", provider: \"chromadb\" };\n }\n\n return { operation: \"unknown\", provider: \"unknown\" };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vector-mock.js","names":["http"],"sources":["../src/vector-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable, JournalEntry } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n VectorMockOptions,\n VectorCollection,\n VectorEntry,\n QueryResult,\n VectorQuery,\n QueryHandler,\n} from \"./vector-types.js\";\nimport { createVectorRequestHandler, type VectorState } from \"./vector-handler.js\";\nimport { flattenHeaders, isJsonObject, readBody } from \"./helpers.js\";\n\nexport class VectorMock implements Mountable {\n private collections: Map<string, VectorCollection> = new Map();\n private queryHandlers: Map<string, QueryHandler> = new Map();\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: VectorMockOptions;\n private requestHandler: ReturnType<typeof createVectorRequestHandler>;\n\n constructor(options?: VectorMockOptions) {\n this.options = options ?? {};\n this.requestHandler = this.buildHandler();\n }\n\n // ---- Configuration ----\n\n addCollection(name: string, opts: { dimension: number }): this {\n const collection: VectorCollection = {\n name,\n dimension: opts.dimension,\n vectors: new Map(),\n };\n this.collections.set(name, collection);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n upsert(collection: string, vectors: VectorEntry[]): this {\n let col = this.collections.get(collection);\n if (!col) {\n const dim = vectors.length > 0 ? vectors[0].values.length : 0;\n col = { name: collection, dimension: dim, vectors: new Map() };\n this.collections.set(collection, col);\n }\n for (const v of vectors) {\n col.vectors.set(v.id, v);\n }\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n onQuery(\n collection: string,\n results: QueryResult[] | ((query: VectorQuery) => QueryResult[]),\n ): this {\n this.queryHandlers.set(collection, results);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n deleteCollection(name: string): this {\n this.collections.delete(name);\n this.queryHandlers.delete(name);\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n // Only consume the request body for methods that carry a body,\n // avoiding double-consumption when the route doesn't match.\n let parsed: Record<string, unknown> = {};\n if (req.method !== \"GET\" && req.method !== \"DELETE\" && req.method !== \"HEAD\") {\n const body = await readBody(req);\n try {\n if (body) parsed = JSON.parse(body);\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 400, fixture: null },\n });\n }\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));\n return true;\n }\n\n // Reject bodies that parsed but are not a JSON object (e.g. `null`)\n // before the route handlers touch fields — `body.ids`, `body.vectors`,\n // `body.points` and friends throw a TypeError on `null` that nothing\n // catches, killing the process rather than answering the request.\n // Arrays and scalars are rejected for the same reason the JSON handlers\n // reject them: every field reads as `undefined` and the handler answers\n // a misleading `200` on a degenerate empty result.\n if (!isJsonObject(parsed)) {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 400, fixture: null },\n });\n }\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Request body must be a JSON object\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return true;\n }\n }\n\n const handled = this.requestHandler(req, res, pathname, parsed);\n\n // Record vector operation metric\n if (handled && this.registry) {\n const { operation, provider } = classifyVectorRequest(req.method ?? \"GET\", pathname);\n this.registry.incrementCounter(\"aimock_vector_requests_total\", { operation, provider });\n }\n\n // Journal the request after the handler completes\n if (handled && this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n\n // When mounted, an unmatched path is answered by the parent server, not\n // here, so journal it once the response is out and only if it really was\n // a 404 — otherwise a path this wrapper declines but the server rewrites\n // and serves would be journaled twice. Keeps mounted mode in step with\n // the standalone 404 journaling below.\n if (!handled && this.journal) {\n const journal = this.journal;\n res.once(\"finish\", () => {\n if (res.statusCode !== 404) return;\n journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 404, fixture: null },\n });\n });\n }\n\n return handled;\n }\n\n health(): { status: string; collections: number } {\n return {\n status: \"ok\",\n collections: this.collections.size,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"Server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise((resolve, reject) => {\n const srv = http.createServer((req, res) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => {\n const body = Buffer.concat(chunks).toString();\n let parsed: Record<string, unknown> = {};\n try {\n if (body) parsed = JSON.parse(body);\n } catch (parseErr) {\n if (req.method !== \"GET\") {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 400, fixture: null },\n });\n }\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Malformed JSON body: ${detail}` }));\n return;\n }\n }\n\n // Same non-object rejection as the mounted path above — a `null`\n // body otherwise reaches the route handlers and throws an uncaught\n // TypeError that takes the standalone server's process down.\n if (req.method !== \"GET\" && !isJsonObject(parsed)) {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 400, fixture: null },\n });\n }\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Request body must be a JSON object\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const url = new URL(req.url ?? \"/\", `http://${host}`);\n\n const handled = this.requestHandler(req, res, url.pathname, parsed);\n\n if (handled && this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n if (!handled) {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"GET\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"vector\",\n response: { status: 404, fixture: null },\n });\n }\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n }\n });\n });\n\n srv.listen(port, host, () => {\n this.server = srv;\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n resolve(`http://${host}:${addr.port}`);\n } else {\n resolve(`http://${host}:${port}`);\n }\n });\n\n srv.on(\"error\", reject);\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"Server not started\");\n }\n const srv = this.server;\n this.server = null;\n await new Promise<void>((resolve, reject) => {\n srv.close((err) => (err ? reject(err) : resolve()));\n });\n }\n\n // ---- Inspection ----\n\n getRequests(): JournalEntry[] {\n if (!this.journal) return [];\n return this.journal.getAll().filter((e) => e.service === \"vector\");\n }\n\n reset(): this {\n this.collections.clear();\n this.queryHandlers.clear();\n this.requestHandler = this.buildHandler();\n return this;\n }\n\n // ---- Internal ----\n\n private buildHandler() {\n const state: VectorState = {\n collections: this.collections,\n queryHandlers: this.queryHandlers,\n };\n return createVectorRequestHandler(state);\n }\n}\n\n// ---- Helpers ----\n\n/**\n * Classify a vector request by operation and provider based on HTTP method and pathname.\n */\nfunction classifyVectorRequest(\n method: string,\n pathname: string,\n): { operation: string; provider: string } {\n // Pinecone paths\n if (pathname === \"/query\" && method === \"POST\") {\n return { operation: \"query\", provider: \"pinecone\" };\n }\n if (pathname === \"/vectors/upsert\" && method === \"POST\") {\n return { operation: \"upsert\", provider: \"pinecone\" };\n }\n if (pathname === \"/vectors/delete\" && method === \"POST\") {\n return { operation: \"delete\", provider: \"pinecone\" };\n }\n if (pathname === \"/describe-index-stats\" && method === \"GET\") {\n return { operation: \"describe\", provider: \"pinecone\" };\n }\n\n // Qdrant paths\n if (/^\\/collections\\/[^/]+\\/points\\/search$/.test(pathname) && method === \"POST\") {\n return { operation: \"query\", provider: \"qdrant\" };\n }\n if (/^\\/collections\\/[^/]+\\/points$/.test(pathname) && method === \"PUT\") {\n return { operation: \"upsert\", provider: \"qdrant\" };\n }\n if (/^\\/collections\\/[^/]+\\/points\\/delete$/.test(pathname) && method === \"POST\") {\n return { operation: \"delete\", provider: \"qdrant\" };\n }\n\n // ChromaDB paths\n if (/^\\/api\\/v1\\/collections\\/[^/]+\\/query$/.test(pathname) && method === \"POST\") {\n return { operation: \"query\", provider: \"chromadb\" };\n }\n if (/^\\/api\\/v1\\/collections\\/[^/]+\\/add$/.test(pathname) && method === \"POST\") {\n return { operation: \"upsert\", provider: \"chromadb\" };\n }\n if (pathname === \"/api/v1/collections\" && method === \"GET\") {\n return { operation: \"list\", provider: \"chromadb\" };\n }\n if (/^\\/api\\/v1\\/collections\\/[^/]+$/.test(pathname) && method === \"DELETE\") {\n return { operation: \"delete\", provider: \"chromadb\" };\n }\n\n return { operation: \"unknown\", provider: \"unknown\" };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector-types.d.cts","names":[],"sources":["../src/vector-types.ts"],"sourcesContent":[],"mappings":";UAAiB,iBAAA;EAAA,IAAA,CAAA,EAAA,MAAA;EAKA,IAAA,CAAA,EAAA,MAAA;;AAGM,UAHN,gBAAA,CAGM;MAAZ,EAAA,MAAA;EAAG,SAAA,EAAA,MAAA;EAGG,OAAA,EAHN,GAGM,CAAA,MAAW,EAHL,
|
|
1
|
+
{"version":3,"file":"vector-types.d.cts","names":[],"sources":["../src/vector-types.ts"],"sourcesContent":[],"mappings":";UAAiB,iBAAA;EAAA,IAAA,CAAA,EAAA,MAAA;EAKA,IAAA,CAAA,EAAA,MAAA;;AAGM,UAHN,gBAAA,CAGM;MAAZ,EAAA,MAAA;EAAG,SAAA,EAAA,MAAA;EAGG,OAAA,EAHN,GAGM,CAAA,MAAW,EAHL,WAMV,CAAM;AAGnB;AAOiB,UAbA,WAAA,CAaW;EAOhB,EAAA,EAAA,MAAA;EAAY,MAAA,EAAA,MAAA,EAAA;UAAG,CAAA,EAjBd,MAiBc,CAAA,MAAA,EAAA,OAAA,CAAA;;AAAyC,UAdnD,WAAA,CAcmD;EAAW,EAAA,EAAA,MAAA;;aAXlE;;;UAII,WAAA;;;;;;KAOL,YAAA,GAAe,yBAAyB,gBAAgB"}
|
package/dist/veo-video.cjs
CHANGED
|
@@ -192,7 +192,7 @@ async function handleVeoVideoCreate(req, res, raw, model, fixtures, journal, def
|
|
|
192
192
|
journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
193
193
|
defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
|
|
194
194
|
} else defaults.logger.debug(`No fixture matched for Veo request (model=${model}, msg="${prompt.slice(0, 80)}")`);
|
|
195
|
-
if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
|
|
195
|
+
if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, req.url, journal, {
|
|
196
196
|
method,
|
|
197
197
|
path,
|
|
198
198
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
@@ -312,7 +312,7 @@ async function handleVeoVideoStatus(req, res, operationName, fixtures, journal,
|
|
|
312
312
|
setCorsHeaders(res);
|
|
313
313
|
const path = req.url ?? `/v1beta/${operationName}`;
|
|
314
314
|
const method = req.method ?? "GET";
|
|
315
|
-
if (require_chaos.applyChaos(res, null, defaults.chaos, req.headers, journal, {
|
|
315
|
+
if (require_chaos.applyChaos(res, null, defaults.chaos, req.headers, req.url, journal, {
|
|
316
316
|
method,
|
|
317
317
|
path,
|
|
318
318
|
headers: require_helpers.flattenHeaders(req.headers),
|