@crvouga/mockingbird-service-bedrock 0.2.0 → 1.0.1

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 CHANGED
@@ -1,6 +1,19 @@
1
1
  # Changelog — @crvouga/mockingbird-service-bedrock
2
2
 
3
- ## 0.2.0 (2026-09-24)
3
+ ## 1.0.1 (2026-09-26)
4
+
5
+ ### Fixes and improvements
6
+
7
+ - take the rejected-request byte count from content-length, never by reading the body ([a0e5fb2](https://github.com/crvouga/mockingbird/commit/a0e5fb2fc80ab725c3552b612f3f091fd6c857fb))
8
+ - reject a present body by media type, and log what a rejected request sent ([12e351a](https://github.com/crvouga/mockingbird/commit/12e351aba53cfb7b41e31a7809419455fc2902eb))
9
+
10
+ ## 1.0.0 (2026-09-24)
11
+
12
+ ### ⚠️ Breaking changes
13
+
14
+ - vendor branding for every service; purge internal mocks and consumer names ([f689534](https://github.com/crvouga/mockingbird/commit/f6895343543550dafcb7d76b426cae7635e03226))
15
+
16
+ ## 0.2.0 (2026-09-23)
4
17
 
5
18
  ### Features
6
19
 
package/README.md CHANGED
@@ -115,7 +115,7 @@ how many calls it answers.
115
115
  (`true` or `{text, trace}`: `guardrail_intervened` with Bedrock's refusal text and a trace),
116
116
  `toolResult` (harness), `userTranscript` (Nova Sonic), `stopReason`, `usage`, `fault`.
117
117
  - **Structured output** (`json`) goes out as text JSON for
118
- `outputConfig.textFormat.structure.jsonSchema` (Makor) and
118
+ `outputConfig.textFormat.structure.jsonSchema` (Python boto3 clients) and
119
119
  `additionalModelRequestFields.output_config.format` (AI SDK native), and as a `toolUse` of the
120
120
  forced tool for `toolChoice: {tool}` or `{any}` (the AI SDK's synthetic `json` tool, insight
121
121
  reports' `record_chat_*`).
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createRuntime,
3
3
  parseScript
4
- } from "./chunk-O5ZO5KMR.js";
4
+ } from "./chunk-QUUSPEWJ.js";
5
5
 
6
6
  // src/h2c.ts
7
7
  import {
@@ -296,4 +296,4 @@ export {
296
296
  createServer,
297
297
  serveTarget
298
298
  };
299
- //# sourceMappingURL=chunk-BMGWOFIJ.js.map
299
+ //# sourceMappingURL=chunk-DN5K7HCG.js.map
@@ -1367,6 +1367,90 @@ var operationMetadata = (operation) => {
1367
1367
 
1368
1368
  // ../core/dist/service.js
1369
1369
  import { Hono } from "hono";
1370
+
1371
+ // ../core/dist/validation.js
1372
+ var requestBodyOf = (context) => {
1373
+ const requestBody = context.operation.operation.requestBody;
1374
+ return requestBody ? deref(context.document, requestBody) : void 0;
1375
+ };
1376
+ var mediaTypeMatches = (accepted, actual) => {
1377
+ const pattern = mediaTypeOf(accepted) ?? accepted.toLowerCase();
1378
+ if (pattern === actual || pattern === "*/*")
1379
+ return true;
1380
+ const [type, subtype] = pattern.split("/");
1381
+ const [actualType, actualSubtype] = actual.split("/");
1382
+ if (type !== actualType && type !== "*")
1383
+ return false;
1384
+ if (subtype === void 0 || subtype === "*")
1385
+ return true;
1386
+ if (subtype.startsWith("*+"))
1387
+ return actualSubtype?.endsWith(subtype.slice(1)) === true;
1388
+ return false;
1389
+ };
1390
+ var acceptedKeyFor = (content, mediaType) => {
1391
+ if (!content || mediaType === void 0)
1392
+ return void 0;
1393
+ const keys = Object.keys(content);
1394
+ return keys.find((key) => mediaTypeOf(key) === mediaType) ?? keys.find((key) => mediaTypeMatches(key, mediaType));
1395
+ };
1396
+ var unsupportedMediaType = (context, options = {}) => {
1397
+ if (context.body.kind === "empty" && options.includeEmpty !== true)
1398
+ return void 0;
1399
+ const content = requestBodyOf(context)?.content;
1400
+ if (!content || Object.keys(content).length === 0)
1401
+ return void 0;
1402
+ const mediaType = mediaTypeOf(context.request.headers.get("content-type"));
1403
+ if (acceptedKeyFor(content, mediaType) !== void 0)
1404
+ return void 0;
1405
+ return { mediaType: mediaType ?? null, accepted: Object.keys(content) };
1406
+ };
1407
+ var recorded = /* @__PURE__ */ new WeakMap();
1408
+ var recordedIssues = (request) => recorded.get(request);
1409
+ var bodyIssues = (context, contentType = "application/json") => {
1410
+ const issues = findBodyIssues(context, contentType);
1411
+ if (issues.length > 0)
1412
+ recorded.set(context.request, issues);
1413
+ return issues;
1414
+ };
1415
+ var findBodyIssues = (context, contentType) => {
1416
+ const resolved = requestBodyOf(context);
1417
+ if (!resolved)
1418
+ return [];
1419
+ const unsupported = unsupportedMediaType(context);
1420
+ if (unsupported) {
1421
+ return [
1422
+ {
1423
+ path: "",
1424
+ kind: "media_type",
1425
+ message: unsupported.mediaType === null ? "request body has no content-type" : `request body media type ${unsupported.mediaType} is not supported`
1426
+ }
1427
+ ];
1428
+ }
1429
+ const key = acceptedKeyFor(resolved.content, mediaTypeOf(context.request.headers.get("content-type"))) ?? contentType;
1430
+ const schema = resolved.content?.[key]?.schema;
1431
+ if (!schema)
1432
+ return [];
1433
+ if (context.body.kind === "invalid") {
1434
+ return [
1435
+ {
1436
+ path: "",
1437
+ kind: "syntax",
1438
+ message: `request body is not valid ${context.body.mediaType}`
1439
+ }
1440
+ ];
1441
+ }
1442
+ const value = context.body.kind === "json" || context.body.kind === "form" || context.body.kind === "text" ? context.body.value : void 0;
1443
+ if (value === void 0) {
1444
+ return resolved.required && context.body.kind === "empty" ? [{ path: "", kind: "required", message: "request body is required" }] : [];
1445
+ }
1446
+ return validateValue(context.document, resolveSchema(context.document, schema), value).map((issue) => ({ path: issue.path.join("."), kind: "schema", message: issue.message }));
1447
+ };
1448
+
1449
+ // ../core/dist/service.js
1450
+ var withIssues = (request, response) => {
1451
+ const issues = response.status >= 400 ? recordedIssues(request) : void 0;
1452
+ return issues && issues.length > 0 ? annotateResponse(response, { issues }) : response;
1453
+ };
1370
1454
  var defineOperations = (handlers) => handlers;
1371
1455
  var OperationRegistryError = class extends Error {
1372
1456
  problems;
@@ -1452,9 +1536,7 @@ var createService = (options) => {
1452
1536
  now
1453
1537
  };
1454
1538
  const short = await options.before?.(context);
1455
- if (short)
1456
- return short;
1457
- return handler(context);
1539
+ return withIssues(request, short ?? await handler(context));
1458
1540
  };
1459
1541
  app.on(operation.method.toUpperCase(), honoPath(operation.path), route);
1460
1542
  }
@@ -1491,7 +1573,7 @@ var restoreNamespace = (sqlite, namespace, snapshot) => {
1491
1573
  };
1492
1574
 
1493
1575
  // ../core/dist/version.js
1494
- var PACKAGE_VERSION = true ? "0.2.0" : UNRELEASED_VERSION;
1576
+ var PACKAGE_VERSION = true ? "1.0.1" : UNRELEASED_VERSION;
1495
1577
 
1496
1578
  // ../core/dist/signing.js
1497
1579
  var encoder = new TextEncoder();
@@ -1936,8 +2018,15 @@ var createRuntime = (options) => {
1936
2018
  const started = monotonicNow();
1937
2019
  const url = new URL(request.url);
1938
2020
  const operationId = operationIdFor(request, url.pathname);
2021
+ const contentLength = request.headers.get("content-length");
2022
+ const received = {
2023
+ contentType: request.headers.get("content-type"),
2024
+ bodyBytes: contentLength !== null && /^\d+$/.test(contentLength) ? Number(contentLength) : null,
2025
+ transferEncoding: request.headers.get("transfer-encoding")
2026
+ };
1939
2027
  const log = (status, faultId, response2) => {
1940
2028
  const noted = response2 ? responseNotes(response2) : void 0;
2029
+ const rejected = status >= 400 && faultId === void 0;
1941
2030
  const entry = {
1942
2031
  service: options.name,
1943
2032
  namespace,
@@ -1949,7 +2038,9 @@ var createRuntime = (options) => {
1949
2038
  unmatched: options.document !== void 0 && operationId === void 0,
1950
2039
  ...faultId !== void 0 ? { faultId } : {},
1951
2040
  ...noted?.ids && Object.keys(noted.ids).length > 0 ? { ids: noted.ids } : {},
1952
- ...noted?.adopted ? { adopted: true } : {}
2041
+ ...noted?.adopted ? { adopted: true } : {},
2042
+ ...rejected ? { request: { ...received } } : {},
2043
+ ...rejected && noted?.issues && noted.issues.length > 0 ? { issues: noted.issues.map((issue) => ({ ...issue })) } : {}
1953
2044
  };
1954
2045
  metrics.record(entry);
1955
2046
  journal.record({ ...entry, at: new Date(clock.now()).toISOString() });
@@ -2156,25 +2247,6 @@ var presetRoutes = (presets, runtime) => ({
2156
2247
  }
2157
2248
  });
2158
2249
 
2159
- // ../core/dist/validation.js
2160
- var bodyIssues = (context, contentType = "application/json") => {
2161
- const requestBody = context.operation.operation.requestBody;
2162
- if (!requestBody)
2163
- return [];
2164
- const resolved = deref(context.document, requestBody);
2165
- const schema = resolved.content?.[contentType]?.schema;
2166
- if (!schema)
2167
- return [];
2168
- const value = context.body.kind === "json" || context.body.kind === "form" ? context.body.value : void 0;
2169
- if (context.body.kind === "invalid") {
2170
- return [{ path: "", message: `request body is not valid ${context.body.mediaType}` }];
2171
- }
2172
- if (value === void 0) {
2173
- return resolved.required ? [{ path: "", message: "request body is required" }] : [];
2174
- }
2175
- return validateValue(context.document, resolveSchema(context.document, schema), value).map((issue) => ({ path: issue.path.join("."), message: issue.message }));
2176
- };
2177
-
2178
2250
  // src/analyze.ts
2179
2251
  var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2180
2252
  var array = (value) => Array.isArray(value) ? value : [];
@@ -4638,4 +4710,4 @@ export {
4638
4710
  titanEmbedding,
4639
4711
  BedrockAPI
4640
4712
  };
4641
- //# sourceMappingURL=chunk-O5ZO5KMR.js.map
4713
+ //# sourceMappingURL=chunk-QUUSPEWJ.js.map