@webpieces/core-util 0.4.710 → 0.4.711

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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/src/http/ApiCallContext.d.ts +17 -29
  3. package/src/http/ApiCallContext.js +0 -36
  4. package/src/http/ApiCallContext.js.map +1 -1
  5. package/src/http/ApiCallInfo.d.ts +4 -4
  6. package/src/http/ApiCallInfo.js +1 -1
  7. package/src/http/ApiCallInfo.js.map +1 -1
  8. package/src/http/ApiCallLogName.d.ts +4 -4
  9. package/src/http/ApiCallLogName.js +4 -4
  10. package/src/http/ApiCallLogName.js.map +1 -1
  11. package/src/http/ApiMethodInfo.d.ts +3 -3
  12. package/src/http/ApiMethodInfo.js +2 -2
  13. package/src/http/ApiMethodInfo.js.map +1 -1
  14. package/src/http/LogApiCall.d.ts +23 -13
  15. package/src/http/LogApiCall.js +30 -18
  16. package/src/http/LogApiCall.js.map +1 -1
  17. package/src/http/LogFieldMask.d.ts +1 -1
  18. package/src/http/LogFieldMask.js +1 -1
  19. package/src/http/LogFieldMask.js.map +1 -1
  20. package/src/http/RouteMetadata.d.ts +1 -1
  21. package/src/http/RouteMetadata.js +1 -1
  22. package/src/http/RouteMetadata.js.map +1 -1
  23. package/src/http/RuntimeLocality.d.ts +3 -2
  24. package/src/http/RuntimeLocality.js +3 -2
  25. package/src/http/RuntimeLocality.js.map +1 -1
  26. package/src/http/WebpiecesCoreHeaders.d.ts +1 -1
  27. package/src/http/WebpiecesCoreHeaders.js +1 -1
  28. package/src/http/WebpiecesCoreHeaders.js.map +1 -1
  29. package/src/http/decorators.d.ts +1 -1
  30. package/src/http/decorators.js +1 -1
  31. package/src/http/decorators.js.map +1 -1
  32. package/src/index.d.ts +1 -2
  33. package/src/index.js +6 -6
  34. package/src/index.js.map +1 -1
  35. package/src/logging/LogChunker.d.ts +2 -2
  36. package/src/logging/LogChunker.js +2 -2
  37. package/src/logging/LogChunker.js.map +1 -1
package/src/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.assertApiKind = exports.getApiKind = exports.ENDPOINT_KINDS_BY_API_KIND = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthMeta = exports.RouteMetadata = exports.METADATA_KEYS = exports.validateNoConflictingDecorators = exports.assertEveryWebhookEndpointRetainsRawBody = exports.assertEveryExternalEndpointDeclaresCaller = exports.assertEveryEndpointHasAuthMode = exports.getAuthMode = exports.getAuthMeta = exports.isApiPath = exports.isRawBody = exports.isFormPost = exports.getMaskSpec = exports.getEndpointKinds = exports.getEndpointKind = exports.getEndpointOptions = exports.getEndpoints = exports.getApiPath = exports.MaskLog = exports.AuthLocalOnly = exports.AuthApiKey = exports.AuthWebhook = exports.AuthSharedSecret = exports.AuthOidc = exports.MISSING_AUTH_DECORATOR_FIX = exports.rolesRequired = exports.AuthJwt = exports.Public = exports.Endpoint = exports.ApiPath = exports.GCP_LOG_BUDGET_BYTES = exports.MAX_GCP_LOG_BYTES = exports.LogChunkInfo = exports.LogChunkerImpl = exports.LogChunker = exports.LogManager = exports.ConsoleLoggerFactory = exports.ConsoleLogger = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.ContextTuple = exports.ContextKey = exports.toError = void 0;
12
12
  exports.ContextMgr = exports.DestinationTrust = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_FAILURE_CLASSIFIER = exports.WebpiecesDefaultFailureClassifier = exports.KeyedFailureClassifier = exports.ErrorWireForm = exports.RuntimeLocality = exports.ServiceInfo = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NetworkRejectClassifier = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = exports.ENTITY_NOT_FOUND = exports.OfflineError = exports.HttpUserError = exports.HttpVendorError = exports.HttpTooManyRequestsError = exports.HttpInternalServerError = exports.HttpGatewayTimeoutError = exports.HttpServiceUnavailableError = exports.HttpBadGatewayError = exports.HttpTimeoutError = exports.HttpForbiddenError = exports.HttpUnauthorizedError = exports.HttpBadRequestError = exports.EndpointNotFoundError = exports.HttpNotFoundError = exports.HttpError = exports.ProtocolError = exports.SECRETS = exports.Secrets = exports.getEndpointCaller = exports.isExternalSystemKind = exports.ExternalCaller = exports.DEFAULT_CALLER_KIND = exports.EXTERNAL_SYSTEM_KINDS = exports.getQueueName = exports.assertPubSubConventions = void 0;
