@crvouga/mockingbird-service-aws-speech 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/README.md +132 -0
- package/dist/chunk-3DE3INNY.js +3410 -0
- package/dist/chunk-3DE3INNY.js.map +7 -0
- package/dist/chunk-A46XUZ6Z.js +306 -0
- package/dist/chunk-A46XUZ6Z.js.map +7 -0
- package/dist/cli.js +371 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +1066 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +7 -0
- package/dist/server.d.ts +1314 -0
- package/dist/server.js +14 -0
- package/dist/server.js.map +7 -0
- package/package.json +96 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRuntime
|
|
3
|
+
} from "./chunk-3DE3INNY.js";
|
|
4
|
+
|
|
5
|
+
// src/h2c.ts
|
|
6
|
+
import {
|
|
7
|
+
createServer as createHttp1Server
|
|
8
|
+
} from "node:http";
|
|
9
|
+
import {
|
|
10
|
+
createServer as createHttp2Server,
|
|
11
|
+
constants as http2Constants
|
|
12
|
+
} from "node:http2";
|
|
13
|
+
import { connect, createServer as createNetServer } from "node:net";
|
|
14
|
+
import { Readable } from "node:stream";
|
|
15
|
+
var PREFACE = "PRI * HTTP/2.0";
|
|
16
|
+
var CONNECTION_HEADERS = /* @__PURE__ */ new Set([
|
|
17
|
+
"connection",
|
|
18
|
+
"keep-alive",
|
|
19
|
+
"proxy-connection",
|
|
20
|
+
"transfer-encoding",
|
|
21
|
+
"upgrade",
|
|
22
|
+
"host"
|
|
23
|
+
]);
|
|
24
|
+
var toWebBody = (stream) => Readable.toWeb(stream);
|
|
25
|
+
var isDrop = (error) => error?.code === "MOCKINGBIRD_DROP";
|
|
26
|
+
var internalError = (error) => JSON.stringify({
|
|
27
|
+
error: {
|
|
28
|
+
type: "mockingbird_internal",
|
|
29
|
+
message: error instanceof Error ? error.message : String(error)
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
var pump = async (body, write, drained, closed) => {
|
|
33
|
+
const reader = body.getReader();
|
|
34
|
+
try {
|
|
35
|
+
for (; ; ) {
|
|
36
|
+
if (closed()) {
|
|
37
|
+
await reader.cancel().catch(() => void 0);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const { done, value } = await reader.read();
|
|
41
|
+
if (done) return;
|
|
42
|
+
if (!write(value)) await drained();
|
|
43
|
+
}
|
|
44
|
+
} finally {
|
|
45
|
+
reader.releaseLock();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var handleHttp1 = async (api, base, req, res) => {
|
|
49
|
+
const method = req.method ?? "GET";
|
|
50
|
+
const url = new URL((req.url ?? "/").replace(/^\/+/, "/"), `http://${req.headers.host ?? base}`);
|
|
51
|
+
const headers = new Headers();
|
|
52
|
+
for (const [name, value] of Object.entries(req.headers)) {
|
|
53
|
+
if (value === void 0) continue;
|
|
54
|
+
for (const each of Array.isArray(value) ? value : [value]) headers.append(name, each);
|
|
55
|
+
}
|
|
56
|
+
const aborted = new AbortController();
|
|
57
|
+
res.once("close", () => {
|
|
58
|
+
if (!res.writableFinished) aborted.abort();
|
|
59
|
+
});
|
|
60
|
+
const hasBody = method !== "GET" && method !== "HEAD";
|
|
61
|
+
const request = new Request(url, {
|
|
62
|
+
method,
|
|
63
|
+
headers,
|
|
64
|
+
signal: aborted.signal,
|
|
65
|
+
...hasBody ? { body: toWebBody(req), duplex: "half" } : {}
|
|
66
|
+
});
|
|
67
|
+
let response;
|
|
68
|
+
try {
|
|
69
|
+
response = await api.fetch(request);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (isDrop(error)) {
|
|
72
|
+
req.socket.destroy();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
res.writeHead(500, { "content-type": "application/json" });
|
|
76
|
+
res.end(internalError(error));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const out = Object.fromEntries(response.headers);
|
|
80
|
+
const cookies = response.headers.getSetCookie();
|
|
81
|
+
if (cookies.length > 0) out["set-cookie"] = cookies;
|
|
82
|
+
res.writeHead(response.status, out);
|
|
83
|
+
if (!response.body) {
|
|
84
|
+
res.end();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
res.flushHeaders();
|
|
88
|
+
try {
|
|
89
|
+
await pump(
|
|
90
|
+
response.body,
|
|
91
|
+
(chunk) => res.write(chunk),
|
|
92
|
+
() => new Promise((resolve) => res.once("drain", resolve)),
|
|
93
|
+
() => res.destroyed
|
|
94
|
+
);
|
|
95
|
+
res.end();
|
|
96
|
+
} catch {
|
|
97
|
+
res.destroy();
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var handleHttp2 = async (api, base, stream, incoming) => {
|
|
101
|
+
const method = String(incoming[":method"] ?? "GET");
|
|
102
|
+
const authority = String(incoming[":authority"] ?? incoming.host ?? base);
|
|
103
|
+
const url = new URL(String(incoming[":path"] ?? "/").replace(/^\/+/, "/"), `http://${authority}`);
|
|
104
|
+
const headers = new Headers();
|
|
105
|
+
for (const [name, value] of Object.entries(incoming)) {
|
|
106
|
+
if (name.startsWith(":") || value === void 0) continue;
|
|
107
|
+
for (const each of Array.isArray(value) ? value : [value]) headers.append(name, String(each));
|
|
108
|
+
}
|
|
109
|
+
const aborted = new AbortController();
|
|
110
|
+
stream.once("close", () => {
|
|
111
|
+
if (!stream.writableFinished) aborted.abort();
|
|
112
|
+
});
|
|
113
|
+
stream.on("error", () => void 0);
|
|
114
|
+
const hasBody = method !== "GET" && method !== "HEAD";
|
|
115
|
+
const request = new Request(url, {
|
|
116
|
+
method,
|
|
117
|
+
headers,
|
|
118
|
+
signal: aborted.signal,
|
|
119
|
+
...hasBody ? { body: toWebBody(stream), duplex: "half" } : {}
|
|
120
|
+
});
|
|
121
|
+
let response;
|
|
122
|
+
try {
|
|
123
|
+
response = await api.fetch(request);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (isDrop(error)) {
|
|
126
|
+
stream.close(http2Constants.NGHTTP2_INTERNAL_ERROR);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (stream.destroyed) return;
|
|
130
|
+
stream.respond({ ":status": 500, "content-type": "application/json" });
|
|
131
|
+
stream.end(internalError(error));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (stream.destroyed || stream.closed) return;
|
|
135
|
+
const out = { ":status": response.status };
|
|
136
|
+
for (const [name, value] of response.headers) {
|
|
137
|
+
if (!CONNECTION_HEADERS.has(name)) out[name] = value;
|
|
138
|
+
}
|
|
139
|
+
if (!response.body) {
|
|
140
|
+
stream.respond(out, { endStream: true });
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
stream.respond(out);
|
|
144
|
+
try {
|
|
145
|
+
await pump(
|
|
146
|
+
response.body,
|
|
147
|
+
(chunk) => stream.write(chunk),
|
|
148
|
+
() => new Promise((resolve) => stream.once("drain", resolve)),
|
|
149
|
+
() => stream.destroyed || stream.closed
|
|
150
|
+
);
|
|
151
|
+
if (!stream.destroyed) stream.end();
|
|
152
|
+
} catch {
|
|
153
|
+
if (!stream.destroyed) stream.close(http2Constants.NGHTTP2_INTERNAL_ERROR);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
var listenInternal = (server) => new Promise((resolve, reject) => {
|
|
157
|
+
;
|
|
158
|
+
server.once("error", reject);
|
|
159
|
+
server.listen(0, "127.0.0.1", () => resolve(server.address().port));
|
|
160
|
+
});
|
|
161
|
+
var listenH2c = async (api, options = {}) => {
|
|
162
|
+
const host = options.host ?? "127.0.0.1";
|
|
163
|
+
const shown = host.includes(":") ? `[${host}]` : host;
|
|
164
|
+
let base = `${shown}:${options.port ?? 0}`;
|
|
165
|
+
const h1 = createHttp1Server((req, res) => {
|
|
166
|
+
void handleHttp1(api, base, req, res);
|
|
167
|
+
});
|
|
168
|
+
const h2 = createHttp2Server();
|
|
169
|
+
h2.on("stream", (stream, headers) => {
|
|
170
|
+
void handleHttp2(api, base, stream, headers);
|
|
171
|
+
});
|
|
172
|
+
h2.on("sessionError", () => void 0);
|
|
173
|
+
const h1Port = await listenInternal(h1);
|
|
174
|
+
const h2Port = await listenInternal(h2);
|
|
175
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
176
|
+
const track = (socket) => {
|
|
177
|
+
sockets.add(socket);
|
|
178
|
+
socket.once("close", () => sockets.delete(socket));
|
|
179
|
+
};
|
|
180
|
+
const front = createNetServer((socket) => {
|
|
181
|
+
track(socket);
|
|
182
|
+
socket.setNoDelay(true);
|
|
183
|
+
let seen = Buffer.alloc(0);
|
|
184
|
+
const onData = (chunk) => {
|
|
185
|
+
seen = Buffer.concat([seen, chunk]);
|
|
186
|
+
if (seen.length < 3) return;
|
|
187
|
+
socket.off("data", onData);
|
|
188
|
+
socket.pause();
|
|
189
|
+
const http2 = seen.subarray(0, 3).toString("latin1") === PREFACE.slice(0, 3);
|
|
190
|
+
const upstream = connect(http2 ? h2Port : h1Port, "127.0.0.1");
|
|
191
|
+
track(upstream);
|
|
192
|
+
upstream.setNoDelay(true);
|
|
193
|
+
const destroy = () => {
|
|
194
|
+
socket.destroy();
|
|
195
|
+
upstream.destroy();
|
|
196
|
+
};
|
|
197
|
+
socket.on("error", destroy);
|
|
198
|
+
upstream.on("error", destroy);
|
|
199
|
+
socket.once("close", () => upstream.destroy());
|
|
200
|
+
upstream.once("close", () => socket.destroy());
|
|
201
|
+
upstream.write(seen);
|
|
202
|
+
socket.pipe(upstream);
|
|
203
|
+
upstream.pipe(socket);
|
|
204
|
+
socket.resume();
|
|
205
|
+
};
|
|
206
|
+
socket.on("data", onData);
|
|
207
|
+
socket.on("error", () => socket.destroy());
|
|
208
|
+
});
|
|
209
|
+
await new Promise((resolve, reject) => {
|
|
210
|
+
front.once("error", reject);
|
|
211
|
+
front.listen(options.port ?? 0, host, () => resolve());
|
|
212
|
+
});
|
|
213
|
+
const port = front.address().port;
|
|
214
|
+
base = `${shown}:${port}`;
|
|
215
|
+
const closeServer = (server) => new Promise((resolve) => {
|
|
216
|
+
server.close(() => resolve());
|
|
217
|
+
});
|
|
218
|
+
return {
|
|
219
|
+
url: `http://${shown}:${port}`,
|
|
220
|
+
port,
|
|
221
|
+
host,
|
|
222
|
+
close: async () => {
|
|
223
|
+
const closing = [
|
|
224
|
+
closeServer(front),
|
|
225
|
+
closeServer(h1),
|
|
226
|
+
closeServer(h2)
|
|
227
|
+
];
|
|
228
|
+
for (const socket of sockets) socket.destroy();
|
|
229
|
+
h1.closeAllConnections?.();
|
|
230
|
+
await Promise.all(closing);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// src/server.ts
|
|
236
|
+
var DEFAULT_PORT = 8797;
|
|
237
|
+
var createServer = async (options = {}) => {
|
|
238
|
+
const { port, host, ...rest } = options;
|
|
239
|
+
const runtime = createRuntime(rest);
|
|
240
|
+
const listening = await listenH2c(runtime, {
|
|
241
|
+
port: port ?? 0,
|
|
242
|
+
...host !== void 0 ? { host } : {}
|
|
243
|
+
});
|
|
244
|
+
return { ...listening, runtime };
|
|
245
|
+
};
|
|
246
|
+
var text = (value) => typeof value === "string" ? value : void 0;
|
|
247
|
+
var loadTranscripts = async (path) => {
|
|
248
|
+
const { readFile } = await import("node:fs/promises");
|
|
249
|
+
const raw = JSON.parse(await readFile(path, "utf8"));
|
|
250
|
+
const list = Array.isArray(raw) ? raw : raw.transcripts;
|
|
251
|
+
if (!Array.isArray(list)) throw new Error(`${path}: expected {"transcripts": [...]}`);
|
|
252
|
+
return list.map((each, index) => {
|
|
253
|
+
const item = each;
|
|
254
|
+
if (typeof item?.final !== "string")
|
|
255
|
+
throw new Error(`${path}: transcripts[${index}].final must be a string`);
|
|
256
|
+
return { ...item, id: item.id ?? `transcript_${index + 1}` };
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
var serveTarget = {
|
|
260
|
+
name: "aws-speech",
|
|
261
|
+
defaultPort: DEFAULT_PORT,
|
|
262
|
+
options: {
|
|
263
|
+
transcripts: {
|
|
264
|
+
type: "string",
|
|
265
|
+
value: "<file.json>",
|
|
266
|
+
description: 'Transcripts every namespace starts with ({"transcripts": [...]}, as PUT /__admin/transcripts)'
|
|
267
|
+
},
|
|
268
|
+
"default-transcript": {
|
|
269
|
+
type: "string",
|
|
270
|
+
value: "<text>",
|
|
271
|
+
description: 'What an unscripted session or job hears (default "Hello.")'
|
|
272
|
+
},
|
|
273
|
+
"s3-endpoint": {
|
|
274
|
+
type: "string",
|
|
275
|
+
value: "<url>",
|
|
276
|
+
description: "Write completed batch transcripts to this S3 (s3rver) at OutputBucketName/OutputKey"
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
create: async (values, common) => {
|
|
280
|
+
const transcriptsPath = text(values.transcripts);
|
|
281
|
+
const defaultTranscript = text(values["default-transcript"]);
|
|
282
|
+
const s3 = text(values["s3-endpoint"]);
|
|
283
|
+
return createRuntime({
|
|
284
|
+
...transcriptsPath ? { transcripts: await loadTranscripts(transcriptsPath) } : {},
|
|
285
|
+
...defaultTranscript !== void 0 ? { settings: { defaultTranscript } } : {},
|
|
286
|
+
...s3 ? { transcriptStore: { endpoint: s3 } } : {},
|
|
287
|
+
...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {},
|
|
288
|
+
...common.seed !== void 0 ? { seed: common.seed } : {},
|
|
289
|
+
...common.onLog ? { onLog: common.onLog } : {}
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
banner: () => [
|
|
293
|
+
"point the app at it: AWS_ENDPOINT_URL_POLLY / AWS_ENDPOINT_URL_TRANSCRIBE_STREAMING / AWS_ENDPOINT_URL_TRANSCRIBE = this URL",
|
|
294
|
+
"protocols: h2c (prior knowledge) and HTTP/1.1 on the same port",
|
|
295
|
+
"namespaces: x-mockingbird-namespace, /ns/<name>/\u2026, or PUT /__admin/credentials {<AWS_ACCESS_KEY_ID>: <ns>}",
|
|
296
|
+
'transcripts: PUT /__admin/transcripts {match: {sessionIndex} | {any: true}, partials: [...], final: "..."}'
|
|
297
|
+
]
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export {
|
|
301
|
+
listenH2c,
|
|
302
|
+
DEFAULT_PORT,
|
|
303
|
+
createServer,
|
|
304
|
+
serveTarget
|
|
305
|
+
};
|
|
306
|
+
//# sourceMappingURL=chunk-A46XUZ6Z.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/h2c.ts", "../src/server.ts"],
|
|
4
|
+
"sourcesContent": ["// Copied verbatim from @crvouga/mockingbird-service-bedrock/src/h2c.ts (published services cannot share\n// source without a runtime dependency on each other). Keep the two copies identical.\n/// <reference types=\"node\" />\n/**\n * One port that speaks both cleartext HTTP/2 (prior knowledge, \"h2c\") and HTTP/1.1.\n *\n * AWS SDK v3 clients for Bedrock Runtime, Polly and Transcribe Streaming default to\n * `NodeHttp2Handler`, so against an `http://` endpoint they open an h2c connection and\n * send the HTTP/2 connection preface straight away; bidirectional operations (Nova Sonic,\n * `StartSpeechSynthesisStream`, `StartStreamTranscription`) need HTTP/2 duplex. The AI SDK,\n * AgentCore and Transcribe batch clients speak HTTP/1.1. A front `node:net` server reads\n * the first bytes of each connection and hands it to an internal HTTP/2 or HTTP/1.1\n * server; both turn requests into Fetch `Request`s whose bodies stream in as they arrive,\n * and stream responses back chunk by chunk.\n */\nimport {\n createServer as createHttp1Server,\n type IncomingMessage,\n type ServerResponse,\n} from \"node:http\"\nimport {\n createServer as createHttp2Server,\n constants as http2Constants,\n type IncomingHttpHeaders,\n type ServerHttp2Stream,\n} from \"node:http2\"\nimport { type AddressInfo, connect, createServer as createNetServer, type Socket } from \"node:net\"\nimport { Readable } from \"node:stream\"\nimport type { FetchAPI } from \"@crvouga/mockingbird-core\"\n\nexport type H2cListenOptions = {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`. */\n host?: string\n}\n\n/** A running dual-protocol listener. */\nexport type H2cListening = {\n url: string\n port: number\n host: string\n close(): Promise<void>\n}\n\nconst PREFACE = \"PRI * HTTP/2.0\"\n\n/** Headers HTTP/2 forbids on a response (RFC 9113 \u00A78.2.2). */\nconst CONNECTION_HEADERS = new Set([\n \"connection\",\n \"keep-alive\",\n \"proxy-connection\",\n \"transfer-encoding\",\n \"upgrade\",\n \"host\",\n])\n\nconst toWebBody = (stream: Readable): ReadableStream<Uint8Array> =>\n Readable.toWeb(stream) as unknown as ReadableStream<Uint8Array>\n\nconst isDrop = (error: unknown) => (error as { code?: string } | null)?.code === \"MOCKINGBIRD_DROP\"\n\nconst internalError = (error: unknown) =>\n JSON.stringify({\n error: {\n type: \"mockingbird_internal\",\n message: error instanceof Error ? error.message : String(error),\n },\n })\n\n/** Write a Fetch response body to a Node writable, respecting backpressure. */\nconst pump = async (\n body: ReadableStream<Uint8Array>,\n write: (chunk: Uint8Array) => boolean,\n drained: () => Promise<void>,\n closed: () => boolean,\n): Promise<void> => {\n const reader = body.getReader()\n try {\n for (;;) {\n if (closed()) {\n await reader.cancel().catch(() => undefined)\n return\n }\n const { done, value } = await reader.read()\n if (done) return\n if (!write(value)) await drained()\n }\n } finally {\n reader.releaseLock()\n }\n}\n\nconst handleHttp1 = async (\n api: FetchAPI,\n base: string,\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> => {\n const method = req.method ?? \"GET\"\n const url = new URL((req.url ?? \"/\").replace(/^\\/+/, \"/\"), `http://${req.headers.host ?? base}`)\n const headers = new Headers()\n for (const [name, value] of Object.entries(req.headers)) {\n if (value === undefined) continue\n for (const each of Array.isArray(value) ? value : [value]) headers.append(name, each)\n }\n const aborted = new AbortController()\n res.once(\"close\", () => {\n if (!res.writableFinished) aborted.abort()\n })\n const hasBody = method !== \"GET\" && method !== \"HEAD\"\n const request = new Request(url, {\n method,\n headers,\n signal: aborted.signal,\n ...(hasBody ? { body: toWebBody(req), duplex: \"half\" } : {}),\n } as RequestInit)\n let response: Response\n try {\n response = await api.fetch(request)\n } catch (error) {\n if (isDrop(error)) {\n req.socket.destroy()\n return\n }\n res.writeHead(500, { \"content-type\": \"application/json\" })\n res.end(internalError(error))\n return\n }\n const out: Record<string, string | string[]> = Object.fromEntries(response.headers)\n const cookies = response.headers.getSetCookie()\n if (cookies.length > 0) out[\"set-cookie\"] = cookies\n res.writeHead(response.status, out)\n if (!response.body) {\n res.end()\n return\n }\n res.flushHeaders()\n try {\n await pump(\n response.body,\n (chunk) => res.write(chunk),\n () => new Promise((resolve) => res.once(\"drain\", resolve)),\n () => res.destroyed,\n )\n res.end()\n } catch {\n res.destroy()\n }\n}\n\nconst handleHttp2 = async (\n api: FetchAPI,\n base: string,\n stream: ServerHttp2Stream,\n incoming: IncomingHttpHeaders,\n): Promise<void> => {\n const method = String(incoming[\":method\"] ?? \"GET\")\n const authority = String(incoming[\":authority\"] ?? incoming.host ?? base)\n const url = new URL(String(incoming[\":path\"] ?? \"/\").replace(/^\\/+/, \"/\"), `http://${authority}`)\n const headers = new Headers()\n for (const [name, value] of Object.entries(incoming)) {\n if (name.startsWith(\":\") || value === undefined) continue\n for (const each of Array.isArray(value) ? value : [value]) headers.append(name, String(each))\n }\n const aborted = new AbortController()\n stream.once(\"close\", () => {\n if (!stream.writableFinished) aborted.abort()\n })\n stream.on(\"error\", () => undefined)\n const hasBody = method !== \"GET\" && method !== \"HEAD\"\n const request = new Request(url, {\n method,\n headers,\n signal: aborted.signal,\n ...(hasBody ? { body: toWebBody(stream), duplex: \"half\" } : {}),\n } as RequestInit)\n let response: Response\n try {\n response = await api.fetch(request)\n } catch (error) {\n if (isDrop(error)) {\n stream.close(http2Constants.NGHTTP2_INTERNAL_ERROR)\n return\n }\n if (stream.destroyed) return\n stream.respond({ \":status\": 500, \"content-type\": \"application/json\" })\n stream.end(internalError(error))\n return\n }\n if (stream.destroyed || stream.closed) return\n const out: Record<string, string | string[] | number> = { \":status\": response.status }\n for (const [name, value] of response.headers) {\n if (!CONNECTION_HEADERS.has(name)) out[name] = value\n }\n if (!response.body) {\n stream.respond(out, { endStream: true })\n return\n }\n stream.respond(out)\n try {\n await pump(\n response.body,\n (chunk) => stream.write(chunk),\n () => new Promise((resolve) => stream.once(\"drain\", resolve)),\n () => stream.destroyed || stream.closed,\n )\n if (!stream.destroyed) stream.end()\n } catch {\n if (!stream.destroyed) stream.close(http2Constants.NGHTTP2_INTERNAL_ERROR)\n }\n}\n\nconst listenInternal = (server: {\n listen: (...args: unknown[]) => unknown\n once: (...args: never[]) => unknown\n address(): AddressInfo | string | null\n}) =>\n new Promise<number>((resolve, reject) => {\n ;(server.once as (event: string, listener: (error: Error) => void) => void)(\"error\", reject)\n server.listen(0, \"127.0.0.1\", () => resolve((server.address() as AddressInfo).port))\n })\n\n/**\n * Serve `api` over h2c and HTTP/1.1 on one port. Each accepted connection is sniffed for\n * the HTTP/2 preface and relayed to an internal loopback server for that protocol.\n */\nexport const listenH2c = async (\n api: FetchAPI,\n options: H2cListenOptions = {},\n): Promise<H2cListening> => {\n const host = options.host ?? \"127.0.0.1\"\n const shown = host.includes(\":\") ? `[${host}]` : host\n let base = `${shown}:${options.port ?? 0}`\n const h1 = createHttp1Server((req, res) => {\n void handleHttp1(api, base, req, res)\n })\n const h2 = createHttp2Server()\n h2.on(\"stream\", (stream, headers) => {\n void handleHttp2(api, base, stream, headers)\n })\n h2.on(\"sessionError\", () => undefined)\n const h1Port = await listenInternal(h1 as never)\n const h2Port = await listenInternal(h2 as never)\n const sockets = new Set<Socket>()\n const track = (socket: Socket) => {\n sockets.add(socket)\n socket.once(\"close\", () => sockets.delete(socket))\n }\n const front = createNetServer((socket) => {\n track(socket)\n socket.setNoDelay(true)\n let seen = Buffer.alloc(0)\n const onData = (chunk: Buffer) => {\n seen = Buffer.concat([seen, chunk])\n // \"PRI\" opens only the HTTP/2 preface; no HTTP/1.1 method starts that way.\n if (seen.length < 3) return\n socket.off(\"data\", onData)\n socket.pause()\n const http2 = seen.subarray(0, 3).toString(\"latin1\") === PREFACE.slice(0, 3)\n const upstream = connect(http2 ? h2Port : h1Port, \"127.0.0.1\")\n track(upstream)\n upstream.setNoDelay(true)\n const destroy = () => {\n socket.destroy()\n upstream.destroy()\n }\n socket.on(\"error\", destroy)\n upstream.on(\"error\", destroy)\n socket.once(\"close\", () => upstream.destroy())\n upstream.once(\"close\", () => socket.destroy())\n upstream.write(seen)\n socket.pipe(upstream)\n upstream.pipe(socket)\n socket.resume()\n }\n socket.on(\"data\", onData)\n socket.on(\"error\", () => socket.destroy())\n })\n await new Promise<void>((resolve, reject) => {\n front.once(\"error\", reject)\n front.listen(options.port ?? 0, host, () => resolve())\n })\n const port = (front.address() as AddressInfo).port\n base = `${shown}:${port}`\n const closeServer = (server: { close: (cb: (error?: Error) => void) => unknown }) =>\n new Promise<void>((resolve) => {\n server.close(() => resolve())\n })\n return {\n url: `http://${shown}:${port}`,\n port,\n host,\n close: async () => {\n const closing = [\n closeServer(front as never),\n closeServer(h1 as never),\n closeServer(h2 as never),\n ]\n for (const socket of sockets) socket.destroy()\n h1.closeAllConnections?.()\n await Promise.all(closing)\n },\n }\n}\n", "/// <reference types=\"node\" />\nimport type { ServeTarget } from \"@crvouga/mockingbird-adapter-node\"\nimport { type H2cListening, listenH2c } from \"./h2c.js\"\nimport { createRuntime, type SpeechRuntime, type SpeechRuntimeOptions } from \"./runtime.js\"\nimport type { TranscriptScript } from \"./state.js\"\n\n/** Port `mockingbird-aws-speech serve` listens on when none is given. */\nexport const DEFAULT_PORT = 8797\n\nexport type SpeechServerOptions = SpeechRuntimeOptions & {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`. */\n host?: string\n}\n\nexport type SpeechServer = H2cListening & { runtime: SpeechRuntime }\n\n/**\n * Serve the Polly + Transcribe mock on one port that speaks h2c (the SDKs' default for\n * Polly and Transcribe Streaming, and required for their duplex streams) and HTTP/1.1\n * (Transcribe batch).\n */\nexport const createServer = async (options: SpeechServerOptions = {}): Promise<SpeechServer> => {\n const { port, host, ...rest } = options\n const runtime = createRuntime(rest)\n const listening = await listenH2c(runtime, {\n port: port ?? 0,\n ...(host !== undefined ? { host } : {}),\n })\n return { ...listening, runtime }\n}\n\nconst text = (value: string | boolean | undefined) =>\n typeof value === \"string\" ? value : undefined\n\nconst loadTranscripts = async (path: string): Promise<TranscriptScript[]> => {\n const { readFile } = await import(\"node:fs/promises\")\n const raw = JSON.parse(await readFile(path, \"utf8\")) as unknown\n const list = Array.isArray(raw) ? raw : (raw as { transcripts?: unknown[] }).transcripts\n if (!Array.isArray(list)) throw new Error(`${path}: expected {\"transcripts\": [...]}`)\n return list.map((each, index) => {\n const item = each as TranscriptScript\n if (typeof item?.final !== \"string\")\n throw new Error(`${path}: transcripts[${index}].final must be a string`)\n return { ...item, id: item.id ?? `transcript_${index + 1}` }\n })\n}\n\n/**\n * How `serve` builds the speech mock from flags. `serve --config` in another service's CLI\n * listens over HTTP/1.1 only; `mockingbird-aws-speech serve` listens with h2c as well.\n */\nexport const serveTarget: ServeTarget = {\n name: \"aws-speech\",\n defaultPort: DEFAULT_PORT,\n options: {\n transcripts: {\n type: \"string\",\n value: \"<file.json>\",\n description:\n 'Transcripts every namespace starts with ({\"transcripts\": [...]}, as PUT /__admin/transcripts)',\n },\n \"default-transcript\": {\n type: \"string\",\n value: \"<text>\",\n description: 'What an unscripted session or job hears (default \"Hello.\")',\n },\n \"s3-endpoint\": {\n type: \"string\",\n value: \"<url>\",\n description:\n \"Write completed batch transcripts to this S3 (s3rver) at OutputBucketName/OutputKey\",\n },\n },\n create: async (values, common) => {\n const transcriptsPath = text(values.transcripts)\n const defaultTranscript = text(values[\"default-transcript\"])\n const s3 = text(values[\"s3-endpoint\"])\n return createRuntime({\n ...(transcriptsPath ? { transcripts: await loadTranscripts(transcriptsPath) } : {}),\n ...(defaultTranscript !== undefined ? { settings: { defaultTranscript } } : {}),\n ...(s3 ? { transcriptStore: { endpoint: s3 } } : {}),\n ...(common.adminKey !== undefined ? { adminKey: common.adminKey } : {}),\n ...(common.seed !== undefined ? { seed: common.seed } : {}),\n ...(common.onLog ? { onLog: common.onLog } : {}),\n }) as never\n },\n banner: () => [\n \"point the app at it: AWS_ENDPOINT_URL_POLLY / AWS_ENDPOINT_URL_TRANSCRIBE_STREAMING / AWS_ENDPOINT_URL_TRANSCRIBE = this URL\",\n \"protocols: h2c (prior knowledge) and HTTP/1.1 on the same port\",\n \"namespaces: x-mockingbird-namespace, /ns/<name>/\u2026, or PUT /__admin/credentials {<AWS_ACCESS_KEY_ID>: <ns>}\",\n 'transcripts: PUT /__admin/transcripts {match: {sessionIndex} | {any: true}, partials: [...], final: \"...\"}',\n ],\n}\n\nexport type { H2cListening, H2cListenOptions } from \"./h2c.js\"\nexport { listenH2c } from \"./h2c.js\"\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|