@alwaysmeticulous/backend-recorder-workerd 2.346.0 → 2.350.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { SIDECAR_BINDING_ENV_KEY, SIDECAR_BINDING_ORIGIN, SIDECAR_URL_ENV_KEY, SidecarFetcher, SidecarTransport } from "./sidecar-transport.js";
2
2
  import { MeticulousExecutionContext, WithMeticulousOptions } from "./with-meticulous.js";
3
3
  import { BODY_ENCODING_BASE64, BodyEncoding, EncodedBody, HTTP_RESPONSE_BODY_ENCODING_ATTR, decodeBodyBytes, encodeBodyBytes } from "./body-encoding.js";
4
- import { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, BindingRequestEvent, CAPTURED_HEADERS, CaptureEvent, CaptureEventsPayload, CapturedBody, CoverageReportRequest, FRONTEND_SESSION_ID_HEADER, InboundRequestEvent, KvOmittedReason, KvOperation, KvOperationEvent, METICULOUS_PASSTHROUGH_HEADER, OutboundFetchAgentSwarmTestingRequest, OutboundFetchAgentSwarmTestingResponse, OutboundFetchLookupRequest, OutboundFetchLookupResponse, OutboundRequestEvent, PostgresQueryEvent, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, ReplaySessionInfoResponse, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, VIRTUAL_TIME_HEADER, WORKERD_SHIM_VERSION_HEADER, parseVirtualTimeMs } from "./protocol.js";
4
+ import { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, BE_MOCK_PING_INTERVAL_MS, BeMockCall, BeMockCallsReport, BeMockFailedMessage, BeMockInstallMessage, BeMockLoadSessionMessage, BeMockLogMessage, BeMockReadyMessage, BeMockRecordedRequest, BeMockSessionMessage, BeMockSidecarMessage, BeMockWorkerMessage, BindingRequestEvent, CAPTURED_HEADERS, CaptureEvent, CaptureEventsPayload, CapturedBody, CoverageReportRequest, FRONTEND_SESSION_ID_HEADER, InboundRequestEvent, KvOmittedReason, KvOperation, KvOperationEvent, METICULOUS_PASSTHROUGH_HEADER, OutboundFetchAgentSwarmTestingRequest, OutboundFetchAgentSwarmTestingResponse, OutboundFetchLookupRequest, OutboundFetchLookupResponse, OutboundRequestEvent, PostgresQueryEvent, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, ReplaySessionInfoResponse, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_CALLS_PATH, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_PATH, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, VIRTUAL_TIME_HEADER, WORKERD_SHIM_VERSION_HEADER, parseVirtualTimeMs } from "./protocol.js";
5
5
  import { WORKERD_SHIM_VERSION } from "./version.js";
6
6
  import { MAX_BODY_CAPTURE_SIZE, readBodyWithCap, readRequestBodyWithCap, readResponseBodyWithCap } from "./body-capture.js";
7
7
  import { headersToRecord } from "./outbound-capture.js";