13
- exports.FilterChain = exports.Filter = exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiCallContextHolder = exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.LogApiCall = void 0;
13
+ exports.FilterChain = exports.Filter = exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = void 0;
14
14
  var errorUtils_1 = require("./lib/errorUtils");
15
15
  Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return errorUtils_1.toError; } });
16
16
  var ContextKey_1 = require("./ContextKey");
@@ -171,16 +171,18 @@ Object.defineProperty(exports, "DestinationTrust", { enumerable: true, get: func
171
171
  // RequestContextHeaders in the Node-only @webpieces/core-context.
172
172
  var ContextMgr_1 = require("./http/ContextMgr");
173
173
  Object.defineProperty(exports, "ContextMgr", { enumerable: true, get: function () { return ContextMgr_1.ContextMgr; } });
174
- // API-call logging helper (uses LogManager above). Singleton: use the LogApiCall constant, not `new`.
174
+ // API-call logging helper (uses LogManager above). NOT a singleton: construct one per environment
175
+ // with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`
176
+ // on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.
175
177
  var LogApiCall_1 = require("./http/LogApiCall");
176
- Object.defineProperty(exports, "LogApiCall", { enumerable: true, get: function () { return LogApiCall_1.LogApiCall; } });
177
178
  Object.defineProperty(exports, "LogApiCallImpl", { enumerable: true, get: function () { return LogApiCall_1.LogApiCallImpl; } });
178
179
  // Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets
179
180
  // (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.
180
181
  var LogFieldMask_1 = require("./http/LogFieldMask");
181
182
  Object.defineProperty(exports, "MaskSpec", { enumerable: true, get: function () { return LogFieldMask_1.MaskSpec; } });
182
183
  // The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node
183
- // RequestContext-backed impl is installed by @webpieces/core-context; the browser gets the no-op.
184
+ // RequestContext-backed impl lives in @webpieces/core-context, the browser one in
185
+ // @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.
184
186
  var ApiCallInfo_1 = require("./http/ApiCallInfo");
185
187
  Object.defineProperty(exports, "ApiCallInfo", { enumerable: true, get: function () { return ApiCallInfo_1.ApiCallInfo; } });
186
188
  // Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.
@@ -190,8 +192,6 @@ Object.defineProperty(exports, "ApiCallLogNameImpl", { enumerable: true, get: fu
190
192
  Object.defineProperty(exports, "LOG_API_CALL_LOGGER_NAME", { enumerable: true, get: function () { return ApiCallLogName_1.LOG_API_CALL_LOGGER_NAME; } });
191
193
  var ApiMethodInfo_1 = require("./http/ApiMethodInfo");
192
194
  Object.defineProperty(exports, "ApiMethodInfo", { enumerable: true, get: function () { return ApiMethodInfo_1.ApiMethodInfo; } });
193
- var ApiCallContext_1 = require("./http/ApiCallContext");
194
- Object.defineProperty(exports, "ApiCallContextHolder", { enumerable: true, get: function () { return ApiCallContext_1.ApiCallContextHolder; } });
195
195
  // Test-case recording contract (impl lives in http-server; hooks in http-client)
196
196
  var TestCaseRecorder_1 = require("./http/recorder/TestCaseRecorder");
197
197
  Object.defineProperty(exports, "RecorderKeys", { enumerable: true, get: function () { return TestCaseRecorder_1.RecorderKeys; } });
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type { AnyContextKey, AnyTrustedContextKey, AnyUntrustedContextKey, Trust } from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport { LogChunker, LogChunkerImpl, LogChunkInfo, MAX_GCP_LOG_BYTES, GCP_LOG_BUDGET_BYTES } from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the HttpError hierarchy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n Public,\n AuthJwt,\n rolesRequired,\n MISSING_AUTH_DECORATOR_FIX,\n AuthOidc,\n AuthSharedSecret,\n AuthWebhook,\n AuthApiKey,\n AuthLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointOptions,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport type { EndpointKind, EndpointOptions, ExternalEndpointOptions } from './http/decorators';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta } from './http/auth-mode';\nexport type { AuthMode, ApiKeyCredential, ApiKeyCredentials, JwtRoles, JwtRequirement } from './http/auth-mode';\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming. Split out of decorators.ts for file size only;\n// one-way dependency api-kind -> decorators, and the barrel keeps the surface identical.\nexport {\n Rpc,\n PubSub,\n Queue,\n ENDPOINT_KINDS_BY_API_KIND,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n} from './http/api-kind';\nexport type { ApiKind } from './http/api-kind';\n// WHO calls an `external` endpoint — the caller declaration @Endpoint(..., 'external', {calledBy})\n// requires, and the reader for it.\nexport { EXTERNAL_SYSTEM_KINDS, DEFAULT_CALLER_KIND, ExternalCaller, isExternalSystemKind, getEndpointCaller } from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// HTTP errors\nexport {\n ProtocolError,\n HttpError,\n HttpNotFoundError,\n EndpointNotFoundError,\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpTimeoutError,\n HttpBadGatewayError,\n HttpServiceUnavailableError,\n HttpGatewayTimeoutError,\n HttpInternalServerError,\n HttpTooManyRequestsError,\n HttpVendorError,\n HttpUserError,\n OfflineError,\n // Error subtype constants\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './http/errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @AuthLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\n// Pluggable, bidirectional error translation (app exception <-> wire form). Registered on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport { ErrorWireForm } from './http/ErrorTranslation';\nexport type { ErrorTranslation } from './http/ErrorTranslation';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\n\n// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being\n// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.\nexport { DestinationTrust } from './http/DestinationTrust';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). Singleton: use the LogApiCall constant, not `new`.\nexport { LogApiCall, LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl is installed by @webpieces/core-context; the browser gets the no-op.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiType, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport { ApiCallLogName, ApiCallLogNameImpl, LOG_API_CALL_LOGGER_NAME } from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport { ApiCallContextHolder } from './http/ApiCallContext';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport { RecordedEndpoint, RecordedError, RecordedTestCase } from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n\n// ---------------------------------------------------------------------------------------------\n// Filter-chain primitives, shared by BOTH chains: the inbound server chain\n// (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client\n// chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in\n// the package both depend on — see the class doc for why a second pair would be a shim.\nexport { Filter } from './filters/Filter';\nexport type { Service } from './filters/Filter';\nexport { FilterChain } from './filters/FilterChain';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type { AnyContextKey, AnyTrustedContextKey, AnyUntrustedContextKey, Trust } from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport { LogChunker, LogChunkerImpl, LogChunkInfo, MAX_GCP_LOG_BYTES, GCP_LOG_BUDGET_BYTES } from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the HttpError hierarchy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n Public,\n AuthJwt,\n rolesRequired,\n MISSING_AUTH_DECORATOR_FIX,\n AuthOidc,\n AuthSharedSecret,\n AuthWebhook,\n AuthApiKey,\n AuthLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointOptions,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport type { EndpointKind, EndpointOptions, ExternalEndpointOptions } from './http/decorators';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta } from './http/auth-mode';\nexport type { AuthMode, ApiKeyCredential, ApiKeyCredentials, JwtRoles, JwtRequirement } from './http/auth-mode';\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming. Split out of decorators.ts for file size only;\n// one-way dependency api-kind -> decorators, and the barrel keeps the surface identical.\nexport {\n Rpc,\n PubSub,\n Queue,\n ENDPOINT_KINDS_BY_API_KIND,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n} from './http/api-kind';\nexport type { ApiKind } from './http/api-kind';\n// WHO calls an `external` endpoint — the caller declaration @Endpoint(..., 'external', {calledBy})\n// requires, and the reader for it.\nexport { EXTERNAL_SYSTEM_KINDS, DEFAULT_CALLER_KIND, ExternalCaller, isExternalSystemKind, getEndpointCaller } from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// HTTP errors\nexport {\n ProtocolError,\n HttpError,\n HttpNotFoundError,\n EndpointNotFoundError,\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpTimeoutError,\n HttpBadGatewayError,\n HttpServiceUnavailableError,\n HttpGatewayTimeoutError,\n HttpInternalServerError,\n HttpTooManyRequestsError,\n HttpVendorError,\n HttpUserError,\n OfflineError,\n // Error subtype constants\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './http/errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @AuthLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\n// Pluggable, bidirectional error translation (app exception <-> wire form). Registered on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport { ErrorWireForm } from './http/ErrorTranslation';\nexport type { ErrorTranslation } from './http/ErrorTranslation';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\n\n// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being\n// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.\nexport { DestinationTrust } from './http/DestinationTrust';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiType, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport { ApiCallLogName, ApiCallLogNameImpl, LOG_API_CALL_LOGGER_NAME } from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport { RecordedEndpoint, RecordedError, RecordedTestCase } from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n\n// ---------------------------------------------------------------------------------------------\n// Filter-chain primitives, shared by BOTH chains: the inbound server chain\n// (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client\n// chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in\n// the package both depend on — see the class doc for why a second pair would be a shim.\nexport { Filter } from './filters/Filter';\nexport type { Service } from './filters/Filter';\nexport { FilterChain } from './filters/FilterChain';\n"]}
@@ -22,7 +22,7 @@
22
22
  * BROWSER-SAFE: lives in core-util, which ships in the browser bundle — so `TextEncoder`, never
23
23
  * `Buffer`.
24
24
  *
25
- * Singleton, mirroring `LogApiCall` / `RequestContext`: use the exported {@link LogChunker}, not `new`.
25
+ * Singleton, mirroring `RequestContext`: use the exported {@link LogChunker}, not `new`.
26
26
  */
27
27
  /**
28
28
  * GCP Cloud Logging's maximum size for a single LogEntry: 256 KiB. Note KiB, not KB — the docs say
@@ -139,7 +139,7 @@ export declare class LogChunkerImpl {
139
139
  private escapedCost;
140
140
  }
141
141
  /**
142
- * The process-wide {@link LogChunkerImpl} singleton — mirrors the `LogApiCall` export pattern.
142
+ * The process-wide {@link LogChunkerImpl} singleton — mirrors the `RequestContext` export pattern.
143
143
  * Callers use `LogChunker.chunk(...)`, never `new`.
144
144
  */
145
145
  export declare const LogChunker: LogChunkerImpl;
@@ -23,7 +23,7 @@
23
23
  * BROWSER-SAFE: lives in core-util, which ships in the browser bundle — so `TextEncoder`, never
24
24
  * `Buffer`.
25
25
  *
26
- * Singleton, mirroring `LogApiCall` / `RequestContext`: use the exported {@link LogChunker}, not `new`.
26
+ * Singleton, mirroring `RequestContext`: use the exported {@link LogChunker}, not `new`.
27
27
  */
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.LogChunker = exports.LogChunkerImpl = exports.ChunkBudgets = exports.LogChunkInfo = exports.GCP_LOG_BUDGET_BYTES = exports.MAX_GCP_LOG_BYTES = void 0;
@@ -239,7 +239,7 @@ class LogChunkerImpl {
239
239
  }
240
240
  exports.LogChunkerImpl = LogChunkerImpl;
241
241
  /**
242
- * The process-wide {@link LogChunkerImpl} singleton — mirrors the `LogApiCall` export pattern.
242
+ * The process-wide {@link LogChunkerImpl} singleton — mirrors the `RequestContext` export pattern.
243
243
  * Callers use `LogChunker.chunk(...)`, never `new`.
244
244
  */
245
245
  exports.LogChunker = new LogChunkerImpl();
@@ -1 +1 @@
1
- {"version":3,"file":"LogChunker.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/logging/LogChunker.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * LogChunker - splits an oversized log field into pieces small enough that each emitted record\n * survives GCP Cloud Logging's per-entry size limit.\n *\n * WHY THIS EXISTS: an oversized entry does NOT come back as an error — it is SILENTLY DROPPED.\n * Per the GKE docs: \"Any LogEntry exceeding the size limit is dropped for jsonPayload logs and\n * truncated for textPayload logs.\" Our GCP backends emit structured JSON, so we are squarely in the\n * \"dropped\" half: a 300KB response body or a giant stack trace makes the whole line vanish with no\n * diagnostic anywhere. Chunking is what turns that silent loss into N recoverable lines.\n *\n * WHY NOT TRUNCATE: the content that blows the limit (a stack trace, a response body) is precisely\n * the content you opened the logs to read. Splitting keeps all of it.\n *\n * WHY NOT SPLIT THE SERIALIZED LINE: a fragment of a JSON line is not valid JSON, and the logging\n * agent would file each piece as an unparsed `textPayload` — losing every structured field. So\n * callers chunk a FIELD and emit N COMPLETE records, each carrying a `logChunk` tag.\n *\n * WHY GCP's OWN SPLITTING DOES NOT HELP: Cloud Logging's `LogSplit` (split.uid/index/totalSplits) is\n * only applied to Google-generated audit logs, never to user-written entries. We mirror its field\n * shape ({@link LogChunkInfo}) but must do the work ourselves.\n *\n * BROWSER-SAFE: lives in core-util, which ships in the browser bundle — so `TextEncoder`, never\n * `Buffer`.\n *\n * Singleton, mirroring `LogApiCall` / `RequestContext`: use the exported {@link LogChunker}, not `new`.\n */\n\n/**\n * GCP Cloud Logging's maximum size for a single LogEntry: 256 KiB. Note KiB, not KB — the docs say\n * 256 KiB, so 262,144 bytes and NOT the 256,000 that several client libraries hardcode as their own\n * conservative guard.\n */\nexport const MAX_GCP_LOG_BYTES = 262_144;\n\n/**\n * The per-record budget we actually chunk to: 75% of {@link MAX_GCP_LOG_BYTES}.\n *\n * The 25% headroom is NOT superstition — three things eat into the limit that a caller cannot see:\n * 1. The limit is explicitly \"approximate and based on internal data sizes, not the actual REST API\n * request size\" (GCP quotas docs), so byte-exact packing against 262,144 is not a thing you can do.\n * 2. Labels, resource, and metadata share the entry's budget with the payload.\n * 3. The record envelope — context keys (requestId, tenantId, ...), the `api` tag, svcName, severity,\n * timestamps — is serialized alongside the field being chunked.\n */\nexport const GCP_LOG_BUDGET_BYTES = 196_608;\n\n/**\n * The tag stamped on every record of a split message, mirroring GCP's own `LogSplit` shape.\n * Data-only structure → a class, per CLAUDE.md.\n *\n * Reassembling in Cloud Logging: filter `jsonPayload.logChunk.uid=\"<uid>\"`, sort by\n * `jsonPayload.logChunk.index`, concatenate the `message` fields.\n *\n * WHY A DEDICATED uid, given every line already carries requestId: requestId correlates a whole\n * REQUEST, which emits many lines (LogApiCall alone emits a request line AND a response line per\n * call). It cannot tell you which lines are pieces of ONE message. This uid can.\n */\nexport class LogChunkInfo {\n constructor(\n /** Correlates the pieces of ONE split message. */\n readonly uid: string,\n /** 0-based position of this piece. */\n readonly index: number,\n /** How many pieces this message was split into. */\n readonly total: number,\n ) {}\n}\n\n/**\n * Bytes reserved for the `logChunk` tag a backend adds to each piece, e.g.\n * `,\"logChunk\":{\"uid\":\"chunk-mabc1234-x7f2q1\",\"index\":12,\"total\":34}` — ~70 bytes, rounded up.\n */\nconst CHUNK_TAG_BYTES = 128;\n\n/**\n * Floor for a per-record field budget. Only reachable if the ENVELOPE alone (context keys, the api\n * tag, svcName) already fills the budget — pathological, and slicing a message into 1-byte pieces\n * would be worse than emitting one slightly-oversized record.\n */\nconst MIN_FIELD_BYTES = 1024;\n\n/**\n * How many bytes each of two chunked fields may spend PER RECORD. Data-only structure → a class.\n */\nexport class ChunkBudgets {\n constructor(\n readonly firstBudget: number,\n readonly secondBudget: number,\n ) {}\n}\n\nexport class LogChunkerImpl {\n private readonly encoder = new TextEncoder();\n\n /**\n * Plain UTF-8 byte length — for measuring text that is ALREADY in its final serialized form\n * (e.g. winston's fully-rendered JSON line), where no further escaping will happen.\n */\n byteLength(text: string): number {\n return this.encoder.encode(text).length;\n }\n\n /**\n * The byte cost of `text` once it has been JSON-escaped as a string VALUE inside a record.\n *\n * This is the measurement that matters, and it is why chunking on raw UTF-8 length is a bug: a\n * log message holding a JSON body is escaped a SECOND time when the record is serialized, so\n * every `\"` becomes `\\\"`, every newline `\\n`, and a control character explodes to a 6-byte\n * `\\u00XX`. A body that is dense in quotes can inflate by ~2x on that second pass — enough to\n * push a \"196KB\" chunk past the 262KB ceiling and silently drop it.\n *\n * Exact for `JSON.stringify` semantics (V8 does not \\u-escape non-ASCII). For the bunyan GCP\n * path — which ships over gRPC/protobuf rather than as JSON text — this over-counts slightly,\n * which is the safe direction.\n */\n escapedByteLength(text: string): number {\n let bytes = 0;\n // for...of iterates CODE POINTS, so a surrogate pair is one step, not two.\n for (const char of text) {\n bytes += this.escapedCost(char.codePointAt(0)!);\n }\n return bytes;\n }\n\n /**\n * Split `text` so each piece costs at most `maxBytes` once JSON-escaped\n * (see {@link escapedByteLength}).\n *\n * GUARANTEES:\n * - `chunk(t, n).join('') === t` — nothing is lost, so the pieces reassemble exactly.\n * - No piece splits a code point: a 4-byte emoji or a CJK character is never cut in half (which\n * would corrupt the boundary character into replacement junk on reassembly).\n * - Always returns at least one piece (`['']` for empty input), so callers can treat the result\n * uniformly.\n *\n * Degenerate case: if a SINGLE code point costs more than `maxBytes`, that piece necessarily\n * exceeds the budget — unavoidable, and irrelevant at any sane budget (max cost is 6 bytes).\n */\n chunk(text: string, maxBytes: number): string[] {\n if (maxBytes <= 0) {\n throw new Error(`maxBytes must be positive, was ${maxBytes}`);\n }\n if (this.escapedByteLength(text) <= maxBytes) {\n return [text];\n }\n\n const chunks: string[] = [];\n let start = 0;\n // UTF-16 index (what slice() wants), advanced by each code point's unit length so every\n // boundary we cut on is a code-point boundary.\n let position = 0;\n let bytes = 0;\n for (const char of text) {\n const cost = this.escapedCost(char.codePointAt(0)!);\n if (bytes + cost > maxBytes && position > start) {\n chunks.push(text.slice(start, position));\n start = position;\n bytes = 0;\n }\n bytes += cost;\n position += char.length;\n }\n chunks.push(text.slice(start));\n return chunks;\n }\n\n /**\n * Divide a record's budget between the TWO fields a backend chunks — the message and the stack\n * trace. Shared by the winston and bunyan GCP backends, which differ only in what those fields\n * are called (`message`/`errStack` vs `msg`/`err.stack`), never in this arithmetic.\n *\n * The envelope's cost is derived by SUBTRACTION: `renderedBytes` minus the escaped cost of the\n * two fields IS the envelope, whatever it happens to hold. That stays correct as apps register\n * new context keys, where summing up known parts would silently drift.\n *\n * The split: whichever field is small enough to fit whole gets exactly what it needs and the\n * other takes the rest; if BOTH are oversized they share evenly. Either way record N holds\n * first[N] + second[N] and still lands within budget.\n *\n * @param renderedBytes - size of the fully-serialized record as it stands today\n * @param budgetBytes - the per-record ceiling (typically {@link GCP_LOG_BUDGET_BYTES})\n */\n chunkBudgets(renderedBytes: number, budgetBytes: number, first: string, second: string): ChunkBudgets {\n const firstBytes = this.escapedByteLength(first);\n const secondBytes = this.escapedByteLength(second);\n const envelopeBytes = renderedBytes - firstBytes - secondBytes;\n const available = Math.max(budgetBytes - envelopeBytes - CHUNK_TAG_BYTES, MIN_FIELD_BYTES);\n const half = Math.floor(available / 2);\n\n // Both too big to fit alongside anything → split the room evenly.\n if (firstBytes > half && secondBytes > half) {\n return new ChunkBudgets(half, half);\n }\n // A field that fits in one piece is given exactly its own size (never 0 — chunk() rejects\n // that), and the oversized field gets everything left over.\n if (secondBytes <= half) {\n return new ChunkBudgets(Math.max(available - secondBytes, MIN_FIELD_BYTES), Math.max(secondBytes, 1));\n }\n return new ChunkBudgets(Math.max(firstBytes, 1), Math.max(available - firstBytes, MIN_FIELD_BYTES));\n }\n\n /**\n * A fresh id correlating the pieces of one split message. Uses Math.random rather than\n * crypto.randomUUID so it works in every browser context (randomUUID needs a secure context) and\n * on older Node — matching how RequestContextHeaders generates its fallback request id. These\n * only need to be unique among the lines an operator is grepping, not cryptographically strong.\n */\n newUid(): string {\n return `chunk-${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 10)}`;\n }\n\n /** Bytes this code point occupies once JSON-escaped inside a string value. */\n private escapedCost(codePoint: number): number {\n // The characters JSON.stringify escapes with a 2-char backslash sequence: \" \\ \\b \\t \\n \\f \\r\n if (codePoint === 0x22 || codePoint === 0x5c) {\n return 2;\n }\n if (\n codePoint === 0x08 ||\n codePoint === 0x09 ||\n codePoint === 0x0a ||\n codePoint === 0x0c ||\n codePoint === 0x0d\n ) {\n return 2;\n }\n // Every other control character becomes a 6-byte \\u00XX escape.\n if (codePoint < 0x20) {\n return 6;\n }\n // Otherwise the character is emitted as-is, costing its UTF-8 length.\n if (codePoint < 0x80) {\n return 1;\n }\n if (codePoint < 0x800) {\n return 2;\n }\n if (codePoint < 0x10000) {\n return 3;\n }\n return 4;\n }\n}\n\n/**\n * The process-wide {@link LogChunkerImpl} singleton — mirrors the `LogApiCall` export pattern.\n * Callers use `LogChunker.chunk(...)`, never `new`.\n */\nexport const LogChunker = new LogChunkerImpl();\n"]}
1
+ {"version":3,"file":"LogChunker.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/logging/LogChunker.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * LogChunker - splits an oversized log field into pieces small enough that each emitted record\n * survives GCP Cloud Logging's per-entry size limit.\n *\n * WHY THIS EXISTS: an oversized entry does NOT come back as an error — it is SILENTLY DROPPED.\n * Per the GKE docs: \"Any LogEntry exceeding the size limit is dropped for jsonPayload logs and\n * truncated for textPayload logs.\" Our GCP backends emit structured JSON, so we are squarely in the\n * \"dropped\" half: a 300KB response body or a giant stack trace makes the whole line vanish with no\n * diagnostic anywhere. Chunking is what turns that silent loss into N recoverable lines.\n *\n * WHY NOT TRUNCATE: the content that blows the limit (a stack trace, a response body) is precisely\n * the content you opened the logs to read. Splitting keeps all of it.\n *\n * WHY NOT SPLIT THE SERIALIZED LINE: a fragment of a JSON line is not valid JSON, and the logging\n * agent would file each piece as an unparsed `textPayload` — losing every structured field. So\n * callers chunk a FIELD and emit N COMPLETE records, each carrying a `logChunk` tag.\n *\n * WHY GCP's OWN SPLITTING DOES NOT HELP: Cloud Logging's `LogSplit` (split.uid/index/totalSplits) is\n * only applied to Google-generated audit logs, never to user-written entries. We mirror its field\n * shape ({@link LogChunkInfo}) but must do the work ourselves.\n *\n * BROWSER-SAFE: lives in core-util, which ships in the browser bundle — so `TextEncoder`, never\n * `Buffer`.\n *\n * Singleton, mirroring `RequestContext`: use the exported {@link LogChunker}, not `new`.\n */\n\n/**\n * GCP Cloud Logging's maximum size for a single LogEntry: 256 KiB. Note KiB, not KB — the docs say\n * 256 KiB, so 262,144 bytes and NOT the 256,000 that several client libraries hardcode as their own\n * conservative guard.\n */\nexport const MAX_GCP_LOG_BYTES = 262_144;\n\n/**\n * The per-record budget we actually chunk to: 75% of {@link MAX_GCP_LOG_BYTES}.\n *\n * The 25% headroom is NOT superstition — three things eat into the limit that a caller cannot see:\n * 1. The limit is explicitly \"approximate and based on internal data sizes, not the actual REST API\n * request size\" (GCP quotas docs), so byte-exact packing against 262,144 is not a thing you can do.\n * 2. Labels, resource, and metadata share the entry's budget with the payload.\n * 3. The record envelope — context keys (requestId, tenantId, ...), the `api` tag, svcName, severity,\n * timestamps — is serialized alongside the field being chunked.\n */\nexport const GCP_LOG_BUDGET_BYTES = 196_608;\n\n/**\n * The tag stamped on every record of a split message, mirroring GCP's own `LogSplit` shape.\n * Data-only structure → a class, per CLAUDE.md.\n *\n * Reassembling in Cloud Logging: filter `jsonPayload.logChunk.uid=\"<uid>\"`, sort by\n * `jsonPayload.logChunk.index`, concatenate the `message` fields.\n *\n * WHY A DEDICATED uid, given every line already carries requestId: requestId correlates a whole\n * REQUEST, which emits many lines (LogApiCall alone emits a request line AND a response line per\n * call). It cannot tell you which lines are pieces of ONE message. This uid can.\n */\nexport class LogChunkInfo {\n constructor(\n /** Correlates the pieces of ONE split message. */\n readonly uid: string,\n /** 0-based position of this piece. */\n readonly index: number,\n /** How many pieces this message was split into. */\n readonly total: number,\n ) {}\n}\n\n/**\n * Bytes reserved for the `logChunk` tag a backend adds to each piece, e.g.\n * `,\"logChunk\":{\"uid\":\"chunk-mabc1234-x7f2q1\",\"index\":12,\"total\":34}` — ~70 bytes, rounded up.\n */\nconst CHUNK_TAG_BYTES = 128;\n\n/**\n * Floor for a per-record field budget. Only reachable if the ENVELOPE alone (context keys, the api\n * tag, svcName) already fills the budget — pathological, and slicing a message into 1-byte pieces\n * would be worse than emitting one slightly-oversized record.\n */\nconst MIN_FIELD_BYTES = 1024;\n\n/**\n * How many bytes each of two chunked fields may spend PER RECORD. Data-only structure → a class.\n */\nexport class ChunkBudgets {\n constructor(\n readonly firstBudget: number,\n readonly secondBudget: number,\n ) {}\n}\n\nexport class LogChunkerImpl {\n private readonly encoder = new TextEncoder();\n\n /**\n * Plain UTF-8 byte length — for measuring text that is ALREADY in its final serialized form\n * (e.g. winston's fully-rendered JSON line), where no further escaping will happen.\n */\n byteLength(text: string): number {\n return this.encoder.encode(text).length;\n }\n\n /**\n * The byte cost of `text` once it has been JSON-escaped as a string VALUE inside a record.\n *\n * This is the measurement that matters, and it is why chunking on raw UTF-8 length is a bug: a\n * log message holding a JSON body is escaped a SECOND time when the record is serialized, so\n * every `\"` becomes `\\\"`, every newline `\\n`, and a control character explodes to a 6-byte\n * `\\u00XX`. A body that is dense in quotes can inflate by ~2x on that second pass — enough to\n * push a \"196KB\" chunk past the 262KB ceiling and silently drop it.\n *\n * Exact for `JSON.stringify` semantics (V8 does not \\u-escape non-ASCII). For the bunyan GCP\n * path — which ships over gRPC/protobuf rather than as JSON text — this over-counts slightly,\n * which is the safe direction.\n */\n escapedByteLength(text: string): number {\n let bytes = 0;\n // for...of iterates CODE POINTS, so a surrogate pair is one step, not two.\n for (const char of text) {\n bytes += this.escapedCost(char.codePointAt(0)!);\n }\n return bytes;\n }\n\n /**\n * Split `text` so each piece costs at most `maxBytes` once JSON-escaped\n * (see {@link escapedByteLength}).\n *\n * GUARANTEES:\n * - `chunk(t, n).join('') === t` — nothing is lost, so the pieces reassemble exactly.\n * - No piece splits a code point: a 4-byte emoji or a CJK character is never cut in half (which\n * would corrupt the boundary character into replacement junk on reassembly).\n * - Always returns at least one piece (`['']` for empty input), so callers can treat the result\n * uniformly.\n *\n * Degenerate case: if a SINGLE code point costs more than `maxBytes`, that piece necessarily\n * exceeds the budget — unavoidable, and irrelevant at any sane budget (max cost is 6 bytes).\n */\n chunk(text: string, maxBytes: number): string[] {\n if (maxBytes <= 0) {\n throw new Error(`maxBytes must be positive, was ${maxBytes}`);\n }\n if (this.escapedByteLength(text) <= maxBytes) {\n return [text];\n }\n\n const chunks: string[] = [];\n let start = 0;\n // UTF-16 index (what slice() wants), advanced by each code point's unit length so every\n // boundary we cut on is a code-point boundary.\n let position = 0;\n let bytes = 0;\n for (const char of text) {\n const cost = this.escapedCost(char.codePointAt(0)!);\n if (bytes + cost > maxBytes && position > start) {\n chunks.push(text.slice(start, position));\n start = position;\n bytes = 0;\n }\n bytes += cost;\n position += char.length;\n }\n chunks.push(text.slice(start));\n return chunks;\n }\n\n /**\n * Divide a record's budget between the TWO fields a backend chunks — the message and the stack\n * trace. Shared by the winston and bunyan GCP backends, which differ only in what those fields\n * are called (`message`/`errStack` vs `msg`/`err.stack`), never in this arithmetic.\n *\n * The envelope's cost is derived by SUBTRACTION: `renderedBytes` minus the escaped cost of the\n * two fields IS the envelope, whatever it happens to hold. That stays correct as apps register\n * new context keys, where summing up known parts would silently drift.\n *\n * The split: whichever field is small enough to fit whole gets exactly what it needs and the\n * other takes the rest; if BOTH are oversized they share evenly. Either way record N holds\n * first[N] + second[N] and still lands within budget.\n *\n * @param renderedBytes - size of the fully-serialized record as it stands today\n * @param budgetBytes - the per-record ceiling (typically {@link GCP_LOG_BUDGET_BYTES})\n */\n chunkBudgets(renderedBytes: number, budgetBytes: number, first: string, second: string): ChunkBudgets {\n const firstBytes = this.escapedByteLength(first);\n const secondBytes = this.escapedByteLength(second);\n const envelopeBytes = renderedBytes - firstBytes - secondBytes;\n const available = Math.max(budgetBytes - envelopeBytes - CHUNK_TAG_BYTES, MIN_FIELD_BYTES);\n const half = Math.floor(available / 2);\n\n // Both too big to fit alongside anything → split the room evenly.\n if (firstBytes > half && secondBytes > half) {\n return new ChunkBudgets(half, half);\n }\n // A field that fits in one piece is given exactly its own size (never 0 — chunk() rejects\n // that), and the oversized field gets everything left over.\n if (secondBytes <= half) {\n return new ChunkBudgets(Math.max(available - secondBytes, MIN_FIELD_BYTES), Math.max(secondBytes, 1));\n }\n return new ChunkBudgets(Math.max(firstBytes, 1), Math.max(available - firstBytes, MIN_FIELD_BYTES));\n }\n\n /**\n * A fresh id correlating the pieces of one split message. Uses Math.random rather than\n * crypto.randomUUID so it works in every browser context (randomUUID needs a secure context) and\n * on older Node — matching how RequestContextHeaders generates its fallback request id. These\n * only need to be unique among the lines an operator is grepping, not cryptographically strong.\n */\n newUid(): string {\n return `chunk-${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 10)}`;\n }\n\n /** Bytes this code point occupies once JSON-escaped inside a string value. */\n private escapedCost(codePoint: number): number {\n // The characters JSON.stringify escapes with a 2-char backslash sequence: \" \\ \\b \\t \\n \\f \\r\n if (codePoint === 0x22 || codePoint === 0x5c) {\n return 2;\n }\n if (\n codePoint === 0x08 ||\n codePoint === 0x09 ||\n codePoint === 0x0a ||\n codePoint === 0x0c ||\n codePoint === 0x0d\n ) {\n return 2;\n }\n // Every other control character becomes a 6-byte \\u00XX escape.\n if (codePoint < 0x20) {\n return 6;\n }\n // Otherwise the character is emitted as-is, costing its UTF-8 length.\n if (codePoint < 0x80) {\n return 1;\n }\n if (codePoint < 0x800) {\n return 2;\n }\n if (codePoint < 0x10000) {\n return 3;\n }\n return 4;\n }\n}\n\n/**\n * The process-wide {@link LogChunkerImpl} singleton — mirrors the `RequestContext` export pattern.\n * Callers use `LogChunker.chunk(...)`, never `new`.\n */\nexport const LogChunker = new LogChunkerImpl();\n"]}