@@ -67,7 +67,11 @@ interface MeticulousWorkerHandler<Env = never> {
67
67
  *
68
68
  * **Agent-swarm testing** activates on two deployment env vars,
69
69
  * `METICULOUS_AGENT_SWARM_TESTING_SIDECAR_URL` and `METICULOUS_AGENT_SWARM_TESTING_RUN_ID`,
70
- * set by whoever deploys the worker for a swarm run. Every outgoing `fetch` call is first
70
+ * set by whoever deploys the worker for a swarm run. These must be Worker bindings,
71
+ * not just environment variables on the host process: forward them at startup through
72
+ * Wrangler vars or the `.dev.vars` file consumed by the runtime. The app calls a sidecar
73
+ * in the same pod through loopback; configuring the sidecar's own environment does not
74
+ * configure the Worker's bindings. Every outgoing `fetch` call is first
71
75
  * offered to the interceptor service the URL names; it either supplies the response to serve
72
76
  * or the call passes through to the real service. Unlike replay the mode is best-effort by
73
77
  * design: a call the interceptor cannot answer is never failed, there is no frozen clock and
@@ -99,5 +103,5 @@ declare const withMeticulous: <H extends MeticulousWorkerHandler<never>>(handler
99
103
  */
100
104
  declare const getMeticulousSessionId: () => string | undefined;
101
105
  //#endregion
102
- export { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, BODY_ENCODING_BASE64, type BindingRequestEvent, type BodyEncoding, CAPTURED_HEADERS, CLIENT_TECHNOLOGY_ATTR, CLIENT_TECHNOLOGY_POSTGRES_JS, CLIENT_TECHNOLOGY_WORKERD_BINDING, CLIENT_TECHNOLOGY_WORKERD_FETCH, CLIENT_TECHNOLOGY_WORKERD_KV, type CaptureEvent, type CaptureEventsPayload, type CapturedBody, type CoverageMap, type CoverageMapFile, type CoverageReportRequest, type EncodedBody, FRONTEND_SESSION_ID_ATTR, FRONTEND_SESSION_ID_HEADER, HTTP_RESPONSE_BODY_ENCODING_ATTR, type HeaderReader, HealthProbeEventFilter, type HrTime, type InboundRequestEvent, type KvCaptureFields, type KvCaptureOutcome, type KvOmittedReason, type KvOperation, type KvOperationEvent, MAX_BODY_CAPTURE_SIZE, MAX_POSTGRES_JS_RESULT_SIZE, METICULOUS_PASSTHROUGH_HEADER, type MeticulousExecutionContext, type MeticulousPagesFunction, type MeticulousPagesFunctionContext, MeticulousWorkerHandler, type OutboundCallTarget, type OutboundFetchAgentSwarmTestingRequest, type OutboundFetchAgentSwarmTestingResponse, type OutboundFetchLookupRequest, type OutboundFetchLookupResponse, type OutboundRequestEvent, POSTGRES_JS_ERROR_ATTR, POSTGRES_JS_QUERY_PARAMS_ATTR, POSTGRES_JS_QUERY_TEXT_ATTR, POSTGRES_JS_RESULT_ATTR, POSTGRES_JS_RESULT_TRUNCATED_ATTR, POSTGRES_JS_ROW_MODE_ATTR, type PostgresJsQueryLike, type PostgresJsRowMode, type PostgresQueryEvent, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, type ReadableSpan, type ReplaySessionInfoResponse, SERVER_TIMING_HEADER, SERVER_TIMING_SESSION_METRIC, SESSION_ID_ORIGIN_ATTR, SESSION_ID_ORIGIN_BACKEND, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_BINDING_ENV_KEY, SIDECAR_BINDING_ORIGIN, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, SIDECAR_URL_ENV_KEY, SPAN_KIND_CLIENT, SPAN_KIND_INTERNAL, SPAN_KIND_SERVER, SPAN_STATUS_ERROR, SPAN_STATUS_OK, SPAN_STATUS_UNSET, STR_REDACTED, type SerializedSpanBase, type SidecarFetcher, type SidecarTransport, SpanBuilder, type SpanStatus, VIRTUAL_TIME_HEADER, WORKERD_BINDING_NAME_ATTR, WORKERD_KV_ARGS_ATTR, WORKERD_KV_ARGS_TRUNCATED_ATTR, WORKERD_KV_KEY_ATTR, WORKERD_KV_OMITTED_ATTR, WORKERD_KV_OPERATION_ATTR, WORKERD_KV_RESULT_ATTR, WORKERD_KV_RESULT_TRUNCATED_ATTR, WORKERD_KV_VALUE_ATTR, WORKERD_KV_VALUE_TRUNCATED_ATTR, WORKERD_SHIM_VERSION, WORKERD_SHIM_VERSION_HEADER, type WithMeticulousOptions, __mcEnter, __mcHit, buildServerTimingSessionEntry, collectHitIds, createCoverageSink, decodeBodyBytes, deserializeCapturedError, deserializeCapturedResult, encodeBodyBytes, getCoverageMap, getMeticulousSessionId, getUnreportedCoverageFiles, headersToRecord, hrTimeToMs, isHealthProbeRequest, isOrphanedDatadogAgentRequest, isOrphanedDatadogAgentUrl, isProvisionalSessionIdCandidate, isSupportedPostgresJsQuery, markCoverageFilesReported, mintProvisionalSessionId, msToHrTime, parseVirtualTimeMs, postgresJsCommandOf, randomHex, readBodyWithCap, readRequestBodyWithCap, readResponseBodyWithCap, reconstructQueryText, redactRequestBody, redactUrlCredentials, registerCoverageFile, resolveRowMode, serializeCapturedError, serializeCapturedResult, serializeKvArgs, serializeKvCaptureFields, serializePostgresJsArgs, serializePostgresJsError, serializePostgresJsResult, serializeSpan, withMeticulous, withMeticulousPagesFunction, withMeticulousPostgres };
106
+ export { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, BE_MOCK_PING_INTERVAL_MS, BODY_ENCODING_BASE64, type BeMockCall, type BeMockCallsReport, type BeMockFailedMessage, type BeMockInstallMessage, type BeMockLoadSessionMessage, type BeMockLogMessage, type BeMockReadyMessage, type BeMockRecordedRequest, type BeMockSessionMessage, type BeMockSidecarMessage, type BeMockWorkerMessage, type BindingRequestEvent, type BodyEncoding, CAPTURED_HEADERS, CLIENT_TECHNOLOGY_ATTR, CLIENT_TECHNOLOGY_POSTGRES_JS, CLIENT_TECHNOLOGY_WORKERD_BINDING, CLIENT_TECHNOLOGY_WORKERD_FETCH, CLIENT_TECHNOLOGY_WORKERD_KV, type CaptureEvent, type CaptureEventsPayload, type CapturedBody, type CoverageMap, type CoverageMapFile, type CoverageReportRequest, type EncodedBody, FRONTEND_SESSION_ID_ATTR, FRONTEND_SESSION_ID_HEADER, HTTP_RESPONSE_BODY_ENCODING_ATTR, type HeaderReader, HealthProbeEventFilter, type HrTime, type InboundRequestEvent, type KvCaptureFields, type KvCaptureOutcome, type KvOmittedReason, type KvOperation, type KvOperationEvent, MAX_BODY_CAPTURE_SIZE, MAX_POSTGRES_JS_RESULT_SIZE, METICULOUS_PASSTHROUGH_HEADER, type MeticulousExecutionContext, type MeticulousPagesFunction, type MeticulousPagesFunctionContext, MeticulousWorkerHandler, type OutboundCallTarget, type OutboundFetchAgentSwarmTestingRequest, type OutboundFetchAgentSwarmTestingResponse, type OutboundFetchLookupRequest, type OutboundFetchLookupResponse, type OutboundRequestEvent, POSTGRES_JS_ERROR_ATTR, POSTGRES_JS_QUERY_PARAMS_ATTR, POSTGRES_JS_QUERY_TEXT_ATTR, POSTGRES_JS_RESULT_ATTR, POSTGRES_JS_RESULT_TRUNCATED_ATTR, POSTGRES_JS_ROW_MODE_ATTR, type PostgresJsQueryLike, type PostgresJsRowMode, type PostgresQueryEvent, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, type ReadableSpan, type ReplaySessionInfoResponse, SERVER_TIMING_HEADER, SERVER_TIMING_SESSION_METRIC, SESSION_ID_ORIGIN_ATTR, SESSION_ID_ORIGIN_BACKEND, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_CALLS_PATH, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_PATH, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_BINDING_ENV_KEY, SIDECAR_BINDING_ORIGIN, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, SIDECAR_URL_ENV_KEY, SPAN_KIND_CLIENT, SPAN_KIND_INTERNAL, SPAN_KIND_SERVER, SPAN_STATUS_ERROR, SPAN_STATUS_OK, SPAN_STATUS_UNSET, STR_REDACTED, type SerializedSpanBase, type SidecarFetcher, type SidecarTransport, SpanBuilder, type SpanStatus, VIRTUAL_TIME_HEADER, WORKERD_BINDING_NAME_ATTR, WORKERD_KV_ARGS_ATTR, WORKERD_KV_ARGS_TRUNCATED_ATTR, WORKERD_KV_KEY_ATTR, WORKERD_KV_OMITTED_ATTR, WORKERD_KV_OPERATION_ATTR, WORKERD_KV_RESULT_ATTR, WORKERD_KV_RESULT_TRUNCATED_ATTR, WORKERD_KV_VALUE_ATTR, WORKERD_KV_VALUE_TRUNCATED_ATTR, WORKERD_SHIM_VERSION, WORKERD_SHIM_VERSION_HEADER, type WithMeticulousOptions, __mcEnter, __mcHit, buildServerTimingSessionEntry, collectHitIds, createCoverageSink, decodeBodyBytes, deserializeCapturedError, deserializeCapturedResult, encodeBodyBytes, getCoverageMap, getMeticulousSessionId, getUnreportedCoverageFiles, headersToRecord, hrTimeToMs, isHealthProbeRequest, isOrphanedDatadogAgentRequest, isOrphanedDatadogAgentUrl, isProvisionalSessionIdCandidate, isSupportedPostgresJsQuery, markCoverageFilesReported, mintProvisionalSessionId, msToHrTime, parseVirtualTimeMs, postgresJsCommandOf, randomHex, readBodyWithCap, readRequestBodyWithCap, readResponseBodyWithCap, reconstructQueryText, redactRequestBody, redactUrlCredentials, registerCoverageFile, resolveRowMode, serializeCapturedError, serializeCapturedResult, serializeKvArgs, serializeKvCaptureFields, serializePostgresJsArgs, serializePostgresJsError, serializePostgresJsResult, serializeSpan, withMeticulous, withMeticulousPagesFunction, withMeticulousPostgres };
103
107
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { requestCaptureContext } from "./context.js";
3
3
  import { BODY_ENCODING_BASE64, HTTP_RESPONSE_BODY_ENCODING_ATTR, decodeBodyBytes, encodeBodyBytes } from "./body-encoding.js";
4
4
  import { STR_REDACTED, redactRequestBody } from "./redact-body.js";
5
5
  import { MAX_BODY_CAPTURE_SIZE, readBodyWithCap, readRequestBodyWithCap, readResponseBodyWithCap } from "./body-capture.js";
6
- import { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, CAPTURED_HEADERS, FRONTEND_SESSION_ID_HEADER, METICULOUS_PASSTHROUGH_HEADER, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, VIRTUAL_TIME_HEADER, WORKERD_SHIM_VERSION_HEADER, parseVirtualTimeMs } from "./protocol.js";
6
+ import { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, BE_MOCK_PING_INTERVAL_MS, CAPTURED_HEADERS, FRONTEND_SESSION_ID_HEADER, METICULOUS_PASSTHROUGH_HEADER, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_CALLS_PATH, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_PATH, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, VIRTUAL_TIME_HEADER, WORKERD_SHIM_VERSION_HEADER, parseVirtualTimeMs } from "./protocol.js";
7
7
  import { headersToRecord } from "./outbound-capture.js";
8
8
  import { __mcEnter, __mcHit, collectHitIds, createCoverageSink, getCoverageMap, getUnreportedCoverageFiles, markCoverageFilesReported, registerCoverageFile } from "./coverage/runtime.js";
9
9
  import { isOrphanedDatadogAgentRequest, isOrphanedDatadogAgentUrl } from "./datadog-agent.js";
@@ -62,7 +62,11 @@ import { serializeSpan } from "./spans/serialize-span.js";
62
62
  *
63
63
  * **Agent-swarm testing** activates on two deployment env vars,
64
64
  * `METICULOUS_AGENT_SWARM_TESTING_SIDECAR_URL` and `METICULOUS_AGENT_SWARM_TESTING_RUN_ID`,
65
- * set by whoever deploys the worker for a swarm run. Every outgoing `fetch` call is first
65
+ * set by whoever deploys the worker for a swarm run. These must be Worker bindings,
66
+ * not just environment variables on the host process: forward them at startup through
67
+ * Wrangler vars or the `.dev.vars` file consumed by the runtime. The app calls a sidecar
68
+ * in the same pod through loopback; configuring the sidecar's own environment does not
69
+ * configure the Worker's bindings. Every outgoing `fetch` call is first
66
70
  * offered to the interceptor service the URL names; it either supplies the response to serve
67
71
  * or the call passes through to the real service. Unlike replay the mode is best-effort by
68
72
  * design: a call the interceptor cannot answer is never failed, there is no frozen clock and
@@ -105,6 +109,6 @@ const withMeticulous = (handler, options) => {
105
109
  */
106
110
  const getMeticulousSessionId = () => requestCaptureContext.getStore()?.frontendSessionId;
107
111
  //#endregion
108
- export { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, BODY_ENCODING_BASE64, CAPTURED_HEADERS, CLIENT_TECHNOLOGY_ATTR, CLIENT_TECHNOLOGY_POSTGRES_JS, CLIENT_TECHNOLOGY_WORKERD_BINDING, CLIENT_TECHNOLOGY_WORKERD_FETCH, CLIENT_TECHNOLOGY_WORKERD_KV, FRONTEND_SESSION_ID_ATTR, FRONTEND_SESSION_ID_HEADER, HTTP_RESPONSE_BODY_ENCODING_ATTR, HealthProbeEventFilter, MAX_BODY_CAPTURE_SIZE, MAX_POSTGRES_JS_RESULT_SIZE, METICULOUS_PASSTHROUGH_HEADER, POSTGRES_JS_ERROR_ATTR, POSTGRES_JS_QUERY_PARAMS_ATTR, POSTGRES_JS_QUERY_TEXT_ATTR, POSTGRES_JS_RESULT_ATTR, POSTGRES_JS_RESULT_TRUNCATED_ATTR, POSTGRES_JS_ROW_MODE_ATTR, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, SERVER_TIMING_HEADER, SERVER_TIMING_SESSION_METRIC, SESSION_ID_ORIGIN_ATTR, SESSION_ID_ORIGIN_BACKEND, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_BINDING_ENV_KEY, SIDECAR_BINDING_ORIGIN, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, SIDECAR_URL_ENV_KEY, SPAN_KIND_CLIENT, SPAN_KIND_INTERNAL, SPAN_KIND_SERVER, SPAN_STATUS_ERROR, SPAN_STATUS_OK, SPAN_STATUS_UNSET, STR_REDACTED, SpanBuilder, VIRTUAL_TIME_HEADER, WORKERD_BINDING_NAME_ATTR, WORKERD_KV_ARGS_ATTR, WORKERD_KV_ARGS_TRUNCATED_ATTR, WORKERD_KV_KEY_ATTR, WORKERD_KV_OMITTED_ATTR, WORKERD_KV_OPERATION_ATTR, WORKERD_KV_RESULT_ATTR, WORKERD_KV_RESULT_TRUNCATED_ATTR, WORKERD_KV_VALUE_ATTR, WORKERD_KV_VALUE_TRUNCATED_ATTR, WORKERD_SHIM_VERSION, WORKERD_SHIM_VERSION_HEADER, __mcEnter, __mcHit, buildServerTimingSessionEntry, collectHitIds, createCoverageSink, decodeBodyBytes, deserializeCapturedError, deserializeCapturedResult, encodeBodyBytes, getCoverageMap, getMeticulousSessionId, getUnreportedCoverageFiles, headersToRecord, hrTimeToMs, isHealthProbeRequest, isOrphanedDatadogAgentRequest, isOrphanedDatadogAgentUrl, isProvisionalSessionIdCandidate, isSupportedPostgresJsQuery, markCoverageFilesReported, mintProvisionalSessionId, msToHrTime, parseVirtualTimeMs, postgresJsCommandOf, randomHex, readBodyWithCap, readRequestBodyWithCap, readResponseBodyWithCap, reconstructQueryText, redactRequestBody, redactUrlCredentials, registerCoverageFile, resolveRowMode, serializeCapturedError, serializeCapturedResult, serializeKvArgs, serializeKvCaptureFields, serializePostgresJsArgs, serializePostgresJsError, serializePostgresJsResult, serializeSpan, withMeticulous, withMeticulousPagesFunction, withMeticulousPostgres };
112
+ export { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, BE_MOCK_PING_INTERVAL_MS, BODY_ENCODING_BASE64, CAPTURED_HEADERS, CLIENT_TECHNOLOGY_ATTR, CLIENT_TECHNOLOGY_POSTGRES_JS, CLIENT_TECHNOLOGY_WORKERD_BINDING, CLIENT_TECHNOLOGY_WORKERD_FETCH, CLIENT_TECHNOLOGY_WORKERD_KV, FRONTEND_SESSION_ID_ATTR, FRONTEND_SESSION_ID_HEADER, HTTP_RESPONSE_BODY_ENCODING_ATTR, HealthProbeEventFilter, MAX_BODY_CAPTURE_SIZE, MAX_POSTGRES_JS_RESULT_SIZE, METICULOUS_PASSTHROUGH_HEADER, POSTGRES_JS_ERROR_ATTR, POSTGRES_JS_QUERY_PARAMS_ATTR, POSTGRES_JS_QUERY_TEXT_ATTR, POSTGRES_JS_RESULT_ATTR, POSTGRES_JS_RESULT_TRUNCATED_ATTR, POSTGRES_JS_ROW_MODE_ATTR, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, SERVER_TIMING_HEADER, SERVER_TIMING_SESSION_METRIC, SESSION_ID_ORIGIN_ATTR, SESSION_ID_ORIGIN_BACKEND, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_CALLS_PATH, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_PATH, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_BINDING_ENV_KEY, SIDECAR_BINDING_ORIGIN, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, SIDECAR_URL_ENV_KEY, SPAN_KIND_CLIENT, SPAN_KIND_INTERNAL, SPAN_KIND_SERVER, SPAN_STATUS_ERROR, SPAN_STATUS_OK, SPAN_STATUS_UNSET, STR_REDACTED, SpanBuilder, VIRTUAL_TIME_HEADER, WORKERD_BINDING_NAME_ATTR, WORKERD_KV_ARGS_ATTR, WORKERD_KV_ARGS_TRUNCATED_ATTR, WORKERD_KV_KEY_ATTR, WORKERD_KV_OMITTED_ATTR, WORKERD_KV_OPERATION_ATTR, WORKERD_KV_RESULT_ATTR, WORKERD_KV_RESULT_TRUNCATED_ATTR, WORKERD_KV_VALUE_ATTR, WORKERD_KV_VALUE_TRUNCATED_ATTR, WORKERD_SHIM_VERSION, WORKERD_SHIM_VERSION_HEADER, __mcEnter, __mcHit, buildServerTimingSessionEntry, collectHitIds, createCoverageSink, decodeBodyBytes, deserializeCapturedError, deserializeCapturedResult, encodeBodyBytes, getCoverageMap, getMeticulousSessionId, getUnreportedCoverageFiles, headersToRecord, hrTimeToMs, isHealthProbeRequest, isOrphanedDatadogAgentRequest, isOrphanedDatadogAgentUrl, isProvisionalSessionIdCandidate, isSupportedPostgresJsQuery, markCoverageFilesReported, mintProvisionalSessionId, msToHrTime, parseVirtualTimeMs, postgresJsCommandOf, randomHex, readBodyWithCap, readRequestBodyWithCap, readResponseBodyWithCap, reconstructQueryText, redactRequestBody, redactUrlCredentials, registerCoverageFile, resolveRowMode, serializeCapturedError, serializeCapturedResult, serializeKvArgs, serializeKvCaptureFields, serializePostgresJsArgs, serializePostgresJsError, serializePostgresJsResult, serializeSpan, withMeticulous, withMeticulousPagesFunction, withMeticulousPostgres };
109
113
 
110
114
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { requestCaptureContext } from \"./context\";\nimport {\n type MeticulousExecutionContext,\n runWithMeticulous,\n type WithMeticulousOptions,\n} from \"./with-meticulous\";\n\nexport type {\n BindingRequestEvent,\n CaptureEvent,\n CapturedBody,\n CaptureEventsPayload,\n CoverageReportRequest,\n InboundRequestEvent,\n KvOmittedReason,\n KvOperation,\n KvOperationEvent,\n OutboundFetchAgentSwarmTestingRequest,\n OutboundFetchAgentSwarmTestingResponse,\n OutboundFetchLookupRequest,\n OutboundFetchLookupResponse,\n OutboundRequestEvent,\n PostgresQueryEvent,\n ReplaySessionInfoResponse,\n} from \"./protocol\";\nexport {\n AGENT_SWARM_TESTING_RUN_ID_ENV_KEY,\n AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY,\n CAPTURED_HEADERS,\n FRONTEND_SESSION_ID_HEADER,\n METICULOUS_PASSTHROUGH_HEADER,\n parseVirtualTimeMs,\n REPLAY_ID_HEADER,\n REPLAY_SIDECAR_URL_HEADER,\n SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH,\n SIDECAR_EVENTS_PATH,\n SIDECAR_PROTOCOL_VERSION,\n SIDECAR_PROTOCOL_VERSION_HEADER,\n SIDECAR_REPLAY_COVERAGE_PATH,\n SIDECAR_REPLAY_OUTBOUND_FETCH_PATH,\n SIDECAR_REPLAY_SESSION_PATH,\n VIRTUAL_TIME_HEADER,\n WORKERD_SHIM_VERSION_HEADER,\n} from \"./protocol\";\nexport { WORKERD_SHIM_VERSION } from \"./version\";\nexport {\n MAX_BODY_CAPTURE_SIZE,\n readBodyWithCap,\n readRequestBodyWithCap,\n readResponseBodyWithCap,\n} from \"./body-capture\";\n/**\n * Exported for the Node recorder, which captures response bodies through its own instrumentations\n * and must store them in the same form, and for its replay side, which serves them back.\n */\nexport {\n BODY_ENCODING_BASE64,\n type BodyEncoding,\n decodeBodyBytes,\n encodeBodyBytes,\n type EncodedBody,\n HTTP_RESPONSE_BODY_ENCODING_ATTR,\n} from \"./body-encoding\";\nexport { headersToRecord } from \"./outbound-capture\";\n/**\n * Exported for the Node recorder, whose `health-probe.ts` adapts this to\n * `http.IncomingMessage`. Which requests count as a health probe is a rule both surfaces\n * have to agree on, so — like the provisional-session-id rules below — it has exactly one\n * implementation.\n */\nexport { isHealthProbeRequest } from \"./health-probe\";\n/**\n * Exported for the Node recorder, whose `datadog-agent.ts` adapts these to the forked http and\n * undici instrumentations' ignore hooks. Which destinations count as a Datadog agent is a rule\n * both surfaces have to agree on, so — like the health-probe rule above — it has exactly one\n * implementation.\n */\nexport {\n isOrphanedDatadogAgentRequest,\n isOrphanedDatadogAgentUrl,\n type OutboundCallTarget,\n} from \"./datadog-agent\";\n/**\n * Exported for both sidecar implementations — the deployed Worker\n * (`@alwaysmeticulous/backend-recorder-sidecar-worker`) and the local Node one\n * (`backend-recorder-js`'s `sidecar/server.ts`) — which repeat the shim's health-probe\n * verdict on the events they receive, so the exclusion reaches a deployment whose bundled\n * shim predates it.\n */\nexport { HealthProbeEventFilter } from \"./health-probe-filter\";\nexport { redactRequestBody, STR_REDACTED } from \"./redact-body\";\nexport type { CoverageMap, CoverageMapFile } from \"./coverage/coverage.types\";\n// __mcEnter / __mcHit are the entry points the Vite plugin's injected imports\n// bind to, so they are part of the package's public surface even though no\n// human writes a call to them.\nexport {\n __mcEnter,\n __mcHit,\n collectHitIds,\n createCoverageSink,\n getCoverageMap,\n getUnreportedCoverageFiles,\n markCoverageFilesReported,\n registerCoverageFile,\n} from \"./coverage/runtime\";\nexport type { MeticulousExecutionContext, WithMeticulousOptions };\nexport {\n type MeticulousPagesFunction,\n type MeticulousPagesFunctionContext,\n withMeticulousPagesFunction,\n} from \"./pages-function\";\nexport {\n SIDECAR_BINDING_ENV_KEY,\n SIDECAR_BINDING_ORIGIN,\n SIDECAR_URL_ENV_KEY,\n type SidecarFetcher,\n type SidecarTransport,\n} from \"./sidecar-transport\";\n/**\n * Exported for the Node recorder's `withMeticulousCloudflareEnv`, which records the same KV\n * operations from a process whose bindings come from wrangler's `getPlatformProxy`. Both surfaces\n * must produce identical fields, so the contract has exactly one implementation.\n */\nexport {\n type KvCaptureFields,\n type KvCaptureOutcome,\n serializeKvArgs,\n serializeKvCaptureFields,\n} from \"./kv-capture\";\n/**\n * Exported for the same reason: the Node recorder re-exports these so a postgres.js query, a\n * captured error and a built span are byte-identical whichever surface produced them. A\n * Worker-hosted sidecar needs them too, and cannot load the Node bundle.\n */\nexport {\n deserializeCapturedError,\n serializeCapturedError,\n} from \"./error-capture\";\nexport {\n deserializeCapturedResult,\n serializeCapturedResult,\n} from \"./captured-result-codec\";\nexport {\n isSupportedPostgresJsQuery,\n MAX_POSTGRES_JS_RESULT_SIZE,\n POSTGRES_JS_ERROR_ATTR,\n POSTGRES_JS_QUERY_PARAMS_ATTR,\n POSTGRES_JS_QUERY_TEXT_ATTR,\n POSTGRES_JS_RESULT_ATTR,\n POSTGRES_JS_RESULT_TRUNCATED_ATTR,\n POSTGRES_JS_ROW_MODE_ATTR,\n postgresJsCommandOf,\n type PostgresJsQueryLike,\n type PostgresJsRowMode,\n reconstructQueryText,\n resolveRowMode,\n serializePostgresJsArgs,\n serializePostgresJsError,\n serializePostgresJsResult,\n} from \"./postgres/capture\";\nexport { withMeticulousPostgres } from \"./postgres/with-meticulous-postgres\";\nexport * from \"./spans/attributes\";\nexport {\n hrTimeToMs,\n msToHrTime,\n type HrTime,\n type ReadableSpan,\n type SerializedSpanBase,\n SPAN_KIND_CLIENT,\n SPAN_KIND_INTERNAL,\n SPAN_KIND_SERVER,\n SPAN_STATUS_ERROR,\n SPAN_STATUS_OK,\n SPAN_STATUS_UNSET,\n type SpanStatus,\n} from \"./spans/readable-span\";\nexport { serializeSpan } from \"./spans/serialize-span\";\nexport { randomHex, SpanBuilder } from \"./spans/span-builder\";\nexport { redactUrlCredentials } from \"./spans/url-redaction\";\n/**\n * Exported for the Node recorder, whose own `provisional-session-id.ts` adapts these to\n * `http.IncomingMessage` / `http.ServerResponse`. The mint format, the rule for which requests\n * get an id and the `Server-Timing` metric are all wire contracts with the page, so — as with\n * the KV capture fields above — they have exactly one implementation.\n */\nexport {\n type HeaderReader,\n SERVER_TIMING_HEADER,\n SERVER_TIMING_SESSION_METRIC,\n buildServerTimingSessionEntry,\n isProvisionalSessionIdCandidate,\n mintProvisionalSessionId,\n} from \"./provisional-session-id\";\n\n/** Structural subset of an ES-module Worker handler (`export default {...}`). */\nexport interface MeticulousWorkerHandler<Env = never> {\n fetch(\n request: Request,\n env: Env,\n ctx: MeticulousExecutionContext,\n ): Response | Promise<Response>;\n}\n\n/**\n * Wraps an ES-module Worker handler so Meticulous can record, or replay, the app's HTTP\n * behaviour:\n *\n * export default withMeticulous({\n * async fetch(request, env, ctx) { ... },\n * });\n *\n * For a Cloudflare Pages project, whose worker exports `onRequest` instead, use\n * {@link withMeticulousPagesFunction} — same behaviour, different handler shape.\n *\n * **Recording** activates on either a `METICULOUS_SIDECAR` service binding (how a **deployed**\n * Worker records: the binding points at a Meticulous recorder sidecar Worker in the same account)\n * or a `METICULOUS_SIDECAR_URL` var (how `wrangler dev` records against a local sidecar process),\n * or the matching `options`. Inbound requests, outgoing `fetch` calls, calls through\n * `fetch`-shaped bindings, KV namespace operations and — with `withMeticulousPostgres` — postgres.js\n * queries are reported to the sidecar, batched into one report per request, without affecting the\n * app. A binding beats a var if both are configured: only the binding can have been added to\n * *this* deployment's wrangler configuration deliberately, whereas a stale `.dev.vars` travels\n * with an image.\n *\n * **Replay** activates on the `x-meticulous-backend-replay-sidecar-url` header, injected by the\n * Meticulous replay runner on requests to the app under test. Outgoing `fetch` calls are then\n * served from the recording instead of reaching the real service — and a call the recording does\n * not cover fails, rather than quietly becoming live traffic; put a `meticulous-passthrough: true`\n * header on a request that must stay real. The clock is frozen at the recorded session's end so\n * recorded credentials are still valid, and `Math.random` / `crypto.randomUUID` /\n * `crypto.getRandomValues` are seeded so ids the app mints are the same in every replay of that\n * session. Workerd cannot read container environment variables, which is why per-replay config\n * arrives as a request header; the shim validates it and only honours a loopback /\n * docker-gateway / private-network `http:` origin.\n *\n * Replay takes precedence over recording when both are configured. A stale sidecar var baked into\n * an image is far more likely than a spurious replay header (nothing but the replay runner emits\n * one), and letting the env win would silently record a replay instead of mocking it.\n *\n * **Agent-swarm testing** activates on two deployment env vars,\n * `METICULOUS_AGENT_SWARM_TESTING_SIDECAR_URL` and `METICULOUS_AGENT_SWARM_TESTING_RUN_ID`,\n * set by whoever deploys the worker for a swarm run. Every outgoing `fetch` call is first\n * offered to the interceptor service the URL names; it either supplies the response to serve\n * or the call passes through to the real service. Unlike replay the mode is best-effort by\n * design: a call the interceptor cannot answer is never failed, there is no frozen clock and\n * no seeded randomness, and nothing is recorded. The sidecar URL is validated exactly like\n * the replay one.\n *\n * With neither configured the wrapper is a complete pass-through, so it is safe to keep in\n * deployed code. Requires the `nodejs_als` (or `nodejs_compat`) compatibility flag. A capture\n * failure or unreachable sidecar never affects the app.\n */\nexport const withMeticulous = <H extends MeticulousWorkerHandler<never>>(\n handler: H,\n options?: WithMeticulousOptions,\n): H => {\n const wrappedFetch = (\n request: Request,\n env: unknown,\n ctx: MeticulousExecutionContext,\n ): Promise<Response> =>\n runWithMeticulous(\n {\n request,\n env,\n ctx,\n invokeHandler: () => handler.fetch(request, env as never, ctx),\n },\n options,\n );\n\n // The wrapper only replaces `fetch` (same call signature), but TypeScript cannot verify a\n // spread against the bare generic H.\n return { ...handler, fetch: wrappedFetch } as H;\n};\n\n/**\n * The Meticulous session id of the request currently being served, or `undefined` if this\n * call cannot be tied to one — including whenever the wrapper is a pass-through.\n *\n * Exposed so a server-rendering worker can put the id into the HTML it is about to return,\n * typically as `data-session-id` on the recorder's own `<script>` tag. That is the exact,\n * cache-proof alternative to the `Server-Timing` header the shim publishes automatically;\n * doing both is fine, since the recorder prefers the rendered value. It is also how the id\n * reaches another process — a document navigation has no inbound header for the app to\n * forward, so a worker that fans out to its own services has to pass this on as\n * `x-meticulous-session-id` itself.\n *\n * During a replay this returns the replayed session's id, which is the same value the render\n * being replayed was given when it was recorded (the page adopted the minted id, so that is\n * what the recording is stored under, and it is what the replay runner injects back). An app\n * that renders the id therefore renders the same bytes both times, rather than the id turning\n * every server-rendered page into a diff.\n */\nexport const getMeticulousSessionId = (): string | undefined =>\n requestCaptureContext.getStore()?.frontendSessionId;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4PA,MAAa,kBACX,SACA,YACM;CACN,MAAM,gBACJ,SACA,KACA,QAEA,kBACE;EACE;EACA;EACA;EACA,qBAAqB,QAAQ,MAAM,SAAS,KAAc,IAAI;EAC/D,EACD,QACD;AAIH,QAAO;EAAE,GAAG;EAAS,OAAO;EAAc;;;;;;;;;;;;;;;;;;;;AAqB5C,MAAa,+BACX,sBAAsB,UAAU,EAAE"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { requestCaptureContext } from \"./context\";\nimport {\n type MeticulousExecutionContext,\n runWithMeticulous,\n type WithMeticulousOptions,\n} from \"./with-meticulous\";\n\nexport type {\n BeMockCall,\n BeMockCallsReport,\n BeMockFailedMessage,\n BeMockInstallMessage,\n BeMockLoadSessionMessage,\n BeMockLogMessage,\n BeMockReadyMessage,\n BeMockRecordedRequest,\n BeMockSessionMessage,\n BeMockSidecarMessage,\n BeMockWorkerMessage,\n BindingRequestEvent,\n CaptureEvent,\n CapturedBody,\n CaptureEventsPayload,\n CoverageReportRequest,\n InboundRequestEvent,\n KvOmittedReason,\n KvOperation,\n KvOperationEvent,\n OutboundFetchAgentSwarmTestingRequest,\n OutboundFetchAgentSwarmTestingResponse,\n OutboundFetchLookupRequest,\n OutboundFetchLookupResponse,\n OutboundRequestEvent,\n PostgresQueryEvent,\n ReplaySessionInfoResponse,\n} from \"./protocol\";\nexport {\n AGENT_SWARM_TESTING_RUN_ID_ENV_KEY,\n AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY,\n BE_MOCK_PING_INTERVAL_MS,\n CAPTURED_HEADERS,\n FRONTEND_SESSION_ID_HEADER,\n METICULOUS_PASSTHROUGH_HEADER,\n parseVirtualTimeMs,\n REPLAY_ID_HEADER,\n REPLAY_SIDECAR_URL_HEADER,\n SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_CALLS_PATH,\n SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_PATH,\n SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH,\n SIDECAR_EVENTS_PATH,\n SIDECAR_PROTOCOL_VERSION,\n SIDECAR_PROTOCOL_VERSION_HEADER,\n SIDECAR_REPLAY_COVERAGE_PATH,\n SIDECAR_REPLAY_OUTBOUND_FETCH_PATH,\n SIDECAR_REPLAY_SESSION_PATH,\n VIRTUAL_TIME_HEADER,\n WORKERD_SHIM_VERSION_HEADER,\n} from \"./protocol\";\nexport { WORKERD_SHIM_VERSION } from \"./version\";\nexport {\n MAX_BODY_CAPTURE_SIZE,\n readBodyWithCap,\n readRequestBodyWithCap,\n readResponseBodyWithCap,\n} from \"./body-capture\";\n/**\n * Exported for the Node recorder, which captures response bodies through its own instrumentations\n * and must store them in the same form, and for its replay side, which serves them back.\n */\nexport {\n BODY_ENCODING_BASE64,\n type BodyEncoding,\n decodeBodyBytes,\n encodeBodyBytes,\n type EncodedBody,\n HTTP_RESPONSE_BODY_ENCODING_ATTR,\n} from \"./body-encoding\";\nexport { headersToRecord } from \"./outbound-capture\";\n/**\n * Exported for the Node recorder, whose `health-probe.ts` adapts this to\n * `http.IncomingMessage`. Which requests count as a health probe is a rule both surfaces\n * have to agree on, so — like the provisional-session-id rules below — it has exactly one\n * implementation.\n */\nexport { isHealthProbeRequest } from \"./health-probe\";\n/**\n * Exported for the Node recorder, whose `datadog-agent.ts` adapts these to the forked http and\n * undici instrumentations' ignore hooks. Which destinations count as a Datadog agent is a rule\n * both surfaces have to agree on, so — like the health-probe rule above — it has exactly one\n * implementation.\n */\nexport {\n isOrphanedDatadogAgentRequest,\n isOrphanedDatadogAgentUrl,\n type OutboundCallTarget,\n} from \"./datadog-agent\";\n/**\n * Exported for both sidecar implementations — the deployed Worker\n * (`@alwaysmeticulous/backend-recorder-sidecar-worker`) and the local Node one\n * (`backend-recorder-js`'s `sidecar/server.ts`) — which repeat the shim's health-probe\n * verdict on the events they receive, so the exclusion reaches a deployment whose bundled\n * shim predates it.\n */\nexport { HealthProbeEventFilter } from \"./health-probe-filter\";\nexport { redactRequestBody, STR_REDACTED } from \"./redact-body\";\nexport type { CoverageMap, CoverageMapFile } from \"./coverage/coverage.types\";\n// __mcEnter / __mcHit are the entry points the Vite plugin's injected imports\n// bind to, so they are part of the package's public surface even though no\n// human writes a call to them.\nexport {\n __mcEnter,\n __mcHit,\n collectHitIds,\n createCoverageSink,\n getCoverageMap,\n getUnreportedCoverageFiles,\n markCoverageFilesReported,\n registerCoverageFile,\n} from \"./coverage/runtime\";\nexport type { MeticulousExecutionContext, WithMeticulousOptions };\nexport {\n type MeticulousPagesFunction,\n type MeticulousPagesFunctionContext,\n withMeticulousPagesFunction,\n} from \"./pages-function\";\nexport {\n SIDECAR_BINDING_ENV_KEY,\n SIDECAR_BINDING_ORIGIN,\n SIDECAR_URL_ENV_KEY,\n type SidecarFetcher,\n type SidecarTransport,\n} from \"./sidecar-transport\";\n/**\n * Exported for the Node recorder's `withMeticulousCloudflareEnv`, which records the same KV\n * operations from a process whose bindings come from wrangler's `getPlatformProxy`. Both surfaces\n * must produce identical fields, so the contract has exactly one implementation.\n */\nexport {\n type KvCaptureFields,\n type KvCaptureOutcome,\n serializeKvArgs,\n serializeKvCaptureFields,\n} from \"./kv-capture\";\n/**\n * Exported for the same reason: the Node recorder re-exports these so a postgres.js query, a\n * captured error and a built span are byte-identical whichever surface produced them. A\n * Worker-hosted sidecar needs them too, and cannot load the Node bundle.\n */\nexport {\n deserializeCapturedError,\n serializeCapturedError,\n} from \"./error-capture\";\nexport {\n deserializeCapturedResult,\n serializeCapturedResult,\n} from \"./captured-result-codec\";\nexport {\n isSupportedPostgresJsQuery,\n MAX_POSTGRES_JS_RESULT_SIZE,\n POSTGRES_JS_ERROR_ATTR,\n POSTGRES_JS_QUERY_PARAMS_ATTR,\n POSTGRES_JS_QUERY_TEXT_ATTR,\n POSTGRES_JS_RESULT_ATTR,\n POSTGRES_JS_RESULT_TRUNCATED_ATTR,\n POSTGRES_JS_ROW_MODE_ATTR,\n postgresJsCommandOf,\n type PostgresJsQueryLike,\n type PostgresJsRowMode,\n reconstructQueryText,\n resolveRowMode,\n serializePostgresJsArgs,\n serializePostgresJsError,\n serializePostgresJsResult,\n} from \"./postgres/capture\";\nexport { withMeticulousPostgres } from \"./postgres/with-meticulous-postgres\";\nexport * from \"./spans/attributes\";\nexport {\n hrTimeToMs,\n msToHrTime,\n type HrTime,\n type ReadableSpan,\n type SerializedSpanBase,\n SPAN_KIND_CLIENT,\n SPAN_KIND_INTERNAL,\n SPAN_KIND_SERVER,\n SPAN_STATUS_ERROR,\n SPAN_STATUS_OK,\n SPAN_STATUS_UNSET,\n type SpanStatus,\n} from \"./spans/readable-span\";\nexport { serializeSpan } from \"./spans/serialize-span\";\nexport { randomHex, SpanBuilder } from \"./spans/span-builder\";\nexport { redactUrlCredentials } from \"./spans/url-redaction\";\n/**\n * Exported for the Node recorder, whose own `provisional-session-id.ts` adapts these to\n * `http.IncomingMessage` / `http.ServerResponse`. The mint format, the rule for which requests\n * get an id and the `Server-Timing` metric are all wire contracts with the page, so — as with\n * the KV capture fields above — they have exactly one implementation.\n */\nexport {\n type HeaderReader,\n SERVER_TIMING_HEADER,\n SERVER_TIMING_SESSION_METRIC,\n buildServerTimingSessionEntry,\n isProvisionalSessionIdCandidate,\n mintProvisionalSessionId,\n} from \"./provisional-session-id\";\n\n/** Structural subset of an ES-module Worker handler (`export default {...}`). */\nexport interface MeticulousWorkerHandler<Env = never> {\n fetch(\n request: Request,\n env: Env,\n ctx: MeticulousExecutionContext,\n ): Response | Promise<Response>;\n}\n\n/**\n * Wraps an ES-module Worker handler so Meticulous can record, or replay, the app's HTTP\n * behaviour:\n *\n * export default withMeticulous({\n * async fetch(request, env, ctx) { ... },\n * });\n *\n * For a Cloudflare Pages project, whose worker exports `onRequest` instead, use\n * {@link withMeticulousPagesFunction} — same behaviour, different handler shape.\n *\n * **Recording** activates on either a `METICULOUS_SIDECAR` service binding (how a **deployed**\n * Worker records: the binding points at a Meticulous recorder sidecar Worker in the same account)\n * or a `METICULOUS_SIDECAR_URL` var (how `wrangler dev` records against a local sidecar process),\n * or the matching `options`. Inbound requests, outgoing `fetch` calls, calls through\n * `fetch`-shaped bindings, KV namespace operations and — with `withMeticulousPostgres` — postgres.js\n * queries are reported to the sidecar, batched into one report per request, without affecting the\n * app. A binding beats a var if both are configured: only the binding can have been added to\n * *this* deployment's wrangler configuration deliberately, whereas a stale `.dev.vars` travels\n * with an image.\n *\n * **Replay** activates on the `x-meticulous-backend-replay-sidecar-url` header, injected by the\n * Meticulous replay runner on requests to the app under test. Outgoing `fetch` calls are then\n * served from the recording instead of reaching the real service — and a call the recording does\n * not cover fails, rather than quietly becoming live traffic; put a `meticulous-passthrough: true`\n * header on a request that must stay real. The clock is frozen at the recorded session's end so\n * recorded credentials are still valid, and `Math.random` / `crypto.randomUUID` /\n * `crypto.getRandomValues` are seeded so ids the app mints are the same in every replay of that\n * session. Workerd cannot read container environment variables, which is why per-replay config\n * arrives as a request header; the shim validates it and only honours a loopback /\n * docker-gateway / private-network `http:` origin.\n *\n * Replay takes precedence over recording when both are configured. A stale sidecar var baked into\n * an image is far more likely than a spurious replay header (nothing but the replay runner emits\n * one), and letting the env win would silently record a replay instead of mocking it.\n *\n * **Agent-swarm testing** activates on two deployment env vars,\n * `METICULOUS_AGENT_SWARM_TESTING_SIDECAR_URL` and `METICULOUS_AGENT_SWARM_TESTING_RUN_ID`,\n * set by whoever deploys the worker for a swarm run. These must be Worker bindings,\n * not just environment variables on the host process: forward them at startup through\n * Wrangler vars or the `.dev.vars` file consumed by the runtime. The app calls a sidecar\n * in the same pod through loopback; configuring the sidecar's own environment does not\n * configure the Worker's bindings. Every outgoing `fetch` call is first\n * offered to the interceptor service the URL names; it either supplies the response to serve\n * or the call passes through to the real service. Unlike replay the mode is best-effort by\n * design: a call the interceptor cannot answer is never failed, there is no frozen clock and\n * no seeded randomness, and nothing is recorded. The sidecar URL is validated exactly like\n * the replay one.\n *\n * With neither configured the wrapper is a complete pass-through, so it is safe to keep in\n * deployed code. Requires the `nodejs_als` (or `nodejs_compat`) compatibility flag. A capture\n * failure or unreachable sidecar never affects the app.\n */\nexport const withMeticulous = <H extends MeticulousWorkerHandler<never>>(\n handler: H,\n options?: WithMeticulousOptions,\n): H => {\n const wrappedFetch = (\n request: Request,\n env: unknown,\n ctx: MeticulousExecutionContext,\n ): Promise<Response> =>\n runWithMeticulous(\n {\n request,\n env,\n ctx,\n invokeHandler: () => handler.fetch(request, env as never, ctx),\n },\n options,\n );\n\n // The wrapper only replaces `fetch` (same call signature), but TypeScript cannot verify a\n // spread against the bare generic H.\n return { ...handler, fetch: wrappedFetch } as H;\n};\n\n/**\n * The Meticulous session id of the request currently being served, or `undefined` if this\n * call cannot be tied to one — including whenever the wrapper is a pass-through.\n *\n * Exposed so a server-rendering worker can put the id into the HTML it is about to return,\n * typically as `data-session-id` on the recorder's own `<script>` tag. That is the exact,\n * cache-proof alternative to the `Server-Timing` header the shim publishes automatically;\n * doing both is fine, since the recorder prefers the rendered value. It is also how the id\n * reaches another process — a document navigation has no inbound header for the app to\n * forward, so a worker that fans out to its own services has to pass this on as\n * `x-meticulous-session-id` itself.\n *\n * During a replay this returns the replayed session's id, which is the same value the render\n * being replayed was given when it was recorded (the page adopted the minted id, so that is\n * what the recording is stored under, and it is what the replay runner injects back). An app\n * that renders the id therefore renders the same bytes both times, rather than the id turning\n * every server-rendered page into a diff.\n */\nexport const getMeticulousSessionId = (): string | undefined =>\n requestCaptureContext.getStore()?.frontendSessionId;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8QA,MAAa,kBACX,SACA,YACM;CACN,MAAM,gBACJ,SACA,KACA,QAEA,kBACE;EACE;EACA;EACA;EACA,qBAAqB,QAAQ,MAAM,SAAS,KAAc,IAAI;EAC/D,EACD,QACD;AAIH,QAAO;EAAE,GAAG;EAAS,OAAO;EAAc;;;;;;;;;;;;;;;;;;;;AAqB5C,MAAa,+BACX,sBAAsB,UAAU,EAAE"}
@@ -360,6 +360,145 @@ declare const AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY = "METICULOUS_AGENT_SWARM_
360
360
  declare const AGENT_SWARM_TESTING_RUN_ID_ENV_KEY = "METICULOUS_AGENT_SWARM_TESTING_RUN_ID";
361
361
  /** Agent-swarm-testing route. */
362
362
  declare const SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH = "/v1/agent-swarm-testing/outbound-fetch";
363
+ /**
364
+ * Agent-swarm-testing WebSocket route that hosts one be-mock: an agent-authored
365
+ * `be-mock.ts` (backend mocks), compiled by the agentic session-generation
366
+ * worker into a self-contained sandbox bundle. The sidecar runs it in its own
367
+ * permission-restricted child process over the frozen RPC toolkit, and routes
368
+ * every intercepted outbound fetch through the module's `interceptRequest`
369
+ * export — see `packages/backend-recorder-js/src/sidecar/be-mock` and
370
+ * `packages/agentic-session-generation-worker/src/app-access/be-mocking`.
371
+ *
372
+ * One connection is one installed module: the worker opens it, sends
373
+ * {@link BeMockInstallMessage}, answers every {@link BeMockLoadSessionMessage}
374
+ * while the module loads, and receives {@link BeMockReadyMessage} (or
375
+ * {@link BeMockFailedMessage}) and then the module's log lines. Closing the
376
+ * connection — from either side — uninstalls the module.
377
+ */
378
+ declare const SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_PATH = "/v1/agent-swarm-testing/be-mock";
379
+ /**
380
+ * Agent-swarm-testing route reporting what one installed be-mock decided for
381
+ * each outbound call routed to it: `GET` with `runId` and `frontendSessionId`
382
+ * query params, answered with {@link BeMockCallsReport} — or 404 when no module
383
+ * is installed under that id. Calls routed to no module are never reported: no
384
+ * case could have mocked them.
385
+ */
386
+ declare const SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_CALLS_PATH = "/v1/agent-swarm-testing/be-mock/calls";
387
+ /**
388
+ * One outbound call an installed be-mock was offered, and what came of it. A
389
+ * passed-through call's real response is not observed: the sidecar only takes
390
+ * the module's decision.
391
+ */
392
+ type BeMockCall = {
393
+ method: string;
394
+ url: string;
395
+ requestBody?: CapturedBody;
396
+ } & ({
397
+ /** interceptRequest's response was served. */outcome: "mock";
398
+ response: {
399
+ statusCode: number;
400
+ body: string;
401
+ };
402
+ } | {
403
+ /**
404
+ * The call went to the real service: interceptRequest returned
405
+ * undefined, or its answer could not be used (`error` says why).
406
+ */
407
+ outcome: "passthrough";
408
+ error?: string;
409
+ });
410
+ interface BeMockCallsReport {
411
+ /** In completion order (the module finished deciding). */
412
+ calls: BeMockCall[];
413
+ /** Calls not kept once the report reached its bound. */
414
+ droppedCount: number;
415
+ }
416
+ /**
417
+ * How often the sidecar pings each be-mock connection (a WebSocket ping
418
+ * frame). The sidecar terminates a connection that misses a pong; the worker
419
+ * treats several missed pings as a lost module.
420
+ */
421
+ declare const BE_MOCK_PING_INTERVAL_MS = 15000;
422
+ /** Worker → sidecar: the module to install. Always the connection's first message. */
423
+ interface BeMockInstallMessage {
424
+ type: "install";
425
+ /** Whose run this module belongs to; see {@link AGENT_SWARM_TESTING_RUN_ID_ENV_KEY}. */
426
+ runId: string;
427
+ /**
428
+ * The routing key: the sidecar serves this module only to outbound calls whose
429
+ * inbound request carried this {@link FRONTEND_SESSION_ID_HEADER}, so parallel
430
+ * cases sharing one backend each get their own module.
431
+ */
432
+ frontendSessionId: string;
433
+ /** The compiled, self-contained sandbox bundle to install. */
434
+ code: string;
435
+ }
436
+ /**
437
+ * Worker → sidecar: one recorded session's backend spans, answering a
438
+ * {@link BeMockLoadSessionMessage}. `error` instead when the worker could not
439
+ * read the session.
440
+ */
441
+ type BeMockSessionMessage = {
442
+ type: "session";
443
+ sessionId: string;
444
+ backendSpans: unknown[];
445
+ } | {
446
+ type: "session";
447
+ sessionId: string;
448
+ error: string;
449
+ };
450
+ type BeMockWorkerMessage = BeMockInstallMessage | BeMockSessionMessage;
451
+ /**
452
+ * Sidecar → worker: the loading module called `loadSessionBackendRequests`
453
+ * for this session. Sent only before {@link BeMockReadyMessage}: a module
454
+ * loads its recordings at the top level.
455
+ */
456
+ interface BeMockLoadSessionMessage {
457
+ type: "loadSession";
458
+ sessionId: string;
459
+ }
460
+ /** Sidecar → worker: the module loaded and now answers outbound calls. */
461
+ interface BeMockReadyMessage {
462
+ type: "ready";
463
+ }
464
+ /** Sidecar → worker: the install failed; the sidecar closes the connection after it. */
465
+ interface BeMockFailedMessage {
466
+ type: "failed";
467
+ error: string;
468
+ }
469
+ /** Sidecar → worker: one line from the module (stdout/stderr or its `log(...)`). */
470
+ interface BeMockLogMessage {
471
+ type: "log";
472
+ line: string;
473
+ }
474
+ type BeMockSidecarMessage = BeMockLoadSessionMessage | BeMockReadyMessage | BeMockFailedMessage | BeMockLogMessage;
475
+ /**
476
+ * One outbound HTTP call a recorded session's backend made, as
477
+ * `loadSessionBackendRequests` returns it to be-mock.ts. The request mirrors
478
+ * {@link OutboundFetchAgentSwarmTestingRequest} so a live request compares
479
+ * field for field; the response is servable as-is.
480
+ */
481
+ interface BeMockRecordedRequest {
482
+ method: string;
483
+ /**
484
+ * Full URL including query string, as recorded — or only the path and query
485
+ * when the recording stored no scheme or host (older recorders).
486
+ */
487
+ url: string;
488
+ requestBody?: CapturedBody;
489
+ /**
490
+ * True when ingestion attached this call by its time alone, not by this
491
+ * session's id: it may belong to another user's traffic on the same backend.
492
+ */
493
+ inferred: boolean;
494
+ response: {
495
+ statusCode: number; /** As recorded: text, or base64 when `bodyEncoding` is set. */
496
+ body: string;
497
+ bodyEncoding?: BodyEncoding; /** True when the recorder cut the body at the capture cap. */
498
+ bodyTruncated: boolean;
499
+ headers: Record<string, string>;
500
+ };
501
+ }
363
502
  /**
364
503
  * One outbound `fetch` offered to the interceptor, sent to
365
504
  * {@link SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH}. Unlike replay's lookup there is no
@@ -389,5 +528,5 @@ type OutboundFetchAgentSwarmTestingResponse = {
389
528
  outcome: "passthrough";
390
529
  };
391
530
  //#endregion
392
- export { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, BindingRequestEvent, CAPTURED_HEADERS, CaptureEvent, CaptureEventsPayload, CapturedBody, CoverageReportRequest, FRONTEND_SESSION_ID_HEADER, InboundRequestEvent, KvOmittedReason, KvOperation, KvOperationEvent, METICULOUS_PASSTHROUGH_HEADER, OutboundFetchAgentSwarmTestingRequest, OutboundFetchAgentSwarmTestingResponse, OutboundFetchLookupRequest, OutboundFetchLookupResponse, OutboundRequestEvent, PostgresQueryEvent, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, ReplaySessionInfoResponse, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, VIRTUAL_TIME_HEADER, WORKERD_SHIM_VERSION_HEADER, parseVirtualTimeMs };
531
+ export { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, BE_MOCK_PING_INTERVAL_MS, BeMockCall, BeMockCallsReport, BeMockFailedMessage, BeMockInstallMessage, BeMockLoadSessionMessage, BeMockLogMessage, BeMockReadyMessage, BeMockRecordedRequest, BeMockSessionMessage, BeMockSidecarMessage, BeMockWorkerMessage, BindingRequestEvent, CAPTURED_HEADERS, CaptureEvent, CaptureEventsPayload, CapturedBody, CoverageReportRequest, FRONTEND_SESSION_ID_HEADER, InboundRequestEvent, KvOmittedReason, KvOperation, KvOperationEvent, METICULOUS_PASSTHROUGH_HEADER, OutboundFetchAgentSwarmTestingRequest, OutboundFetchAgentSwarmTestingResponse, OutboundFetchLookupRequest, OutboundFetchLookupResponse, OutboundRequestEvent, PostgresQueryEvent, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, ReplaySessionInfoResponse, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_CALLS_PATH, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_PATH, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, VIRTUAL_TIME_HEADER, WORKERD_SHIM_VERSION_HEADER, parseVirtualTimeMs };
393
532
  //# sourceMappingURL=protocol.d.ts.map
package/dist/protocol.js CHANGED
@@ -93,7 +93,37 @@ const AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY = "METICULOUS_AGENT_SWARM_TESTING_
93
93
  const AGENT_SWARM_TESTING_RUN_ID_ENV_KEY = "METICULOUS_AGENT_SWARM_TESTING_RUN_ID";
94
94
  /** Agent-swarm-testing route. */
95
95
  const SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH = "/v1/agent-swarm-testing/outbound-fetch";
96
+ /**
97
+ * Agent-swarm-testing WebSocket route that hosts one be-mock: an agent-authored
98
+ * `be-mock.ts` (backend mocks), compiled by the agentic session-generation
99
+ * worker into a self-contained sandbox bundle. The sidecar runs it in its own
100
+ * permission-restricted child process over the frozen RPC toolkit, and routes
101
+ * every intercepted outbound fetch through the module's `interceptRequest`
102
+ * export — see `packages/backend-recorder-js/src/sidecar/be-mock` and
103
+ * `packages/agentic-session-generation-worker/src/app-access/be-mocking`.
104
+ *
105
+ * One connection is one installed module: the worker opens it, sends
106
+ * {@link BeMockInstallMessage}, answers every {@link BeMockLoadSessionMessage}
107
+ * while the module loads, and receives {@link BeMockReadyMessage} (or
108
+ * {@link BeMockFailedMessage}) and then the module's log lines. Closing the
109
+ * connection — from either side — uninstalls the module.
110
+ */
111
+ const SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_PATH = "/v1/agent-swarm-testing/be-mock";
112
+ /**
113
+ * Agent-swarm-testing route reporting what one installed be-mock decided for
114
+ * each outbound call routed to it: `GET` with `runId` and `frontendSessionId`
115
+ * query params, answered with {@link BeMockCallsReport} — or 404 when no module
116
+ * is installed under that id. Calls routed to no module are never reported: no
117
+ * case could have mocked them.
118
+ */
119
+ const SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_CALLS_PATH = "/v1/agent-swarm-testing/be-mock/calls";
120
+ /**
121
+ * How often the sidecar pings each be-mock connection (a WebSocket ping
122
+ * frame). The sidecar terminates a connection that misses a pong; the worker
123
+ * treats several missed pings as a lost module.
124
+ */
125
+ const BE_MOCK_PING_INTERVAL_MS = 15e3;
96
126
  //#endregion
97
- export { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, CAPTURED_HEADERS, FRONTEND_SESSION_ID_HEADER, METICULOUS_PASSTHROUGH_HEADER, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, VIRTUAL_TIME_HEADER, WORKERD_SHIM_VERSION_HEADER, parseVirtualTimeMs };
127
+ export { AGENT_SWARM_TESTING_RUN_ID_ENV_KEY, AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY, BE_MOCK_PING_INTERVAL_MS, CAPTURED_HEADERS, FRONTEND_SESSION_ID_HEADER, METICULOUS_PASSTHROUGH_HEADER, REPLAY_ID_HEADER, REPLAY_SIDECAR_URL_HEADER, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_CALLS_PATH, SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_PATH, SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH, SIDECAR_EVENTS_PATH, SIDECAR_PROTOCOL_VERSION, SIDECAR_PROTOCOL_VERSION_HEADER, SIDECAR_REPLAY_COVERAGE_PATH, SIDECAR_REPLAY_OUTBOUND_FETCH_PATH, SIDECAR_REPLAY_SESSION_PATH, VIRTUAL_TIME_HEADER, WORKERD_SHIM_VERSION_HEADER, parseVirtualTimeMs };
98
128
 
99
129
  //# sourceMappingURL=protocol.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","names":[],"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Wire protocol between the in-worker shim and the Meticulous backend\n * recorder sidecar. The sidecar imports these types (type-only), so this file\n * is the single source of truth for the shape of capture events.\n */\n\nimport type { BodyEncoding } from \"./body-encoding\";\n\nexport const SIDECAR_PROTOCOL_VERSION = \"2\";\n\n/** Header carrying {@link SIDECAR_PROTOCOL_VERSION} on every shim → sidecar request. */\nexport const SIDECAR_PROTOCOL_VERSION_HEADER =\n \"x-meticulous-sidecar-protocol-version\";\n\n/**\n * Header the Meticulous frontend recorder stamps on requests made by the app\n * under recording; its value ties backend spans to the frontend session.\n */\nexport const FRONTEND_SESSION_ID_HEADER = \"x-meticulous-session-id\";\n\n/** Header identifying one replay attempt of a recorded frontend session. */\nexport const REPLAY_ID_HEADER = \"x-meticulous-replay-id\";\n\n/**\n * Elapsed virtual time (ms from replay start) of the triggering frontend\n * request. Injected by the replay runner on app-url requests during backend\n * testing; the sidecar uses it to pick the closest recorded burst when several\n * candidates share a loosened match key.\n */\nexport const VIRTUAL_TIME_HEADER = \"x-meticulous-virtual-time\";\n\n/**\n * Parses {@link VIRTUAL_TIME_HEADER}. `0` is a valid start-of-session time;\n * anything non-finite or negative is ignored so a garbage header cannot skew matching.\n */\nexport const parseVirtualTimeMs = (\n value: string | null | undefined,\n): number | undefined => {\n if (value == null || value === \"\") {\n return undefined;\n }\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed < 0) {\n return undefined;\n }\n return parsed;\n};\n\n/**\n * A request carrying this header set to `\"true\"` is never served from the recording and\n * never fails on a miss — it goes to the real network. Use it for a call that must be live\n * during replay (a health probe, telemetry, a resource the recording cannot cover).\n *\n * Same convention and value as the frontend network stubbing and the Node backend recorder\n * (`METICULOUS_PASSTHROUGH_HEADER` in `@alwaysmeticulous/backend-recorder-js`); the value\n * must stay in step with both.\n */\nexport const METICULOUS_PASSTHROUGH_HEADER = \"meticulous-passthrough\";\n\nexport const SIDECAR_EVENTS_PATH = \"/v1/events\";\n\n/**\n * Replay routes. A sidecar started in record mode answers 404 on both. Replay is hermetic,\n * so an unanswered outbound-fetch lookup fails the app's call rather than letting it reach\n * the real service — the same as any other failure to stub.\n */\nexport const SIDECAR_REPLAY_SESSION_PATH = \"/v1/replay/session\";\nexport const SIDECAR_REPLAY_OUTBOUND_FETCH_PATH = \"/v1/replay/outbound-fetch\";\n\n/**\n * Line-coverage reporting. Additive: an older sidecar answers 404, which the\n * shim treats as \"coverage not collected here\" and stops reporting for the life\n * of the isolate. Deliberately NOT guarded by a\n * {@link SIDECAR_PROTOCOL_VERSION} bump — that check is exact equality, so a\n * bump would stop every already-built customer image from mocking at all.\n */\nexport const SIDECAR_REPLAY_COVERAGE_PATH = \"/v1/replay/coverage\";\n\n/**\n * Header carrying the replay sidecar's origin, injected by the Meticulous replay runner\n * on requests to the app under test. Workerd cannot see container environment variables,\n * so an inbound header is the only way per-replay config can reach the shim.\n *\n * The shim validates the value before using it and only honours a loopback /\n * `host.docker.internal` / private-network `http:` origin — see `sidecar-url.ts`.\n */\nexport const REPLAY_SIDECAR_URL_HEADER =\n \"x-meticulous-backend-replay-sidecar-url\";\n\n/**\n * Header the in-worker shim stamps on every app response (record and replay)\n * carrying the shim's npm package version. The replay runner reads this from\n * Puppeteer — workerd cannot see container env, and the execute pod cannot\n * reach the in-pod sidecar, so a response header is the only channel that\n * reports which bundled shim produced a replay's After screenshots.\n */\nexport const WORKERD_SHIM_VERSION_HEADER = \"x-meticulous-workerd-shim-version\";\n\n/**\n * The only headers persisted on capture events. Everything else — notably\n * authorization, cookie/set-cookie, and API-key headers — is dropped at\n * capture time, before it ever leaves the worker.\n *\n * The Node backend recorder persists the same two, plus the inbound request's\n * `Cookie` header on SERVER spans (`recorded-request-headers.ts`). This list\n * has not followed it there, so a workerd recording of an authenticated server\n * render still carries no session cookie.\n */\nexport const CAPTURED_HEADERS = [\n \"content-type\",\n FRONTEND_SESSION_ID_HEADER,\n] as const;\n\nexport interface CapturedBody {\n /**\n * The body truncated to the capture cap: its UTF-8 text, or base64 of its bytes when\n * `encoding` is set.\n */\n body: string;\n /** True when the body exceeded the capture cap or the read was cut short. */\n truncated: boolean;\n /**\n * Set only on a response body whose bytes are not UTF-8, which is stored base64 so that replay\n * can serve it back exactly. Additive and non-version-bumping: an older shim omits it, and its\n * bodies are text as before.\n */\n encoding?: BodyEncoding;\n}\n\n/**\n * The fields every capture event carries to say which request, session and trace it belongs to.\n */\ninterface CorrelatedEvent {\n /** Correlates outbound events to the inbound request being handled. */\n requestId: string;\n /** Value of {@link FRONTEND_SESSION_ID_HEADER} on the inbound request, if any. */\n frontendSessionId?: string;\n /**\n * Trace the request's spans belong to (32 hex chars), minted by the shim.\n *\n * The shim mints it, rather than the sidecar deriving it from {@link requestId}, so span\n * assembly is stateless: a sidecar that had to remember `requestId → traceId` would give a\n * request's SERVER span and its CLIENT children different traces whenever it lost that memory\n * between two batches — which, for a Worker-hosted sidecar, an eviction can do at any moment.\n *\n * Optional so a sidecar keeps working against a shim that predates this field, in which case\n * it falls back to its own per-`requestId` map.\n */\n traceId?: string;\n /**\n * Span id of the request's SERVER span (16 hex chars), also minted by the shim: it is the\n * parent of every CLIENT span the request produced, and the inbound event's own span id.\n */\n serverSpanId?: string;\n}\n\ninterface BaseRequestEvent extends CorrelatedEvent {\n method: string;\n /** Full URL including query string. */\n url: string;\n /** Lower-cased header name → values. */\n requestHeaders: Record<string, string[]>;\n responseHeaders?: Record<string, string[]>;\n statusCode?: number;\n startTimeMs: number;\n endTimeMs: number;\n /** Set when the request failed before a response was produced. */\n error?: string;\n}\n\n/**\n * The worker's own inbound request (becomes a SERVER span). Bodies are not\n * captured, matching the Node backend recorder's SERVER-span contract.\n */\nexport interface InboundRequestEvent extends BaseRequestEvent {\n kind: \"inbound\";\n /**\n * Where {@link BaseRequestEvent.frontendSessionId} came from. Absent means the browser\n * minted it and sent it on the request, which is the normal case. `\"backend\"` means the\n * shim minted it for a document navigation the browser could not tag, and the page may or\n * may not have gone on to adopt it — a distinction ingestion needs, because an unadopted\n * backend-minted id names a session that will never exist and must be treated as unstamped.\n * The sidecar turns it into `meticulous.session_id_origin`.\n *\n * Optional, and therefore compatible with an older sidecar (which ignores it) — which is\n * why adding it does not bump {@link SIDECAR_PROTOCOL_VERSION}, a strict-equality check\n * whose bump would instead make this shim unusable against one.\n */\n sessionIdOrigin?: \"backend\";\n}\n\n/**\n * An outgoing `fetch` call made while handling an inbound request (becomes a\n * CLIENT span).\n */\nexport interface OutboundRequestEvent extends BaseRequestEvent {\n kind: \"outbound\";\n requestBody?: CapturedBody;\n responseBody?: CapturedBody;\n}\n\n/**\n * A `fetch` through a Cloudflare binding — a service binding, an assets binding, or a\n * Durable Object stub — made while handling an inbound request (becomes a CLIENT span).\n *\n * Request/Response-shaped like {@link OutboundRequestEvent}, but a distinct kind because the\n * call never leaves the isolate and its URL is whatever the caller invented. Keeping the two\n * apart means a binding call and real egress can never be confused for one another when\n * these spans are eventually mocked.\n */\nexport interface BindingRequestEvent extends BaseRequestEvent {\n kind: \"binding\";\n /**\n * The `env` key the binding was found under. Absent when the instance was never seen on\n * `env` — most commonly a Durable Object stub, which `namespace.get()` produces rather\n * than being a binding in its own right.\n */\n bindingName?: string;\n requestBody?: CapturedBody;\n responseBody?: CapturedBody;\n}\n\n/** KV namespace methods the shim records. */\nexport type KvOperation = \"get\" | \"getWithMetadata\" | \"put\" | \"delete\" | \"list\";\n\n/**\n * Why a KV value is absent from an event: workerd handed it over in a form that cannot be\n * persisted as text. A `\"stream\"` value must not even be read — consuming it would take the\n * bytes away from the app — and binary values are skipped deliberately, since KV blobs are\n * large and would both bloat the recording and be mangled by UTF-8 capture.\n */\nexport type KvOmittedReason = \"binary\" | \"stream\";\n\n/**\n * An operation on a Cloudflare KV namespace binding, made while handling an inbound request\n * (becomes a CLIENT span).\n *\n * KV is not HTTP: there is no method, URL or status code, so this event shares nothing with\n * {@link BaseRequestEvent} beyond correlation and timing. Values are JSON so that a single\n * `JSON.parse` reconstructs exactly what the app saw, whichever `type` the read asked for.\n */\nexport interface KvOperationEvent extends CorrelatedEvent {\n kind: \"kv\";\n /**\n * The `env` key the KV namespace was found under. Always present, unlike on a binding\n * event: a namespace has no factory equivalent to `DurableObjectNamespace.get()`, so one\n * that was never seen on `env` is not recorded at all.\n */\n bindingName: string;\n operation: KvOperation;\n /**\n * The key operated on, when it is a single string. Absent for `list` (which spans keys) and\n * for a bulk `get` (which takes an array) — both keep their arguments in {@link args}.\n */\n key?: string;\n /**\n * JSON of the call's arguments: the read `type`, the `put` options, the `list` selector.\n * A `put` value appears as `null` here — it is captured in {@link value} instead, so that\n * redaction applies to it.\n */\n args?: CapturedBody;\n /** JSON of the value a `put` wrote, with secret-looking fields redacted. */\n value?: CapturedBody;\n /**\n * JSON of what the operation returned: the value for `get`, `{value, metadata}` for\n * `getWithMetadata`, the key page for `list`. Absent for `put`/`delete`, which return\n * nothing, and for a value that could not be captured (see {@link omitted}).\n */\n result?: CapturedBody;\n /**\n * Why the operation's value is missing. Refers to the written value for `put` and to the\n * read value otherwise — a `put` has no result, so this is never ambiguous.\n */\n omitted?: KvOmittedReason;\n startTimeMs: number;\n endTimeMs: number;\n /** Set when the operation threw. */\n error?: string;\n}\n\n/**\n * One postgres.js query made while handling an inbound request (becomes a CLIENT span).\n *\n * Deliberately carries the same fields the Node recorder puts on a postgres.js span rather than\n * anything Worker-specific: `PostgresJsMockStore` builds its match key from those attributes at\n * load time, so a query recorded here replays through the existing Node mock path unchanged.\n * See `postgres/capture.ts` for the serialization, which both surfaces share.\n */\nexport interface PostgresQueryEvent extends CorrelatedEvent {\n kind: \"postgres\";\n /** SQL rebuilt from the tagged template's chunks, with `$1`, `$2`… placeholders. */\n queryText: string;\n /** JSON of the interpolated values, from `serializePostgresJsArgs`. */\n params: string;\n /** `\"\"`, `\"raw\"` or `\"values\"` — the row shape the query asked for. */\n rowMode: string;\n /** JSON of the resolved `Result`, capped at {@link MAX_POSTGRES_JS_RESULT_SIZE}. */\n result?: CapturedBody;\n /**\n * JSON of the rejection, from `serializePostgresJsError`. Set instead of {@link result}, and\n * distinct from the transport-level `error` string on the HTTP-shaped events: this one is a\n * recorded outcome the replay reproduces, not a note that capture failed.\n */\n errorJson?: string;\n startTimeMs: number;\n endTimeMs: number;\n}\n\nexport type CaptureEvent =\n | InboundRequestEvent\n | OutboundRequestEvent\n | BindingRequestEvent\n | KvOperationEvent\n | PostgresQueryEvent;\n\nexport interface CaptureEventsPayload {\n events: CaptureEvent[];\n}\n\n/**\n * Answer to `GET {SIDECAR_REPLAY_SESSION_PATH}?sessionId=<id>`: whether the sidecar holds\n * mocks for a session, and the virtual clock anchor to freeze `Date` at while serving it.\n */\nexport interface ReplaySessionInfoResponse {\n found: boolean;\n /**\n * Epoch ms of the session's last recorded activity. The worker freezes its clock here so\n * that credentials minted during the recording are already issued but not yet expired.\n */\n clockAnchorMs?: number;\n}\n\n/**\n * One request's line coverage, sent to {@link SIDECAR_REPLAY_COVERAGE_PATH}\n * after the response has been produced.\n *\n * `files` carries the build-time id→line map and is sent only on the first\n * report of an isolate: it is the same for every request the isolate serves and\n * is far larger than the hits themselves.\n */\nexport interface CoverageReportRequest {\n frontendSessionId: string;\n /** Global line ids marked during this request, ascending. */\n hitIds: number[];\n /** Present on an isolate's first report only. */\n files?: Array<{\n path: string;\n firstId: number;\n /** Inclusive 1-based line ranges, flattened as `[s1, e1, s2, e2, ...]`. */\n lineRanges: number[];\n }>;\n}\n\n/** Lookup for a single outbound `fetch`, sent to {@link SIDECAR_REPLAY_OUTBOUND_FETCH_PATH}. */\nexport interface OutboundFetchLookupRequest {\n frontendSessionId: string;\n replayId: string;\n method: string;\n /** Full URL including query string. */\n url: string;\n /**\n * Request body as captured by `readBodyWithCap`. The sidecar hashes this itself, so the\n * hash it compares against a recording is derived from byte-identical input on both the\n * record and replay sides.\n */\n requestBody?: CapturedBody;\n /**\n * Elapsed virtual time (ms from replay start) of the inbound request that\n * dispatched this outbound call, forwarded from {@link VIRTUAL_TIME_HEADER}.\n * The sidecar clusters candidates by recorded timestamp against this value.\n *\n * Additive and non-version-bumping: an older shim omits it, and matching\n * then hashes across every candidate at the matched key.\n */\n virtualTimeMs?: number;\n}\n\n/**\n * A recorded response to serve, or a signal the caller must fail the request. Replay is\n * hermetic, so the shim never turns any of these into a real call — nor an outcome it does\n * not recognise. The only live path is the request-side `meticulous-passthrough` header,\n * handled before the lookup, not any response here.\n */\nexport type OutboundFetchLookupResponse =\n | {\n outcome: \"mock\";\n statusCode: number;\n body: string;\n /**\n * Set when `body` is base64 of a recorded response that was not UTF-8, so the shim must\n * serve the decoded bytes rather than the text. An older shim ignores it.\n */\n bodyEncoding?: BodyEncoding;\n headers: Record<string, string>;\n }\n /**\n * The store holds nothing for this call — a genuine miss, or a call the sidecar could not\n * look up at all (an unparseable URL). Replay is hermetic, so the shim fails the request\n * rather than letting it reach the real service — matching the Node backend recorder's\n * http/undici mocks. There is deliberately no \"let the call through\" outcome: the only\n * live path is the request-side `meticulous-passthrough` header.\n */\n | { outcome: \"no-mock\" };\n\n/**\n * Env var holding the origin of the agent-swarm-testing interceptor service, as a wrangler\n * var on the deployment. Validated exactly like the replay sidecar URL — a loopback /\n * docker-gateway / private-network `http:` origin only — see `sidecar-url.ts`.\n */\nexport const AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY =\n \"METICULOUS_AGENT_SWARM_TESTING_SIDECAR_URL\";\n\n/**\n * Env var identifying the agent-swarm run, so an interceptor shared by several runs can tell\n * whose traffic a call belongs to.\n */\nexport const AGENT_SWARM_TESTING_RUN_ID_ENV_KEY =\n \"METICULOUS_AGENT_SWARM_TESTING_RUN_ID\";\n\n/** Agent-swarm-testing route. */\nexport const SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH =\n \"/v1/agent-swarm-testing/outbound-fetch\";\n\n/**\n * One outbound `fetch` offered to the interceptor, sent to\n * {@link SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH}. Unlike replay's lookup there is no\n * consume-once mock state, so a single run id suffices.\n */\nexport interface OutboundFetchAgentSwarmTestingRequest {\n runId: string;\n /** Value of {@link FRONTEND_SESSION_ID_HEADER} on the inbound request, if any. */\n frontendSessionId?: string;\n method: string;\n /** Full URL including query string. */\n url: string;\n /** Request body as captured by `readBodyWithCap`, mirroring the replay lookup. */\n requestBody?: CapturedBody;\n}\n\n/**\n * A response for the interceptor to serve, or a signal to let the call through. Unlike\n * replay's lookup response there is no failure outcome: agent-swarm testing is best-effort,\n * so anything the shim cannot act on becomes a passthrough, never a failed call.\n */\nexport type OutboundFetchAgentSwarmTestingResponse =\n | {\n outcome: \"mock\";\n statusCode: number;\n body: string;\n headers: Record<string, string>;\n }\n | { outcome: \"passthrough\" };\n"],"mappings":";AAQA,MAAa,2BAA2B;;AAGxC,MAAa,kCACX;;;;;AAMF,MAAa,6BAA6B;;AAG1C,MAAa,mBAAmB;;;;;;;AAQhC,MAAa,sBAAsB;;;;;AAMnC,MAAa,sBACX,UACuB;AACvB,KAAI,SAAS,QAAQ,UAAU,GAC7B;CAEF,MAAM,SAAS,OAAO,MAAM;AAC5B,KAAI,CAAC,OAAO,SAAS,OAAO,IAAI,SAAS,EACvC;AAEF,QAAO;;;;;;;;;;;AAYT,MAAa,gCAAgC;AAE7C,MAAa,sBAAsB;;;;;;AAOnC,MAAa,8BAA8B;AAC3C,MAAa,qCAAqC;;;;;;;;AASlD,MAAa,+BAA+B;;;;;;;;;AAU5C,MAAa,4BACX;;;;;;;;AASF,MAAa,8BAA8B;;;;;;;;;;;AAY3C,MAAa,mBAAmB,CAC9B,gBACA,2BACD;;;;;;AA0SD,MAAa,0CACX;;;;;AAMF,MAAa,qCACX;;AAGF,MAAa,kDACX"}
1
+ {"version":3,"file":"protocol.js","names":[],"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Wire protocol between the in-worker shim and the Meticulous backend\n * recorder sidecar. The sidecar imports these types (type-only), so this file\n * is the single source of truth for the shape of capture events.\n */\n\nimport type { BodyEncoding } from \"./body-encoding\";\n\nexport const SIDECAR_PROTOCOL_VERSION = \"2\";\n\n/** Header carrying {@link SIDECAR_PROTOCOL_VERSION} on every shim → sidecar request. */\nexport const SIDECAR_PROTOCOL_VERSION_HEADER =\n \"x-meticulous-sidecar-protocol-version\";\n\n/**\n * Header the Meticulous frontend recorder stamps on requests made by the app\n * under recording; its value ties backend spans to the frontend session.\n */\nexport const FRONTEND_SESSION_ID_HEADER = \"x-meticulous-session-id\";\n\n/** Header identifying one replay attempt of a recorded frontend session. */\nexport const REPLAY_ID_HEADER = \"x-meticulous-replay-id\";\n\n/**\n * Elapsed virtual time (ms from replay start) of the triggering frontend\n * request. Injected by the replay runner on app-url requests during backend\n * testing; the sidecar uses it to pick the closest recorded burst when several\n * candidates share a loosened match key.\n */\nexport const VIRTUAL_TIME_HEADER = \"x-meticulous-virtual-time\";\n\n/**\n * Parses {@link VIRTUAL_TIME_HEADER}. `0` is a valid start-of-session time;\n * anything non-finite or negative is ignored so a garbage header cannot skew matching.\n */\nexport const parseVirtualTimeMs = (\n value: string | null | undefined,\n): number | undefined => {\n if (value == null || value === \"\") {\n return undefined;\n }\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed < 0) {\n return undefined;\n }\n return parsed;\n};\n\n/**\n * A request carrying this header set to `\"true\"` is never served from the recording and\n * never fails on a miss — it goes to the real network. Use it for a call that must be live\n * during replay (a health probe, telemetry, a resource the recording cannot cover).\n *\n * Same convention and value as the frontend network stubbing and the Node backend recorder\n * (`METICULOUS_PASSTHROUGH_HEADER` in `@alwaysmeticulous/backend-recorder-js`); the value\n * must stay in step with both.\n */\nexport const METICULOUS_PASSTHROUGH_HEADER = \"meticulous-passthrough\";\n\nexport const SIDECAR_EVENTS_PATH = \"/v1/events\";\n\n/**\n * Replay routes. A sidecar started in record mode answers 404 on both. Replay is hermetic,\n * so an unanswered outbound-fetch lookup fails the app's call rather than letting it reach\n * the real service — the same as any other failure to stub.\n */\nexport const SIDECAR_REPLAY_SESSION_PATH = \"/v1/replay/session\";\nexport const SIDECAR_REPLAY_OUTBOUND_FETCH_PATH = \"/v1/replay/outbound-fetch\";\n\n/**\n * Line-coverage reporting. Additive: an older sidecar answers 404, which the\n * shim treats as \"coverage not collected here\" and stops reporting for the life\n * of the isolate. Deliberately NOT guarded by a\n * {@link SIDECAR_PROTOCOL_VERSION} bump — that check is exact equality, so a\n * bump would stop every already-built customer image from mocking at all.\n */\nexport const SIDECAR_REPLAY_COVERAGE_PATH = \"/v1/replay/coverage\";\n\n/**\n * Header carrying the replay sidecar's origin, injected by the Meticulous replay runner\n * on requests to the app under test. Workerd cannot see container environment variables,\n * so an inbound header is the only way per-replay config can reach the shim.\n *\n * The shim validates the value before using it and only honours a loopback /\n * `host.docker.internal` / private-network `http:` origin — see `sidecar-url.ts`.\n */\nexport const REPLAY_SIDECAR_URL_HEADER =\n \"x-meticulous-backend-replay-sidecar-url\";\n\n/**\n * Header the in-worker shim stamps on every app response (record and replay)\n * carrying the shim's npm package version. The replay runner reads this from\n * Puppeteer — workerd cannot see container env, and the execute pod cannot\n * reach the in-pod sidecar, so a response header is the only channel that\n * reports which bundled shim produced a replay's After screenshots.\n */\nexport const WORKERD_SHIM_VERSION_HEADER = \"x-meticulous-workerd-shim-version\";\n\n/**\n * The only headers persisted on capture events. Everything else — notably\n * authorization, cookie/set-cookie, and API-key headers — is dropped at\n * capture time, before it ever leaves the worker.\n *\n * The Node backend recorder persists the same two, plus the inbound request's\n * `Cookie` header on SERVER spans (`recorded-request-headers.ts`). This list\n * has not followed it there, so a workerd recording of an authenticated server\n * render still carries no session cookie.\n */\nexport const CAPTURED_HEADERS = [\n \"content-type\",\n FRONTEND_SESSION_ID_HEADER,\n] as const;\n\nexport interface CapturedBody {\n /**\n * The body truncated to the capture cap: its UTF-8 text, or base64 of its bytes when\n * `encoding` is set.\n */\n body: string;\n /** True when the body exceeded the capture cap or the read was cut short. */\n truncated: boolean;\n /**\n * Set only on a response body whose bytes are not UTF-8, which is stored base64 so that replay\n * can serve it back exactly. Additive and non-version-bumping: an older shim omits it, and its\n * bodies are text as before.\n */\n encoding?: BodyEncoding;\n}\n\n/**\n * The fields every capture event carries to say which request, session and trace it belongs to.\n */\ninterface CorrelatedEvent {\n /** Correlates outbound events to the inbound request being handled. */\n requestId: string;\n /** Value of {@link FRONTEND_SESSION_ID_HEADER} on the inbound request, if any. */\n frontendSessionId?: string;\n /**\n * Trace the request's spans belong to (32 hex chars), minted by the shim.\n *\n * The shim mints it, rather than the sidecar deriving it from {@link requestId}, so span\n * assembly is stateless: a sidecar that had to remember `requestId → traceId` would give a\n * request's SERVER span and its CLIENT children different traces whenever it lost that memory\n * between two batches — which, for a Worker-hosted sidecar, an eviction can do at any moment.\n *\n * Optional so a sidecar keeps working against a shim that predates this field, in which case\n * it falls back to its own per-`requestId` map.\n */\n traceId?: string;\n /**\n * Span id of the request's SERVER span (16 hex chars), also minted by the shim: it is the\n * parent of every CLIENT span the request produced, and the inbound event's own span id.\n */\n serverSpanId?: string;\n}\n\ninterface BaseRequestEvent extends CorrelatedEvent {\n method: string;\n /** Full URL including query string. */\n url: string;\n /** Lower-cased header name → values. */\n requestHeaders: Record<string, string[]>;\n responseHeaders?: Record<string, string[]>;\n statusCode?: number;\n startTimeMs: number;\n endTimeMs: number;\n /** Set when the request failed before a response was produced. */\n error?: string;\n}\n\n/**\n * The worker's own inbound request (becomes a SERVER span). Bodies are not\n * captured, matching the Node backend recorder's SERVER-span contract.\n */\nexport interface InboundRequestEvent extends BaseRequestEvent {\n kind: \"inbound\";\n /**\n * Where {@link BaseRequestEvent.frontendSessionId} came from. Absent means the browser\n * minted it and sent it on the request, which is the normal case. `\"backend\"` means the\n * shim minted it for a document navigation the browser could not tag, and the page may or\n * may not have gone on to adopt it — a distinction ingestion needs, because an unadopted\n * backend-minted id names a session that will never exist and must be treated as unstamped.\n * The sidecar turns it into `meticulous.session_id_origin`.\n *\n * Optional, and therefore compatible with an older sidecar (which ignores it) — which is\n * why adding it does not bump {@link SIDECAR_PROTOCOL_VERSION}, a strict-equality check\n * whose bump would instead make this shim unusable against one.\n */\n sessionIdOrigin?: \"backend\";\n}\n\n/**\n * An outgoing `fetch` call made while handling an inbound request (becomes a\n * CLIENT span).\n */\nexport interface OutboundRequestEvent extends BaseRequestEvent {\n kind: \"outbound\";\n requestBody?: CapturedBody;\n responseBody?: CapturedBody;\n}\n\n/**\n * A `fetch` through a Cloudflare binding — a service binding, an assets binding, or a\n * Durable Object stub — made while handling an inbound request (becomes a CLIENT span).\n *\n * Request/Response-shaped like {@link OutboundRequestEvent}, but a distinct kind because the\n * call never leaves the isolate and its URL is whatever the caller invented. Keeping the two\n * apart means a binding call and real egress can never be confused for one another when\n * these spans are eventually mocked.\n */\nexport interface BindingRequestEvent extends BaseRequestEvent {\n kind: \"binding\";\n /**\n * The `env` key the binding was found under. Absent when the instance was never seen on\n * `env` — most commonly a Durable Object stub, which `namespace.get()` produces rather\n * than being a binding in its own right.\n */\n bindingName?: string;\n requestBody?: CapturedBody;\n responseBody?: CapturedBody;\n}\n\n/** KV namespace methods the shim records. */\nexport type KvOperation = \"get\" | \"getWithMetadata\" | \"put\" | \"delete\" | \"list\";\n\n/**\n * Why a KV value is absent from an event: workerd handed it over in a form that cannot be\n * persisted as text. A `\"stream\"` value must not even be read — consuming it would take the\n * bytes away from the app — and binary values are skipped deliberately, since KV blobs are\n * large and would both bloat the recording and be mangled by UTF-8 capture.\n */\nexport type KvOmittedReason = \"binary\" | \"stream\";\n\n/**\n * An operation on a Cloudflare KV namespace binding, made while handling an inbound request\n * (becomes a CLIENT span).\n *\n * KV is not HTTP: there is no method, URL or status code, so this event shares nothing with\n * {@link BaseRequestEvent} beyond correlation and timing. Values are JSON so that a single\n * `JSON.parse` reconstructs exactly what the app saw, whichever `type` the read asked for.\n */\nexport interface KvOperationEvent extends CorrelatedEvent {\n kind: \"kv\";\n /**\n * The `env` key the KV namespace was found under. Always present, unlike on a binding\n * event: a namespace has no factory equivalent to `DurableObjectNamespace.get()`, so one\n * that was never seen on `env` is not recorded at all.\n */\n bindingName: string;\n operation: KvOperation;\n /**\n * The key operated on, when it is a single string. Absent for `list` (which spans keys) and\n * for a bulk `get` (which takes an array) — both keep their arguments in {@link args}.\n */\n key?: string;\n /**\n * JSON of the call's arguments: the read `type`, the `put` options, the `list` selector.\n * A `put` value appears as `null` here — it is captured in {@link value} instead, so that\n * redaction applies to it.\n */\n args?: CapturedBody;\n /** JSON of the value a `put` wrote, with secret-looking fields redacted. */\n value?: CapturedBody;\n /**\n * JSON of what the operation returned: the value for `get`, `{value, metadata}` for\n * `getWithMetadata`, the key page for `list`. Absent for `put`/`delete`, which return\n * nothing, and for a value that could not be captured (see {@link omitted}).\n */\n result?: CapturedBody;\n /**\n * Why the operation's value is missing. Refers to the written value for `put` and to the\n * read value otherwise — a `put` has no result, so this is never ambiguous.\n */\n omitted?: KvOmittedReason;\n startTimeMs: number;\n endTimeMs: number;\n /** Set when the operation threw. */\n error?: string;\n}\n\n/**\n * One postgres.js query made while handling an inbound request (becomes a CLIENT span).\n *\n * Deliberately carries the same fields the Node recorder puts on a postgres.js span rather than\n * anything Worker-specific: `PostgresJsMockStore` builds its match key from those attributes at\n * load time, so a query recorded here replays through the existing Node mock path unchanged.\n * See `postgres/capture.ts` for the serialization, which both surfaces share.\n */\nexport interface PostgresQueryEvent extends CorrelatedEvent {\n kind: \"postgres\";\n /** SQL rebuilt from the tagged template's chunks, with `$1`, `$2`… placeholders. */\n queryText: string;\n /** JSON of the interpolated values, from `serializePostgresJsArgs`. */\n params: string;\n /** `\"\"`, `\"raw\"` or `\"values\"` — the row shape the query asked for. */\n rowMode: string;\n /** JSON of the resolved `Result`, capped at {@link MAX_POSTGRES_JS_RESULT_SIZE}. */\n result?: CapturedBody;\n /**\n * JSON of the rejection, from `serializePostgresJsError`. Set instead of {@link result}, and\n * distinct from the transport-level `error` string on the HTTP-shaped events: this one is a\n * recorded outcome the replay reproduces, not a note that capture failed.\n */\n errorJson?: string;\n startTimeMs: number;\n endTimeMs: number;\n}\n\nexport type CaptureEvent =\n | InboundRequestEvent\n | OutboundRequestEvent\n | BindingRequestEvent\n | KvOperationEvent\n | PostgresQueryEvent;\n\nexport interface CaptureEventsPayload {\n events: CaptureEvent[];\n}\n\n/**\n * Answer to `GET {SIDECAR_REPLAY_SESSION_PATH}?sessionId=<id>`: whether the sidecar holds\n * mocks for a session, and the virtual clock anchor to freeze `Date` at while serving it.\n */\nexport interface ReplaySessionInfoResponse {\n found: boolean;\n /**\n * Epoch ms of the session's last recorded activity. The worker freezes its clock here so\n * that credentials minted during the recording are already issued but not yet expired.\n */\n clockAnchorMs?: number;\n}\n\n/**\n * One request's line coverage, sent to {@link SIDECAR_REPLAY_COVERAGE_PATH}\n * after the response has been produced.\n *\n * `files` carries the build-time id→line map and is sent only on the first\n * report of an isolate: it is the same for every request the isolate serves and\n * is far larger than the hits themselves.\n */\nexport interface CoverageReportRequest {\n frontendSessionId: string;\n /** Global line ids marked during this request, ascending. */\n hitIds: number[];\n /** Present on an isolate's first report only. */\n files?: Array<{\n path: string;\n firstId: number;\n /** Inclusive 1-based line ranges, flattened as `[s1, e1, s2, e2, ...]`. */\n lineRanges: number[];\n }>;\n}\n\n/** Lookup for a single outbound `fetch`, sent to {@link SIDECAR_REPLAY_OUTBOUND_FETCH_PATH}. */\nexport interface OutboundFetchLookupRequest {\n frontendSessionId: string;\n replayId: string;\n method: string;\n /** Full URL including query string. */\n url: string;\n /**\n * Request body as captured by `readBodyWithCap`. The sidecar hashes this itself, so the\n * hash it compares against a recording is derived from byte-identical input on both the\n * record and replay sides.\n */\n requestBody?: CapturedBody;\n /**\n * Elapsed virtual time (ms from replay start) of the inbound request that\n * dispatched this outbound call, forwarded from {@link VIRTUAL_TIME_HEADER}.\n * The sidecar clusters candidates by recorded timestamp against this value.\n *\n * Additive and non-version-bumping: an older shim omits it, and matching\n * then hashes across every candidate at the matched key.\n */\n virtualTimeMs?: number;\n}\n\n/**\n * A recorded response to serve, or a signal the caller must fail the request. Replay is\n * hermetic, so the shim never turns any of these into a real call — nor an outcome it does\n * not recognise. The only live path is the request-side `meticulous-passthrough` header,\n * handled before the lookup, not any response here.\n */\nexport type OutboundFetchLookupResponse =\n | {\n outcome: \"mock\";\n statusCode: number;\n body: string;\n /**\n * Set when `body` is base64 of a recorded response that was not UTF-8, so the shim must\n * serve the decoded bytes rather than the text. An older shim ignores it.\n */\n bodyEncoding?: BodyEncoding;\n headers: Record<string, string>;\n }\n /**\n * The store holds nothing for this call — a genuine miss, or a call the sidecar could not\n * look up at all (an unparseable URL). Replay is hermetic, so the shim fails the request\n * rather than letting it reach the real service — matching the Node backend recorder's\n * http/undici mocks. There is deliberately no \"let the call through\" outcome: the only\n * live path is the request-side `meticulous-passthrough` header.\n */\n | { outcome: \"no-mock\" };\n\n/**\n * Env var holding the origin of the agent-swarm-testing interceptor service, as a wrangler\n * var on the deployment. Validated exactly like the replay sidecar URL — a loopback /\n * docker-gateway / private-network `http:` origin only — see `sidecar-url.ts`.\n */\nexport const AGENT_SWARM_TESTING_SIDECAR_URL_ENV_KEY =\n \"METICULOUS_AGENT_SWARM_TESTING_SIDECAR_URL\";\n\n/**\n * Env var identifying the agent-swarm run, so an interceptor shared by several runs can tell\n * whose traffic a call belongs to.\n */\nexport const AGENT_SWARM_TESTING_RUN_ID_ENV_KEY =\n \"METICULOUS_AGENT_SWARM_TESTING_RUN_ID\";\n\n/** Agent-swarm-testing route. */\nexport const SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH =\n \"/v1/agent-swarm-testing/outbound-fetch\";\n\n/**\n * Agent-swarm-testing WebSocket route that hosts one be-mock: an agent-authored\n * `be-mock.ts` (backend mocks), compiled by the agentic session-generation\n * worker into a self-contained sandbox bundle. The sidecar runs it in its own\n * permission-restricted child process over the frozen RPC toolkit, and routes\n * every intercepted outbound fetch through the module's `interceptRequest`\n * export — see `packages/backend-recorder-js/src/sidecar/be-mock` and\n * `packages/agentic-session-generation-worker/src/app-access/be-mocking`.\n *\n * One connection is one installed module: the worker opens it, sends\n * {@link BeMockInstallMessage}, answers every {@link BeMockLoadSessionMessage}\n * while the module loads, and receives {@link BeMockReadyMessage} (or\n * {@link BeMockFailedMessage}) and then the module's log lines. Closing the\n * connection — from either side — uninstalls the module.\n */\nexport const SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_PATH =\n \"/v1/agent-swarm-testing/be-mock\";\n\n/**\n * Agent-swarm-testing route reporting what one installed be-mock decided for\n * each outbound call routed to it: `GET` with `runId` and `frontendSessionId`\n * query params, answered with {@link BeMockCallsReport} — or 404 when no module\n * is installed under that id. Calls routed to no module are never reported: no\n * case could have mocked them.\n */\nexport const SIDECAR_AGENT_SWARM_TESTING_BE_MOCK_CALLS_PATH =\n \"/v1/agent-swarm-testing/be-mock/calls\";\n\n/**\n * One outbound call an installed be-mock was offered, and what came of it. A\n * passed-through call's real response is not observed: the sidecar only takes\n * the module's decision.\n */\nexport type BeMockCall = {\n method: string;\n url: string;\n requestBody?: CapturedBody;\n} & (\n | {\n /** interceptRequest's response was served. */\n outcome: \"mock\";\n response: { statusCode: number; body: string };\n }\n | {\n /**\n * The call went to the real service: interceptRequest returned\n * undefined, or its answer could not be used (`error` says why).\n */\n outcome: \"passthrough\";\n error?: string;\n }\n);\n\nexport interface BeMockCallsReport {\n /** In completion order (the module finished deciding). */\n calls: BeMockCall[];\n /** Calls not kept once the report reached its bound. */\n droppedCount: number;\n}\n\n/**\n * How often the sidecar pings each be-mock connection (a WebSocket ping\n * frame). The sidecar terminates a connection that misses a pong; the worker\n * treats several missed pings as a lost module.\n */\nexport const BE_MOCK_PING_INTERVAL_MS = 15_000;\n\n/** Worker → sidecar: the module to install. Always the connection's first message. */\nexport interface BeMockInstallMessage {\n type: \"install\";\n /** Whose run this module belongs to; see {@link AGENT_SWARM_TESTING_RUN_ID_ENV_KEY}. */\n runId: string;\n /**\n * The routing key: the sidecar serves this module only to outbound calls whose\n * inbound request carried this {@link FRONTEND_SESSION_ID_HEADER}, so parallel\n * cases sharing one backend each get their own module.\n */\n frontendSessionId: string;\n /** The compiled, self-contained sandbox bundle to install. */\n code: string;\n}\n\n/**\n * Worker → sidecar: one recorded session's backend spans, answering a\n * {@link BeMockLoadSessionMessage}. `error` instead when the worker could not\n * read the session.\n */\nexport type BeMockSessionMessage =\n | { type: \"session\"; sessionId: string; backendSpans: unknown[] }\n | { type: \"session\"; sessionId: string; error: string };\n\nexport type BeMockWorkerMessage = BeMockInstallMessage | BeMockSessionMessage;\n\n/**\n * Sidecar → worker: the loading module called `loadSessionBackendRequests`\n * for this session. Sent only before {@link BeMockReadyMessage}: a module\n * loads its recordings at the top level.\n */\nexport interface BeMockLoadSessionMessage {\n type: \"loadSession\";\n sessionId: string;\n}\n\n/** Sidecar → worker: the module loaded and now answers outbound calls. */\nexport interface BeMockReadyMessage {\n type: \"ready\";\n}\n\n/** Sidecar → worker: the install failed; the sidecar closes the connection after it. */\nexport interface BeMockFailedMessage {\n type: \"failed\";\n error: string;\n}\n\n/** Sidecar → worker: one line from the module (stdout/stderr or its `log(...)`). */\nexport interface BeMockLogMessage {\n type: \"log\";\n line: string;\n}\n\nexport type BeMockSidecarMessage =\n | BeMockLoadSessionMessage\n | BeMockReadyMessage\n | BeMockFailedMessage\n | BeMockLogMessage;\n\n/**\n * One outbound HTTP call a recorded session's backend made, as\n * `loadSessionBackendRequests` returns it to be-mock.ts. The request mirrors\n * {@link OutboundFetchAgentSwarmTestingRequest} so a live request compares\n * field for field; the response is servable as-is.\n */\nexport interface BeMockRecordedRequest {\n method: string;\n /**\n * Full URL including query string, as recorded — or only the path and query\n * when the recording stored no scheme or host (older recorders).\n */\n url: string;\n requestBody?: CapturedBody;\n /**\n * True when ingestion attached this call by its time alone, not by this\n * session's id: it may belong to another user's traffic on the same backend.\n */\n inferred: boolean;\n response: {\n statusCode: number;\n /** As recorded: text, or base64 when `bodyEncoding` is set. */\n body: string;\n bodyEncoding?: BodyEncoding;\n /** True when the recorder cut the body at the capture cap. */\n bodyTruncated: boolean;\n headers: Record<string, string>;\n };\n}\n\n/**\n * One outbound `fetch` offered to the interceptor, sent to\n * {@link SIDECAR_AGENT_SWARM_TESTING_OUTBOUND_FETCH_PATH}. Unlike replay's lookup there is no\n * consume-once mock state, so a single run id suffices.\n */\nexport interface OutboundFetchAgentSwarmTestingRequest {\n runId: string;\n /** Value of {@link FRONTEND_SESSION_ID_HEADER} on the inbound request, if any. */\n frontendSessionId?: string;\n method: string;\n /** Full URL including query string. */\n url: string;\n /** Request body as captured by `readBodyWithCap`, mirroring the replay lookup. */\n requestBody?: CapturedBody;\n}\n\n/**\n * A response for the interceptor to serve, or a signal to let the call through. Unlike\n * replay's lookup response there is no failure outcome: agent-swarm testing is best-effort,\n * so anything the shim cannot act on becomes a passthrough, never a failed call.\n */\nexport type OutboundFetchAgentSwarmTestingResponse =\n | {\n outcome: \"mock\";\n statusCode: number;\n body: string;\n headers: Record<string, string>;\n }\n | { outcome: \"passthrough\" };\n"],"mappings":";AAQA,MAAa,2BAA2B;;AAGxC,MAAa,kCACX;;;;;AAMF,MAAa,6BAA6B;;AAG1C,MAAa,mBAAmB;;;;;;;AAQhC,MAAa,sBAAsB;;;;;AAMnC,MAAa,sBACX,UACuB;AACvB,KAAI,SAAS,QAAQ,UAAU,GAC7B;CAEF,MAAM,SAAS,OAAO,MAAM;AAC5B,KAAI,CAAC,OAAO,SAAS,OAAO,IAAI,SAAS,EACvC;AAEF,QAAO;;;;;;;;;;;AAYT,MAAa,gCAAgC;AAE7C,MAAa,sBAAsB;;;;;;AAOnC,MAAa,8BAA8B;AAC3C,MAAa,qCAAqC;;;;;;;;AASlD,MAAa,+BAA+B;;;;;;;;;AAU5C,MAAa,4BACX;;;;;;;;AASF,MAAa,8BAA8B;;;;;;;;;;;AAY3C,MAAa,mBAAmB,CAC9B,gBACA,2BACD;;;;;;AA0SD,MAAa,0CACX;;;;;AAMF,MAAa,qCACX;;AAGF,MAAa,kDACX;;;;;;;;;;;;;;;;AAiBF,MAAa,2CACX;;;;;;;;AASF,MAAa,iDACX;;;;;;AAuCF,MAAa,2BAA2B"}
package/dist/version.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * Replaced by tsdown's `define`. The guard keeps a direct source import
9
9
  * (vitest, typecheck) working, where no bundler has substituted the value.
10
10
  */
11
- const WORKERD_SHIM_VERSION = "2.346.0";
11
+ const WORKERD_SHIM_VERSION = "2.350.0";
12
12
  //#endregion
13
13
  export { WORKERD_SHIM_VERSION };
14
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alwaysmeticulous/backend-recorder-workerd",
3
- "version": "2.346.0",
3
+ "version": "2.350.0",
4
4
  "type": "module",
5
5
  "description": "Meticulous backend recorder shim for Cloudflare Workers (workerd). Reports inbound and outbound HTTP capture events to a local Meticulous recorder sidecar during development recording.",
6
6
  "license": "ISC",