From 3f40da735c4021f8bcdcef3594df79aac02243fe Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Mon, 12 Oct 2026 19:03:47 +0500 Subject: [PATCH 01/12] docs(packages): drop internal design, progress and review references from code comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comments no longer point at the internal design, progress and review documents (docs/design, docs/PROGRESS.md, docs/review, implementation standards), nor at section numbers, review finding IDs, wave/round/slice/tier labels or phase tags that only made sense alongside those documents. The rationale each comment carried is kept. Only comments and JSDoc changed; the minified output of every changed JS/TS file is identical to HEAD. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) --- packages/adapter-kit/src/index.ts | 3 +- .../adapter-kit/src/report-server-error.ts | 2 +- .../adapter-kit/src/trace-data.fuzz.test.ts | 2 +- packages/adapter-kit/src/trace-data.ts | 2 +- packages/adapter-kit/vitest.config.ts | 2 +- packages/angular/src/error.ts | 5 +- packages/angular/src/host-boundary.test.ts | 1 - packages/angular/src/index.ts | 4 +- packages/angular/src/router.ts | 6 +- packages/angular/vitest.config.ts | 2 +- packages/astro-e2e/test/astro.e2e.ts | 2 +- packages/astro-e2e/test/collector.ts | 4 +- packages/astro/src/client.ts | 2 +- packages/astro/src/edge-relaunch.test.ts | 4 +- packages/astro/src/edge.ts | 10 +- packages/astro/src/middleware.fuzz.test.ts | 4 +- packages/astro/src/server.ts | 2 +- packages/astro/vitest.config.ts | 2 +- .../src/index.test.ts | 6 +- .../src/index.ts | 4 +- .../vitest.config.ts | 2 +- .../browser-utils/src/coexistence.test.ts | 4 +- packages/browser-utils/src/coexistence.ts | 5 +- packages/browser-utils/src/fetch-transport.ts | 2 +- .../src/idb-chunk-backend.test.ts | 12 +- .../browser-utils/src/idb-chunk-backend.ts | 12 +- packages/browser-utils/src/index.ts | 2 +- .../browser-utils/src/instance-coexistence.ts | 3 +- .../browser-utils/src/web-lock-liveness.ts | 3 +- packages/browser-utils/vitest.config.ts | 2 +- packages/browser/src/call-site-jsc.test.ts | 2 +- packages/browser/src/component-name.ts | 4 +- packages/browser/src/detection-providers.ts | 2 +- packages/browser/src/environment.test.ts | 2 +- packages/browser/src/environment.ts | 6 +- packages/browser/src/index.ts | 6 +- packages/browser/src/input-source.test.ts | 2 +- packages/browser/src/input-source.ts | 6 +- packages/browser/src/interaction-source.ts | 6 +- packages/browser/src/keycodes.ts | 2 +- .../src/launch-options.invalid.test.ts | 6 +- .../browser/src/launch-stop-recovery.test.ts | 2 +- .../src/launch-stop-shared-drain.test.ts | 6 +- packages/browser/src/launch.test.ts | 32 ++-- packages/browser/src/launch.ts | 54 +++---- packages/browser/src/meta-trace.ts | 8 +- .../browser/src/mobile-options.launch.test.ts | 2 +- packages/browser/src/navigation-source.ts | 2 +- packages/browser/src/notify.launch.test.ts | 2 +- packages/browser/src/page-lifecycle.test.ts | 2 +- packages/browser/src/report-dialog.ts | 2 +- packages/browser/src/user-agent.ts | 1 - packages/browser/src/viewport-source.ts | 2 +- packages/browser/vitest.config.ts | 2 +- packages/bugsee/src/export-parity.test-d.ts | 2 +- packages/bugsee/src/index.ts | 4 +- packages/bugsee/src/launch.test.ts | 12 +- packages/bugsee/src/lifecycle-exports.test.ts | 2 +- packages/bugsee/src/node.test.ts | 4 +- packages/bugsee/src/option-directions.test.ts | 16 +- packages/bugsee/src/relaunch-options.test.ts | 6 +- packages/bugsee/src/server-launch.ts | 2 +- packages/bugsee/src/wire.ts | 28 ++-- packages/bugsee/src/worker-entries.test.ts | 2 +- packages/bugsee/tsup.config.ts | 2 +- packages/bugsee/vitest.config.ts | 2 +- packages/bun/src/bun-serve-interceptor.ts | 4 +- packages/bun/src/index.exports.test.ts | 2 +- packages/bun/src/index.ts | 2 +- packages/bun/src/launch.test.ts | 2 +- packages/bun/src/relaunch.test.ts | 2 +- packages/bun/vitest.config.ts | 2 +- packages/bundler-plugin-core/src/build-id.ts | 6 +- packages/bundler-plugin-core/src/index.ts | 1 - .../bundler-plugin-core/src/orchestrate.ts | 8 +- .../bundler-plugin-core/src/plugin.test.ts | 2 +- packages/bundler-plugin-core/src/plugin.ts | 8 +- .../src/plugin.upload.test.ts | 2 +- .../src/register-build.test.ts | 4 +- .../bundler-plugin-core/src/register-build.ts | 12 +- .../bundler-plugin-core/src/resolve.test.ts | 4 +- packages/bundler-plugin-core/src/resolve.ts | 10 +- .../bundler-plugin-core/src/run-cli.test.ts | 4 +- packages/bundler-plugin-core/src/run-cli.ts | 4 +- packages/bundler-plugin-core/src/sri.ts | 4 +- .../bundler-plugin-core/src/stamp-assets.ts | 3 +- packages/bundler-plugin-core/src/vcs.ts | 4 +- packages/bundler-plugin-core/vitest.config.ts | 2 +- packages/capture/src/absolutize-url.ts | 2 +- packages/capture/src/console-interceptor.ts | 2 +- .../capture/src/detector-url-corpus.test-d.ts | 4 +- .../src/detector-url-sanitizer.corpus.test.ts | 10 +- .../src/detector-url-sanitizer.test.ts | 14 +- .../capture/src/detector-url-sanitizer.ts | 24 +-- .../capture/src/fetch-interceptor.test.ts | 2 +- packages/capture/src/fetch-interceptor.ts | 6 +- packages/capture/src/index.ts | 8 +- packages/capture/src/input-provider.ts | 2 +- packages/capture/src/log-provider.test.ts | 2 +- packages/capture/src/log-provider.ts | 6 +- packages/capture/src/network-body.test.ts | 2 +- packages/capture/src/network-interceptor.ts | 2 +- packages/capture/src/network-provider.test.ts | 5 +- packages/capture/src/network-provider.ts | 7 +- packages/capture/src/request-decorator.ts | 2 +- .../capture/src/send-beacon-interceptor.ts | 9 +- packages/capture/src/sse-interceptor.ts | 2 +- .../capture/src/system-events-provider.ts | 2 +- .../capture/src/system-traces-provider.ts | 2 +- .../capture/src/traceparent.targets.test.ts | 2 +- packages/capture/src/traceparent.ts | 9 +- packages/capture/src/tracestate.ts | 7 +- .../capture/src/web-socket-interceptor.ts | 4 +- .../capture/src/web-transport-interceptor.ts | 2 +- packages/capture/src/xhr-interceptor.ts | 2 +- packages/capture/vitest.config.ts | 2 +- .../src/handler-attributes.fuzz.test.ts | 2 +- packages/cloudflare/src/handler-attributes.ts | 2 +- packages/cloudflare/src/index.ts | 9 +- .../cloudflare/src/instrument-class.test.ts | 4 +- packages/cloudflare/src/instrument-class.ts | 6 +- .../instrument-durable-object.fuzz.test.ts | 2 +- .../src/instrument-durable-object.test.ts | 4 +- .../src/instrument-durable-object.ts | 4 +- packages/cloudflare/src/launch-config.ts | 2 +- packages/cloudflare/src/launch.test.ts | 10 +- packages/cloudflare/src/launch.ts | 13 +- packages/cloudflare/src/node-async-hooks.d.ts | 3 +- packages/cloudflare/src/relaunch.test.ts | 2 +- packages/cloudflare/src/request-cf.ts | 2 +- packages/cloudflare/src/with-bugsee.test.ts | 2 +- packages/cloudflare/src/with-bugsee.ts | 2 +- packages/cloudflare/vitest.config.ts | 2 +- packages/core/src/attachments.ts | 8 +- packages/core/src/bounded.fuzz.test.ts | 2 +- packages/core/src/bugsee-api.ts | 10 +- packages/core/src/bundle-assembler.test.ts | 4 +- packages/core/src/bundle-assembler.ts | 19 ++- packages/core/src/bundle-uploader.ts | 2 +- packages/core/src/bundle-writer.ts | 4 +- packages/core/src/capture-aggregator.test.ts | 4 +- packages/core/src/capture-aggregator.ts | 7 +- packages/core/src/capture-coordinator.ts | 6 +- packages/core/src/capture-exporter.test.ts | 4 +- packages/core/src/capture-exporter.ts | 2 +- packages/core/src/capture-recovery.test.ts | 2 +- packages/core/src/capture-recovery.ts | 4 +- packages/core/src/carrier.test.ts | 4 +- packages/core/src/carrier.ts | 28 ++-- packages/core/src/chunk-backend.ts | 5 +- packages/core/src/chunk-capture-store.test.ts | 16 +- packages/core/src/chunk-storage.ts | 2 +- .../src/client-blackout-ownership.test.ts | 8 +- .../client-blackout-started-deferred.test.ts | 2 +- .../core/src/client-lifecycle-state.test.ts | 3 +- .../core/src/client-notify-handover.test.ts | 2 +- packages/core/src/client-notify.test.ts | 2 +- packages/core/src/client-stop-safety.test.ts | 6 +- .../core/src/client-stop-shared-drain.test.ts | 4 +- packages/core/src/client.test.ts | 19 ++- packages/core/src/client.ts | 118 +++++++------- packages/core/src/clock.ts | 4 +- packages/core/src/contracts.test-d.ts | 2 +- packages/core/src/contracts.ts | 39 +++-- packages/core/src/crash.ts | 5 +- packages/core/src/dedup.ts | 2 +- packages/core/src/detection-coordinator.ts | 4 +- .../core/src/durable-upload-pipeline.test.ts | 4 +- packages/core/src/durable-upload-pipeline.ts | 4 +- packages/core/src/emitter.ts | 2 +- packages/core/src/environment.ts | 2 +- packages/core/src/errors.ts | 4 +- packages/core/src/event-emitter.ts | 4 +- packages/core/src/events.ts | 6 +- packages/core/src/extension-registry.ts | 4 +- packages/core/src/file-capture-store.test.ts | 16 +- packages/core/src/filters.ts | 4 +- packages/core/src/index.ts | 6 +- packages/core/src/interceptor-base.ts | 2 +- packages/core/src/lifecycle-events.ts | 3 +- .../core/src/memory-capture-store.test.ts | 2 +- packages/core/src/memory-capture-store.ts | 4 +- .../core/src/memory-chunk-backend.test.ts | 4 +- .../core/src/native-crash-recovery.test.ts | 2 +- packages/core/src/native-crash-recovery.ts | 2 +- packages/core/src/never-throw.test.ts | 2 +- packages/core/src/never-throw.ts | 6 +- packages/core/src/notify-relay.ts | 5 +- packages/core/src/operation-dispatcher.ts | 2 +- packages/core/src/options.test.ts | 4 +- packages/core/src/options.ts | 14 +- .../src/partitioned-capture-store.test.ts | 11 +- .../core/src/partitioned-capture-store.ts | 10 +- packages/core/src/rate-limiter.ts | 6 +- packages/core/src/relaunch-timer.test.ts | 2 +- packages/core/src/relaunch.test.ts | 2 +- packages/core/src/relaunch.ts | 8 +- packages/core/src/request-context.ts | 5 +- packages/core/src/ring-buffer.ts | 4 +- packages/core/src/services.ts | 2 +- packages/core/src/stack.fuzz.test.ts | 2 +- packages/core/src/stack.ts | 8 +- packages/core/src/transport.ts | 42 ++--- packages/core/src/trigger-pipeline.ts | 2 +- packages/core/src/upload-pipeline.test.ts | 2 +- packages/core/src/upload-pipeline.ts | 12 +- packages/core/vitest.config.ts | 2 +- packages/deno/src/deno-serve-interceptor.ts | 4 +- packages/deno/src/index.exports.test.ts | 2 +- packages/deno/src/index.ts | 2 +- packages/deno/src/launch.test.ts | 2 +- packages/deno/src/relaunch.test.ts | 2 +- packages/deno/vitest.config.ts | 2 +- packages/e2e-kit/src/bundle.ts | 8 +- packages/e2e-kit/src/index.ts | 2 +- packages/electron/src/crash-reporter.ts | 2 +- packages/electron/src/electron-e2e.test.ts | 4 +- packages/electron/src/index.ts | 2 +- packages/electron/src/launch-main.test.ts | 4 +- packages/electron/src/launch-main.ts | 25 ++- .../src/launch-renderer-after.test.ts | 2 +- packages/electron/src/launch-renderer.test.ts | 8 +- packages/electron/src/launch-renderer.ts | 22 +-- packages/electron/src/main-control.test.ts | 4 +- packages/electron/src/main-control.ts | 6 +- packages/electron/src/main-receiver.ts | 9 +- packages/electron/src/main.ts | 4 +- packages/electron/src/native-crash-source.ts | 2 +- .../src/pixel-video-controller.test.ts | 2 +- .../electron/src/pixel-video-controller.ts | 4 +- packages/electron/src/preload-bridge.ts | 10 +- packages/electron/src/protocol.fuzz.test.ts | 1 - packages/electron/src/protocol.test.ts | 6 +- packages/electron/src/protocol.ts | 10 +- packages/electron/src/renderer-control.ts | 2 +- .../src/renderer-convergence.e2e.test.ts | 19 ++- packages/electron/src/renderer-gone.test.ts | 6 +- packages/electron/src/renderer-gone.ts | 13 +- .../src/renderer-incident-provider.test.ts | 2 +- .../src/renderer-incident-provider.ts | 14 +- .../src/renderer-report-pipeline.test.ts | 6 +- .../electron/src/renderer-report-pipeline.ts | 12 +- packages/electron/src/renderer.ts | 4 +- packages/electron/src/video-capture.ts | 4 +- packages/electron/vitest.config.ts | 2 +- packages/elysia/src/hooks.test.ts | 4 +- packages/elysia/src/hooks.ts | 10 +- packages/elysia/src/host-boundary.test.ts | 2 +- packages/elysia/src/index.ts | 2 +- packages/elysia/vitest.config.ts | 2 +- packages/express/src/host-boundary.test.ts | 2 +- packages/express/src/index.ts | 2 +- packages/express/src/middleware.test.ts | 4 +- packages/express/src/middleware.ts | 5 +- packages/express/src/real-express.test.ts | 2 +- packages/express/src/setup.ts | 6 +- packages/express/vitest.config.ts | 2 +- packages/fastify/src/hooks.test.ts | 4 +- packages/fastify/src/hooks.ts | 6 +- packages/fastify/src/host-boundary.test.ts | 2 +- packages/fastify/src/index.ts | 2 +- packages/fastify/vitest.config.ts | 2 +- packages/hapi/src/hooks.test.ts | 4 +- packages/hapi/src/hooks.ts | 6 +- packages/hapi/src/host-boundary.test.ts | 2 +- packages/hapi/src/index.ts | 2 +- packages/hapi/vitest.config.ts | 2 +- packages/hono/src/host-boundary.test.ts | 2 +- packages/hono/src/index.ts | 2 +- packages/hono/src/middleware.test.ts | 4 +- packages/hono/src/middleware.ts | 8 +- packages/hono/src/setup.ts | 6 +- packages/hono/vitest.config.ts | 2 +- .../app/browser-scenario.ts | 2 +- .../app/do-rpc-worker.ts | 2 +- .../app/do-tenants-worker.ts | 5 +- .../instrumentation-tests/app/scenario.ts | 22 +-- .../harness/invariants.mts | 147 +++++++----------- .../test/blackout.e2e.ts | 2 +- .../instrumentation-tests/test/bundle.test.ts | 5 +- packages/instrumentation-tests/test/bundle.ts | 2 +- .../instrumentation-tests/test/collector.ts | 2 +- .../test/durable-object-rpc.e2e.ts | 4 +- .../test/durable-object-tenants.e2e.ts | 15 +- .../instrumentation-tests/test/edge-bundle.ts | 2 +- .../instrumentation-tests/test/edge.e2e.ts | 4 +- .../test/instrumentation.e2e.ts | 25 ++- .../test/lifecycle.e2e.ts | 2 +- .../test/opentelemetry.e2e.ts | 2 +- .../instrumentation-tests/test/replay.e2e.ts | 2 +- .../instrumentation-tests/test/runtimes.ts | 6 +- .../test/sourcemaps.e2e.ts | 2 +- .../instrumentation-tests/test/sri.e2e.ts | 6 +- .../test/tsup-node-protocol.e2e.ts | 10 +- .../test/umbrella-conditions.e2e.ts | 2 +- .../test/webview-bundle.e2e.ts | 2 +- .../test/webview-conformance.e2e.ts | 34 ++-- .../vitest.unit.config.ts | 1 - packages/integration-shims/src/index.test.ts | 2 +- packages/integration-shims/src/index.ts | 4 +- packages/integration-shims/vitest.config.ts | 2 +- packages/koa/src/host-boundary.test.ts | 2 +- packages/koa/src/index.ts | 2 +- packages/koa/src/middleware.test.ts | 4 +- packages/koa/src/middleware.ts | 2 +- packages/koa/src/real-koa.test.ts | 2 +- packages/koa/src/setup.ts | 6 +- packages/koa/vitest.config.ts | 2 +- packages/logger/src/index.ts | 2 +- packages/logger/vitest.config.ts | 2 +- packages/nestjs/src/filter.test.ts | 2 +- packages/nestjs/src/filter.ts | 2 +- packages/nestjs/src/host-boundary.test.ts | 2 +- packages/nestjs/src/index.ts | 2 +- packages/nestjs/src/interceptor.test.ts | 4 +- packages/nestjs/src/interceptor.ts | 2 +- .../nestjs/src/nestjs-integration.test.ts | 2 +- packages/nestjs/src/setup.ts | 6 +- packages/nestjs/src/shared.ts | 4 +- packages/nestjs/vitest.config.ts | 2 +- packages/nextjs-e2e/next-env.d.ts | 2 +- packages/nextjs/src/client.ts | 6 +- packages/nextjs/src/edge.ts | 6 +- packages/nextjs/src/index.ts | 3 - packages/nextjs/src/on-request-error.ts | 4 +- packages/nextjs/src/otel-provider.test.ts | 2 +- packages/nextjs/src/otel-provider.ts | 6 +- packages/nextjs/src/register.ts | 4 +- packages/nextjs/src/relaunch.test.ts | 6 +- packages/nextjs/src/server.ts | 12 +- packages/nextjs/src/trace-data.ts | 4 +- packages/nextjs/tsup.config.ts | 2 +- packages/nextjs/vitest.config.ts | 2 +- .../src/batched-fs-chunk-storage.test.ts | 4 +- .../src/batched-fs-chunk-storage.ts | 12 +- packages/node-utils/src/bundle-store.ts | 2 +- .../node-utils/src/capture-ring-drainer.ts | 4 +- .../node-utils/src/capture-ring-writer.ts | 8 +- packages/node-utils/src/capture-ring.test.ts | 4 +- packages/node-utils/src/capture-ring.ts | 2 +- .../src/crashpad-session-marker-store.ts | 3 +- packages/node-utils/src/fs-storage.ts | 2 +- packages/node-utils/src/http-request.ts | 4 +- packages/node-utils/src/index.ts | 1 - packages/node-utils/src/sha256.ts | 2 +- .../node-utils/src/worker-ring-worker.test.ts | 2 +- packages/node-utils/src/worker-ring-worker.ts | 4 +- packages/node-utils/vitest.config.ts | 2 +- .../src/active-span-store.integration.test.ts | 2 +- packages/node/src/active-span-store.test.ts | 20 +-- packages/node/src/active-span-store.ts | 6 +- packages/node/src/data-location.ts | 4 +- packages/node/src/detection-providers.ts | 4 +- packages/node/src/environment.ts | 4 +- packages/node/src/event-loop-watchdog.test.ts | 2 +- packages/node/src/event-loop-watchdog.ts | 2 +- packages/node/src/fetch-server-wrap.test.ts | 2 +- packages/node/src/fetch-server-wrap.ts | 6 +- packages/node/src/http-interceptor.ts | 4 +- ...ttp-server-interceptor.integration.test.ts | 4 +- .../node/src/http-server-interceptor.test.ts | 2 +- packages/node/src/http-server-interceptor.ts | 7 +- packages/node/src/index.ts | 7 +- packages/node/src/instance-layout.ts | 10 +- packages/node/src/launch-lifecycle.test.ts | 4 +- .../node/src/launch-privacy-options.test.ts | 4 +- .../node/src/launch-recovery-wiring.test.ts | 2 +- .../node/src/launch-stop-recovery.test.ts | 2 +- packages/node/src/launch-stop-safety.test.ts | 4 +- .../node/src/launch-stop-shared-drain.test.ts | 4 +- packages/node/src/launch.data-size.test.ts | 2 +- packages/node/src/launch.test.ts | 35 ++--- packages/node/src/launch.ts | 85 +++++----- packages/node/src/liveness-heartbeat.test.ts | 2 +- packages/node/src/liveness-heartbeat.ts | 6 +- packages/node/src/liveness.ts | 4 +- packages/node/src/local-variables.test.ts | 6 +- packages/node/src/local-variables.ts | 4 +- .../node/src/mobile-options.launch.test.ts | 2 +- packages/node/src/notify.launch.test.ts | 2 +- packages/node/src/options.ts | 4 +- packages/node/src/process-policy.ts | 11 +- packages/node/src/profiling-controller.ts | 2 +- packages/node/src/recover-instances.test.ts | 6 +- packages/node/src/recover-instances.ts | 16 +- packages/node/src/recovery-claim.ts | 2 +- packages/node/src/request-context-store.ts | 2 +- .../src/server-instrument-boundary.test.ts | 3 +- packages/node/src/server-instrument.test.ts | 16 +- packages/node/src/server-instrument.ts | 22 +-- packages/node/src/sweep-instances.ts | 6 +- packages/node/src/system-events.test.ts | 2 +- packages/node/src/system-events.ts | 2 +- packages/node/src/trace-propagation.test.ts | 2 +- packages/node/src/trace-propagation.ts | 6 +- packages/node/vitest.config.ts | 2 +- packages/nuxt-e2e/test/collector.ts | 4 +- packages/nuxt-e2e/test/nuxt.e2e.ts | 2 +- packages/nuxt/src/client.ts | 2 +- packages/nuxt/src/index.ts | 2 +- packages/nuxt/src/module.ts | 4 +- packages/nuxt/src/nitro-edge-relaunch.test.ts | 4 +- packages/nuxt/src/nitro-edge.ts | 12 +- packages/nuxt/src/nitro.test.ts | 4 +- packages/nuxt/src/nitro.ts | 4 +- packages/nuxt/src/server.ts | 2 +- packages/nuxt/vitest.config.ts | 2 +- packages/opentelemetry/src/from-otlp.test.ts | 2 +- packages/opentelemetry/src/from-otlp.ts | 10 +- packages/opentelemetry/src/index.ts | 8 +- packages/opentelemetry/src/otlp-exporter.ts | 2 +- packages/opentelemetry/src/otlp-wire.ts | 4 +- packages/opentelemetry/src/otlp.fuzz.test.ts | 6 +- packages/opentelemetry/src/span-processor.ts | 4 +- packages/opentelemetry/src/to-otlp.test.ts | 6 +- packages/opentelemetry/src/to-otlp.ts | 22 +-- .../opentelemetry/src/trace-assembler.test.ts | 6 +- packages/opentelemetry/src/trace-assembler.ts | 4 +- packages/opentelemetry/vitest.config.ts | 2 +- packages/performance/src/active-span-store.ts | 2 +- packages/performance/src/capture-provider.ts | 2 +- packages/performance/src/controller.test.ts | 6 +- packages/performance/src/controller.ts | 22 +-- packages/performance/src/extension.test.ts | 2 +- packages/performance/src/extension.ts | 8 +- packages/performance/src/http-spans.test.ts | 4 +- packages/performance/src/http-spans.ts | 18 +-- .../performance/src/idle-transaction.test.ts | 2 +- packages/performance/src/idle-transaction.ts | 2 +- packages/performance/src/index.ts | 3 +- packages/performance/src/interactions.test.ts | 6 +- packages/performance/src/interactions.ts | 14 +- packages/performance/src/navigations.test.ts | 6 +- packages/performance/src/navigations.ts | 8 +- packages/performance/src/options.ts | 4 +- packages/performance/src/page-load.ts | 2 +- .../performance-bundle.integration.test.ts | 2 +- packages/performance/src/performance-send.ts | 2 +- packages/performance/src/span.test.ts | 4 +- packages/performance/src/span.ts | 21 ++- .../performance/src/wire-performance.test.ts | 6 +- packages/performance/src/wire-performance.ts | 14 +- packages/performance/vitest.config.ts | 2 +- packages/protocol/src/constants.test.ts | 2 +- packages/protocol/src/constants.ts | 20 +-- packages/protocol/src/index.ts | 4 +- packages/protocol/src/levels.ts | 4 +- .../protocol/src/linear-time.test-helper.ts | 2 +- packages/protocol/src/linear-time.test.ts | 2 +- packages/protocol/src/options.test.ts | 2 +- packages/protocol/src/options.ts | 2 +- packages/protocol/src/pairs.ts | 4 +- packages/protocol/src/redaction.fuzz.test.ts | 2 +- packages/protocol/src/sanitize.test.ts | 12 +- packages/protocol/src/sanitize.ts | 14 +- packages/protocol/src/sensitive.ts | 2 +- packages/protocol/src/shapes.ts | 4 +- packages/protocol/src/units.ts | 2 +- packages/protocol/src/upload-contract.test.ts | 9 +- packages/protocol/src/url.test.ts | 6 +- packages/protocol/src/url.ts | 6 +- packages/protocol/src/wire.ts | 26 ++-- packages/protocol/vitest.config.ts | 2 +- packages/react/src/error-boundary.ts | 2 +- packages/react/src/handlers.ts | 2 +- packages/react/src/host-boundary.test.ts | 2 +- packages/react/src/index.ts | 4 +- packages/react/src/profiler.render.test.ts | 2 +- packages/react/src/profiler.test.ts | 2 +- packages/react/src/profiler.ts | 4 +- packages/react/src/report.ts | 7 +- packages/react/src/router.ts | 10 +- packages/react/vitest.config.ts | 2 +- packages/remix/src/client.ts | 4 +- packages/remix/src/index.ts | 3 +- .../src/meta-tag-transformer.fuzz.test.ts | 2 +- packages/remix/src/meta-tag-transformer.ts | 2 +- packages/remix/src/server.ts | 4 +- packages/remix/src/trace-meta.ts | 4 +- packages/remix/tsup.config.ts | 2 +- packages/remix/vitest.config.ts | 2 +- packages/replay-canvas/src/canvas-config.ts | 4 +- packages/replay-canvas/src/index.ts | 2 +- .../replay-canvas/src/integration.test.ts | 2 +- packages/replay-canvas/vitest.config.ts | 2 +- packages/replay/src/encoder.ts | 4 +- packages/replay/src/index.ts | 3 +- packages/replay/src/masking.fuzz.test.ts | 7 +- .../replay/src/masking.integration.test.ts | 20 +-- packages/replay/src/masking.test.ts | 6 +- packages/replay/src/masking.ts | 19 ++- packages/replay/src/recorder.test.ts | 2 +- packages/replay/src/recorder.ts | 10 +- packages/replay/src/register.test.ts | 4 +- packages/replay/src/register.ts | 10 +- packages/replay/vitest.config.ts | 2 +- packages/rrweb/src/index.ts | 4 +- packages/rrweb/vitest.config.ts | 2 +- packages/service/src/index.ts | 4 +- packages/service/vitest.config.ts | 2 +- packages/solid/src/error.ts | 6 +- packages/solid/src/host-boundary.test.ts | 2 +- packages/solid/src/index.ts | 4 +- packages/solid/src/router.ts | 6 +- packages/solid/vitest.config.ts | 2 +- .../src/index.ts | 2 +- .../vitest.config.ts | 2 +- packages/svelte/src/error.ts | 4 +- packages/svelte/src/host-boundary.test.ts | 2 +- packages/svelte/src/index.ts | 4 +- packages/svelte/src/router.ts | 7 +- packages/svelte/vitest.config.ts | 2 +- packages/sveltekit-e2e/test/collector.ts | 4 +- packages/sveltekit-e2e/test/sveltekit.e2e.ts | 2 +- packages/sveltekit/src/client.ts | 2 +- packages/sveltekit/src/edge-relaunch.test.ts | 4 +- packages/sveltekit/src/edge.ts | 10 +- packages/sveltekit/src/server.ts | 2 +- packages/sveltekit/vitest.config.ts | 2 +- packages/types/src/index.ts | 12 +- packages/util/src/backoff.ts | 4 +- packages/util/src/deep-merge.ts | 2 +- packages/util/src/deferred.ts | 2 +- packages/util/src/describe-value.ts | 2 +- packages/util/src/env.ts | 2 +- packages/util/src/fflate.test.ts | 2 +- packages/util/src/fflate.ts | 2 +- packages/util/src/index.ts | 1 - packages/util/src/report-guarded.ts | 2 +- packages/util/src/sha256.test.ts | 2 +- packages/util/vitest.config.ts | 2 +- packages/vercel-edge/src/detection.ts | 2 +- packages/vercel-edge/src/edge-context.test.ts | 4 +- packages/vercel-edge/src/edge-context.ts | 4 +- packages/vercel-edge/src/environment.ts | 2 +- packages/vercel-edge/src/fetch-handler.ts | 4 +- .../vercel-edge/src/host-boundary.test.ts | 2 +- .../vercel-edge/src/index.exports.test.ts | 2 +- packages/vercel-edge/src/index.ts | 6 +- .../vercel-edge/src/launch-lifecycle.test.ts | 2 +- packages/vercel-edge/src/launch.test.ts | 24 +-- packages/vercel-edge/src/launch.ts | 24 ++- .../vercel-edge/src/relaunch-edge.test.ts | 2 +- packages/vercel-edge/src/relaunch-edge.ts | 4 +- packages/vercel-edge/src/relaunch.test.ts | 2 +- .../vercel-edge/src/request-context-store.ts | 2 +- packages/vercel-edge/src/wait-until.ts | 2 +- packages/vercel-edge/tsup.config.ts | 2 +- packages/vercel-edge/vitest.config.ts | 2 +- packages/vite-plugin/src/index.ts | 1 - packages/vite-plugin/vitest.config.ts | 2 +- packages/vue/src/component-annotate.ts | 2 +- packages/vue/src/error.ts | 4 +- packages/vue/src/host-boundary.test.ts | 6 +- packages/vue/src/index.ts | 4 +- packages/vue/src/router.ts | 8 +- packages/vue/vitest.config.ts | 2 +- packages/web-adapter/src/adapter.ts | 8 +- packages/web-adapter/src/index.ts | 3 +- .../src/never-throw-boundary.test.ts | 4 +- packages/web-adapter/src/render-span.ts | 2 +- packages/web-adapter/vitest.config.ts | 2 +- packages/webpack-plugin/src/index.ts | 1 - packages/webpack-plugin/vitest.config.ts | 2 +- .../webview/src/control-token.fuzz.test.ts | 2 +- packages/webview/src/control-token.test.ts | 2 +- packages/webview/src/control-token.ts | 4 +- .../webview/src/host-bridge-capture-store.ts | 6 +- .../webview/src/host-bridge-control.test.ts | 28 ++-- packages/webview/src/host-bridge-control.ts | 28 ++-- packages/webview/src/host-bridge.fuzz.test.ts | 2 +- packages/webview/src/host-bridge.test.ts | 16 +- packages/webview/src/host-bridge.ts | 12 +- packages/webview/src/iife.ts | 2 +- packages/webview/src/index.exports.test.ts | 2 +- packages/webview/src/index.ts | 8 +- packages/webview/src/launch.test.ts | 61 ++++---- packages/webview/src/launch.ts | 113 +++++++------- packages/webview/src/obscuring-channel.ts | 5 +- .../webview/src/obscuring-composer.test.ts | 2 +- packages/webview/src/obscuring-composer.ts | 6 +- .../webview/src/obscuring-failclosed.test.ts | 10 +- packages/webview/src/obscuring-source.test.ts | 2 +- packages/webview/src/obscuring-source.ts | 2 +- packages/webview/src/protocol.test.ts | 2 +- packages/webview/src/protocol.ts | 34 ++-- packages/webview/src/redaction-provenance.ts | 2 +- .../webview/src/webview-report-pipeline.ts | 14 +- packages/webview/tsup.config.ts | 4 +- packages/webview/vitest.config.ts | 4 +- .../webworker/src/environment.fuzz.test.ts | 2 +- packages/webworker/src/environment.test.ts | 2 +- packages/webworker/src/environment.ts | 4 +- packages/webworker/src/event.test.ts | 2 +- packages/webworker/src/event.ts | 4 +- packages/webworker/src/index.exports.test.ts | 2 +- packages/webworker/src/index.ts | 5 +- .../webworker/src/launch-lifecycle.test.ts | 4 +- .../src/launch-stop-shared-drain.test.ts | 6 +- packages/webworker/src/launch.test.ts | 16 +- packages/webworker/src/launch.ts | 30 ++-- packages/webworker/src/relaunch.test.ts | 2 +- packages/webworker/vitest.config.ts | 4 +- 603 files changed, 1793 insertions(+), 1907 deletions(-) diff --git a/packages/adapter-kit/src/index.ts b/packages/adapter-kit/src/index.ts index 6a263b7f..1c074665 100644 --- a/packages/adapter-kit/src/index.ts +++ b/packages/adapter-kit/src/index.ts @@ -1,6 +1,5 @@ // @bugsee/adapter-kit β€” runtime-portable primitives shared by the SSR meta-framework adapters -// (@bugsee/{nextjs,remix,nuxt,sveltekit,astro}). See docs/design/meta-framework-adapters.md Β§3 (the -// shared kit) β€” P4 (server-error bridge) + P5 (trace-data). Depends only on @bugsee/core; no runtime- +// (@bugsee/{nextjs,remix,nuxt,sveltekit,astro}). The shared kit adds the server-error bridge and the trace-data primitive. Depends only on @bugsee/core; no runtime- // specific imports, so it is safe in node, edge, and browser graphs alike. export { type ReportServerErrorOptions, reportServerError } from './report-server-error'; export { diff --git a/packages/adapter-kit/src/report-server-error.ts b/packages/adapter-kit/src/report-server-error.ts index 14f330cf..e5bb346b 100644 --- a/packages/adapter-kit/src/report-server-error.ts +++ b/packages/adapter-kit/src/report-server-error.ts @@ -1,4 +1,4 @@ -// @bugsee/adapter-kit β€” the server-error bridge (P4): stitch a framework's server-side throw to the +// @bugsee/adapter-kit β€” the server-error bridge: stitch a framework's server-side throw to the // active Bugsee session. Shared by every SSR meta-framework adapter (Next.js `onRequestError`, SvelteKit // `handleError`, Remix `handleError`, Nuxt `nitroApp.hooks('error')`, Astro middleware catch) β€” each maps // its framework's error context to a capture `event`, then delegates the reporting here. diff --git a/packages/adapter-kit/src/trace-data.fuzz.test.ts b/packages/adapter-kit/src/trace-data.fuzz.test.ts index 465f378a..8392e042 100644 --- a/packages/adapter-kit/src/trace-data.fuzz.test.ts +++ b/packages/adapter-kit/src/trace-data.fuzz.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it } from 'vitest'; import { getTraceparent, traceMetaEntries, traceMetaTag } from './trace-data'; /** - * Property-based tests for the trace-data primitive (P5) β€” the BEβ†’FE continuation capstone. Its output + * Property-based tests for the trace-data primitive β€” the BEβ†’FE continuation capstone. Its output * leaves the SDK twice over: as a W3C `traceparent` a peer parser must accept, and as raw HTML injected * into an SSR ``. Both are "for any trace" claims, so both are checked as properties: * diff --git a/packages/adapter-kit/src/trace-data.ts b/packages/adapter-kit/src/trace-data.ts index 166479ff..b803f383 100644 --- a/packages/adapter-kit/src/trace-data.ts +++ b/packages/adapter-kit/src/trace-data.ts @@ -1,4 +1,4 @@ -// @bugsee/adapter-kit β€” the trace-data primitive (P5): read the active server-request trace as a W3C +// @bugsee/adapter-kit β€” the trace-data primitive: read the active server-request trace as a W3C // `traceparent`, for injecting into the SSR HTML (``) so the client pageload // adopts the server trace (BEβ†’FE continuation, the moat's correlation capstone). Shared by every SSR // meta-framework adapter (Next.js `generateMetadata`, SvelteKit `transformPageChunk`, Nuxt `render:html`, diff --git a/packages/adapter-kit/vitest.config.ts b/packages/adapter-kit/vitest.config.ts index 1bf5471d..558ca571 100644 --- a/packages/adapter-kit/vitest.config.ts +++ b/packages/adapter-kit/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. export default defineConfig({ test: { // The runner is far slower than a dev machine β€” roughly 7-18x under coverage instrumentation β€” and diff --git a/packages/angular/src/error.ts b/packages/angular/src/error.ts index 88753b00..318475a9 100644 --- a/packages/angular/src/error.ts +++ b/packages/angular/src/error.ts @@ -5,7 +5,7 @@ import { reportError, } from '@bugsee/web-adapter'; -// The @bugsee/angular ERROR SEAM (frontend-adapters Β§7 fan-out β€” the F6-thin pattern for Angular). Angular +// The @bugsee/angular ERROR SEAM (frontend-adapters fan-out β€” the thin pattern for Angular). Angular // reports uncaught errors through its injectable `ErrorHandler` (`handleError(error)`); the app replaces it // via `{ provide: ErrorHandler, useClass: BugseeErrorHandler }`. We report the error to the launched Bugsee // client, UNWRAPPING Angular's error wrapper (`error.ngOriginalError`) to the real thrown error, and DELEGATE @@ -60,7 +60,7 @@ export function createAngularErrorHandler(options: AngularErrorHandlerOptions = } { return { handleError(error) { - // Contained (Wave 2.1): unguarded, an SDK failure escaped into Angular's error pipeline AND skipped + // Contained: unguarded, an SDK failure escaped into Angular's error pipeline AND skipped // the delegate below β€” so the app lost both its error handling and its own handler. neverThrow(() => reportAngularError(error, options), options.onError); options.delegate?.handleError(error); // keep a chained handler (e.g. the default ErrorHandler) @@ -76,7 +76,6 @@ export function createAngularErrorHandler(options: AngularErrorHandlerOptions = * custom `ErrorHandler` lost it silently, and an app that provided none lost Angular's default, which is the * only thing that surfaces an uncaught error in the console. Measured against real `@angular/core`: the * default prints one `console.error('ERROR', error)`; resolving `BugseeErrorHandler` printed none - * (docs/review/frontend-adapters-vue-angular-svelte-solid.md SEV1 #2). * * Angular is a structural peer here β€” never imported β€” so its default is reproduced rather than delegated to. */ diff --git a/packages/angular/src/host-boundary.test.ts b/packages/angular/src/host-boundary.test.ts index 79d1ea5b..0de845da 100644 --- a/packages/angular/src/host-boundary.test.ts +++ b/packages/angular/src/host-boundary.test.ts @@ -5,7 +5,6 @@ import { BugseeErrorHandler, createAngularErrorHandler } from './error'; import * as renderTracker from './render-tracker'; import * as routerMod from './router'; -// Wave 2.1/2.3 β€” docs/review/frontend-adapters-vue-angular-svelte-solid.md SEV1 #1 + #2. const throwingClient = () => ({ logException: () => { diff --git a/packages/angular/src/index.ts b/packages/angular/src/index.ts index b691920d..cbd513f5 100644 --- a/packages/angular/src/index.ts +++ b/packages/angular/src/index.ts @@ -1,5 +1,5 @@ -// @bugsee/angular β€” Angular adapter (tier 4). Error seam (ErrorHandler) + Router naming. -// See docs/design/frontend-adapters.md Β§7. Structural peer β€” no @angular/core / @angular/router import. +// @bugsee/angular β€” Angular adapter. Error seam (ErrorHandler) + Router naming. +// Structural peer β€” no @angular/core / @angular/router import. // v1: error + routing only (component/change-detection depth is the shared depth pass). // Single-install re-export: surface the full public SDK (launch, Bugsee, options + manual-API diff --git a/packages/angular/src/router.ts b/packages/angular/src/router.ts index 3828d752..d48819af 100644 --- a/packages/angular/src/router.ts +++ b/packages/angular/src/router.ts @@ -1,9 +1,9 @@ import { neverThrow, type RouteNamingOptions, setRouteName } from '@bugsee/web-adapter'; -// The @bugsee/angular ROUTER NAMING integration (frontend-adapters Β§7 + the F5/D5 two-phase naming seam). +// The @bugsee/angular ROUTER NAMING integration (the two-phase naming seam). // Angular's activated-route snapshot tree carries the matched route config; walk it to build the // parameterized pattern (`/users/:id` β€” Angular's colon syntax, same as the backend adapters) and refine -// the active navigation transaction (opened raw-URL by the F1 navigation source) via the shared +// the active navigation transaction (opened raw-URL by the navigation source) via the shared // `setRouteName` seam. A STRUCTURAL PEER over the snapshot/router shapes (no `@angular/router` import) β†’ // version-agnostic + unit-testable. The user owns the `NavigationEnd` filter (they have the import), // keeping this robust + decoupled β€” wire once after the router is ready: @@ -52,7 +52,7 @@ function routePatternFromSnapshotUnsafe( return segments.length > 0 ? `/${segments.join('/')}` : undefined; } -/** Refine the active transaction to the router's CURRENT activated route pattern (D5 phase-2). Call on each +/** Refine the active transaction to the router's CURRENT activated route pattern. Call on each * `NavigationEnd`. A no-op when the current route has no usable pattern. */ export function setRouteNameFromRouter( router: AngularRouterLike, diff --git a/packages/angular/vitest.config.ts b/packages/angular/vitest.config.ts index 943da3ac..5190263d 100644 --- a/packages/angular/vitest.config.ts +++ b/packages/angular/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. // environment 'node': the Angular adapter is a STRUCTURAL PEER (no @angular import) tested injection-first // with plain objects β€” the same discipline as the other adapters. export default defineConfig({ diff --git a/packages/astro-e2e/test/astro.e2e.ts b/packages/astro-e2e/test/astro.e2e.ts index 24b24ca3..aafb0ef5 100644 --- a/packages/astro-e2e/test/astro.e2e.ts +++ b/packages/astro-e2e/test/astro.e2e.ts @@ -127,7 +127,7 @@ describe('@bugsee/astro β€” real Astro boot e2e', () => { // WAVE V0 β€” the collector VALIDATES every upload against the shipped wire contract, and this is what // makes it count. Until the shared kit landed, this harness carried its own collector copy that did no - // validation at all, so the entry-payload contract added in Wave 3b.2 (logs.json / network.json / + // validation at all, so the entry-payload contract (logs.json / network.json / // events.json β€” the files that carry the actual captured data) covered exactly one of the five suites. // // A violation the collector records but nobody asserts is the same false assurance as no check at all. diff --git a/packages/astro-e2e/test/collector.ts b/packages/astro-e2e/test/collector.ts index 99ef6f23..176690dd 100644 --- a/packages/astro-e2e/test/collector.ts +++ b/packages/astro-e2e/test/collector.ts @@ -1,4 +1,4 @@ -// The mock collector now lives in @bugsee/e2e-kit (Wave V0). This package used to carry its own diverged -// copy, which β€” critically β€” did NO schema validation, so the entry-payload contract added in Wave 3b.2 +// The mock collector now lives in @bugsee/e2e-kit. This package used to carry its own diverged +// copy, which β€” critically β€” did NO schema validation, so the entry-payload contract // covered instrumentation-tests alone. Re-exported here so existing `./collector` imports keep working. export { type CapturedUpload, type MockCollector, startMockCollector } from '@bugsee/e2e-kit'; diff --git a/packages/astro/src/client.ts b/packages/astro/src/client.ts index 75ba1ccf..a1f8be3e 100644 --- a/packages/astro/src/client.ts +++ b/packages/astro/src/client.ts @@ -20,7 +20,7 @@ export async function registerClient( return await launch(appToken, options); } -// API parity with the umbrella entries (review R2D-4): the same VALUE surface, thin re-exports of the same functions. +// API parity with the umbrella entries: the same VALUE surface, thin re-exports of the same functions. export { addReportAttachment, BugseeStatus, diff --git a/packages/astro/src/edge-relaunch.test.ts b/packages/astro/src/edge-relaunch.test.ts index c5da8abc..4b51723e 100644 --- a/packages/astro/src/edge-relaunch.test.ts +++ b/packages/astro/src/edge-relaunch.test.ts @@ -2,7 +2,7 @@ import { type Bugsee, BugseeStatus, getStatus, launchEdge } from '@bugsee/vercel import { afterEach, describe, expect, it, vi } from 'vitest'; import { registerServerEdge, relaunch } from './edge'; -// R3P-9: the `/edge` entry's `relaunch` used to be the plain vercel-edge one, which re-runs `launchEdge` and +// the `/edge` entry's `relaunch` used to be the plain vercel-edge one, which re-runs `launchEdge` and // ignored the `launch` seam `registerServerEdge` accepts (e.g. Cloudflare's launch). It now takes the same seam. const okTransport = () => vi.fn(async () => ({ @@ -52,7 +52,7 @@ describe('@bugsee/astro/edge relaunch', () => { expect(getStatus(carrier)).toBe(BugseeStatus.Launched); }); - // R4H-4: the install-time seam is the DEFAULT for a plain `relaunch()` (an explicit argument still wins). + // the install-time seam is the DEFAULT for a plain `relaunch()` (an explicit argument still wins). it('a plain relaunch() re-runs the custom launch the entry was INSTALLED with (R4H-4)', async () => { const carrier = {}; const custom = vi.fn((token: string, options: Parameters[1]) => { diff --git a/packages/astro/src/edge.ts b/packages/astro/src/edge.ts index 5ee67ee5..18ef0b60 100644 --- a/packages/astro/src/edge.ts +++ b/packages/astro/src/edge.ts @@ -30,7 +30,7 @@ export interface AstroEdgeOptions extends BugseeEdgeLaunchOptions { export function registerServerEdge(appToken: string, options: AstroEdgeOptions = {}): Bugsee { const { launch = launchEdge, ...launchOptions } = options; const client = launch(appToken, launchOptions); - rememberEdgeLaunch(launch, launchOptions.carrier); // the default for a plain relaunch() (R4H-4) + rememberEdgeLaunch(launch, launchOptions.carrier); // the default for a plain relaunch() return client; } @@ -56,7 +56,7 @@ function edgeAttributes(context: AstroMiddlewareContext): Record { - // The Wave 4.5 class, generalized: no status, and no content type, may turn a working response into a + // The class, generalized: no status, and no content type, may turn a working response into a // thrown error. An adapter that breaks the response is worse than an adapter that injects nothing. it('never throws, for any constructible status Γ— content-type (the 304 class, generalized)', async () => { await fc.assert( diff --git a/packages/astro/src/server.ts b/packages/astro/src/server.ts index 358cc950..265fa00b 100644 --- a/packages/astro/src/server.ts +++ b/packages/astro/src/server.ts @@ -23,7 +23,7 @@ export function registerServer(appToken: string, options: AstroServerOptions = { return launch(appToken, options); } -// API parity with the umbrella entries (review R2D-4): the same VALUE surface, thin re-exports of the same functions. +// API parity with the umbrella entries: the same VALUE surface, thin re-exports of the same functions. export { addReportAttachment, BugseeStatus, diff --git a/packages/astro/vitest.config.ts b/packages/astro/vitest.config.ts index 1bf5471d..558ca571 100644 --- a/packages/astro/vitest.config.ts +++ b/packages/astro/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. export default defineConfig({ test: { // The runner is far slower than a dev machine β€” roughly 7-18x under coverage instrumentation β€” and diff --git a/packages/babel-plugin-component-annotate/src/index.test.ts b/packages/babel-plugin-component-annotate/src/index.test.ts index d527ad54..72aa5210 100644 --- a/packages/babel-plugin-component-annotate/src/index.test.ts +++ b/packages/babel-plugin-component-annotate/src/index.test.ts @@ -86,7 +86,7 @@ describe('componentAnnotatePlugin', () => { // babel reuses one plugin instance across a multi-file build, but creates a fresh `PluginPass` per // FILE β€” and the component stack hangs off that state, so an aborted transform (component pushed, // then a throw before exit pops it) cannot leak into the next file. (An earlier revision reset a - // per-instance closure in `pre()`; that is what the SEV1 re-entrancy suite below replaced.) + // per-instance closure in `pre()`; that is what the re-entrancy suite below replaced.) const instance = componentAnnotatePlugin({ types: babelTypes }); const boom = () => ({ visitor: { @@ -218,7 +218,7 @@ describe('TypeScript (.tsx)', () => { }); }); -// SEV2 #1 β€” a peer plugin's nested `transformSync` wiped the shared stack. +// A peer plugin's nested `transformSync` wiped the shared stack. // // `pre()` resets a closure held per plugin INSTANCE, but babel calls it per File. The // `babel-plugin-macros` / `preval` / `codegen` family transforms code mid-traversal, which re-enters this @@ -250,7 +250,7 @@ describe('re-entrancy: a peer plugin transforming mid-traversal', () => { }); }); -// SEV1 β€” a NAMED FUNCTION/CLASS EXPRESSION was not recognised as a component, so `export default +// A NAMED FUNCTION/CLASS EXPRESSION was not recognised as a component, so `export default // memo(function Card() { … })` and `export default forwardRef(function Input(props, ref) { … })` β€” two of // the shapes React's own documentation shows β€” produced ZERO annotations. `componentNameOf` fell back to // `node.id` only for a *declaration*; a named expression with no enclosing `const` had a PascalCase id diff --git a/packages/babel-plugin-component-annotate/src/index.ts b/packages/babel-plugin-component-annotate/src/index.ts index f125d1ed..b1891b72 100644 --- a/packages/babel-plugin-component-annotate/src/index.ts +++ b/packages/babel-plugin-component-annotate/src/index.ts @@ -1,8 +1,8 @@ import type { types as BabelTypes, NodePath, PluginObj, PluginPass } from '@babel/core'; -// @bugsee/babel-plugin-component-annotate (frontend-adapters depth pass D3). Annotates each HOST (lowercase- +// @bugsee/babel-plugin-component-annotate (frontend-adapters depth pass). Annotates each HOST (lowercase- // tag) JSX element with `data-bugsee-component=""` β€” the name of the enclosing framework -// component β€” so the @bugsee/browser D2 runtime can attribute a click/error to a COMPONENT name that +// component β€” so the @bugsee/browser runtime can attribute a click/error to a COMPONENT name that // survives minification (the literal name is emitted at build time). Covers the JSX ecosystems (React / // Preact / Solid). A component is a PascalCase function/class; host elements are lowercase tags (component // elements like become props, not DOM attributes, so they are skipped). JSX in a nested callback diff --git a/packages/babel-plugin-component-annotate/vitest.config.ts b/packages/babel-plugin-component-annotate/vitest.config.ts index 1bf5471d..558ca571 100644 --- a/packages/babel-plugin-component-annotate/vitest.config.ts +++ b/packages/babel-plugin-component-annotate/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. export default defineConfig({ test: { // The runner is far slower than a dev machine β€” roughly 7-18x under coverage instrumentation β€” and diff --git a/packages/browser-utils/src/coexistence.test.ts b/packages/browser-utils/src/coexistence.test.ts index 6b826519..e3c78ab8 100644 --- a/packages/browser-utils/src/coexistence.test.ts +++ b/packages/browser-utils/src/coexistence.test.ts @@ -597,7 +597,7 @@ describe('createCoexistence β€” capture + marker recovery', () => { expect(reports).toHaveBeenCalledTimes(1); // 'msib' (from the working marker db) still recovered }); - // SEV1 (recovery double-upload). A tab/worker killed between the durable stage and the upload settling + // (recovery double-upload). A tab/worker killed between the durable stage and the upload settling // leaves BOTH a staged bundle and its incident's report marker; recovering the two legs independently // reported it twice. Reconciliation is PER INCIDENT, on the report id the durable frame carries β€” the // browser and the (service) worker share this one implementation. @@ -726,7 +726,7 @@ describe('createCoexistence β€” capture + marker recovery', () => { expect(seen?.markers.list().map((m) => m.request.id)).toEqual(['inc1']); }); - // R2-1: an explicit `bundleStore` override BYPASSES coexistence, so its blobs live in a store that is + // an explicit `bundleStore` override BYPASSES coexistence, so its blobs live in a store that is // stable across launches while the incident's marker sits in this dead sibling's namespace. The launch // reconciles that store through `reconcileOwnQueue`; whatever it settles with is withheld from the // marker leg alongside the coexisting queue's own ids. diff --git a/packages/browser-utils/src/coexistence.ts b/packages/browser-utils/src/coexistence.ts index e6b2559b..0db5d672 100644 --- a/packages/browser-utils/src/coexistence.ts +++ b/packages/browser-utils/src/coexistence.ts @@ -25,8 +25,7 @@ import { import { recoverSiblingBundleQueue } from './recover-dead-instances'; import { createWebLockLiveness, type LockManagerLike } from './web-lock-liveness'; -// The browser/worker multi-instance IndexedDB coexistence root (docs/design/browser-multi-instance- -// coexistence.md). IndexedDB is origin-scoped, so N tabs + the page's workers all share it; this gives each +// The browser/worker multi-instance IndexedDB coexistence root. IndexedDB is origin-scoped, so N tabs + the page's workers all share it; this gives each // launch its OWN per-instance namespace (a `"/"` key prefix) inside per-APP-TOKEN databases, has // it hold ONE Web Lock for its lifetime (liveness), and β€” after launch β€” recovers ONLY DEAD siblings' // leftover data (under each sibling's lock so concurrent peers don't double-recover). It covers, per @@ -216,7 +215,7 @@ export function createCoexistence(options: CoexistenceOptions): Coexistence { // directly (no re-persist into our queue) + run its capture/marker recovery. Per-sibling failures are // isolated so one bad sibling doesn't abort the others. // - // SEV1 (recovery double-upload): those two legs can both hold the SAME incident. `client.ts`'s + // (recovery double-upload): those two legs can both hold the SAME incident. `client.ts`'s // submitReport writes an incident's report marker before assembly and clears it only once the upload // SETTLES, while the durable queue stages the assembled bundle before that upload β€” a tab/worker // killed inside that window leaves both. Core's `createMarkerAwareBundleReplay` reconciles them PER diff --git a/packages/browser-utils/src/fetch-transport.ts b/packages/browser-utils/src/fetch-transport.ts index 4d53ce40..12e3a244 100644 --- a/packages/browser-utils/src/fetch-transport.ts +++ b/packages/browser-utils/src/fetch-transport.ts @@ -1,6 +1,6 @@ import type { HttpRequestOptions, HttpResponse, HttpTransport } from '@bugsee/core'; -// The browser implementation of core's HttpTransport primitive (design Β§7.5) β€” the fetch analog of +// The browser implementation of core's HttpTransport primitive β€” the fetch analog of // node-utils `httpRequest`. Wraps the platform `fetch`; non-2xx statuses RESOLVE (the caller maps // status β†’ outcome), only network errors and timeouts reject. Unlike the node transport it does NOT // touch Accept-Encoding or decompress: the browser owns content negotiation and decodes the body diff --git a/packages/browser-utils/src/idb-chunk-backend.test.ts b/packages/browser-utils/src/idb-chunk-backend.test.ts index b28a6f66..3407c6e7 100644 --- a/packages/browser-utils/src/idb-chunk-backend.test.ts +++ b/packages/browser-utils/src/idb-chunk-backend.test.ts @@ -90,7 +90,7 @@ describe('createIdbChunkBackend β€” backend contract', () => { b.openPart(ref(5, 1), 2000); b.appendEntry(ref(5, 1), rec('c')); const snap = b.snapshot([ - { ref: ref(5, 0), count: 1 }, // only the first record of part 0 + { ref: ref(5, 0), count: 1 }, // only the first record of { ref: ref(5, 1), count: 1 }, ]); b.appendEntry(ref(5, 0), rec('after')); // captured after the snapshot @@ -319,10 +319,10 @@ describe('createIdbChunkCaptureStore β€” chunk store over IndexedDB', () => { it('evicts parts outside the recording window on tick', async () => { const store = mk(keyed(), { maxRecordingTimeMs: 2000 }); - store.add(rec('old', 'log', 10_000)); // part 0 - store.tick(11_000); // part 0 closes @11000 - store.add(rec('new', 'log', 11_000)); // part 1 - store.tick(15_000); // cutting 12000 β†’ part 0 evicted + store.add(rec('old', 'log', 10_000)); + store.tick(11_000); // closes @11000 + store.add(rec('new', 'log', 11_000)); + store.tick(15_000); // cutting 12000 β†’ evicted expect((await store.snapshot().drainAll()).get('log')?.map((r) => r.serialized)).toEqual([ 'new', ]); @@ -370,7 +370,7 @@ describe('createIdbChunkCaptureStore β€” chunk store over IndexedDB', () => { generation: 42, }); store1.add(rec('before-reload', 'log', 10_000)); - store1.tick(11_000); // close part 0 so its data + meta are durable + store1.tick(11_000); // close so its data + meta are durable await store1.snapshot().drainAll(); // drain the write queue // A fresh backend over the SAME database + generation, preserving prior chunks, reads them back. diff --git a/packages/browser-utils/src/idb-chunk-backend.ts b/packages/browser-utils/src/idb-chunk-backend.ts index c57594e1..b97c95d4 100644 --- a/packages/browser-utils/src/idb-chunk-backend.ts +++ b/packages/browser-utils/src/idb-chunk-backend.ts @@ -43,7 +43,7 @@ const dataKey = (gen: number, chunk: number, seq: number): string => // so a generation wider than GEN_PAD (β‰₯1e13 ms / an injected large id) is not truncated. const genOfMetaKey = (key: string): number => Number(key.slice(2, key.indexOf('/', 2))); // The chunk number embedded in a meta key `m//`. The KEY survives a corrupt VALUE, so this is -// how a chunk whose meta failed to parse keeps its identity (Wave 6.5). +// how a chunk whose meta failed to parse keeps its identity. const numberOfMetaKey = (key: string): number | undefined => { const n = Number(key.slice(key.indexOf('/', 2) + 1)); return Number.isInteger(n) ? n : undefined; @@ -64,7 +64,7 @@ interface StoredData { const enc = (value: StoredMeta | StoredData): Uint8Array => new TextEncoder().encode(JSON.stringify(value)); /** - * Decode one stored record, or `undefined` if it is unreadable (Wave 6.5). + * Decode one stored record, or `undefined` if it is unreadable. * * A bare `JSON.parse` here made ONE corrupt value reject `listParts()` / `drainAll()` for the whole * generation, taking every intact record with it β€” and permanently: capture-recovery catches per @@ -109,7 +109,7 @@ export interface IdbChunkBackendOptions { /** Failure sink for the async write queue. Default no-op. */ onError?: (error: unknown) => void; /** - * Max writes allowed to wait for a stalled store before the OLDEST are given up (Wave 6.3). Default 2048. + * Max writes allowed to wait for a stalled store before the OLDEST are given up. Default 2048. * * A stalled IndexedDB used to accept everything: one write in flight, the rest retained live in the * pending chain (measured: 20,000 entries Γ— 10 KB β†’ ~200 MB, with `add()` still returning success). This @@ -124,7 +124,7 @@ export interface IdbChunkBackendOptions { */ maxPendingBytes?: number; /** - * Consecutive write failures after which the backend stops ATTEMPTING (Wave 6.3). Default 16. + * Consecutive write failures after which the backend stops ATTEMPTING. Default 16. * * A store rejecting every put otherwise costs one transaction, one rejected promise and one `onError` * per captured entry β€” measured at 5001 for 5001 entries. It re-probes periodically, so a transient @@ -143,7 +143,7 @@ export function createIdbChunkBackend( const generation = options.generation; const onError = options.onError ?? ((): void => {}); - // The in-order async write queue (Wave 6.3). An explicit array rather than a promise chain, because a + // The in-order async write queue. An explicit array rather than a promise chain, because a // chain can only be APPENDED to: with `queue = queue.then(op)` there is no way to count what is waiting, // and no way to give up the oldest work when the store stops draining. Both are required here. // @@ -433,7 +433,7 @@ export function createIdbChunkBackend( return readMetas(gen); }, - // Wave 6.2 β€” the commit seam the page-hide hook awaits. Chaining on `queue` is the whole + // the commit seam the page-hide hook awaits. Chaining on `queue` is the whole // implementation: every write is already a link in it, so waiting for the tail waits for all of them. // It resolves rather than rejects β€” each link already routes its own failure to `onError`, and a page // that is going away can do nothing with a rejection anyway. diff --git a/packages/browser-utils/src/index.ts b/packages/browser-utils/src/index.ts index c8815f49..fe3c3490 100644 --- a/packages/browser-utils/src/index.ts +++ b/packages/browser-utils/src/index.ts @@ -1,7 +1,7 @@ // @bugsee/browser-utils // Browser runtime primitives shared by the browser / web-worker / service-worker tiers: // the fetch HttpTransport + IndexedDB-backed durable stores (DOM instrumentation β€” pending). -// Tier 3. See docs/design/sdk-design.md Β§5 and docs/implementation-standards.md. +// and. export { type Coexistence, type CoexistenceOptions, diff --git a/packages/browser-utils/src/instance-coexistence.ts b/packages/browser-utils/src/instance-coexistence.ts index da2dc6ea..f6774632 100644 --- a/packages/browser-utils/src/instance-coexistence.ts +++ b/packages/browser-utils/src/instance-coexistence.ts @@ -1,8 +1,7 @@ import { randomId } from '@bugsee/util'; import type { AsyncBlobStore, AsyncKeyedStore } from './idb'; -// Per-instance IndexedDB namespacing for browser/worker multi-instance coexistence (docs/design/ -// browser-multi-instance-coexistence.md, BD1/BD5). Several instances on one origin (N tabs, page + workers) +// Per-instance IndexedDB namespacing for browser/worker multi-instance coexistence. Several instances on one origin (N tabs, page + workers) // share the origin's IndexedDB; each writes its durable data under its OWN `"/"` key prefix inside a // per-APP-TOKEN database, so they coexist without cross-contamination. The recovery coordinator reads the shared // store to find dead siblings (the prefixes ARE the instance registry β€” no separate registry store needed). diff --git a/packages/browser-utils/src/web-lock-liveness.ts b/packages/browser-utils/src/web-lock-liveness.ts index e680c759..6bf1cb60 100644 --- a/packages/browser-utils/src/web-lock-liveness.ts +++ b/packages/browser-utils/src/web-lock-liveness.ts @@ -1,5 +1,4 @@ -// Web Locks liveness for browser/worker multi-instance IndexedDB coexistence (docs/design/ -// browser-multi-instance-coexistence.md, BD2/BD3) β€” the browser analog of node's pid-probe + heartbeat, but +// Web Locks liveness for browser/worker multi-instance IndexedDB coexistence: the browser analog of node's pid-probe + heartbeat, but // STRICTLY better: a lock held via `navigator.locks` is auto-released the instant its realm dies (tab close / // crash / worker terminate), so there is no staleness window and no PID-reuse ambiguity. The same lock doubles // as the recovery CLAIM + serializer: a dead sibling is recovered while HOLDING its lock, so a concurrent peer diff --git a/packages/browser-utils/vitest.config.ts b/packages/browser-utils/vitest.config.ts index ebbb168c..f4746e85 100644 --- a/packages/browser-utils/vitest.config.ts +++ b/packages/browser-utils/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. // environment 'node': browser code is tested via injected seams (fake fetch/window), not a DOM env β€” // the same injection-first discipline as the node tier. (B5's IndexedDB store adds a fake-indexeddb // setupFiles, still under 'node'.) diff --git a/packages/browser/src/call-site-jsc.test.ts b/packages/browser/src/call-site-jsc.test.ts index 341cf11b..79a1c2bb 100644 --- a/packages/browser/src/call-site-jsc.test.ts +++ b/packages/browser/src/call-site-jsc.test.ts @@ -3,7 +3,7 @@ import { strFromU8, unzipSync } from '@bugsee/util'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { parseStack } from './stack'; -// R3P-1: a non-Error throwable's synthetic stack must start at the APPLICATION on JavaScriptCore too. These +// a non-Error throwable's synthetic stack must start at the APPLICATION on JavaScriptCore too. These // strings were recorded from the macOS `jsc` shell (the engine under Safari and every iOS WebView), // 2026-10-12, paths shortened: there is no `at`, frames are `function@location`. // - with Error.captureStackTrace: the boundary and everything above it are already dropped; diff --git a/packages/browser/src/component-name.ts b/packages/browser/src/component-name.ts index dccad4e0..72c17895 100644 --- a/packages/browser/src/component-name.ts +++ b/packages/browser/src/component-name.ts @@ -1,5 +1,5 @@ -// Component-name attribution (frontend-adapters depth pass D2). A build plugin (@bugsee/babel-plugin- -// component-annotate, D3) stamps each host DOM element with `data-bugsee-component=""` β€” the +// Component-name attribution. A build plugin (@bugsee/babel-plugin- +// component-annotate) stamps each host DOM element with `data-bugsee-component=""` β€” the // name of the framework component that rendered it, preserved through minification (the plugin emits the // literal name). At capture time we resolve the nearest annotated ancestor of an interaction/input target, // so a click/error attributes to a COMPONENT name, not just a DOM selector. Foundation-level (benefits any diff --git a/packages/browser/src/detection-providers.ts b/packages/browser/src/detection-providers.ts index 42a6db47..a11cc0b8 100644 --- a/packages/browser/src/detection-providers.ts +++ b/packages/browser/src/detection-providers.ts @@ -10,7 +10,7 @@ import { import { BugseeOption } from '@bugsee/protocol'; import { parseStack } from './stack'; -// Browser crash/error detection providers (design Β§3.2 globalErrorInterceptor on the browser, Β§8.5 +// Browser crash/error detection providers (globalErrorInterceptor on the browser, // mechanisms). Each subscribes to a window event and submits a ReportingRequest via // DetectionProviderBase: `error` β†’ crash (mechanism 'uncaught'), `unhandledrejection` β†’ error // (mechanism 'unhandledrejection'). They DETECT + submit only; flush policy lives in launch(). The diff --git a/packages/browser/src/environment.test.ts b/packages/browser/src/environment.test.ts index 365fdfa8..34732d23 100644 --- a/packages/browser/src/environment.test.ts +++ b/packages/browser/src/environment.test.ts @@ -46,7 +46,7 @@ describe('buildBrowserEnvironment β€” platform is the OS, not the runtime', () = }); it('reports the host OS and its version, not the sandbox tag and the user agent', () => { - // The defect this replaced (samples/FINDINGS.md F-X20): the web tier sent `type: 'web'` and the + // The defect this replaced: the web tier sent `type: 'web'` and the // WHOLE user-agent string as `version` β€” the field the backend indexes as `os_version` β€” so a // browser session was the only kind of Bugsee session that named no operating system at all. const env = buildBrowserEnvironment({ sdkVersion: '1.0.0' }, fullProbe); diff --git a/packages/browser/src/environment.ts b/packages/browser/src/environment.ts index 4634161f..94eedfa8 100644 --- a/packages/browser/src/environment.ts +++ b/packages/browser/src/environment.ts @@ -2,17 +2,17 @@ import { serviceToken } from '@bugsee/core'; import { type EnvironmentEnvelope, optionsToWire } from '@bugsee/protocol'; import { detectBrowser, detectOs, UNKNOWN_OS } from './user-agent'; -// Builds the Β§8.6 environment envelope for the browser from navigator/screen/window/Intl. System reads +// Builds the environment envelope for the browser from navigator/screen/window/Intl. System reads // go through an injectable BrowserProbe so the mapping is testable deterministically (the DOM globals // are stubbed in tests); realBrowserProbe is the default. Pure given (input, probe) β€” persistence // (device_id) is the caller's concern. Canonical (dotted) option keys are translated to colon wire form -// for sdk.options here, because the server treats dots as nested-document paths (Β§2.4). +// for sdk.options here, because the server treats dots as nested-document paths. // // The OS and the browser are identified IN THE BROWSER (see ./user-agent.ts), from // `navigator.userAgentData.platform` where the browser declares it and a user-agent parse otherwise. // This used to be deferred to the backend β€” `platform.type: 'web'` with the raw UA string as // `platform.version` β€” which left a browser session as the only kind naming no OS at all, in the very -// field the backend indexes as `os_version` (samples/FINDINGS.md F-X20). Doing it here also costs the +// field the backend indexes as `os_version`. Doing it here also costs the // server nothing per report. hardware.model / manufacturer stay omitted: no browser exposes them. // deviceMemory + hardwareConcurrency are non-standard / not-everywhere, so their readers are optional // and the builder omits absent fields. diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index c93e7470..53584611 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -1,11 +1,11 @@ // @bugsee/browser // Browser platform: fetch transport, IndexedDB storage, window error handlers, stack-trace parser, -// and the launch() composition root. Tier 2. See docs/design/sdk-design.md Β§5. +// and the launch() composition root. -// The built-in network redaction, for composing with a custom `setNetworkEventFilter` (review R2D-4: the +// The built-in network redaction, for composing with a custom `setNetworkEventFilter` (review: the // directly imported platform package exposes the same value surface as the umbrella entries). export { type DefaultNetworkSanitizer, getDefaultNetworkSanitizer } from '@bugsee/capture'; -// Public report-attachment input type (T3), so `UploadOptions.attachments` / `ReportHandler.before` code types without reaching into core. +// Public report-attachment input type, so `UploadOptions.attachments` / `ReportHandler.before` code types without reaching into core. export type { ReportAttachmentInput } from '@bugsee/core'; // Lifecycle events: the event-name constants + listener type for `setLifecycleEventsListener`. // Helper form of writing `report.userAttachments` from `ReportHandler.before` (Android `Report.addAttachment`). diff --git a/packages/browser/src/input-source.test.ts b/packages/browser/src/input-source.test.ts index badb3316..4e69e215 100644 --- a/packages/browser/src/input-source.test.ts +++ b/packages/browser/src/input-source.test.ts @@ -525,7 +525,7 @@ describe('createBrowserInputSource', () => { target.emit('pointerdown', pen()); target.emit('pointermove', pen()); // identical to the begin target.emit('pointermove', pen({ clientX: 12.2 })); // rounds to the same x - target.emit('pointermove', pen({ tiltX: 30 })); // L3 angles win, so nothing recorded changed + target.emit('pointermove', pen({ tiltX: 30 })); // angles win, so nothing recorded changed target.emit('pointermove', pen({ pressure: 0.6 })); // force changed target.emit('pointermove', pen({ pressure: 0.6 })); // same as the previous MOVE expect(events.map((e) => [e.type, e.force])).toStrictEqual([ diff --git a/packages/browser/src/input-source.ts b/packages/browser/src/input-source.ts index dd1b8e1e..191a8156 100644 --- a/packages/browser/src/input-source.ts +++ b/packages/browser/src/input-source.ts @@ -126,7 +126,7 @@ export interface TargetDescriptor { text?: string; /** A one-level CSS-ish selector hint (tag#id.class). */ selector?: string; - /** The nearest annotated framework component name (`data-bugsee-component`, D2) β€” not PII (the component + /** The nearest annotated framework component name (`data-bugsee-component`) β€” not PII (the component * name, never a value), so it is reported even for a masked target. */ component?: string; /** True when the element was fully masked (sensitive field / mask-selector subtree); no value-bearing fields. */ @@ -183,7 +183,7 @@ export function describeTarget(node: unknown, maskSelector: string): TargetDescr const el = (node ?? undefined) as ElementLike | undefined; const tag = typeof el?.tagName === 'string' ? el.tagName.toLowerCase() : undefined; if (el === undefined || tag === undefined) return {}; - const component = componentNameFromElement(node); // D2: nearest data-bugsee-component (not PII) + const component = componentNameFromElement(node); // nearest data-bugsee-component (not PII) const type = typeof el.type === 'string' ? el.type : undefined; // Two independent reasons to mask: the app marked this subtree hidden, or the field holds secret // content by the SHARED definition (@bugsee/core's SENSITIVE_INPUT_MATCHERS β€” the same list @@ -749,7 +749,7 @@ class BrowserInputSource extends InterceptorBase<{ input: InputEventDetail }> { /** * Re-point the source at a NEW client's blackout gate. The source is carrier-shared and outlives the * client that created it, so a relaunch must hand it the new client's gate; otherwise it keeps reading - * the dead client's state (R2V-3). Pending wheel deltas are dropped; a live subscription is moved. + * the dead client's state. Pending wheel deltas are dropped; a live subscription is moved. */ bindBlackout(gate: BlackoutGate | undefined): void { this.#offBlackout?.(); diff --git a/packages/browser/src/interaction-source.ts b/packages/browser/src/interaction-source.ts index daf21e7c..cb1389b9 100644 --- a/packages/browser/src/interaction-source.ts +++ b/packages/browser/src/interaction-source.ts @@ -2,7 +2,7 @@ import { type BlackoutGate, type Interceptor, InterceptorBase } from '@bugsee/co import { componentNameFromElement } from './component-name'; import { describeTarget } from './input-source'; -// The browser INTERACTION SOURCE (frontend-adapters F4 / D6) β€” an extensible, listenable source of +// The browser INTERACTION SOURCE β€” an extensible, listenable source of // discrete user interactions derived from the Event Timing API. A `PerformanceObserver({type:'event'})` // observes pointer/keyboard events; events of one interaction share an `interactionId` (the INP unit), so // we emit ONE `interact` per interaction whose latency passes the threshold. @bugsee/performance turns @@ -18,7 +18,7 @@ export interface InteractionDetail { /** A PII-safe selector label for the target (`describeTarget` β€” e.g. 'button#submit'); omitted when * the target is gone/not an element. A masked target collapses to its tag only. */ target?: string; - /** The nearest annotated component name for the target (`data-bugsee-component`, D2); omitted when the + /** The nearest annotated component name for the target (`data-bugsee-component`); omitted when the * app is not annotated (no build plugin) or the target has no annotated ancestor. */ component?: string; /** The interaction latency (ms) β€” the Event Timing entry duration (the INP contribution). */ @@ -163,7 +163,7 @@ class BrowserInteractionSource extends InterceptorBase<{ interact: InteractionDe // Blackout: the id is still consumed above, so a buffered entry cannot be replayed after it ends. if (this.#blackout?.isActive() === true || this.#inBlackoutSpan(entry)) continue; const target = targetLabel(entry.target, this.#mask); - const component = componentNameFromElement(entry.target); // D2: nearest data-bugsee-component + const component = componentNameFromElement(entry.target); // nearest data-bugsee-component this.emit('interact', { interactionType: entry.name, ...(target !== undefined ? { target } : {}), diff --git a/packages/browser/src/keycodes.ts b/packages/browser/src/keycodes.ts index 48307721..9f53e8ed 100644 --- a/packages/browser/src/keycodes.ts +++ b/packages/browser/src/keycodes.ts @@ -4,7 +4,7 @@ // carries `keyCode: int` and `metaState: int`, iOS's `BGSInputEvent` the same, and the viewer reads those. // The web tier used to emit a `key` STRING and four bespoke booleans instead, so a web recording and a // mobile one described the same key press in two unrelated vocabularies and nothing downstream could read -// both (docs/review/OPEN-FINDINGS.md R3-12). +// both. // // Every number below was read out of the real `android.view.KeyEvent` in `android.jar` with `javap`, not // recalled: this file is a wire contract with another SDK, and a plausible-looking wrong constant would diff --git a/packages/browser/src/launch-options.invalid.test.ts b/packages/browser/src/launch-options.invalid.test.ts index c53adcdd..90f87018 100644 --- a/packages/browser/src/launch-options.invalid.test.ts +++ b/packages/browser/src/launch-options.invalid.test.ts @@ -6,11 +6,11 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import type { BrowserProbe } from './environment'; import { type Bugsee, type BugseeLaunchOptions, launch } from './launch'; -// R2P-4 / R2P-5. Every browser-owned boolean launch option, every kind of invalid value. Read by plain +// Every browser-owned boolean launch option, every kind of invalid value. Read by plain // truthiness these used to be wrong in BOTH directions: `persistUserIdentifier: 'false'` persisted PII, // `captureInteractions: 0` kept capturing input. The rules, per option: // captureInteractions, captureViewHierarchy (default ON, capture) -> invalid = OFF (fail-closed) + onError -// startInBlackout (default OFF, but ON is the safe side) -> invalid = ON (fail-on) + onError (R3V-2) +// startInBlackout (default OFF, but ON is the safe side) -> invalid = ON (fail-on) + onError // persistUserIdentifier, report* UI flags (default OFF) -> only boolean true turns on const probe: BrowserProbe = { @@ -157,7 +157,7 @@ describe('browser boolean launch options: invalid values (R2P-4 / R2P-5)', () => expect(absent.isBlackout()).toBe(false); }); - // R3V-3/R4V-2: `replay` is ON by default and collects data. A scalar that is not a boolean must not THROW + // `replay` is ON by default and collects data. A scalar that is not a boolean must not THROW // (`null` did); it resolves to OFF (fail closed) and is reported. it.each([ null, diff --git a/packages/browser/src/launch-stop-recovery.test.ts b/packages/browser/src/launch-stop-recovery.test.ts index 4ed7af2a..aa84e0ed 100644 --- a/packages/browser/src/launch-stop-recovery.test.ts +++ b/packages/browser/src/launch-stop-recovery.test.ts @@ -7,7 +7,7 @@ import { resetBrowserDeviceIdCache } from './device-id'; import type { BrowserProbe } from './environment'; import { type BugseeLaunchOptions, launch } from './launch'; -// L7: stop() must not release the instance lock / IndexedDB while the launch-time recovery is still +// stop() must not release the instance lock / IndexedDB while the launch-time recovery is still // running (it may be reading or writing), and must release once it has settled. vi.mock('@bugsee/replay', () => ({ registerReplay: vi.fn() })); vi.mock('@bugsee/core', async (importOriginal) => { diff --git a/packages/browser/src/launch-stop-shared-drain.test.ts b/packages/browser/src/launch-stop-shared-drain.test.ts index de8dd1c8..19ef353b 100644 --- a/packages/browser/src/launch-stop-shared-drain.test.ts +++ b/packages/browser/src/launch-stop-shared-drain.test.ts @@ -9,10 +9,10 @@ import { type Bugsee, type BugseeLaunchOptions, launch } from './launch'; vi.mock('@bugsee/replay', () => ({ registerReplay: vi.fn() })); -// Review round 3 (H1), browser half: R3L-1. The instance lock and IndexedDB handles are released when the ONE +// Browser half: the instance lock and IndexedDB handles are released when the ONE // shared real stop drain settles true, from whichever stop() observes it (exactly once), even after a stop(timeout) // gave up. -// The coexistence handle is real except that, on demand, its release() throws AFTER doing its work (R4L-4). +// The coexistence handle is real except that, on demand, its release() throws AFTER doing its work. let releaseThrows = false; vi.mock('@bugsee/browser-utils', async (importOriginal) => { const actual = await importOriginal(); @@ -203,7 +203,7 @@ describe('R3L-1 β€” the lock is released by whichever stop() observes the shared client.notify('cannot be delivered'); expect(await client.stop()).toBe(false); // The observer hangs off the same promise stop() just returned, and a FALSE drain does no I/O: a few - // microtask turns run every continuation it could have (no wall-clock sleep, R4H-8). + // microtask turns run every continuation it could have (no wall-clock sleep). await microtasks(); expect(locks.held.size).toBe(1); }); diff --git a/packages/browser/src/launch.test.ts b/packages/browser/src/launch.test.ts index c4f553f8..7ba425b0 100644 --- a/packages/browser/src/launch.test.ts +++ b/packages/browser/src/launch.test.ts @@ -1105,7 +1105,7 @@ describe('launch', () => { } ).environment; // The OS, derived in-browser β€” `platform` is no longer the sandbox tag with a UA string for a - // version, and the runtime/browser identity has its own blocks (F-X20). + // version, and the runtime/browser identity has its own blocks. expect(env.platform.type).toBe('macos'); expect(env.platform.version).toBe('10.15.7'); expect(env.browser).toEqual({ type: 'Chrome', version: '119.0.0.0' }); @@ -1348,7 +1348,7 @@ describe('launch', () => { }); }); -// --- capture recovery (BR2): a prior run's detected incident is rebuilt + delivered next launch ------- +// --- capture recovery: a prior run's detected incident is rebuilt + delivered next launch ------- // A transport that satisfies the upload path AND records every signed-PUT body (the delivered bundle). function recordingTransport() { @@ -1480,13 +1480,13 @@ describe('launch β€” capture recovery (multi-instance)', () => { await vi.waitFor(async () => expect((await siblingGenerations(idb, 'deadsib')).has(500)).toBe(false), ); - // ...and the LIVE sibling's marker + capture are completely untouched (the SEV1 fix). + // ...and the LIVE sibling's marker + capture are completely untouched (the fix). expect(await siblingMarkers(idb, 'livesib')).toEqual(['livesib/inc-livesib']); expect((await siblingGenerations(idb, 'livesib')).has(700)).toBe(true); expect(puts).toHaveLength(1); // only the dead sibling was delivered, not the live one }); - // SEV1 (recovery double-upload, confirmed on 4 samples β€” events_count +2 per incident, never +1): the + // (recovery double-upload, confirmed on 4 samples β€” events_count +2 per incident, never +1): the // process can die AFTER a crash bundle reaches the durable bundle queue but BEFORE the upload settles β€” // client.ts's submitReport clears the report marker only once that upload settles β€” leaving a dead // sibling with BOTH a pending bundle AND its incident's still-present marker for the SAME crash. Both @@ -1576,7 +1576,7 @@ describe('launch β€” capture recovery (multi-instance)', () => { // legs await the SAME call, and when that call is the failing one neither reaches /v2/issues. Deleting // the guard changed nothing observable in this fixture β€” not the session count, not the staged-blob // keys, not the marker state β€” with a failing session OR a succeeding one, so no assertion over it - // could have worked. Before round 6 the test failed on the mutation only incidentally, through the + // could have worked. Before the test failed on the mutation only incidentally, through the // blob deletion that a 403's PERMANENT verdict caused; making control-plane failures retryable removed // that side effect and left the test unable to fail. // @@ -1588,7 +1588,7 @@ describe('launch β€” capture recovery (multi-instance)', () => { // `node/src/launch.test.ts` (3). Deleting the guard fails 15 tests across those four packages β€” // counted by deleting it, not estimated. - // R2-1. An explicit `bundleStore` BYPASSES coexistence: it is the integrator's own store, stable across + // An explicit `bundleStore` BYPASSES coexistence: it is the integrator's own store, stable across // page loads, so it holds the previous session's staged bundle while that incident's marker still sits in // the dead session's IndexedDB namespace. Replaying it blind and then rebuilding from the marker uploaded // the incident twice, with DIFFERING payloads (the staged frame vs a freshly assembled zip). @@ -1619,9 +1619,9 @@ describe('launch β€” capture recovery (multi-instance)', () => { await vi.waitFor(async () => expect(await siblingMarkers(idb, 'deadsib')).toEqual([])); // retired }); - // R5-4. The SAME wiring under a RETRYABLE failure β€” the only condition under which routing a recovered + // The SAME wiring under a RETRYABLE failure β€” the only condition under which routing a recovered // blob back through the durable queue is visible. `pipeline: baseUploadPipeline` β†’ - // `pipeline: durable ?? baseUploadPipeline` survived every suite in three review rounds because every + // `pipeline: durable ?? baseUploadPipeline` survived every suite across several review passes because every // platform test used an accepting transport: the second copy the durable queue stages on the way IN is // removed the instant the upload succeeds, so the duplicate never outlives the pass. On a 503 it does β€” // and the next page load uploads that incident twice, from two blobs, with differing payloads. @@ -1661,7 +1661,7 @@ describe('launch β€” capture recovery (multi-instance)', () => { expect(await siblingMarkers(idb, 'deadsib')).toEqual(['deadsib/inc-deadsib']); // The 5 s retry backoff is deliberately still running: bound the teardown rather than wait it out. await client.stop(0); - // A repeat stop() answers for the REAL drain (R3L-1/2), which is the retry backoff described above: the shared + // A repeat stop() answers for the REAL drain, which is the retry backoff described above: the shared // afterEach must not wait for it, so this already-stopped client leaves the tracked list. clients.splice(clients.indexOf(client), 1); }); @@ -2092,7 +2092,7 @@ describe('launch β€” session replay (lazy)', () => { }); it('forwards the launch onError so a dropped masking selector is actually reported', async () => { - // Wave 1.4. @bugsee/replay reports an invalid selector it had to drop; that report needs a sink on the + // @bugsee/replay reports an invalid selector it had to drop; that report needs a sink on the // production path, or the fix is only reachable from replay's own tests. const onError = vi.fn(); await launchTracked('tok', domReplayOptions({ replay: { blockSelector: 'div[' }, onError })); @@ -2106,7 +2106,7 @@ describe('launch β€” session replay (lazy)', () => { expect(registerReplay.mock.calls[0]?.[2]).toEqual({ blackout: expect.any(Object) }); // no options object }); - // R4V-2 (product decision): `replay` collects data and is ON by default, so an invalid scalar is OFF (fail + // (product decision): `replay` collects data and is ON by default, so an invalid scalar is OFF (fail // closed) and reported. It never throws (`null` once did). Masking stays fail-closed regardless. it.each([ null, @@ -2128,7 +2128,7 @@ describe('launch β€” session replay (lazy)', () => { ).toHaveLength(1); }); - // R4V-2: canvas pixels are unmasked, so ONLY boolean `true` or a plain object enables the add-on. + // canvas pixels are unmasked, so ONLY boolean `true` or a plain object enables the add-on. it.each([ 0, null, @@ -2148,7 +2148,7 @@ describe('launch β€” session replay (lazy)', () => { ).toHaveLength(1); }); - // R5V-4: the canvas warning called the app's sink unguarded, so a sink that rethrows made launch() reject. + // the canvas warning called the app's sink unguarded, so a sink that rethrows made launch() reject. it('a rethrowing onError cannot break launch over an invalid replay.canvas (the add-on stays OFF)', async () => { const onError = vi.fn(() => { throw new Error('the host sink rethrows'); @@ -2342,11 +2342,11 @@ describe('launch β€” session replay (lazy)', () => { }); }); -// R1 (docs/design/electron-renderer-incident-convergence.md Β§4.2): an injectable trigger pipeline. +// an injectable trigger pipeline. // // Symmetric with the `captureStore` seam that already exists. @bugsee/electron needs it because a renderer // must FORWARD its incidents to the main process rather than assembling a bundle from its streaming store β€” -// which yields nothing β€” and uploading under a foreign session id (docs/review/electron.md SEV1 #2). +// which yields nothing β€” and uploading under a foreign session id. // @bugsee/webview solves the same problem, but only because it composes its client directly via // createClient; a full browser SDK cannot do that without duplicating this entire module. describe('launchCore β€” triggerPipeline seam', () => { @@ -2466,7 +2466,7 @@ describe('flush on page hide (Wave 6.2)', () => { }); }); -// R2D-2: RelaunchedAfterCrash is told only when recovery finds a dead sibling's unsettled incident marker. +// RelaunchedAfterCrash is told only when recovery finds a dead sibling's unsettled incident marker. describe('launch β€” RelaunchedAfterCrash needs evidence (R2D-2)', () => { const run = async (seed: (idb: IDBFactory, locks: LockManagerLike) => Promise) => { const idb = new IDBFactory(); diff --git a/packages/browser/src/launch.ts b/packages/browser/src/launch.ts index 993305ee..55e70203 100644 --- a/packages/browser/src/launch.ts +++ b/packages/browser/src/launch.ts @@ -97,7 +97,7 @@ import { createUserIdentifierStore } from './user-identifier-store'; import { createBrowserViewportSource } from './viewport-source'; import { createViewtreeSnapshotSource } from './viewtree'; -// @bugsee/browser launch() β€” the browser composition root (design Β§7.1), the fetch/DOM analog of node's +// @bugsee/browser launch() β€” the browser composition root, the fetch/DOM analog of node's // launch(). It assembles the runtime-agnostic kernel (createClient) with the browser's platform pieces // and the shared capture layer, then starts it: // fetch transport β†’ BugseeApi + BundleUploader β†’ UploadPipeline ─┐ @@ -107,16 +107,16 @@ import { createViewtreeSnapshotSource } from './viewtree'; // Β· system events (process_started + pagehide) // detection: window error (crash) Β· unhandledrejection (error) // Unlike node there is no process.exit window and no signal, so the last reliable moment is a page-hide: -// `pagehide` / `visibilitychange`β†’hidden commit the capture store and drain the client (Wave 6.2). The +// `pagehide` / `visibilitychange`β†’hidden commit the capture store and drain the client. The // returned client IS the public surface. const SDK_VERSION = BUGSEE_SDK_VERSION; -// How long the page-hide flush waits for the client to drain (Wave 6.2). Short on purpose: a hiding page +// How long the page-hide flush waits for the client to drain. Short on purpose: a hiding page // has no guaranteed time at all, so this bounds the attempt rather than promising it completes β€” what makes // the data safe is that it becomes DURABLE, and the next page load recovers it. const PAGE_HIDE_FLUSH_MS = 1000; const DEFAULT_ENDPOINT = 'https://api.bugsee.com'; -// Browser/edge capture buffer ceiling (design Β§966: 10 MB on browser, 50 MB on Node). +// Browser/edge capture buffer ceiling (10 MB on browser, 50 MB on Node). const DEFAULT_MAX_DATA_SIZE_MB = 10; // Browser launch-option definitions = the shared cross-runtime set plus the browser's own maxDataSize @@ -146,11 +146,11 @@ const BROWSER_OPTION_DEFINITIONS: readonly OptionDefinition[] = [ friendly: 'startInBlackout', key: BugseeOption.CaptureStartInBlackout, default: false, - // Safe side is ON: an invalid value (`'true'`, `1`, `null`) means DARK, never "not dark" (R3V-2). + // Safe side is ON: an invalid value (`'true'`, `1`, `null`) means DARK, never "not dark". ...FAIL_ON_BOOLEAN_OPTION, }, // Session replay: on by default, `false` opts out, an object customises it. It COLLECTS data, so an invalid - // scalar (`0`, `null`, `''`, `'false'`, ...) is OFF and reported (R4V-2); it must never throw (R3V-3). + // scalar (`0`, `null`, `''`, `'false'`, ...) is OFF and reported; it must never throw. // Masking is fail-closed regardless. { friendly: 'replay', @@ -344,7 +344,7 @@ export interface BugseeLaunchOptions { * text/inputs, block all media) whether or not the option is given. * * `@bugsee/replay` is lazy-`import()`ed, so it is a separate chunk that is fetched only when replay runs - * (design D2/D8): `replay: false` therefore keeps the errors-only bundle genuinely free of it. + * `replay: false` therefore keeps the errors-only bundle genuinely free of it. * * An INVALID value (anything but a boolean or a plain options object: `0`, `null`, `''`, `'false'`, an array) is * treated as OFF and reported through `onError`: replay captures page content, so it never stays on by accident. @@ -400,7 +400,7 @@ export interface BugseeLaunchOptions { * Exists for hosts where this process is not the uploader. `@bugsee/electron` renderers stream their * capture UP to the main process, so a renderer that assembled locally would produce a bundle from a * streaming store that yields nothing, under its own session id, while the main session holding all the - * capture recorded no incident (docs/design/electron-renderer-incident-convergence.md Β§4.2). + * capture recorded no incident. * * Symmetric with `captureStore` above β€” the same shape of seam, for the read side of the same problem. */ @@ -462,7 +462,7 @@ export interface LaunchInternals { /** The internal-error sink (defaults undefined β†’ extensions use their own no-op). */ onError: ((error: unknown) => void) | undefined; /** - * Where the performance controller's active transaction lives (D2 part 2). ALWAYS `undefined` here: + * Where the performance controller's active transaction lives. ALWAYS `undefined` here: * the browser keeps the process-wide single slot, which is correct for its one in-flight * navigation/interaction. Declared REQUIRED rather than optional (R-16) so the choice is stated, not * defaulted β€” a platform that forgets the field fails to typecheck at `wireUmbrella` instead of @@ -541,7 +541,7 @@ async function buildLaunch( const win = options.window ?? window; const deviceId = await resolveBrowserDeviceId(realDeviceIdEnv(options.deviceIdEnv ?? {})); - // Bugsee is a per-process singleton (Β§1497): if a client was already launched (same SDK version on + // Bugsee is a per-process singleton: if a client was already launched (same SDK version on // the process Carrier), warn and return it rather than building a second client / second handler set. const carrier = options.carrier; const alreadyLaunched = getCarrierClient(carrier); @@ -636,7 +636,7 @@ async function buildLaunch( }; const clock = options.clock ?? createSystemClock(); const captureGeneration = clock.wallNow(); - // The report-marker store records detected-incident metadata at incident time (R1) over the coexistence + // The report-marker store records detected-incident metadata at incident time over the coexistence // marker VIEW; built only when recovery is on. The capture VIEW backs the durable chunk store. const captureKeyed = coexistence.captureView; const reportMarkers = @@ -675,7 +675,7 @@ async function buildLaunch( : undefined; // Session replay (lazy). When enabled, a shared fileEncoders map is threaded into the client's report - // assembly (RP5a) β€” @bugsee/replay writes its `replay.bin` encoder into it after the lazy import resolves. + // assembly β€” @bugsee/replay writes its `replay.bin` encoder into it after the lazy import resolves. // ON BY DEFAULT (an opt-OUT): video is Bugsee's headline feature and both mobile SDKs record by default, // so a web integrator who never sets the option gets a session recording too. `replay: false` is the // explicit errors-only path β€” the value that keeps @bugsee/replay out of the loaded bundle. @@ -707,7 +707,7 @@ async function buildLaunch( // relaunch, which builds a new client. carrier: carrier ?? globalThis, // stop() tears down what this tier armed (the page-hide flush, the carrier slot, the liveness lock), so a - // stopped client must not be restarted in place (R2L-3): a new one comes from `launch(appToken)`/`relaunch()`. + // stopped client must not be restarted in place: a new one comes from `launch(appToken)`/`relaunch()`. restartable: false, ...(options.lifecycleEventsListener !== undefined ? { lifecycleEventsListener: options.lifecycleEventsListener } @@ -811,7 +811,7 @@ async function buildLaunch( carrier, ); // The source is carrier-shared and may belong to an earlier (relaunched-away) client: bind it to THIS - // client's blackout gate, or it keeps reading the dead client's state (R2V-3). + // client's blackout gate, or it keeps reading the dead client's state. bindInputBlackout(inputSource, blackout); client.addCaptureProvider(createInputProvider(inputSource, { blackout })); // The FRAME those coordinates belong to, over time β†’ the `video.aux` stream. Without it a consumer has @@ -861,7 +861,7 @@ async function buildLaunch( : {}; const { canvas: canvasOption, ...replayMasking } = replayOptions; // Canvas pixels are recorded UNMASKED, so only boolean `true` or a plain object enables the add-on; anything - // else (`0`, `null`, `''`, `'false'`, an array) is OFF and reported (R4V-2). + // else (`0`, `null`, `''`, `'false'`, an array) is OFF and reported. const canvasIsBag = typeof canvasOption === 'object' && canvasOption !== null && !Array.isArray(canvasOption); const canvasEnabled = canvasOption === true || canvasIsBag; @@ -876,7 +876,7 @@ async function buildLaunch( void import('@bugsee/replay') .then(async (m) => { // Canvas is an opt-in add-on: lazy-`import()` @bugsee/replay-canvas ONLY when replay.canvas is set, - // resolve the rrweb canvas options, and thread them into the recorder (design RPC3). A no-canvas + // resolve the rrweb canvas options, and thread them into the recorder. A no-canvas // replay never loads it. const canvas = canvasEnabled ? (await import('@bugsee/replay-canvas')).createCanvasRecordConfig( @@ -892,7 +892,7 @@ async function buildLaunch( blackout: client.getService(BlackoutToken), ...(canvas !== undefined ? { canvas } : {}), // Give replay's masking resolver a sink: it drops an invalid caller selector rather than letting - // it disable privacy page-wide, and that downgrade must not be silent (Wave 1.4). + // it disable privacy page-wide, and that downgrade must not be silent. ...(options.onError !== undefined ? { onError: options.onError } : {}), }); }) @@ -900,7 +900,7 @@ async function buildLaunch( try { options.onError?.(error); } catch { - // A throwing host sink must never become an unhandled rejection (R4V-10). + // A throwing host sink must never become an unhandled rejection. } }); } @@ -914,7 +914,7 @@ async function buildLaunch( // The deferred event of `startInBlackout`: owed only when this launch raised the blackout (a carried one is // not a new event; an internal hold never announces itself). Core announces it after Launching/Launched when // the client is still launched and dark, and ahead of the Ended of a Launching listener that lifts the blackout - // at once (R3L-7), so the application never hears an Ended without its Started. + // at once, so the application never hears an Ended without its Started. if (startInBlackout && !wasBlackedOut) { asInternalClient(client).deferBlackoutStarted(); } @@ -939,7 +939,7 @@ async function buildLaunch( // the durability β€” kept + retried on failure). The coordinator reads the shared stores directly (no // mirror hydration needed) and hands this callback the sibling's hydrated marker store plus the // incidents its bundle-queue leg already settled with (`skipReportIds`), so one incident is delivered - // once even when the sibling left BOTH a staged bundle and that incident's marker (the SEV1 + // once even when the sibling left BOTH a staged bundle and that incident's marker (the // double-upload β€” reconciled in browser-utils). let recoverySettled = false; let crashSignalled = false; // RelaunchedAfterCrash is told at most once per launch @@ -958,7 +958,7 @@ async function buildLaunch( ...(recoverEnabled ? { recoverReportsForSibling: async ({ captureView, markers, skipReportIds }) => { - // R2D-2: a dead sibling that left an incident marker behind never settled that incident, which is + // a dead sibling that left an incident marker behind never settled that incident, which is // the honest evidence this tier has that the previous run did not end cleanly. (A tab closed // normally after a failed upload leaves one too, so this can over-report; it never invents one.) if (!crashSignalled && markers.list().length > 0 && client.isLaunched()) { @@ -991,7 +991,7 @@ async function buildLaunch( recoverySettled = true; }); - // Flush on page hide (Wave 6.2). The browser is the one runtime with no shutdown hook at all β€” no + // Flush on page hide. The browser is the one runtime with no shutdown hook at all β€” no // `'exit'`, no signal β€” and a mobile browser kills a backgrounded tab with no further callbacks. Until // now the SDK listened to `pagehide` only to RECORD a `process_exiting` event; nothing consumed it, so // "the browser flushes via the pipeline / pagehide" was an intention rather than a code path. @@ -1017,7 +1017,7 @@ async function buildLaunch( ); // The public client. stop() clears the process Carrier slot so a later launch() starts fresh. const stopCore = client.stop; - // WHEN the lock is released (R3L-1): it follows the ONE shared real drain, not any single stop() call. Every + // WHEN the lock is released: it follows the ONE shared real drain, not any single stop() call. Every // stop() leaves a background observer on that drain (a stop(timeout) that gave up still gets its release when // the upload finishes), and the caller of a stop() that sees the drain finish releases too; whoever is first // releases, once. Instances are distinct (their own lock + IndexedDB prefix), so a relaunch needs no guard. @@ -1029,9 +1029,9 @@ async function buildLaunch( } }; // Commit the capture store's queued writes ONCE per stop, however many paths (the caller, the background - // observer) reach this point: closing IndexedDB under its write queue would lose the queue's tail (R2L-10). + // observer) reach this point: closing IndexedDB under its write queue would lose the queue's tail. let storeFlushed: Promise | undefined; - /** A sink that throws must not turn a background step into an unhandled rejection (R4L-4 / R4H-7). */ + /** A sink that throws must not turn a background step into an unhandled rejection. */ const reportSafe = (error: unknown): void => { try { options.onError?.(error); @@ -1063,7 +1063,7 @@ async function buildLaunch( } try { if (wasLaunched) { - // A FRESH flush, not the memoized one (R4L-4): a stop(timeout) that gave up memoized its flush + // A FRESH flush, not the memoized one: a stop(timeout) that gave up memoized its flush // BEFORE the drain finished, and the drain's own writes (deferred chunk deletes) must be durable // before the connections are released. await flushStore(); @@ -1167,7 +1167,7 @@ async function buildLaunch( // recover any leftovers) and the IndexedDB connections ONLY when that is true: the drain // completed (a timed-out drain's upload may still be running here, and another tab that saw this // instance as dead would upload it again), the launch recovery has settled (it may still be - // reading or writing). A killed client is released too (L14): the collector switched it off, so nothing + // reading or writing). A killed client is released too: the collector switched it off, so nothing // of it is in flight. if (drained) { releaseIfSafe(); diff --git a/packages/browser/src/meta-trace.ts b/packages/browser/src/meta-trace.ts index fbbe3524..ac4449a5 100644 --- a/packages/browser/src/meta-trace.ts +++ b/packages/browser/src/meta-trace.ts @@ -1,11 +1,11 @@ import { parseTraceparent } from '@bugsee/capture'; -// Pageload trace continuation from a server-injected `` (frontend-adapters D4). +// Pageload trace continuation from a server-injected ``. // On a top-level document navigation the browser sends NO trace header, so the SSR/backend instead injects // `` into the rendered HTML (the universal // industry pattern β€” OTel `document-load`, Sentry `getTraceMetaTags()`). On boot the client reads it and // CONTINUES that trace for the `pageload` transaction (adopting the trace id + making the pageload a child of -// the server span, X2 continuation) instead of starting a fresh root β€” so the SSR request and the client +// the server span, continuation) instead of starting a fresh root β€” so the SSR request and the client // pageload are one trace. Absent/invalid β†’ undefined (a fresh root). Fully guarded: a hostile document / // querySelector / getAttribute must never break launch. @@ -16,7 +16,7 @@ export interface MetaTraceEnv { }; } -/** A continuation the performance controller's `startTransaction({ continuation })` consumes (X2). */ +/** A continuation the performance controller's `startTransaction({ continuation })` consumes. */ export interface MetaTraceContinuation { traceId: string; parentSpanId: string; @@ -38,6 +38,6 @@ export function readMetaTraceContinuation( } const parsed = parseTraceparent(content); if (parsed === undefined) return undefined; // no/invalid traceparent β†’ a fresh root pageload trace - // The pageload becomes a CHILD of the server's span, adopting its trace id + sampling decision (D4/X2). + // The pageload becomes a CHILD of the server's span, adopting its trace id + sampling decision. return { traceId: parsed.traceId, parentSpanId: parsed.spanId, sampled: parsed.sampled }; } diff --git a/packages/browser/src/mobile-options.launch.test.ts b/packages/browser/src/mobile-options.launch.test.ts index 33a1b410..b06b64ea 100644 --- a/packages/browser/src/mobile-options.launch.test.ts +++ b/packages/browser/src/mobile-options.launch.test.ts @@ -12,7 +12,7 @@ import { resetBrowserDeviceIdCache } from './device-id'; import type { BrowserProbe } from './environment'; import { type Bugsee, type BugseeLaunchOptions, launch } from './launch'; -// Mobile-parity launch options (T6), end to end through the real browser composition root: the friendly +// Mobile-parity launch options, end to end through the real browser composition root: the friendly // name resolves to the canonical key, reaches `sdk.options` on the wire, and changes what the uploaded // bundle contains. diff --git a/packages/browser/src/navigation-source.ts b/packages/browser/src/navigation-source.ts index 79c55d04..bb74d889 100644 --- a/packages/browser/src/navigation-source.ts +++ b/packages/browser/src/navigation-source.ts @@ -1,6 +1,6 @@ import { type Interceptor, InterceptorBase } from '@bugsee/core'; -// The browser NAVIGATION SOURCE (frontend-adapters design D2/D10) β€” an EXTENSIBLE, listenable source of +// The browser NAVIGATION SOURCE β€” an EXTENSIBLE, listenable source of // SPA navigations. Built-in detection (a route change the browser itself performs): PREFER the new // Navigation API (`window.navigation`) when present, listening to its `currententrychange` event β€” it fires // post-commit for EVERY same-document navigation (`pushState`/`replaceState`, fragment, back/forward, AND a diff --git a/packages/browser/src/notify.launch.test.ts b/packages/browser/src/notify.launch.test.ts index f658f2da..a1d1a8df 100644 --- a/packages/browser/src/notify.launch.test.ts +++ b/packages/browser/src/notify.launch.test.ts @@ -11,7 +11,7 @@ import { resetBrowserDeviceIdCache } from './device-id'; import type { BrowserProbe } from './environment'; import { type Bugsee, type BugseeLaunchOptions, launch } from './launch'; -// `notify()` (T7) end to end through the real browser composition root: it reaches the collector's +// `notify()` end to end through the real browser composition root: it reaches the collector's // POST /v2/notifications through the SAME transport as the session and issue calls, authenticated by the // app token in the body (no session), and the friendly `notifyFlushDelay` option reaches the relay. diff --git a/packages/browser/src/page-lifecycle.test.ts b/packages/browser/src/page-lifecycle.test.ts index 735af715..e30e5b47 100644 --- a/packages/browser/src/page-lifecycle.test.ts +++ b/packages/browser/src/page-lifecycle.test.ts @@ -96,7 +96,7 @@ describe('installPageHideFlush', () => { }); it('does not throw back into the browser’s dispatch when the flush fails', () => { - // This is a host boundary (Wave 2.1): the listener runs inside the browser's own event dispatch, at + // This is a host boundary: the listener runs inside the browser's own event dispatch, at // the exact moment the page is going away. const onError = vi.fn(); const win = fakeTarget(); diff --git a/packages/browser/src/report-dialog.ts b/packages/browser/src/report-dialog.ts index 10d6ed72..27076ed5 100644 --- a/packages/browser/src/report-dialog.ts +++ b/packages/browser/src/report-dialog.ts @@ -9,7 +9,7 @@ import { REPORT_DIALOG_CLASS_PREFIX, REPORT_DIALOG_CSS } from './report-dialog-s // The browser's `showReportDialog()` β€” the web counterpart of the mobile SDKs' report form (Android // `BugReportingActivity`, iOS `BGSReportController`). Same fields, order, strings and validation; shown -// as a modal over a shaded page rather than full screen. Design: docs/design/report-dialog.md. +// as a modal over a shaded page rather than full screen. // // Hard rules this file keeps: // - the UI lives in ONE shadow root on ONE host element, so page CSS cannot accidentally restyle it. diff --git a/packages/browser/src/user-agent.ts b/packages/browser/src/user-agent.ts index dc0cb0a3..ab13508e 100644 --- a/packages/browser/src/user-agent.ts +++ b/packages/browser/src/user-agent.ts @@ -5,7 +5,6 @@ // that pair β€” iOS/Android send the OS name, the Rust SDK sends macos/linux/windows, and the backend // indexes `platform.version` as `os_version` β€” so a browser session was the only kind that yielded // no operating system at all, and carried a UA string in a version field instead -// (samples/FINDINGS.md F-X20). // // Two sources, used for what each is actually good for: // diff --git a/packages/browser/src/viewport-source.ts b/packages/browser/src/viewport-source.ts index ae0c5e9b..1b2d1b85 100644 --- a/packages/browser/src/viewport-source.ts +++ b/packages/browser/src/viewport-source.ts @@ -40,7 +40,7 @@ import { type Interceptor, InterceptorBase } from '@bugsee/core'; // - nothing when the new geometry is identical to the last emitted. // // Observe-only, like every source here: these reads run from listeners the application also uses, so a -// throwing getter or a refused `addEventListener` must never reach the app (design: "interceptors must +// throwing getter or a refused `addEventListener` must never reach the app (the rule that "interceptors must // not alter app behaviour"). /** The window surface we measure and listen on. `visualViewport` is read from it when not injected. */ diff --git a/packages/browser/vitest.config.ts b/packages/browser/vitest.config.ts index b75202e8..efb86a60 100644 --- a/packages/browser/vitest.config.ts +++ b/packages/browser/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. // environment 'node': browser code is tested via injected seams (fake probe/window/fetch) and stubbed // DOM globals, not a real DOM env β€” the same injection-first discipline as the node tier. export default defineConfig({ diff --git a/packages/bugsee/src/export-parity.test-d.ts b/packages/bugsee/src/export-parity.test-d.ts index 4a33cfc3..66447682 100644 --- a/packages/bugsee/src/export-parity.test-d.ts +++ b/packages/bugsee/src/export-parity.test-d.ts @@ -95,7 +95,7 @@ export type WorkerdTypes = [ // Every entry exposes the same getDefaultNetworkSanitizer signature. export type SanitizerFactories = Entry['getDefaultNetworkSanitizer']; -// R2L-4: every entry's `launch()` accepts the `lifecycleEventsListener` option (the worker, edge and webview +// every entry's `launch()` accepts the `lifecycleEventsListener` option (the worker, edge and webview // tiers once did not, so Launching/Launched were unobservable there). `Pick` fails to compile when a key is // missing from the options type, which is the whole assertion. type LaunchOptionsOf unknown }> = diff --git a/packages/bugsee/src/index.ts b/packages/bugsee/src/index.ts index 3ac66ff4..8798dae5 100644 --- a/packages/bugsee/src/index.ts +++ b/packages/bugsee/src/index.ts @@ -1,7 +1,7 @@ // bugsee -// Umbrella package; the batteries-included entry (Tier 5). It re-exports the platform SDK surface and +// Umbrella package; the batteries-included entry. It re-exports the platform SDK surface and // adds launch() = the platform composition root + the on-by-default extensions (@bugsee/performance: -// web-vitals + transactions + http spans) wired in. See docs/design/sdk-design.md Β§5. +// web-vitals + transactions + http spans) wired in. export type { ReportDialogOptions, ReportDialogStrings } from '@bugsee/browser'; // The built-in network redaction, for composing with a custom `setNetworkEventFilter` (which REPLACES the // default sanitizer rather than layering on it). diff --git a/packages/bugsee/src/launch.test.ts b/packages/bugsee/src/launch.test.ts index 654a3974..21f7c7ba 100644 --- a/packages/bugsee/src/launch.test.ts +++ b/packages/bugsee/src/launch.test.ts @@ -208,7 +208,7 @@ describe('bugsee umbrella launch', () => { expect(() => client.ext('performance')).toThrow(/not registered/); // extension never set up }); - // R3V-3: the performance switches were read raw (`?? true` / `?? false`), so `'false'` and `0` meant the + // the performance switches were read raw (`?? true` / `?? false`), so `'false'` and `0` meant the // OPPOSITE of what the app wrote. Data-collecting switches fail CLOSED: only boolean `true` opts in to what is // off by default (propagateTrace), and an invalid value turns off what is on by default (monitoring, navigation, // interaction spans); each is reported. @@ -350,7 +350,7 @@ describe('bugsee umbrella launch', () => { const { pushEvent } = stubPerformanceObserver(); vi.stubGlobal('window', { addEventListener: () => {}, removeEventListener: () => {} }); const client = await track(await launch('tok', base({ carrier: {} }))); - // The active span is the pageload β€” which must NOT block interactions (D12). + // The active span is the pageload β€” which must NOT block interactions. expect((client.ext('performance').getActiveSpan() as Transaction).getOperation()).toBe( 'pageload', ); @@ -613,7 +613,7 @@ describe('bugsee umbrella launch', () => { ).toBeDefined(); }); - // R5V-6: `['']` is a substring of every URL; an invalid list is reported and propagates to nobody cross-origin. + // `['']` is a substring of every URL; an invalid list is reported and propagates to nobody cross-origin. it('an empty-string target never reaches a third party, and an invalid entry is reported to onError', async () => { let received: { init: unknown } | undefined; vi.stubGlobal('fetch', async (_i: unknown, init: unknown) => { @@ -711,11 +711,11 @@ describe('bugsee umbrella launch', () => { value: { stringValue: TEST_DEVICE_ID }, }); expect(body.resourceSpans[0].scopeSpans[0].spans.length).toBeGreaterThan(0); - // Profile v1 Β§5: the instrumentation scope name is com.bugsee./ β€” webjs on the browser. + // Profile v1: the instrumentation scope name is com.bugsee./ β€” webjs on the browser. expect(body.resourceSpans[0].scopeSpans[0].scope.name).toBe('com.bugsee.webjs/performance'); }); - // R4V-10: `otelExportUrl: ''` used to build an exporter that posted to the page's own URL. An empty (or + // `otelExportUrl: ''` used to build an exporter that posted to the page's own URL. An empty (or // non-string) value means "no exporter": data is exported only to an explicitly configured endpoint. it.each([ '', @@ -749,7 +749,7 @@ describe('bugsee umbrella launch', () => { expect(calls.filter((u) => !u.endsWith('/v2/performance/transactions'))).toEqual([]); }); - // R5V-7: a `URL` object worked before R4V-10 and was silently ignored after it; whitespace still built an exporter + // a `URL` object worked before and was silently ignored after it; whitespace still built an exporter // (`fetch(' ')` is the page's own URL); numbers and objects were dropped without a word. describe('otelExportUrl forms (R5V-7)', () => { async function runWith( diff --git a/packages/bugsee/src/lifecycle-exports.test.ts b/packages/bugsee/src/lifecycle-exports.test.ts index d7e404ff..caba6bf9 100644 --- a/packages/bugsee/src/lifecycle-exports.test.ts +++ b/packages/bugsee/src/lifecycle-exports.test.ts @@ -46,7 +46,7 @@ describe('addReportAttachment is re-exported from every umbrella entry', () => { } }); -// The status surface (review L9): getStatus / relaunch / BugseeStatus on EVERY entry, not only the node and +// The status surface: getStatus / relaunch / BugseeStatus on EVERY entry, not only the node and // browser ones, so lifecycle code is portable across runtimes. describe('getStatus, relaunch and BugseeStatus are exported from every umbrella entry', () => { for (const [name, entry] of Object.entries(entries)) { diff --git a/packages/bugsee/src/node.test.ts b/packages/bugsee/src/node.test.ts index 3598a0fe..e11cdb73 100644 --- a/packages/bugsee/src/node.test.ts +++ b/packages/bugsee/src/node.test.ts @@ -99,7 +99,7 @@ const base = (over: Partial = {}): BugseeNodeLaunchOpti systemProbe: probe, systemMetricsSampler: () => [{ name: 'process_memory_rss', value: 42 }], captureNetwork: false, - capturedDataStore: 'memory', // hermetic: stay off the shared default disk root (disk is the default now, D3) + capturedDataStore: 'memory', // hermetic: stay off the shared default disk root (disk is the default now) clock: fixedClock, ...over, }); @@ -183,7 +183,7 @@ describe('bugsee node umbrella launch', () => { expect(transactionsOf(perfPosts).some((t) => t.name === 'app.start')).toBe(true); }); - // R4V-4: node owns `propagateTrace` (default ON, invalid => OFF); the umbrella must not ALSO judge it with the + // node owns `propagateTrace` (default ON, invalid => OFF); the umbrella must not ALSO judge it with the // browser's performance definition (it logged "using false" while node kept propagating). it.each([ 'false', diff --git a/packages/bugsee/src/option-directions.test.ts b/packages/bugsee/src/option-directions.test.ts index 8f09e16c..9b6dcf82 100644 --- a/packages/bugsee/src/option-directions.test.ts +++ b/packages/bugsee/src/option-directions.test.ts @@ -14,7 +14,7 @@ import ts from 'typescript'; import { describe, expect, it } from 'vitest'; import { createSharedProgram, TS_PROGRAM_TEST_TIMEOUT_MS } from './ts-program-cache.test-helper'; -// R3V-3. EVERY boolean-typed launch option of EVERY platform has an explicit fail direction, and that direction is +// EVERY boolean-typed launch option of EVERY platform has an explicit fail direction, and that direction is // pinned HERE. The test reads three things STATICALLY (so a future option cannot dodge it): // 1. every boolean-typed property of every launch-options interface (the TypeScript checker, as // relaunch-options.test.ts does); @@ -28,7 +28,7 @@ import { createSharedProgram, TS_PROGRAM_TEST_TIMEOUT_MS } from './ts-program-ca // off data capture that is ON by default, or collects more: an invalid value is OFF (fail closed) // on protective (hides/blocks data): an invalid value is ON // default keeps the option's default (it is the safe side, or not a privacy switch); only the boolean flips it -// The SAFE side is classified explicitly per option (R4V-1): "OFF unless true" is right for capture, WRONG for an +// The SAFE side is classified explicitly per option: "OFF unless true" is right for capture, WRONG for an // option that PROTECTS (tenant partitioning, canvas blocking, blackout, obscuring), whose invalid value must be ON. // Nested option bags (`replay.*`, `captureLocalVariables.*`, `traceResponse.*`) are audited by NESTED below. // (WebView/Electron/performance consumers of the same rule are covered by their own package tests.) @@ -54,7 +54,7 @@ const DIRECTIONS: Record = { captureInteractions: 'off', captureViewHierarchy: 'off', startInBlackout: 'on', - replay: 'off', // default ON, collects the DOM: an invalid scalar is OFF (R4V-2); masking is fail-closed on its own + replay: 'off', // default ON, collects the DOM: an invalid scalar is OFF; masking is fail-closed on its own persistUserIdentifier: 'default', // default OFF reportSummaryRequired: 'default', reportDescriptionRequired: 'default', @@ -67,7 +67,7 @@ const DIRECTIONS: Record = { detectHangs: 'default', // default ON, a diagnostic captureLocalVariables: 'off', captureSourceContext: 'off', - instrumentIncomingRequests: 'off', // default ON, collects request data: an invalid value is OFF (R4V-2) + instrumentIncomingRequests: 'off', // default ON, collects request data: an invalid value is OFF // webview captureObscuring: 'on', // webworker @@ -77,7 +77,7 @@ const DIRECTIONS: Record = { traceNavigations: 'off', traceInteractions: 'off', // `propagateTrace` exists TWICE with different defaults, so it is keyed by package: - 'node:propagateTrace': 'off', // node's own definition: default ON, invalid => OFF (R4V-4) + 'node:propagateTrace': 'off', // node's own definition: default ON, invalid => OFF 'performance:propagateTrace': 'default', // browser perf-sourced: default OFF, only boolean true opts in }; @@ -169,7 +169,7 @@ const INTERFACES: Array<[string, string, string, string[]]> = [ ['bugsee', 'src/server-launch.ts', 'BugseeServerLaunchOptions', UMBRELLA], ['electron', 'src/launch-main.ts', 'LaunchMainOptions', ['core', 'node']], ['electron', 'src/launch-renderer.ts', 'LaunchRendererOptions', ['core', 'browser']], - // Framework adapters that read an option of their OWN (R5V-8): `setupOtelProvider` was `=== false` and + // Framework adapters that read an option of their OWN: `setupOtelProvider` was `=== false` and // `injectTraceMeta` was truthy, so `'false'` / `0` / `null` kept them ON or flipped them the wrong way. ['nextjs', 'src/server.ts', 'NextjsServerOptions', UMBRELLA], ['nuxt', 'src/nitro.ts', 'InstallBugseeNitroOptions', UMBRELLA], @@ -406,7 +406,7 @@ describe('every boolean launch option has an explicit, tested fail direction (R3 }); it('a protecting (safe: on) option is never classified as a capture (safe: off) one', () => { - // The R4V-1 error was exactly this: a protecting option filed under "OFF unless true". + // The error was exactly this: a protecting option filed under "OFF unless true". const protecting = Object.entries(RAW_STRICT) .filter(([, e]) => e.safe === 'on') .map(([k]) => k); @@ -423,7 +423,7 @@ describe('every boolean launch option has an explicit, tested fail direction (R3 ); }); - // R4V-2: the flat table above cannot see an option READ INSIDE a bag (`replay.canvas`, `blockAllCanvas`, + // the flat table above cannot see an option READ INSIDE a bag (`replay.canvas`, `blockAllCanvas`, // `captureLocalVariables.includeCaught`): a raw `options.replay.canvas` read dodged it, and `'false'` / `0` // enabled canvas pixel recording. Every boolean-typed property of every option BAG is therefore classified // here with its safe side AND pinned to the source read that makes the classification true. diff --git a/packages/bugsee/src/relaunch-options.test.ts b/packages/bugsee/src/relaunch-options.test.ts index 2252d750..0e9ee66e 100644 --- a/packages/bugsee/src/relaunch-options.test.ts +++ b/packages/bugsee/src/relaunch-options.test.ts @@ -5,7 +5,7 @@ import ts from 'typescript'; import { describe, expect, it } from 'vitest'; import { createSharedProgram, TS_PROGRAM_TEST_TIMEOUT_MS } from './ts-program-cache.test-helper'; -// R2P-3: `relaunch()` starts from DEFAULTS for Bugsee behaviour options but must keep the integration seams +// `relaunch()` starts from DEFAULTS for Bugsee behaviour options but must keep the integration seams // AND the deployment identity (endpoint, app version, ...): forgetting one uploads to the default production // endpoint or under the wrong app version. `RELAUNCH_KEPT_OPTIONS` (core `carrier.ts`) is the single source of // truth for what is kept; THIS table makes a new option impossible to forget: every launch option of every @@ -25,7 +25,7 @@ const INTERFACES: Array<[string, string, string]> = [ ['bugsee', 'src/server-launch.ts', 'BugseeServerLaunchOptions'], ['electron', 'src/launch-main.ts', 'LaunchMainOptions'], ['electron', 'src/launch-renderer.ts', 'LaunchRendererOptions'], - // Framework adapters and the remaining runtimes (R3L-6). The ones that only `extends` a platform interface + // Framework adapters and the remaining runtimes. The ones that only `extends` a platform interface // add no key, but they are listed so the day one adds an option it is classified here. A name that is a // function resolves to the type of its `options` (second) parameter; a type alias resolves to its object constituents. ['nextjs', 'src/client.ts', 'NextjsClientOptions'], @@ -119,7 +119,7 @@ function optionKeys(pkg: string, file: string, name: string): string[] { * - `injectTraceMeta` (Nuxt) and `flushTimeoutMs` (Nuxt edge) are adapter behaviour options consumed by the * adapter's own installer, never by the platform launch a relaunch re-runs; * - `instanceIdentity` (node) is a per-instance identity: a pinned one must never be shared by two instances, - * so a relaunch builds a fresh one (R3L-3); + * so a relaunch builds a fresh one; * - `captureNonce`, `controlNonce`, `otelExportHeaders` and `otelExportUrl` are secrets or per-launch * credentials: never kept (`setCarrierLastLaunch` stores only the kept list). */ diff --git a/packages/bugsee/src/server-launch.ts b/packages/bugsee/src/server-launch.ts index 78b4148b..97427a63 100644 --- a/packages/bugsee/src/server-launch.ts +++ b/packages/bugsee/src/server-launch.ts @@ -3,7 +3,7 @@ import type { PerformanceFacade } from '@bugsee/performance'; import { withPerformanceFacade } from './performance-facade'; import { type UmbrellaExtensionOptions, wireUmbrella } from './wire'; -// The shared umbrella launch() for every SERVER runtime β€” node, bun and deno (Wave 3b.1 / 4.1). +// The shared umbrella launch() for every SERVER runtime β€” node, bun and deno. // // All three run the same composition: a platform `launchCore`, then the on-by-default extensions via // `wireUmbrella`. Only the `launchCore` differs, and only in its defaults β€” Bun swaps in the Bun identity diff --git a/packages/bugsee/src/wire.ts b/packages/bugsee/src/wire.ts index 3bc1e762..b35ef679 100644 --- a/packages/bugsee/src/wire.ts +++ b/packages/bugsee/src/wire.ts @@ -60,11 +60,11 @@ interface UmbrellaInternals { onError: ((error: unknown) => void) | undefined; /** * Where the performance controller's active transaction lives. The server `launchCore` supplies a - * per-request-context store (D2 part 2); the browser passes `undefined` and keeps the single-slot + * per-request-context store; the browser passes `undefined` and keeps the single-slot * default. REQUIRED-but-nullable rather than optional (R-16): `UmbrellaInternals` is a * hand-maintained structural mirror of two independently declared `LaunchInternals`, and an * optional field let a platform omit the store with no type error β€” it would silently fall back to - * the single slot, which is the D2 defect. A required key forces every platform to state its choice. + * the single slot, which is the defect. A required key forces every platform to state its choice. */ activeSpanStore: ActiveSpanStore | undefined; } @@ -154,7 +154,7 @@ function teeSend( } /** - * The OTLP endpoint of `otelExportUrl` (R5V-7): a string (trimmed) or a `URL` object (its href). Absent, `null`, an + * The OTLP endpoint of `otelExportUrl`: a string (trimmed) or a `URL` object (its href). Absent, `null`, an * empty or whitespace-only string mean "no exporter" silently; anything else (a number, an object, ...) is reported * and means no exporter either, so nothing is ever posted to an address the host did not give. */ @@ -219,7 +219,7 @@ export function wireUmbrella( options as unknown as Record, // `propagateTrace` is the BROWSER's perf-sourced propagation switch; on Node the `@bugsee/node` launch owns that // option (default ON, invalid => OFF) and reports it, so the umbrella must not judge it a second time with a - // contradictory definition ("using false" while node kept propagating: R4V-4). + // contradictory definition ("using false" while node kept propagating). platform.pageload ? PERFORMANCE_OPTION_DEFINITIONS : PERFORMANCE_OPTION_DEFINITIONS.filter((d) => d.friendly !== 'propagateTrace'), @@ -232,8 +232,8 @@ export function wireUmbrella( baseUrl: internals.baseUrl, getEnvironment: internals.getEnvironment, }); - // Profile v1 Β§5: the OTLP instrumentation scope name is `com.bugsee./`. The SDK token - // follows telemetry.sdk.language (Β§4): `webjs` on the browser, `nodejs` on the node-family runtimes + // Profile v1: the OTLP instrumentation scope name is `com.bugsee./`. The SDK token + // follows telemetry.sdk.language: `webjs` on the browser, `nodejs` on the node-family runtimes // (node/bun/deno all run the node umbrella entry, pageload:false). The provider here is `performance`. const scopeName = `com.bugsee.${platform.pageload ? 'webjs' : 'nodejs'}/performance`; // Produce: when an OTLP endpoint is configured, TEE the drained batch to it too (Bugsee still receives @@ -241,7 +241,7 @@ export function wireUmbrella( const otlpResource = otlpResourceWithDeviceId(options.otelExportResource, internals.deviceId); const otlpUrl = resolveOtlpUrl(options.otelExportUrl, internals.onError); const otlpSend = - // Only a NON-EMPTY URL configures an exporter: `''` / `' '` would post to the page's own URL (R4V-10, R5V-7). + // Only a NON-EMPTY URL configures an exporter: `''` / `' '` would post to the page's own URL. otlpUrl !== undefined ? createOtlpTraceExporter({ transport: internals.transport, @@ -255,7 +255,7 @@ export function wireUmbrella( : undefined; const send = otlpSend !== undefined ? teeSend(bugseeSend, otlpSend) : bugseeSend; - // SPA navigation transactions (F1c) β€” browser only (`platform.pageload`; Node has no History/SPA + // SPA navigation transactions β€” browser only (`platform.pageload`; Node has no History/SPA // navigation), opt-out via `traceNavigations: false`. The detector self-skips if the browser globals are // absent; wirePerformance subscribes to it (which activates it) and tears it down on stop. const navigationSource = @@ -263,7 +263,7 @@ export function wireUmbrella( ? browserSources.createNavigationSource?.() : undefined; - // Interaction transactions (F4) β€” browser only (`platform.pageload`), opt-out via `traceInteractions: + // Interaction transactions β€” browser only (`platform.pageload`), opt-out via `traceInteractions: // false`. The Event Timing source self-skips where the API is unsupported; wirePerformance subscribes // (activating it) and tears it down on stop. const interactionSource = @@ -271,7 +271,7 @@ export function wireUmbrella( ? browserSources.createInteractionSource?.() : undefined; - // Pageload trace continuation (D4): on the browser, continue a server-injected `` + // Pageload trace continuation: on the browser, continue a server-injected `` // so the SSR request and the client pageload are one trace (a fresh root if absent/invalid). const pageloadContinuation = platform.pageload ? browserSources.readMetaTraceContinuation?.() @@ -311,11 +311,11 @@ export function wireUmbrella( const endTimestampMs = client.getService(ClockToken).wallNow(); wired.recordTransaction({ traceId: defaultTraceId(), - spanId: defaultSpanId(), // a real root id β€” see TransactionWire.spanId (Wave 5.3) + spanId: defaultSpanId(), // a real root id β€” see TransactionWire.spanId name: 'app.start', operation: 'app.start', status: 'OK', - sampled: true, // the startup transaction is always recorded + uploaded (Profile v1 Β§6 sampled flag) + sampled: true, // the startup transaction is always recorded + uploaded (Profile v1 sampled flag) startTimestampMs: platform.startupAtMs, endTimestampMs, durationNanos: Math.max(0, Math.round((endTimestampMs - platform.startupAtMs) * 1_000_000)), @@ -325,7 +325,7 @@ export function wireUmbrella( } // Consume (opt-in): hand the user a SpanProcessor to register on their OTel TracerProvider. Consumed - // spans are assembled into Β§8.8 transactions (recordTransaction) and ride the same upload + OTLP tee. + // spans are assembled into transactions (recordTransaction) and ride the same upload + OTLP tee. let spanProcessor: BugseeSpanProcessor | undefined; if (options.otelConsume === true && options.onOtelSpanProcessor !== undefined) { spanProcessor = createBugseeSpanProcessor({ onTransaction: wired.recordTransaction }); @@ -337,7 +337,7 @@ export function wireUmbrella( // seam, propagating the active performance transaction's trace + the `bugsee=` session tracestate. // BROWSER ONLY (`platform.pageload`): on Node the `@bugsee/node` launch already wires its OWN // per-request-context-sourced decorator, so the umbrella must NOT double-wire. The perf-sourced one - // stays browser-only on its own merits even after D2 part 2 made the controller's slot request-scoped + // stays browser-only on its own merits even after made the controller's slot request-scoped // on Node: it reads whatever transaction is active rather than the request context the node decorator // is built on, and it is tied to the OPT-IN APM extension. Same-origin propagates by default; cross-origin only via the // allowlist (`tracePropagationTargets`). diff --git a/packages/bugsee/src/worker-entries.test.ts b/packages/bugsee/src/worker-entries.test.ts index 12d79756..c2eff07b 100644 --- a/packages/bugsee/src/worker-entries.test.ts +++ b/packages/bugsee/src/worker-entries.test.ts @@ -51,7 +51,7 @@ describe.each([ }); }); -// getStatus / relaunch on the worker and edge entries (review L9): relaunch re-runs THIS entry's launch, +// getStatus / relaunch on the worker and edge entries: relaunch re-runs THIS entry's launch, // stopping the old client first, and the status follows the process state. describe.each([ [ diff --git a/packages/bugsee/tsup.config.ts b/packages/bugsee/tsup.config.ts index 2b40e737..c814e89a 100644 --- a/packages/bugsee/tsup.config.ts +++ b/packages/bugsee/tsup.config.ts @@ -4,7 +4,7 @@ import { baseConfig } from '../../tsup.config.base'; // The umbrella ships TWO entries β€” a browser/default entry (index) and a node entry (index.node) β€” // each built dual (ESM+CJS) -> dist/index.{js,cjs,d.ts,d.cts} + dist/index.node.{js,cjs,d.ts,d.cts}. // The package `exports` route per-runtime (browser/node) x per-module (import/require) to the -// matching dist artifact. See docs/design/packaging-dual-module.md Β§D5. +// matching dist artifact. export default defineConfig({ ...baseConfig, entry: [ diff --git a/packages/bugsee/vitest.config.ts b/packages/bugsee/vitest.config.ts index f527aea6..56e8b9f7 100644 --- a/packages/bugsee/vitest.config.ts +++ b/packages/bugsee/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. // environment 'node': the umbrella wires the browser launch via injected seams (fake transport/window/ // scheduler/probe), not a real DOM β€” the same injection-first discipline as the platform tiers. export default defineConfig({ diff --git a/packages/bun/src/bun-serve-interceptor.ts b/packages/bun/src/bun-serve-interceptor.ts index 0f98412c..77ee3e86 100644 --- a/packages/bun/src/bun-serve-interceptor.ts +++ b/packages/bun/src/bun-serve-interceptor.ts @@ -5,11 +5,11 @@ import { wrapFetchHandler, } from '@bugsee/node'; -// Native Bun.serve instrumentation (design: docs/design/incoming-server-instrumentation.md Β§5.3). Replaces +// Native Bun.serve instrumentation. Replaces // `Bun.serve` to wrap its `fetch` handler via the shared `wrapFetchHandler` β€” so idiomatic // `Bun.serve({fetch})` apps (which bypass node:http, and so the emit-patch interceptor) get a per-request // context + http.server transaction. Self-skips when `Bun` is absent (e.g. unit tests on Node). KNOWN GAPS -// (D9): `Bun.serve({routes})` per-route handlers + websocket are NOT wrapped, and `server.reload()` rebinds +// `Bun.serve({routes})` per-route handlers + websocket are NOT wrapped, and `server.reload()` rebinds // the handler without re-instrumenting it β€” both documented follow-ups. interface BunServeOptions { diff --git a/packages/bun/src/index.exports.test.ts b/packages/bun/src/index.exports.test.ts index 03307f8f..86710ba5 100644 --- a/packages/bun/src/index.exports.test.ts +++ b/packages/bun/src/index.exports.test.ts @@ -3,7 +3,7 @@ import { addReportAttachment, BugseeStatus, LifecycleEvents } from '@bugsee/core import { describe, expect, it } from 'vitest'; import * as entry from './index'; -// The package barrel hands out the umbrella's value API (review R2D-4). Loading it here also makes the barrel +// The package barrel hands out the umbrella's value API. Loading it here also makes the barrel // a COVERED file on a cold CI transform (see the note in @bugsee/node's index.test.ts). describe('@bugsee/bun barrel: the shared value API', () => { it('re-exports the same functions and constants as core / capture', () => { diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index edb3dbf6..07d798e0 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -1,4 +1,4 @@ -// @bugsee/bun β€” Bun platform (tier 2). Bun is node-API-compatible, so this package IS @bugsee/node with +// @bugsee/bun β€” Bun platform. Bun is node-API-compatible, so this package IS @bugsee/node with // two Bun-specific overrides: launch()/launchCore() default the system probe (platform.type 'bun' + Bun // version) and the system-metrics sampler (guarded perf_hooks). The full node surface is re-exported; the // explicit launch/launchCore exports below shadow node's same-named exports. diff --git a/packages/bun/src/launch.test.ts b/packages/bun/src/launch.test.ts index ab9b6fef..f00486b6 100644 --- a/packages/bun/src/launch.test.ts +++ b/packages/bun/src/launch.test.ts @@ -225,7 +225,7 @@ describe('@bugsee/bun launch', () => { }); // Proves finishWith actually ran end to end (not just the pre-finish header write): a // non-conforming double with no getAttributes() degrades finishWith silently (TypeError swallowed - // by its own try/catch), so finish()/setName() never run β€” see R3-7. + // by its own try/catch), so finish()/setName() never run. expect(txn.setName).toHaveBeenCalledWith('GET /x'); expect(txn.finish).toHaveBeenCalledWith('OK'); } finally { diff --git a/packages/bun/src/relaunch.test.ts b/packages/bun/src/relaunch.test.ts index a5aadf20..4628a320 100644 --- a/packages/bun/src/relaunch.test.ts +++ b/packages/bun/src/relaunch.test.ts @@ -2,7 +2,7 @@ import { setCarrierClient, setCarrierLastLaunch } from '@bugsee/core'; import { afterEach, describe, expect, it, vi } from 'vitest'; // `relaunch` must re-run THIS package's launch (Bun system probe + runtime gates), not the one re-exported from the layer below -// (review R2D-4). The launch is replaced by a spy; the carrier carries the remembered token like a real one. +// The launch is replaced by a spy; the carrier carries the remembered token like a real one. const { launch } = vi.hoisted(() => ({ launch: vi.fn() })); vi.mock('./launch', () => ({ launch })); diff --git a/packages/bun/vitest.config.ts b/packages/bun/vitest.config.ts index bb6fad0f..9f241546 100644 --- a/packages/bun/vitest.config.ts +++ b/packages/bun/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. export default defineConfig({ test: { // The runner is far slower than a dev machine β€” roughly 7-18x under coverage instrumentation β€” and diff --git a/packages/bundler-plugin-core/src/build-id.ts b/packages/bundler-plugin-core/src/build-id.ts index a8e5e5c6..af533c71 100644 --- a/packages/bundler-plugin-core/src/build-id.ts +++ b/packages/bundler-plugin-core/src/build-id.ts @@ -1,4 +1,4 @@ -// The build UUID a JavaScript build registers under (docs/design/web-build-registration.md, D1). +// The build UUID a JavaScript build registers under. // // The server dedups builds on `uuid` β€” a unique index on (organization, application, uuid), with // replace-then-create on a repeat (bugsee-appserver `builds.service.js`). So the only contract is: @@ -6,7 +6,7 @@ // generates a random `uuid.v4()` when none is sent, which would make every rebuild a new record. // // Derived from the build's debug-ids, which is Android's property copied deliberately: -// `BugseeBuildIdDeriver.deriveFromMappingFile` hashes R8's mapping.txt, so the id changes whenever the +// `BugseeBuildIdDeriver.deriveFromMappingFile` hashes the mapping.txt, so the id changes whenever the // code does and NOT when only the version string does. The debug-ids are the web analog β€” each is // already a UUIDv5 over one bundle's bytes and its map (`bugsee-cli sourcemaps inject`), so the set of // them names the build's content exactly, and it is already computed. @@ -48,7 +48,7 @@ export interface BuildIdentity { * walk happened to find the maps in cannot change it. * * Without any (a build that emitted no maps, or a dry run, where inject writes nothing): from the - * build's identity, as Android falls back to manifest+variant when R8 wrote no mapping. Still + * build's identity, as Android falls back to manifest+variant when wrote no mapping. Still * deterministic, so re-registering a version replaces rather than duplicates. * * The two inputs are prefixed differently so a content-derived id and an identity-derived one can diff --git a/packages/bundler-plugin-core/src/index.ts b/packages/bundler-plugin-core/src/index.ts index a3bfbb76..f852da04 100644 --- a/packages/bundler-plugin-core/src/index.ts +++ b/packages/bundler-plugin-core/src/index.ts @@ -1,6 +1,5 @@ // @bugsee/bundler-plugin-core β€” shared bundler-plugin engine. Collects build context and drives the Rust // `bugsee-cli` (sourcemaps inject + debug-files upload). Consumed by @bugsee/vite-plugin / @bugsee/webpack-plugin. -// See docs/design/source-maps.md. export { deriveBuildUuid } from './build-id'; export { defaultDeleteMapFiles, diff --git a/packages/bundler-plugin-core/src/orchestrate.ts b/packages/bundler-plugin-core/src/orchestrate.ts index 08b30308..b52f01d0 100644 --- a/packages/bundler-plugin-core/src/orchestrate.ts +++ b/packages/bundler-plugin-core/src/orchestrate.ts @@ -48,7 +48,7 @@ export interface UploadSourcemapsOptions { /** Injectable integrity check for the pre-stamped path (default {@link findIntegrityMismatches}). */ verifySri?: (dir: string) => Promise; /** - * FAIL the build when `bugsee-cli` fails. Default `false` (Wave 7). + * FAIL the build when `bugsee-cli` fails. Default `false`. * * A telemetry side effect must not be able to break a production deploy: an expired token, a transient * network error, a Bugsee outage or a binary missing from PATH used to reject `writeBundle` and abort the @@ -63,7 +63,7 @@ export interface UploadSourcemapsOptions { * * Data, not policy: collection is the context-gathering layer's job (`resolve.ts`), this layer only * drives `bugsee-cli`. Carried here because this is where it will be handed to the CLI once the - * upload wire protocol has a field to put it in β€” see docs/design/source-maps.md Β§9. + * upload wire protocol has a field to put it in β€” */ vcs?: VcsMetadata; } @@ -77,7 +77,7 @@ export interface UploadSourcemapsResult { } /** - * Directories never descended into when deleting `.map` files (Wave 7). + * Directories never descended into when deleting `.map` files. * * A relative `output.file` resolves the output dir to `'.'` β€” `path.dirname('bundle.js') === '.'`, an * entirely ordinary Rollup library config β€” so the walk started at the PROJECT ROOT. With no exclusions it @@ -244,7 +244,7 @@ export async function uploadSourcemaps( : []; return { injected: true, uploaded: true, deletedMaps, ...vcsEcho }; } catch (error) { - // A telemetry side effect must not break a production deploy (Wave 7). Reported, never swallowed; + // A telemetry side effect must not break a production deploy. Reported, never swallowed; // `failOnError` is there for teams that would rather the build stopped. if (options.failOnError === true) { throw error; diff --git a/packages/bundler-plugin-core/src/plugin.test.ts b/packages/bundler-plugin-core/src/plugin.test.ts index e4a0843f..d6bc6510 100644 --- a/packages/bundler-plugin-core/src/plugin.test.ts +++ b/packages/bundler-plugin-core/src/plugin.test.ts @@ -85,7 +85,7 @@ describe('convenience per-bundler factories', () => { // against an output directory containing no `.map` files at all β€” real bugsee-cli v0.7.2 exits 10, "no .map // source-map files found under: ". Reproduced in the review against real Vite 6.4.3 and 7.3.6. // -// Containing the failure (Wave 7.3) stops it breaking the build, which turns it into the OTHER failure this +// Containing the failure stops it breaking the build, which turns it into the OTHER failure this // remediation is about: a feature that silently does nothing. The wrapper owns the one thing the engine // cannot see β€” the bundler's own configuration β€” so this is where it has to be fixed. describe('the Vite plugin ensures source maps exist (Wave 7.6)', () => { diff --git a/packages/bundler-plugin-core/src/plugin.ts b/packages/bundler-plugin-core/src/plugin.ts index 6fb134ce..d99df062 100644 --- a/packages/bundler-plugin-core/src/plugin.ts +++ b/packages/bundler-plugin-core/src/plugin.ts @@ -89,7 +89,7 @@ export const bugseeUnpluginFactory: UnpluginFactory { expect(uploadSpy).not.toHaveBeenCalled(); }); - // What the BUNDLER says about the build is what decides whether it registers (D2) β€” the analog of + // What the BUNDLER says about the build is what decides whether it registers β€” the analog of // AGP's `isDebuggable`. Each bundler says it differently, so each hook has to pass it on. describe('hands the bundler’s own production signal to the run', () => { type ConfigResolved = (config: { isProduction: boolean; mode: string }) => void; diff --git a/packages/bundler-plugin-core/src/register-build.test.ts b/packages/bundler-plugin-core/src/register-build.test.ts index 964258f4..b303fa6a 100644 --- a/packages/bundler-plugin-core/src/register-build.test.ts +++ b/packages/bundler-plugin-core/src/register-build.test.ts @@ -14,7 +14,7 @@ import type { RunBugseeCliOptions } from './run-cli'; const ID_A = '0f3c2a1e-7b1d-5e2a-9c4b-1a2b3c4d5e6f'; describe('resolveRegistration', () => { - // D2: the bundler's OWN production signal is the analog of AGP's `isDebuggable` β€” not + // the bundler's OWN production signal is the analog of AGP's `isDebuggable` β€” not // minification, and not a guess from the output directory's name. it('registers a production build by default', () => { expect(resolveRegistration('release', { isProduction: true }, {})).toEqual({ register: true }); @@ -159,7 +159,7 @@ describe('registerJsBuild', () => { expect(calls).toHaveLength(1); const [call] = calls as [Call]; expect(call.args.slice(0, 2)).toEqual(['upload', 'build']); - // D5: register-only. No `--artifact`, so the CLI sends `request_artifact_upload: false` itself; + // register-only. No `--artifact`, so the CLI sends `request_artifact_upload: false` itself; // the flags that only describe artefact bytes would be refused without one. for (const flag of ['--artifact', '--mapping', '--chunked', '--out']) { expect(call.args).not.toContain(flag); diff --git a/packages/bundler-plugin-core/src/register-build.ts b/packages/bundler-plugin-core/src/register-build.ts index 1a1e1586..81f5347c 100644 --- a/packages/bundler-plugin-core/src/register-build.ts +++ b/packages/bundler-plugin-core/src/register-build.ts @@ -1,13 +1,13 @@ -// Register a JavaScript build with Bugsee β€” docs/design/web-build-registration.md (slice 3). +// Register a JavaScript build with Bugsee. // // Product direction (2026-09-19): a JavaScript build SHOULD register a build, and by default only a // release one. The Android Gradle plugin and the iOS agent are the reference, and this follows them: // -// - ON by default, gated only on "release-like" (D2). The analog of AGP's `!isDebuggable` is the +// - ON by default, gated only on "release-like". The analog of AGP's `!isDebuggable` is the // bundler's OWN production signal, not minification and not a name match on the output directory. -// - Register-only (D5). A JavaScript build has no single artefact to ship, so `bugsee-cli upload build` +// - Register-only. A JavaScript build has no single artefact to ship, so `bugsee-cli upload build` // runs with no `--artifact` and the CLI sends `request_artifact_upload: false`. -// - A failure never fails the build (D4) β€” reported and contained, like the source-map upload, and +// - A failure never fails the build β€” reported and contained, like the source-map upload, and // `failOnError` opts into a hard stop. // // Runs AFTER the source-map upload and its map deletion, which it can because it reads the build's @@ -44,7 +44,7 @@ export type RegistrationDecision = | { register: true } | { register: false; reason: 'disabled' | 'not-release' }; -/** D2: decide whether this build registers at all. Pure. */ +/** decide whether this build registers at all. Pure. */ export function resolveRegistration( setting: RegisterBuildSetting, bundler: BundlerBuildContext, @@ -61,7 +61,7 @@ export function resolveRegistration( } /** - * D3: the build's `package_id` β€” the `name` of the NEAREST package.json at or above `from`, scope + * the build's `package_id` β€” the `name` of the NEAREST package.json at or above `from`, scope * included (`@acme/web-app`). It is only a scoping key for size analysis, so being stable matters more * than being reverse-DNS. * diff --git a/packages/bundler-plugin-core/src/resolve.test.ts b/packages/bundler-plugin-core/src/resolve.test.ts index dcf0f299..48444d8d 100644 --- a/packages/bundler-plugin-core/src/resolve.test.ts +++ b/packages/bundler-plugin-core/src/resolve.test.ts @@ -144,7 +144,7 @@ describe('failOnError / onError plumbing (Wave 7)', () => { // // `writeBundle` fires once per OUTPUT. A multi-output config whose entries resolve to the same directory β€” // `output: [{ dir: 'dist' }, { file: 'dist/legacy.js' }]`, both `dist` β€” starts two concurrent pipelines -// over one tree: one can be deleting maps (step 3) while the other is still reading them (steps 1-2). +// over one tree: one can be deleting maps while the other is still reading them (steps 1-2). describe('one pipeline per output directory (Wave 7.5)', () => { /** An upload we can hold open, so overlap is observable rather than a race. */ const held = () => { @@ -737,7 +737,7 @@ describe('stripSourcesContent', () => { }); }); -// Web-build registration (docs/design/web-build-registration.md, slice 3). The public option bag is +// Web-build registration. The public option bag is // where a team decides, so each part of the decision has to be reachable from it. describe('resolvePluginOptions β€” build registration', () => { it("registers release builds by default β€” Android's and iOS's own default", () => { diff --git a/packages/bundler-plugin-core/src/resolve.ts b/packages/bundler-plugin-core/src/resolve.ts index a96349ca..6bb43996 100644 --- a/packages/bundler-plugin-core/src/resolve.ts +++ b/packages/bundler-plugin-core/src/resolve.ts @@ -41,7 +41,7 @@ export interface BugseePluginOptions { /** Disable the plugin entirely (e.g. dev builds). */ disabled?: boolean; /** - * FAIL the build when the source-map upload fails. Default `false` (Wave 7). + * FAIL the build when the source-map upload fails. Default `false`. * * A telemetry side effect must not be able to break a production deploy: before this, an expired token, * a transient network error or a `bugsee-cli` missing from PATH aborted the user's build with no escape @@ -75,7 +75,7 @@ export interface BugseePluginOptions { */ onNotice?: (message: string) => void; /** - * Register this build with Bugsee (docs/design/web-build-registration.md). Falls back to + * Register this build with Bugsee. Falls back to * `BUGSEE_REGISTER_BUILD` (`release` | `always` | `false`). Default `'release'`. * * `'release'` registers a build the BUNDLER calls production β€” Vite's resolved `isProduction`, @@ -151,7 +151,7 @@ export interface ResolvedPluginOptions { } /** Merge plugin options with env vars, apply defaults, and decide whether the plugin is active. */ -/** In-flight uploads, keyed by output directory (Wave 7.5). Entries are removed as each run settles. */ +/** In-flight uploads, keyed by output directory. Entries are removed as each run settles. */ const runsByDir = new Map>(); export function resolvePluginOptions( @@ -204,9 +204,9 @@ export async function runPluginUpload( if (!resolved.enabled) { return undefined; } - // One pipeline per output DIRECTORY (Wave 7.5). `writeBundle` fires once per OUTPUT, so a multi-output + // One pipeline per output DIRECTORY. `writeBundle` fires once per OUTPUT, so a multi-output // config whose entries resolve to the same directory β€” `[{ dir: 'dist' }, { file: 'dist/legacy.js' }]`, - // both `dist` β€” used to start two concurrent pipelines over one tree, with one deleting maps (step 3) + // both `dist` β€” used to start two concurrent pipelines over one tree, with one deleting maps // while the other was still reading them (steps 1-2). A concurrent caller JOINS the in-flight run and // gets its result; the entry is released on settle, so `vite build --watch` still uploads every rebuild. const inFlight = runsByDir.get(outDir); diff --git a/packages/bundler-plugin-core/src/run-cli.test.ts b/packages/bundler-plugin-core/src/run-cli.test.ts index d952b151..87d70093 100644 --- a/packages/bundler-plugin-core/src/run-cli.test.ts +++ b/packages/bundler-plugin-core/src/run-cli.test.ts @@ -163,7 +163,7 @@ describe('the spawned CLI is bounded (Wave 7.4)', () => { it('clears the timer when the child finishes, so nothing keeps the process alive', async () => { // A dangling `setTimeout` would hold the event loop open past the build β€” the same class of defect as - // the un-unref'd watchdog worker (Wave 2.4). + // the un-unref'd watchdog worker. // // The previous body counted `getActiveResourcesInfo()` Timeouts before/after and asserted // `after <= before` β€” which is VACUOUS here, because the watchdog is `unref`'d and an unref'd timer is @@ -242,7 +242,7 @@ describe('spawn options (the security surface)', () => { }); }); -// SEV1 β€” a `bugsee-cli` KILLED BY A SIGNAL was reported as a successful run. +// a `bugsee-cli` KILLED BY A SIGNAL was reported as a successful run. // // `node:child_process` emits `close` with `code === null` (and a `signal`) when the child is terminated // rather than exited: the Linux OOM killer on a big source-map tree, a cancelled CI job's SIGTERM, a diff --git a/packages/bundler-plugin-core/src/run-cli.ts b/packages/bundler-plugin-core/src/run-cli.ts index bdddd10f..bbcd6449 100644 --- a/packages/bundler-plugin-core/src/run-cli.ts +++ b/packages/bundler-plugin-core/src/run-cli.ts @@ -18,7 +18,7 @@ export type SpawnFn = ( options: { cwd?: string; env?: EnvRecord; signal?: AbortSignal }, ) => Promise; -/** Default wall-clock budget for one `bugsee-cli` invocation (Wave 7.4). */ +/** Default wall-clock budget for one `bugsee-cli` invocation. */ const DEFAULT_TIMEOUT_MS = 120_000; /** @@ -60,7 +60,7 @@ export interface RunBugseeCliOptions { /** Base environment (defaults to `process.env`). */ env?: EnvRecord; /** - * Wall-clock budget for the child, in ms. Default 120 000 (Wave 7.4). + * Wall-clock budget for the child, in ms. Default 120 000. * * Without one, the promise settled only on the child's `close`/`error`, so a CLI blocked on a hanging TCP * connect β€” reachable purely through a misconfigured `endpoint` on a firewalled CI network β€” left the diff --git a/packages/bundler-plugin-core/src/sri.ts b/packages/bundler-plugin-core/src/sri.ts index e2279ee9..3641731b 100644 --- a/packages/bundler-plugin-core/src/sri.ts +++ b/packages/bundler-plugin-core/src/sri.ts @@ -13,7 +13,7 @@ // So we look for it and refuse rather than shipping a broken page. Rewriting the hashes is not a // safe alternative: `webpack-subresource-integrity` also embeds the lazy chunks' hashes in the // runtime chunk (`__webpack_require__.sriHashes`), so patching the HTML alone would still break -// every dynamic import. See docs/design/source-maps.md and docs/review/cli-js-flows.md Β§7. +// every dynamic import. import { createHash } from 'node:crypto'; import type { Dirent } from 'node:fs'; import { readdir, readFile } from 'node:fs/promises'; @@ -163,7 +163,7 @@ const SRI_ALGORITHMS = ['sha256', 'sha384', 'sha512'] as const; type SriAlgorithm = (typeof SRI_ALGORITHMS)[number]; /** - * Whether `bytes` would pass `integrity`, by the browser's rule (SRI Β§3.3.5): unknown algorithms are + * Whether `bytes` would pass `integrity`, by the browser's rule (SRI): unknown algorithms are * ignored; of what remains only the STRONGEST algorithm is compared; any one digest of it matching is * enough. A value with no algorithm the browser knows means no check at all. */ diff --git a/packages/bundler-plugin-core/src/stamp-assets.ts b/packages/bundler-plugin-core/src/stamp-assets.ts index 44379c41..f6d57bc4 100644 --- a/packages/bundler-plugin-core/src/stamp-assets.ts +++ b/packages/bundler-plugin-core/src/stamp-assets.ts @@ -1,5 +1,4 @@ // Stamp debug-ids INTO the build, before it is written β€” the real fix for Subresource Integrity -// (docs/review/cli-js-flows.md Β§7, option 1). // // The post-emit path runs `bugsee-cli sourcemaps inject` over the output directory, which appends bytes // to every bundle AFTER a plugin such as `webpack-subresource-integrity` has hashed it. The hash in the @@ -13,7 +12,7 @@ // The Rust `inject` stays the one implementation. This stages the bundles and maps in a temp directory // with the same relative layout, runs the real `sourcemaps inject` over it, and reads back what it // changed. A JavaScript reimplementation of the debug-id algorithm and the runtime stub is exactly the -// second copy that drifts (docs/design/source-maps.md: "spawn the existing Rust bugsee-cli"). +// second copy that drifts ("spawn the existing Rust bugsee-cli"). import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { dirname, isAbsolute, join, normalize, sep } from 'node:path'; diff --git a/packages/bundler-plugin-core/src/vcs.ts b/packages/bundler-plugin-core/src/vcs.ts index 345e0ae7..e1c5adad 100644 --- a/packages/bundler-plugin-core/src/vcs.ts +++ b/packages/bundler-plugin-core/src/vcs.ts @@ -1,6 +1,6 @@ // SM-A4 β€” capture the build's VCS metadata (primarily the commit SHA) so the backend can fetch a frame's // ORIGINAL source from the customer's connected repository when the uploaded source map carries no -// `sourcesContent`. See docs/design/source-maps.md Β§9. +// `sourcesContent`. // // TWO THINGS THIS DELIBERATELY DOES NOT DO: // @@ -8,7 +8,7 @@ // output shape is contractually byte-compatible with the Android Gradle plugin's `VcsMetadataResolver` // and the appserver's `sanitizeVcs`, and the iOS agent + the fastlane plugin were both deleted in favour // of shelling out to it. A fourth implementation, in JavaScript, is exactly the divergence that -// consolidation removed. So we SPAWN it, per the package's D0 (plugins collect context; the CLI is the +// consolidation removed. So we SPAWN it (plugins collect context; the CLI is the // engine). // 2. It does not re-validate or re-shape the resolver's output. The fields are passed through opaquely, so // a provider added on the backend does not need a matching release here to survive the trip. The one diff --git a/packages/bundler-plugin-core/vitest.config.ts b/packages/bundler-plugin-core/vitest.config.ts index 1bf5471d..558ca571 100644 --- a/packages/bundler-plugin-core/vitest.config.ts +++ b/packages/bundler-plugin-core/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. export default defineConfig({ test: { // The runner is far slower than a dev machine β€” roughly 7-18x under coverage instrumentation β€” and diff --git a/packages/capture/src/absolutize-url.ts b/packages/capture/src/absolutize-url.ts index e62af3b0..029b246a 100644 --- a/packages/capture/src/absolutize-url.ts +++ b/packages/capture/src/absolutize-url.ts @@ -1,4 +1,4 @@ -// Absolutization of captured network URLs (protocol + host + port), design Β§16.1. +// Absolutization of captured network URLs (protocol + host + port), // // Every network SOURCE records the target the caller passed, so a same-origin `fetch('/api/x')` was // stored as `/api/x` while a cross-origin call was stored as `http://localhost:5398/nope`. Same capture diff --git a/packages/capture/src/console-interceptor.ts b/packages/capture/src/console-interceptor.ts index ee3d5ab7..e9adc817 100644 --- a/packages/capture/src/console-interceptor.ts +++ b/packages/capture/src/console-interceptor.ts @@ -9,7 +9,7 @@ import { import type { LogLevelName } from '@bugsee/types'; import { jsonSafeStringify } from '@bugsee/util'; -// Cross-runtime console capture SOURCE (design Β§16.2): patches the global `console` methods, emits a +// Cross-runtime console capture SOURCE: patches the global `console` methods, emits a // LogEvent to client.hubs.log on each call, and calls through to the original so the app's own // logging is preserved. `console` is universal across every target runtime, and patching // `globalThis.console[method]` is the same technique everywhere, so this lives in the shared diff --git a/packages/capture/src/detector-url-corpus.test-d.ts b/packages/capture/src/detector-url-corpus.test-d.ts index 5772352f..12efe676 100644 --- a/packages/capture/src/detector-url-corpus.test-d.ts +++ b/packages/capture/src/detector-url-corpus.test-d.ts @@ -1,6 +1,6 @@ -// Seeded corpora for the URL sanitizer's acceptance numbers (R5V-1). Not a test file and not shipped (excluded from +// Seeded corpora for the URL sanitizer's acceptance numbers. Not a test file and not shipped (excluded from // coverage and from the package entry): the unit test imports it, and the 200k scratch measurement imports it too, so -// the figures reported in docs/review/mobile-alignment-round-5.md are produced by exactly the code the test runs. +// the figures reported in are produced by exactly the code the test runs. /** mulberry32: a deterministic PRNG so every figure is reproducible, not luck. */ export function mulberry32(seed: number): () => number { diff --git a/packages/capture/src/detector-url-sanitizer.corpus.test.ts b/packages/capture/src/detector-url-sanitizer.corpus.test.ts index 3eedeb94..14079927 100644 --- a/packages/capture/src/detector-url-sanitizer.corpus.test.ts +++ b/packages/capture/src/detector-url-sanitizer.corpus.test.ts @@ -8,7 +8,7 @@ import { } from './detector-url-corpus.test-d'; import { templatePathSegment } from './detector-url-sanitizer'; -// R5V-1. The word check counted any run of three lower-case letters as a word, so lower-case random tokens (cuid2, +// The word check counted any run of three lower-case letters as a word, so lower-case random tokens (cuid2, // base32, nanoid, ULID, base36) were kept verbatim. A single-case string with no separator is never word-like; a // camel/Pascal identifier needs two Title-case pieces that can each be pronounced; a slug needs pronounceable pieces. describe('templatePathSegment: lower-case random tokens are never "words" (R5V-1)', () => { @@ -22,7 +22,7 @@ describe('templatePathSegment: lower-case random tokens are never "words" (R5V-1 // English words but no separator and no case change: not provably words (privacy over grouping) 'getuserdetailsandsettings2', 'updatenotificationsettings3', - 'abcdefgh1abcdefgh2', // two plain words with a number between, no separator (was kept as "words" in R4V-5) + 'abcdefgh1abcdefgh2', // two plain words with a number between, no separator (was kept as "words" in) 'CLH3AM4PU0000QFW9RG5D1ZK', 'GETUSERDETAILSANDSETTINGS2', ])('templates the single-case string %j', (segment) => { @@ -95,7 +95,7 @@ describe('templatePathSegment: lower-case random tokens are never "words" (R5V-1 }); // The acceptance numbers. Deterministic: the same seed produces the same tokens, so a figure is a fact, not luck. - // Ceilings are the pre-round-4 (a98dabb, no word check at all) keep rates measured on THIS corpus (same seed, + // Ceilings are the (a98dabb, no word check at all) keep rates measured on THIS corpus (same seed, // 20,000 tokens a bucket), rounded up to 0.05. The word check may not raise a bucket above them (two base62 // buckets sit one token in 20,000 above the measured a98dabb figure, inside the rounding). Where the review named // a figure it is used as is: base36-lower 24-31 <= 0.1%, cuid2 24-31 <= 0.2%, base32-lower 24-31 <= 1%. @@ -149,7 +149,7 @@ describe('templatePathSegment: lower-case random tokens are never "words" (R5V-1 }); }); -// R5V-2 / R5V-3. +// /. describe('templatePathSegment: e-mail on ANY @, but a version or an @2x asset is not an address (R5V-2/3)', () => { it.each([ 'react@latest', @@ -200,7 +200,7 @@ describe('templatePathSegment: e-mail on ANY @, but a version or an @2x asset is }); }); -// R5V-10: the cheap pre-round-4 misses. +// the cheap misses. describe('templatePathSegment: addresses, phones and hashed assets (R5V-10)', () => { it.each([ ['10.0.0.1:8080', '{ip}'], diff --git a/packages/capture/src/detector-url-sanitizer.test.ts b/packages/capture/src/detector-url-sanitizer.test.ts index 7b7ff869..6deb46c9 100644 --- a/packages/capture/src/detector-url-sanitizer.test.ts +++ b/packages/capture/src/detector-url-sanitizer.test.ts @@ -91,7 +91,7 @@ describe('templatePathSegment', () => { }); }); -// R2P-8. Short ids and PII the first rule set missed, and the over-templating it did. +// Short ids and PII the first rule set missed, and the over-templating it did. describe('templatePathSegment: short ids, PII-shaped values and dates (R2P-8)', () => { it.each([ // nanoid(21) and a Firebase push id (20): mixed case + digits @@ -168,15 +168,15 @@ describe('templatePathSegment: short ids, PII-shaped values and dates (R2P-8)', ['ord_123', 'ord_{id}'], // slug detection: 3 plain words is a slug, 2 is not ['internationalization-guide-ab1', 'internationalization-guide-ab1'], - // ... but the words must be pronounceable: the alphabet is not a word (R5V-1) + // ... but the words must be pronounceable: the alphabet is not a word ['abcdefghijklmnopqrstuvwx-yz-ab1', '{token}'], ['abcdefghijklmnopqrstuvwx-yz1', '{token}'], // fixed-length ids need a number AND both cases, and exactly 20 or 21 characters ['abcdefghijklmnopqrst1', '{token}'], ['ABCDEFGHIJKLMNOPQRST1', '{token}'], ['AbCdEfGhIjKlMnOpQrStU', 'AbCdEfGhIjKlMnOpQrStU'], - ['AbCdEfGhIjKlMnOpQrSt1u', '{token}'], // 22 chars: past SHORT_ID, below the 24-char token floor (R3V-4) - ['AbCdEfGhIjKlMnOpQr1', '{token}'], // 19 chars, mixed case + a digit (R3V-4) + ['AbCdEfGhIjKlMnOpQrSt1u', '{token}'], // 22 chars: past SHORT_ID, below the 24-char token floor + ['AbCdEfGhIjKlMnOpQr1', '{token}'], // 19 chars, mixed case + a digit ['AbCdEfGhIjKlMnOpQrS1', '{id}'], ['AbCdEfGhIjKlMnOpQrSt1', '{id}'], ])('boundary: %j -> %j', (segment, expected) => { @@ -237,7 +237,7 @@ describe('no ReDoS: adversarial 1,000,000-character inputs finish in bounded tim }); }); -// R3V-4. Leaks the round-2 rules missed: 16-23 character tokens, card numbers with separators, and an address on a +// Leaks the rules missed: 16-23 character tokens, card numbers with separators, and an address on a // dot-less host. Each positive is paired with the ordinary segment it must NOT swallow. describe('templatePathSegment: short tokens, card numbers and dot-less e-mail hosts (R3V-4)', () => { it.each([ @@ -327,7 +327,7 @@ describe('R3V-4 rules stay linear: 1,000,000-character adversarial inputs', () = }); }); -// R4V-5: the 16-23 character `{token}` rule merged ordinary camelCase / versioned route names (every endpoint +// the 16-23 character `{token}` rule merged ordinary camelCase / versioned route names (every endpoint // became one issue). It now needs RANDOMNESS: a segment made of words is an identifier, not a secret. describe('templatePathSegment: identifiers that read as words are NOT tokens (R4V-5)', () => { it.each([ @@ -451,7 +451,7 @@ describe('templatePathSegment: e-mail uses the LAST @ and ignores dist-tags (R4V ); }); - // No wall-clock assertion (R5V-10: it measured 11.7 s on the CI runner against a 30 s bound): a quadratic or + // No wall-clock assertion (it measured 11.7 s on the CI runner against a 30 s bound): a quadratic or // catastrophic regression hangs, and the test timeout is the hang guard. it('the word check stays linear: one million probes and million-character adversarial segments', () => { expect(templatePathSegment('a@'.repeat(500_000))).toBe('{token}'); // past the segment cap diff --git a/packages/capture/src/detector-url-sanitizer.ts b/packages/capture/src/detector-url-sanitizer.ts index 408ce373..c33e1cb2 100644 --- a/packages/capture/src/detector-url-sanitizer.ts +++ b/packages/capture/src/detector-url-sanitizer.ts @@ -7,7 +7,7 @@ // endpoint and an e-mail address in a path can never reach a summary. // Android templates only when a network filter is configured; this SDK templates ALWAYS, because the // default-sanitized timeline is not what a detector reads (it sees the raw event) and a templated path is -// also what makes grouping useful. Recorded under "Kept differences" in docs/design/mobile-alignment.md. +// also what makes grouping useful. Recorded under "Kept differences" in. const ALL_DIGITS = /^[0-9]+$/; const UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/; @@ -41,7 +41,7 @@ const SLUG_CODE_PIECE = /^(?:[A-Za-z]{1,2}[0-9]{0,4}|[0-9]{1,2}[A-Za-z]{1,3})$/; const MID_DIGIT = /[0-9][A-Za-z]/; const VERSION_MARKER = /^[A-Za-z]{1,2}[0-9]{1,2}[A-Za-z]{3,6}[0-9]{0,2}$/; const MID_DIGIT_WORD_MAX = 8; // `v1beta1`, `covid19x2`: the longest slug word with a number inside it -// Word-likeness (R4V-5, R5V-1). No dictionary: a piece "reads as a word" when it can be pronounced, a cheap test on +// Word-likeness. No dictionary: a piece "reads as a word" when it can be pronounced, a cheap test on // its vowel/consonant structure that real words pass and random letters almost never do. A string with no separator // and a single case can never be shown to be words (a lower-case cuid2 or base32 id is made of the same letters), so // only camelCase/PascalCase identifiers and separator-delimited slugs are ever word-like. @@ -423,7 +423,7 @@ const PHONE_MAX_LENGTH = 15; /** No real identifier is this long; a longer segment is a blob (and was never going to be a readable slug). */ const MAX_SEGMENT_LENGTH = 256; -/** npm/CDN dist-tags: `react@latest` is a package spec, not an address (R4V-6). */ +/** npm/CDN dist-tags: `react@latest` is a package spec, not an address. */ const DIST_TAGS = new Set([ 'latest', 'next', @@ -436,12 +436,12 @@ const DIST_TAGS = new Set([ 'experimental', ]); -// A version (`18.2.0`, `18`) or a retina asset (`2x.png`) after an `@` is a package spec / file name, not a domain (R5V-3). +// A version (`18.2.0`, `18`) or a retina asset (`2x.png`) after an `@` is a package spec / file name, not a domain. const VERSION_HOST = /^[0-9.]+$/; const RETINA_HOST = /^[0-9]+x(?:\.[A-Za-z][A-Za-z0-9]{0,4})?$/; // `logo@2x` is the stem of `logo@2x.png` /** - * Whether ANY `@` of the segment (not the first character) yields an address (R4V-6, R5V-2): `@alice@example.com`, + * Whether ANY `@` of the segment (not the first character) yields an address: `@alice@example.com`, * `alice@example.com@latest`, `jane.doe@corp.com@next`. One linear pass: a dotted host after a NON-last `@` is an * address whatever follows it (the dot test is a single `lastIndexOf`), and the last `@`, whose host contains no `@`, * is judged on its own (a version, a retina asset or a dist-tag is a package spec, not a host). @@ -464,12 +464,12 @@ function looksLikeEmail(segment: string): boolean { if (dot > 0 && dot < host.length - 1) { return true; } - // Or a dot-less host (`user@localhost`): still an address (R3V-4). One character is not a host (`a@b`), and a + // Or a dot-less host (`user@localhost`): still an address. One character is not a host (`a@b`), and a // dist-tag is a package version, not a host. return HOST_LABEL.test(host) && !DIST_TAGS.has(host.toLowerCase()); } -/** `10.0.0.1:8080`, `[::1]`, `[2001:db8::1]:8443`: an address with an optional port (R5V-10). */ +/** `10.0.0.1:8080`, `[::1]`, `[2001:db8::1]:8443`: an address with an optional port. */ function looksLikeIpWithPort(segment: string): boolean { if (IPV4_PORT.test(segment)) { return true; @@ -481,7 +481,7 @@ function looksLikeIpWithPort(segment: string): boolean { return PORT_SUFFIX.test(segment.slice(close + 1)) && looksLikeIp(segment.slice(1, close)); } -/** A file name with a content hash in the middle (`main.3f9a2c1b.chunk`, `bundle.068e178e`): templates the hash only (R5V-10). */ +/** A file name with a content hash in the middle (`main.3f9a2c1b.chunk`, `bundle.068e178e`): templates the hash only. */ function templateHashedPieces(stem: string): string { if (!stem.includes('.')) { return stem; @@ -562,7 +562,7 @@ function consonantUnits(run: string): number { } /** - * Whether a lower-case run of letters can be pronounced (R5V-1): at least one vowel in nine letters and at most 80% + * Whether a lower-case run of letters can be pronounced: at least one vowel in nine letters and at most 80% * vowels, no `q` without a `u`, no letter three times in a row, no consonant run of 3+ and no vowel run of 3+ except * the explicit clusters above (`street`, `lengths`, `beautiful`, `queue`). `y` is a vowel for the ratio and ends a run * (`layout`, `player`). A known vowel-less abbreviation (`html`) passes. Real words pass; random letters almost never @@ -765,7 +765,7 @@ function isCamelIdentifier(segment: string, inSlug = false): boolean { } /** - * A separator-delimited slug (R5V-1) in which words outnumber the numbers: a word is a camelCase + * A separator-delimited slug in which words outnumber the numbers: a word is a camelCase * identifier or 3-12 letters (one case, or Title) that can be pronounced, with up to four trailing digits (`guide2`); * a number is up to four digits or a short code (`2024`, `v2`, `q3`, `h264`, `2fa`). A piece that mixes letters and * digits any other way (`k3j4`, `ab3de`) is what a random id is made of. @@ -801,7 +801,7 @@ function isWordSlug(segment: string): boolean { } /** - * Whether a segment READS as an identifier made of words rather than a random token (R4V-5, R5V-1). Never true for a + * Whether a segment READS as an identifier made of words rather than a random token. Never true for a * single-case string without a separator: `clh3am4pu0000qfw9rg5d1zk` is as long and as lower-case as any English * run-on, and keeping it verbatim leaks an id (measured: a lower-case coverage rule kept 8-30% of cuid2 / base32 / * base36 ids). Privacy over grouping: what cannot be shown to be words is templated. @@ -896,7 +896,7 @@ export function templatePathSegment(segment: string): string { return '{token}'; } // `%40` is how an e-mail address is usually encoded into a path; a phone number's space is `%20`. - // Decoded twice: `%2540` is an `@` that was encoded again by a proxy or a framework router (R5V-10). + // Decoded twice: `%2540` is an `@` that was encoded again by a proxy or a framework router. const decoded = segment.includes('%') ? safeDecode(safeDecode(segment)) : segment; if (looksLikeEmail(segment) || looksLikeEmail(decoded)) { return '{email}'; diff --git a/packages/capture/src/fetch-interceptor.test.ts b/packages/capture/src/fetch-interceptor.test.ts index 852d744d..1c30ef9c 100644 --- a/packages/capture/src/fetch-interceptor.test.ts +++ b/packages/capture/src/fetch-interceptor.test.ts @@ -580,7 +580,7 @@ describe('createFetchInterceptor β€” response body', () => { /** * `cancel()` may also return a REJECTED promise (a stream errored while being torn down). Release is * best-effort, so the rejection is swallowed β€” but it must be swallowed, not left unhandled: an - * unhandled rejection in a page the SDK merely observes is exactly the kind of noise Wave 2.1 forbids. + * unhandled rejection in a page the SDK merely observes is exactly the kind of noise the SDK must never make. */ it('swallows a cancel() that rejects, without an unhandled rejection', async () => { // This tier has no Node or DOM lib (it is runtime-portable), so the host hooks come through a cast. diff --git a/packages/capture/src/fetch-interceptor.ts b/packages/capture/src/fetch-interceptor.ts index 31afcf83..ef2c6c28 100644 --- a/packages/capture/src/fetch-interceptor.ts +++ b/packages/capture/src/fetch-interceptor.ts @@ -14,7 +14,7 @@ import { type RequestDecoratorRegistry, } from './request-decorator'; -// Cross-runtime fetch capture SOURCE (design Β§16.2): wraps `fetch` and emits NetworkEvents per stage +// Cross-runtime fetch capture SOURCE: wraps `fetch` and emits NetworkEvents per stage // (before β†’ complete | error). global `fetch` is universal (browser/workers/Nodeβ‰₯18/Bun/Deno/edge), // and wrapping it is the same technique everywhere, so this is shared. The wrap is installed only // while ACTIVE (subscriber-presence / explicit start, via InterceptorBase) and removed when idle. @@ -22,7 +22,7 @@ import { // Metadata-first: url / method / request+response headers / status / timing. Request/response BODIES // are deferred (body capture, size limits, content-type rules are a follow-up). Sanitization is the // consumer's job (networkProvider) β€” the interceptor emits raw events. SDK self-isolation skips the -// SDK's own outbound requests (X-Bugsee-Internal, Β§14.6). Subscribe via on('complete', …) / onAny. +// SDK's own outbound requests (X-Bugsee-Internal). Subscribe via on('complete', …) / onAny. // `fetch` is a DOM/Node lib global; this package carries no such lib types, so reach it via casts. type FetchFn = (input: unknown, init?: unknown) => Promise; @@ -210,7 +210,7 @@ const requestHeaders = (input: unknown, init: unknown): Record = return {}; }; -/** Default self-isolation: skip the SDK's own outbound requests, tagged X-Bugsee-Internal (Β§14.6). */ +/** Default self-isolation: skip the SDK's own outbound requests, tagged X-Bugsee-Internal. */ const hasInternalHeader = (headers: Record): boolean => Object.keys(headers).some((key) => key.toLowerCase() === 'x-bugsee-internal'); diff --git a/packages/capture/src/index.ts b/packages/capture/src/index.ts index 186888bb..b1f3e8fb 100644 --- a/packages/capture/src/index.ts +++ b/packages/capture/src/index.ts @@ -1,11 +1,9 @@ -// @bugsee/capture β€” cross-runtime capture sources + runtime-agnostic providers (design Β§16, shared -// tier-3). Universal interceptors (console/fetch/…) own a global hook and emit to a core hub; the +// @bugsee/capture β€” cross-runtime capture sources + runtime-agnostic providers. Universal interceptors (console/fetch/…) own a global hook and emit to a core hub; the // runtime-agnostic CaptureProviders consume those hubs. Platform packages compose this and add only // their runtime-specific sources (XHR, node:http, error/rejection globals). // `absolutizeUrl`/`resolveBaseUrl`/`UrlBaseGlobals` (./absolutize-url) are an internal helper of // network-provider.ts (imported directly, not via this barrel) β€” no external consumer, not re-exported -// (R3-13). export { type ConsoleInterceptorOptions, type ConsoleStageMap, @@ -23,7 +21,7 @@ export { } from './http-error-provider'; // `InputSource`/`InputProviderOptions` (./input-provider) are structural-typing helpers with no external // consumer β€” callers pass a value that satisfies `InputSource` (e.g. browser's `BrowserInputSource`) -// without ever naming the type, and no caller passes provider options. Not re-exported (R3-13). +// without ever naming the type, and no caller passes provider options. Not re-exported. // `createInputProvider`/`InputEventDetail` ARE genuinely cross-package (browser + webview both consume // them) and stay. export { createInputProvider, type InputEventDetail } from './input-provider'; @@ -50,7 +48,7 @@ export { // `createSendBeaconInterceptor`/`SendBeaconInterceptorOptions`/`SendBeaconTarget` (./send-beacon-interceptor) // are wired internally by install-network-capture.ts (imported directly, not via this barrel) β€” every // platform reaches sendBeacon capture only through `installNetworkCapture()`, never by constructing the -// interceptor itself. No external consumer, not re-exported (R3-13). +// interceptor itself. No external consumer, not re-exported. export { createSseInterceptor, type SseInterceptorOptions, diff --git a/packages/capture/src/input-provider.ts b/packages/capture/src/input-provider.ts index fe1df268..f7d10509 100644 --- a/packages/capture/src/input-provider.ts +++ b/packages/capture/src/input-provider.ts @@ -7,7 +7,7 @@ import { } from '@bugsee/core'; import { BugseeOption } from '@bugsee/protocol'; -// Runtime-agnostic INPUT provider (design Β§16.1, Android `input` / `input.json` parity). An input event is +// Runtime-agnostic INPUT provider (Android `input` / `input.json` parity). An input event is // one press or release the SDK OBSERVED a device make: a touch/mouse/pen pointer going down or up, a key // going down. While started, the provider subscribes to an input SOURCE and routes each event to the // aggregator as an `input` entry, stamping it from the clock when the source did not. diff --git a/packages/capture/src/log-provider.test.ts b/packages/capture/src/log-provider.test.ts index 0ad924eb..11fcfd56 100644 --- a/packages/capture/src/log-provider.test.ts +++ b/packages/capture/src/log-provider.test.ts @@ -66,7 +66,7 @@ describe('createLogCaptureProvider', () => { expect(entries).toHaveLength(1); expect(entries?.[0]?.type).toBe('log'); expect(entries?.[0]?.timestamp).toBe(5); - // The entry carries the NUMERIC wire level (Wave 5.1) while the emitted event keeps its friendly + // The entry carries the NUMERIC wire level while the emitted event keeps its friendly // name, so this compares against the encoded form rather than the raw event. expect(entries?.[0]?.data).toEqual({ ...event, level: 2 }); }); diff --git a/packages/capture/src/log-provider.ts b/packages/capture/src/log-provider.ts index bfac72eb..5f0b5d11 100644 --- a/packages/capture/src/log-provider.ts +++ b/packages/capture/src/log-provider.ts @@ -12,7 +12,7 @@ import { import { BugseeOption, logLevelToWire } from '@bugsee/protocol'; import type { LogLevelName } from '@bugsee/types'; -// Runtime-agnostic log capture CONSUMER (design Β§16.1): subscribes to a log SOURCE and routes each +// Runtime-agnostic log capture CONSUMER: subscribes to a log SOURCE and routes each // LogEvent to the aggregator as a `log` capture entry. The source is any emitter with a 'log' stage β€” // the shared consoleInterceptor, or a platform log source β€” so this single provider serves every // runtime; only the sources vary. Subscribing also drives the source's subscriber-presence activation @@ -22,12 +22,12 @@ import type { LogLevelName } from '@bugsee/types'; export type LogSource = EventSubscribable<{ log: LogEvent }>; /** - * Encode the log level to its NUMERIC wire value (design Β§8.9, mobile parity). + * Encode the log level to its NUMERIC wire value (mobile parity). * * `LogEvent.level` is `LogLevelName | LogLevel`, sources emit the friendly NAME, and nothing converted β€” so * every console-captured line reached the backend as `"error"` where the viewer expects `1`. * `logLevelToWire` had existed in @bugsee/protocol, exported and unit-tested, with zero callers outside its - * own test (Wave 5.1). + * own test. * * Applied HERE because the provider is the single point every log entry passes through, and AFTER the user * filter: a `logFilter` is customer code written against the documented string levels, so converting first diff --git a/packages/capture/src/network-body.test.ts b/packages/capture/src/network-body.test.ts index a93c8acb..4dd3b4bf 100644 --- a/packages/capture/src/network-body.test.ts +++ b/packages/capture/src/network-body.test.ts @@ -87,7 +87,7 @@ describe('decodeUtf8', () => { }); it('reassembles a multibyte sequence genuinely split across two chunks before decoding', () => { - // '€' is 3 UTF-8 bytes (E2 82 AC); split across two chunks. decodeUtf8 concatenates into one + // '€' is 3 UTF-8 bytes (82 AC); split across two chunks. decodeUtf8 concatenates into one // buffer before decoding, so the character is recovered (per-chunk decode would yield U+FFFD). const bytes = new TE().encode('€'); expect(decodeUtf8([bytes.slice(0, 2), bytes.slice(2)])).toBe('€'); diff --git a/packages/capture/src/network-interceptor.ts b/packages/capture/src/network-interceptor.ts index 30dce2ff..89b54dfe 100644 --- a/packages/capture/src/network-interceptor.ts +++ b/packages/capture/src/network-interceptor.ts @@ -6,7 +6,7 @@ import type { RequestDecoratable, RequestDecorator } from './request-decorator'; const isDecoratable = (source: NetworkSource): source is NetworkSource & RequestDecoratable => typeof (source as Partial).addRequestDecorator === 'function'; -// Umbrella network SOURCE (design Β§16): a composite interceptor that aggregates the per-mechanism +// Umbrella network SOURCE: a composite interceptor that aggregates the per-mechanism // network interceptors (fetch/xhr/ws/sse/webtransport) into ONE subscribable stream β€” so a consumer // (the networkProvider, APM, user code) subscribes ONCE for ALL network events instead of to N // sources. Each NetworkEvent carries a unique id + sequence, so a subscriber can still pick out a diff --git a/packages/capture/src/network-provider.test.ts b/packages/capture/src/network-provider.test.ts index cf326abb..bd85809f 100644 --- a/packages/capture/src/network-provider.test.ts +++ b/packages/capture/src/network-provider.test.ts @@ -171,10 +171,9 @@ describe('createNetworkCaptureProvider', () => { expect(event.custom?.headers).toEqual({ authorization: 'secret-token', accept: 'json' }); }); - // Wave 1.1. Every network source writes `event.url` verbatim and the provider is the ONE place that + // Every network source writes `event.url` verbatim and the provider is the ONE place that // redacts β€” so a secret in the URL reached disk on every transport, and on node:http too (it folds into - // this same provider via `additionalSources`). Reproduced end-to-end in docs/review/capture.md SEV1 #4 - // and docs/review/node-B-http-server.md SEV1 #3. + // this same provider via `additionalSources`). Reproduced end-to-end. it('redacts a sensitive query value in the captured URL (non-mutating)', async () => { const store = mkStore(); const source = mkSource(); diff --git a/packages/capture/src/network-provider.ts b/packages/capture/src/network-provider.ts index 19dfe262..e54a0f19 100644 --- a/packages/capture/src/network-provider.ts +++ b/packages/capture/src/network-provider.ts @@ -20,7 +20,7 @@ import { import { absolutizeUrl } from './absolutize-url'; -// Runtime-agnostic network capture CONSUMER (design Β§16.1): subscribes to one or more network SOURCES +// Runtime-agnostic network capture CONSUMER: subscribes to one or more network SOURCES // (the fetch interceptor, and later xhr/ws/webtransport/sse) and routes every NetworkEvent β€” at any // stage β€” to the aggregator as a `network` entry. ONE provider serves all transports because they all // emit the same NetworkEvent shape; only the sources vary. Subscribing drives each source's @@ -29,7 +29,7 @@ import { absolutizeUrl } from './absolutize-url'; /** A network source β€” any emitter exposing NetworkStage channels (e.g. the fetch interceptor). */ export type NetworkSource = EventSubscribable>; -// Per-event default PII redaction (Β§8.10, [R:wire m9]): redact the URL (sensitive query/fragment params +// Per-event default PII redaction: redact the URL (sensitive query/fragment params // and any `user:pass@` credential), the sensitive request/response headers, and the captured body by // Content-Type (JSON key denylist, else form/colon key redaction plus a shape pass). Non-mutating (the hub // event other subscribers see stays raw); returns the same event when there is nothing to redact. @@ -38,7 +38,6 @@ export type NetworkSource = EventSubscribable // body, so an early return on `custom === undefined` would ship `wss://…?token=…` verbatim. This provider // is the single redaction point for every transport β€” including node:http, which folds in through // `installNetworkCapture({ additionalSources })` β€” so a URL not scrubbed here is not scrubbed anywhere -// (docs/review/capture.md SEV1 #4, docs/review/node-B-http-server.md SEV1 #3). /** Optional free text: sanitized when present, passed through untouched (and identity-preserving) when not, * so the unchanged-checks below still short-circuit on an event that carried none of these fields. */ const sanitizeText = (value: string | undefined): string | undefined => @@ -204,7 +203,7 @@ class NetworkCaptureProvider extends CaptureProviderBase { // `sanitizeHeaders` in @bugsee/protocol for what the default sanitizer does, so a filter author knows // what they've taken over). This is intentional, not a bug β€” Android is this SDK's binding API/ // architecture parity target (CLAUDE.md) β€” but it is easy to miss, so it is called out here at the - // one place that implements it, not just in a design doc. + // one place that implements it, not just in documentation. // // The body policy (master toggle, size limit, allow-without-type) is read once per launch and // applied to every event before the filter/sanitizer XOR. diff --git a/packages/capture/src/request-decorator.ts b/packages/capture/src/request-decorator.ts index f9337cd4..81aab373 100644 --- a/packages/capture/src/request-decorator.ts +++ b/packages/capture/src/request-decorator.ts @@ -1,4 +1,4 @@ -// The interception-transformer seam (design: docs/design/opentelemetry-integration.md, D6). A capture +// The interception-transformer seam. A capture // interceptor is OBSERVE-ONLY on its own; a request decorator is the explicit, opt-in way the data it // pipes through may be altered β€” today, adding headers to an outgoing request (the OTel `traceparent` // propagation transformer is the first consumer). Distinct from redaction filters: redaction transforms diff --git a/packages/capture/src/send-beacon-interceptor.ts b/packages/capture/src/send-beacon-interceptor.ts index 3bb015e1..b84ab3c9 100644 --- a/packages/capture/src/send-beacon-interceptor.ts +++ b/packages/capture/src/send-beacon-interceptor.ts @@ -2,7 +2,7 @@ import { type Interceptor, InterceptorBase } from '@bugsee/core'; import type { NetworkEvent, NetworkStage } from '@bugsee/protocol'; import { readSyncRequestBody } from './network-body'; -// navigator.sendBeacon capture SOURCE (design Β§16.2; the mechanism the wire has declared since +// navigator.sendBeacon capture SOURCE (the mechanism the wire has declared since // [wire.ts NetworkMechanism] but nothing implemented). sendBeacon is the standard way an application // ships telemetry from `pagehide`/`visibilitychange`, so leaving it uninstrumented was not only a blind // spot in the recording: beacon traffic bypassed the user network filter AND the built-in PII sanitizer, @@ -13,7 +13,7 @@ import { readSyncRequestBody } from './network-body'; // EventSource siblings. Wrapping is a swap of `navigator.sendBeacon`; the original is put back on // deactivate. Installed only while ACTIVE (subscriber-presence / start, via InterceptorBase). // -// Stages (wire contract Β§8.7 for request/response transports): `before` β†’ `complete`. sendBeacon +// Stages (wire contract for request/response transports): `before` β†’ `complete`. sendBeacon // returns a boolean SYNCHRONOUSLY β€” the user agent queued the payload, or refused it β€” and has no // response at all: no status, no status text, no response headers, and no way to learn the outcome of // the transmission. So `complete` deliberately carries NO `status`: a refusal is not an HTTP failure @@ -28,8 +28,7 @@ import { readSyncRequestBody } from './network-body'; // events like any other source. Payloads that are not synchronously readable (Blob / FormData / binary) // are reported with the existing `cant_read_data` reason rather than blocked on or guessed at. // -// `size` is intentionally not set β€” no interceptor in this SDK sets it (docs/design/ -// viewer-wire-compatibility.md Β§3.5), and the viewer derives it from the body when present. +// `size` is intentionally not set β€” no interceptor in this SDK sets it. The viewer derives it from the body when present. /** `navigator.sendBeacon` β€” a DOM global this package carries no lib types for, so reach it via casts. */ type SendBeaconFn = (url: unknown, data?: unknown) => boolean; @@ -49,7 +48,7 @@ const globalSendBeaconTarget: SendBeaconTarget = { }, }; -/** Default self-isolation: skip the SDK's own outbound requests, tagged X-Bugsee-Internal (Β§14.6). +/** Default self-isolation: skip the SDK's own outbound requests, tagged X-Bugsee-Internal. * A beacon carries no caller headers, so this default never fires in practice β€” the seam exists so a * platform can self-isolate its own beacons by URL. */ const hasInternalHeader = (headers: Record): boolean => diff --git a/packages/capture/src/sse-interceptor.ts b/packages/capture/src/sse-interceptor.ts index 5f0d05b7..07821dbb 100644 --- a/packages/capture/src/sse-interceptor.ts +++ b/packages/capture/src/sse-interceptor.ts @@ -1,7 +1,7 @@ import { type Interceptor, InterceptorBase } from '@bugsee/core'; import type { NetworkEvent, NetworkStage } from '@bugsee/protocol'; -// Server-Sent Events (EventSource) capture SOURCE (design Β§16.2). EventSource is browser/Deno (and +// Server-Sent Events (EventSource) capture SOURCE. EventSource is browser/Deno (and // Node β‰₯ very recent), so the wrap installs only when it exists. Each connection is instrumented at // creation by replacing the constructor with a thin subclass; one id+sequence per connection. SSE is // read-only (serverβ†’client), so every message is direction 'in', with `channel` = the event name. diff --git a/packages/capture/src/system-events-provider.ts b/packages/capture/src/system-events-provider.ts index 1f97773d..12146da1 100644 --- a/packages/capture/src/system-events-provider.ts +++ b/packages/capture/src/system-events-provider.ts @@ -1,7 +1,7 @@ import { type CaptureProvider, CaptureProviderBase, type EventSubscribable } from '@bugsee/core'; import { BugseeOption } from '@bugsee/protocol'; -// Runtime-agnostic SYSTEM EVENTS provider (design Β§16.1, Android events.system parity). A system event +// Runtime-agnostic SYSTEM EVENTS provider (Android events.system parity). A system event // is a discrete, auto-captured occurrence (process lifecycle, memory pressure, …). While started, the // provider subscribes to a system-event SOURCE and routes each event to the aggregator as an // 'events.system' entry, stamping it from the clock. The shell is runtime-agnostic; WHICH events exist diff --git a/packages/capture/src/system-traces-provider.ts b/packages/capture/src/system-traces-provider.ts index 978e43bd..5d9b6cd5 100644 --- a/packages/capture/src/system-traces-provider.ts +++ b/packages/capture/src/system-traces-provider.ts @@ -1,7 +1,7 @@ import { type CaptureProvider, CaptureProviderBase, type Scheduler } from '@bugsee/core'; import { BugseeOption } from '@bugsee/protocol'; -// Runtime-agnostic SYSTEM TRACES provider (design Β§16.1, Android traces.system parity). A trace is a +// Runtime-agnostic SYSTEM TRACES provider (Android traces.system parity). A trace is a // named value sampled over TIME. While started, it periodically calls an injected `sample()` and routes // each {name, value} to the aggregator as a `traces.system` entry. The shell is runtime-agnostic; WHAT // to sample (process memory/cpu/event-loop lag on Node; performance.memory on browser) is the injected diff --git a/packages/capture/src/traceparent.targets.test.ts b/packages/capture/src/traceparent.targets.test.ts index c48e3e11..a822d11e 100644 --- a/packages/capture/src/traceparent.targets.test.ts +++ b/packages/capture/src/traceparent.targets.test.ts @@ -19,7 +19,7 @@ const make = ( ...(onError !== undefined ? { onError } : {}), }); -// R5V-6. `tracePropagationTargets` decides which THIRD PARTIES receive the trace + session ids. +// `tracePropagationTargets` decides which THIRD PARTIES receive the trace + session ids. describe('createTraceparentDecorator: the allow-list is validated', () => { it('an empty string matches nothing (it is a substring of every URL)', () => { const d = make(['']); diff --git a/packages/capture/src/traceparent.ts b/packages/capture/src/traceparent.ts index 9acd4fa9..547b139d 100644 --- a/packages/capture/src/traceparent.ts +++ b/packages/capture/src/traceparent.ts @@ -8,11 +8,10 @@ import { setTracestateEntry, } from './tracestate'; -// Phase D: the W3C trace-context propagation transformer β€” a RequestDecorator (the T seam's first +// the W3C trace-context propagation transformer β€” a RequestDecorator (its first // consumer) that injects `traceparent` on outgoing requests so a frontend trace links to the backend // trace (the Next.js / SSR story). The trace context comes from the Bugsee active transaction (its // traceId/spanId are already W3C-shaped) β€” no @opentelemetry/* dependency. See -// docs/design/opentelemetry-integration.md. // // SECURITY: same-origin requests propagate by default; CROSS-ORIGIN requests are propagated ONLY when the // URL matches an explicit `allowlist` β€” injecting `traceparent` to a third party would leak the trace @@ -38,7 +37,7 @@ export interface TraceparentDecoratorOptions { resolveOrigin?: (url: string, base: string) => string | undefined; /** * The Bugsee `tracestate` payload to propagate alongside `traceparent` (record flag + the originating - * session-correlation id; cross-project-tracing.md T4/T7). Absent or empty β†’ no `tracestate` is added. + * session-correlation id. Absent or empty β†’ no `tracestate` is added. * On a forwarding hop this carries the INBOUND originator's state (so the originating session id flows * through unchanged), set as the `bugsee=` vendor entry on any tracestate already on the outgoing request. */ @@ -131,7 +130,7 @@ const headerValue = ( type AllowTarget = (url: string) => boolean; /** - * Validate `tracePropagationTargets` ONCE (R5V-6): it decides which third parties receive the trace and session + * Validate `tracePropagationTargets` ONCE: it decides which third parties receive the trace and session * ids, so a value that cannot be understood must send to NOBODY and say so. A non-array is ignored (reported); an * entry that is neither a string nor a RegExp is skipped (reported); an empty string is dropped (`''` is a * substring of every URL: `process.env.TARGETS.split(',')` with the variable unset); a `/g` or `/y` RegExp is @@ -207,7 +206,7 @@ export function createTraceparentDecorator(options: TraceparentDecoratorOptions) const result: Record = { traceparent: `${W3C_VERSION}-${span.getTraceId()}-${span.getSpanId()}-${flags}`, }; - // Bugsee vendor tracestate (T4/T7): set our `bugsee=` entry on any tracestate already on the request, + // Bugsee vendor tracestate: set our `bugsee=` entry on any tracestate already on the request, // preserving other vendors + moving ours to the front. Skipped when there is no Bugsee state to carry. const bugseeState = options.getBugseeState?.(); if (bugseeState !== undefined) { diff --git a/packages/capture/src/tracestate.ts b/packages/capture/src/tracestate.ts index 661a51f2..ef7218c8 100644 --- a/packages/capture/src/tracestate.ts +++ b/packages/capture/src/tracestate.ts @@ -1,8 +1,7 @@ import { utf8ByteLength } from '@bugsee/util'; // W3C `tracestate` codec (https://www.w3.org/TR/trace-context/#tracestate-header) + the Bugsee vendor entry. -// `tracestate` is the OTel-legal vendor channel that rides alongside `traceparent` (cross-project-tracing.md, -// T4/T7): a comma-separated, ORDERED (most-recently-mutated first) list of `key=value` members. We parse it +// `tracestate` is the OTel-legal vendor channel that rides alongside `traceparent`: a comma-separated, ORDERED (most-recently-mutated first) list of `key=value` members. We parse it // defensively (skip malformed members, never throw β€” we're reading headers minted by others), and when we // mutate it we move our entry to the front and cap the list, per the spec. @@ -12,7 +11,7 @@ export interface TracestateEntry { value: string; } -// W3C / Bugsee OTLP Profile v1 Β§12 (normative, cross-SDK): cap at 32 entries AND 512 bytes; drop the oldest +// Cap at 32 entries AND 512 bytes; drop the oldest // on overflow. Both caps are pinned so every Bugsee SDK truncates to identical bytes. // // The byte cap is measured in UTF-8 BYTES, which is what "512 bytes" means on the wire and what a peer @@ -77,7 +76,7 @@ export function setTracestateEntry( return next; } -/** The Bugsee `tracestate` payload (cross-project-tracing.md): a record flag + a session-correlation id. */ +/** The Bugsee `tracestate` payload: a record flag + a session-correlation id. */ export interface BugseeTraceState { /** The originator is recording/replaying this trace (distinct from the W3C sampled bit). */ record?: boolean; diff --git a/packages/capture/src/web-socket-interceptor.ts b/packages/capture/src/web-socket-interceptor.ts index 99a37863..3643cac2 100644 --- a/packages/capture/src/web-socket-interceptor.ts +++ b/packages/capture/src/web-socket-interceptor.ts @@ -1,7 +1,7 @@ import { type Interceptor, InterceptorBase } from '@bugsee/core'; import type { NetworkEvent, NetworkStage } from '@bugsee/protocol'; -// WebSocket capture SOURCE (design Β§16.2). global WebSocket is broadly available (browser/workers/ +// WebSocket capture SOURCE. global WebSocket is broadly available (browser/workers/ // Deno/Bun; Node β‰₯ 22), so the wrap installs only when it exists (availability-detected). Because each // connection must be instrumented at CREATION, the wrap replaces the constructor with a thin subclass: // its constructor calls through, assigns a per-connection id, emits `before`, and attaches open/message/ @@ -127,7 +127,7 @@ class WebSocketInterceptor extends InterceptorBase { this.emit('message', { ...this.#event(state, 'message'), direction: 'out', event: 'send' }); diff --git a/packages/capture/src/web-transport-interceptor.ts b/packages/capture/src/web-transport-interceptor.ts index e615f945..e5aed814 100644 --- a/packages/capture/src/web-transport-interceptor.ts +++ b/packages/capture/src/web-transport-interceptor.ts @@ -1,7 +1,7 @@ import { type Interceptor, InterceptorBase } from '@bugsee/core'; import type { NetworkEvent, NetworkStage } from '@bugsee/protocol'; -// WebTransport capture SOURCE (design Β§16.2). WebTransport is browser + Deno, so the wrap installs +// WebTransport capture SOURCE. WebTransport is browser + Deno, so the wrap installs // only when it exists. Each session is instrumented at creation by replacing the constructor with a // thin subclass; one id+sequence per session. WebTransport exposes lifecycle as PROMISES rather than // events: `ready` resolves when the session opens (β†’ open) or rejects on failure (β†’ error); `closed` diff --git a/packages/capture/src/xhr-interceptor.ts b/packages/capture/src/xhr-interceptor.ts index 19d88d79..35137ceb 100644 --- a/packages/capture/src/xhr-interceptor.ts +++ b/packages/capture/src/xhr-interceptor.ts @@ -13,7 +13,7 @@ import { type RequestDecoratorRegistry, } from './request-decorator'; -// XMLHttpRequest capture SOURCE (design Β§16.2). XHR is browser/electron-renderer only, so the wrap is +// XMLHttpRequest capture SOURCE. XHR is browser/electron-renderer only, so the wrap is // installed only when XMLHttpRequest exists (availability-detected) β€” on other runtimes onActivate is // a no-op. Wrapping is done by patching the PROTOTYPE methods (open/setRequestHeader/send) rather than // replacing the constructor, so existing XHR instances are unaffected; per-instance state lives in a diff --git a/packages/capture/vitest.config.ts b/packages/capture/vitest.config.ts index bb6fad0f..9f241546 100644 --- a/packages/capture/vitest.config.ts +++ b/packages/capture/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. export default defineConfig({ test: { // The runner is far slower than a dev machine β€” roughly 7-18x under coverage instrumentation β€” and diff --git a/packages/cloudflare/src/handler-attributes.fuzz.test.ts b/packages/cloudflare/src/handler-attributes.fuzz.test.ts index d8d29f1d..dfd8e65b 100644 --- a/packages/cloudflare/src/handler-attributes.fuzz.test.ts +++ b/packages/cloudflare/src/handler-attributes.fuzz.test.ts @@ -114,7 +114,7 @@ describe('handler attributes β€” never throw, always carry their marker', () => }); describe('cfAttributes β€” the request.cf enrichment is curated and type-guarded', () => { - // The curated `cf.*` subset. latitude/longitude/postalCode are DELIBERATELY not here (PII, design D9). + // The curated `cf.*` subset. latitude/longitude/postalCode are DELIBERATELY not here (PII). const ALLOWED = new Set([ 'cf.colo', 'cf.country', diff --git a/packages/cloudflare/src/handler-attributes.ts b/packages/cloudflare/src/handler-attributes.ts index 9db1f897..e96178c0 100644 --- a/packages/cloudflare/src/handler-attributes.ts +++ b/packages/cloudflare/src/handler-attributes.ts @@ -1,7 +1,7 @@ import type { AttributeValue } from '@bugsee/vercel-edge'; import type { MessageBatch, ScheduledController, TraceItem } from './cloudflare-types'; -// Per-handler-type context attributes for the non-fetch Cloudflare triggers (docs/design/edge-runtime.md C2). +// Per-handler-type context attributes for the non-fetch Cloudflare triggers. // Keys follow OTel FaaS conventions (`faas.trigger`, `faas.cron`, `messaging.*`) plus a `cloudflare.handler` // marker so an incident report names WHICH trigger fired (`faas.trigger` alone can't tell email from tail). // PII-safe: no email addresses, no message bodies/payloads. Defensive β€” a malformed trigger arg degrades to the diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index c4c002e0..9495adc2 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -1,13 +1,12 @@ // @bugsee/cloudflare β€” Cloudflare Workers (workerd): the @bugsee/vercel-edge edge composition with the platform // identity defaulted to 'workers'. ctx.waitUntil comes from the handler's 3rd arg `(req, env, ctx)`; -// AsyncLocalStorage needs the `nodejs_compat`/`nodejs_als` compatibility flag (degrades to a single-slot store -// + one-time warning otherwise). Tier 2. See docs/design/edge-runtime.md (C1) + the README. +// AsyncLocalStorage needs the `nodejs_compat`/`nodejs_als` compatibility flag (degrades to a single-slot store, with a one-time warning otherwise). // // Re-export the ENTIRE edge surface (withBugseeFetch, buildEdgeEnvironment, createEdgeRequestContextStore, // resolveWaitUntil, createEdgeUnhandledRejectionProvider, EdgeContextStoreToken, launchEdge, types) ... export * from '@bugsee/vercel-edge'; // The Cloudflare handler surface: the unified `withBugsee` wrapper (fetch + scheduled/queue/email/tail) + the -// structural handler types (C2). +// structural handler types. export type { Awaitable, EmailMessage, @@ -17,7 +16,7 @@ export type { ScheduledController, TraceItem, } from './cloudflare-types'; -// Class-based handlers (C2d): Durable Objects via instrumentDurableObject; WorkerEntrypoint via withBugsee. +// Class-based handlers: Durable Objects via instrumentDurableObject; WorkerEntrypoint via withBugsee. export { type DurableObjectInstrumentOptions, instrumentDurableObject, @@ -28,6 +27,6 @@ export { launch } from './launch'; export type { BugseeWorkerConfig } from './launch-config'; // ... and `relaunch` likewise re-runs THIS launch (shadowing the plain-edge `relaunch` of the `export *`). export { relaunch } from './relaunch'; -// request.cf geo/network enrichment (C3) β€” applied to fetch by `withBugsee`; exposed for manual DO-fetch use. +// request.cf geo/network enrichment β€” applied to fetch by `withBugsee`; exposed for manual DO-fetch use. export { cfAttributes, cloudflareRequestAttributes } from './request-cf'; export { type WorkerEntrypointInstrumentOptions, withBugsee } from './with-bugsee'; diff --git a/packages/cloudflare/src/instrument-class.test.ts b/packages/cloudflare/src/instrument-class.test.ts index e2bd294a..d0900d63 100644 --- a/packages/cloudflare/src/instrument-class.test.ts +++ b/packages/cloudflare/src/instrument-class.test.ts @@ -172,7 +172,7 @@ describe('instrumentEdgeClass', () => { // Wrapped on the SUBCLASS PROTOTYPE, never as an own property of the instance. These three lines used // to assert `Object.hasOwn(instance, …) === true`, i.e. they pinned the defect: an own property shadows // the prototype method out of Cloudflare's RPC surface, which made every instrumented method uncallable - // over RPC (docs/review/cloudflare.md SEV1 #1, reproduced on real workerd). + // over RPC (reproduced on real workerd). const proto = Object.getPrototypeOf(instance) as object; expect(Object.hasOwn(instance, 'fetch')).toBe(false); // NOT an own property… expect(Object.hasOwn(proto, 'fetch')).toBe(true); // …wrapped on the prototype instead @@ -228,7 +228,7 @@ describe('instrumentEdgeClass', () => { }); }); -// The RPC surface (docs/review/cloudflare.md SEV1 #1), pinned structurally. +// The RPC surface, pinned structurally. // // `instrumentRpcMethods` is a documented opt-in that DELETED the customer's RPC surface: Cloudflare // dispatches RPC by looking methods up on the PROTOTYPE, and the wrapper was assigned as an own property diff --git a/packages/cloudflare/src/instrument-class.ts b/packages/cloudflare/src/instrument-class.ts index 0101670a..98ea5e61 100644 --- a/packages/cloudflare/src/instrument-class.ts +++ b/packages/cloudflare/src/instrument-class.ts @@ -5,7 +5,7 @@ import { runInEdgeContext, } from '@bugsee/vercel-edge'; -// The shared CLASS-instrumentation core (docs/design/edge-runtime.md C2d). Cloudflare's class-based handlers β€” +// The shared CLASS-instrumentation core. Cloudflare's class-based handlers β€” // Durable Objects and WorkerEntrypoint β€” differ from module handlers in ONE load-bearing way: they receive // their `ctx` (a DurableObjectState / ExecutionContext, both with `waitUntil`) and `env` in the CONSTRUCTOR, // not per-method (a method is `fetch(request)`, not `fetch(request, env, ctx)`). So this wraps the CLASS: a @@ -63,7 +63,7 @@ export function instrumentEdgeClass( awaitFlush = false, /** Derives the TENANT key from the constructor's ctx (the Durable Object id). Omitted for * WorkerEntrypoint, which is single-tenant by construction. See - * docs/design/cloudflare-tenant-isolation.md Β§4.1. */ + * */ resolveOwner?: (ctx: unknown) => string | undefined, /** Further per-invocation settings forwarded verbatim to `runInEdgeContext`. An object rather than * yet another positional, so the next one does not extend this list again. */ @@ -106,7 +106,7 @@ export function instrumentEdgeClass( // The wrappers are installed on the SUBCLASS PROTOTYPE, never as own properties of the instance. // - // This is the whole fix for docs/review/cloudflare.md SEV1 #1. Cloudflare's RPC dispatch exposes methods + // This is the whole fix. Cloudflare's RPC dispatch exposes methods // it finds on the PROTOTYPE; assigning a wrapper to `this[name]` in the constructor shadows the prototype // method with an own property, which removes it from the RPC surface entirely. `instrumentRpcMethods` β€” // a documented, advertised opt-in β€” therefore did not merely fail to instrument, it made the customer's diff --git a/packages/cloudflare/src/instrument-durable-object.fuzz.test.ts b/packages/cloudflare/src/instrument-durable-object.fuzz.test.ts index 8ff4dad5..ea4262e4 100644 --- a/packages/cloudflare/src/instrument-durable-object.fuzz.test.ts +++ b/packages/cloudflare/src/instrument-durable-object.fuzz.test.ts @@ -14,7 +14,7 @@ import * as cfLaunch from './launch'; * The owner is derived from `ctx.id.toString()` β€” a value supplied entirely by workerd (or, in a test/stub/ * future-runtime, by anything at all). It is also the value the partitioned capture store keys a tenant's * rolling window on, which is what stops one Durable Object's incident bundle from carrying another - * customer's capture (docs/review/cloudflare.md SEV1 #2). So it has two hard invariants: + * customer's capture. So it has two hard invariants: * * 1. Deriving it must NEVER throw β€” it runs in the DO constructor, and a throw there takes down every * construction of that Durable Object, not merely one capture partition. diff --git a/packages/cloudflare/src/instrument-durable-object.test.ts b/packages/cloudflare/src/instrument-durable-object.test.ts index ba9d4285..75fb4ae4 100644 --- a/packages/cloudflare/src/instrument-durable-object.test.ts +++ b/packages/cloudflare/src/instrument-durable-object.test.ts @@ -238,11 +238,11 @@ describe('instrumentDurableObject', () => { }); }); -// S4: each Durable Object instance stamps its OWN id as the tenant owner. +// each Durable Object instance stamps its OWN id as the tenant owner. // // Different DO instances share one isolate, one client and one capture ring. The owner is what lets the // partitioned store keep tenant C's incident bundle free of tenant A's and B's secrets β€” the leak proven -// on real workerd (docs/review/cloudflare.md SEV1 #2). +// on real workerd. describe('instrumentDurableObject β€” tenant owner', () => { it('runs each instance in a context owned by that instance id', async () => { const owners: Array = []; diff --git a/packages/cloudflare/src/instrument-durable-object.ts b/packages/cloudflare/src/instrument-durable-object.ts index e5b2d99f..68e2dfed 100644 --- a/packages/cloudflare/src/instrument-durable-object.ts +++ b/packages/cloudflare/src/instrument-durable-object.ts @@ -3,7 +3,7 @@ import { instrumentEdgeClass } from './instrument-class'; import { type BugseeWorkerConfig, createLazyLauncher } from './launch-config'; import { cloudflareRequestAttributes } from './request-cf'; -// Durable Object instrumentation (docs/design/edge-runtime.md C2d β€” follows @sentry/cloudflare's +// Durable Object instrumentation (follows @sentry/cloudflare's // `instrumentDurableObjectWithSentry`). A DO is a CLASS bound separately (not a module handler), and it receives // its `ctx` (a DurableObjectState, which has `waitUntil`) + `env` in the CONSTRUCTOR β€” so it can't use the // handler-object `withBugsee`. This wraps the DO class so its lifecycle methods (`fetch` + `alarm`) each run in @@ -83,7 +83,7 @@ export function instrumentDurableObject( true, // a DO's ctx.waitUntil is a no-op β†’ await the flush in-request (see edge-context.ts awaitFlush) // The TENANT key: a Durable Object id identifies the customer/room/user this instance serves, and many // instances share one isolate. Without it their capture shares one ring and an incident in one uploads - // every other tenant's data (docs/review/cloudflare.md SEV1 #2). + // every other tenant's data. durableObjectOwner, options.flushTimeoutMs !== undefined ? { flushTimeoutMs: options.flushTimeoutMs } : undefined, ); diff --git a/packages/cloudflare/src/launch-config.ts b/packages/cloudflare/src/launch-config.ts index 51a2841c..efb62ea3 100644 --- a/packages/cloudflare/src/launch-config.ts +++ b/packages/cloudflare/src/launch-config.ts @@ -1,7 +1,7 @@ import type { Bugsee, BugseeEdgeLaunchOptions } from '@bugsee/vercel-edge'; import { launch } from './launch'; -// Shared launch config + a lazy per-isolate launcher (docs/design/edge-runtime.md C2). Because `env` (and thus +// Shared launch config + a lazy per-isolate launcher. Because `env` (and thus // the app token, a Worker SECRET) is NOT available at module scope on Cloudflare, the client must be launched // from inside a handler/constructor where `env` exists. Both `withBugsee` (handler objects / WorkerEntrypoint // classes) and `instrumentDurableObject` reuse this so the launch happens lazily on the first invocation/ diff --git a/packages/cloudflare/src/launch.test.ts b/packages/cloudflare/src/launch.test.ts index cf840062..db39f43a 100644 --- a/packages/cloudflare/src/launch.test.ts +++ b/packages/cloudflare/src/launch.test.ts @@ -44,12 +44,12 @@ describe('launch (Cloudflare)', () => { }); }); -// S0 (docs/design/cloudflare-tenant-isolation.md Β§7): AsyncLocalStorage is wired AUTOMATICALLY. +// AsyncLocalStorage is wired AUTOMATICALLY. // // `globalThis.AsyncLocalStorage` does not exist on workerd under any compatibility flag β€” ALS is reachable -// only via `node:async_hooks` (verified on real workerd; docs/review/cloudflare.md SEV1 #3). Without this, +// only via `node:async_hooks` (verified on real workerd). Without this, // per-request context silently degrades to a single slot, `contextId`/`owner` are never stamped, and the -// Durable Object tenant fix (S1-S5) is inert. The user must write NO code for this. +// Durable Object tenant fix is inert. The user must write NO code for this. describe('launch (Cloudflare) β€” automatic AsyncLocalStorage', () => { it('supplies a run()-scoped store by default, without the caller passing one', () => { const spy = vi.spyOn(edge, 'launchEdge').mockReturnValue({} as edge.Bugsee); @@ -96,7 +96,7 @@ describe('launch (Cloudflare) β€” automatic AsyncLocalStorage', () => { }); }); -// S4.5: Cloudflare turns tenant partitioning ON by default β€” it is where Durable Objects live. +// Cloudflare turns tenant partitioning ON by default β€” it is where Durable Objects live. describe('launch (Cloudflare) β€” tenant partitioning', () => { it('enables partitionCaptureByTenant by default', () => { const spy = vi.spyOn(edge, 'launchEdge').mockReturnValue({} as edge.Bugsee); @@ -116,7 +116,7 @@ describe('launch (Cloudflare) β€” tenant partitioning', () => { ); }); - // R5V-9: `false` silently disables the cross-tenant isolation Cloudflare turns on by default; say so, once. + // `false` silently disables the cross-tenant isolation Cloudflare turns on by default; say so, once. it('an explicit false warns once per isolate through onError, and still opts out', () => { const spy = vi.spyOn(edge, 'launchEdge').mockReturnValue({} as edge.Bugsee); const carrier = {}; diff --git a/packages/cloudflare/src/launch.ts b/packages/cloudflare/src/launch.ts index eaafeb72..0fd81319 100644 --- a/packages/cloudflare/src/launch.ts +++ b/packages/cloudflare/src/launch.ts @@ -1,14 +1,13 @@ // REQUIRES the `nodejs_compat` compatibility flag. `globalThis.AsyncLocalStorage` does NOT exist on workerd // under any flag β€” ALS is reachable ONLY through `node:async_hooks` (verified on real workerd 1.20260722.1 -// across the flag x compatibility-date matrix; docs/review/cloudflare.md SEV1 #3). Importing it here is what -// makes per-request context work with ZERO user code (docs/design/cloudflare-tenant-isolation.md Β§7); the +// across the flag x compatibility-date matrix. Importing it here is what +// makes per-request context work with ZERO user code; the // same choice @sentry/cloudflare makes, for the same reason. A project without the flag now fails at BUILD // with a resolver error instead of building fine and silently losing context isolation. import { AsyncLocalStorage } from 'node:async_hooks'; import { type Bugsee, type BugseeEdgeLaunchOptions, launchEdge } from '@bugsee/vercel-edge'; -// @bugsee/cloudflare launch() β€” the Cloudflare Workers (workerd) composition root (docs/design/edge-runtime.md -// C1). Cloudflare runs the SAME V8-isolate, Web-APIs-only edge surface as Vercel Edge, so this reuses the ENTIRE +// @bugsee/cloudflare launch() β€” the Cloudflare Workers (workerd) composition root. Cloudflare runs the SAME V8-isolate, Web-APIs-only edge surface as Vercel Edge, so this reuses the ENTIRE // @bugsee/vercel-edge composition (fetch transport, in-memory capture store, run()-only ALS, console/network // capture, unhandledrejection safety net, incident-driven upload via waitUntil) and swaps ONE default: the // platform identity β†’ 'workers'. The two Cloudflare specifics are already handled GENERICALLY by the shared @@ -26,7 +25,7 @@ const warnedIsolates = new WeakSet(); /** * `partitionCaptureByTenant: false` is allowed, but on Cloudflare it silently reinstates the cross-tenant leak the * default exists to prevent (one isolate, many customers' Durable Objects, one capture ring), so say so, once per - * isolate (R5V-9). The host's sink may throw: the warning must never break launch (R5V-4). + * isolate. The host's sink may throw: the warning must never break launch. */ function warnOnceIfIsolationDisabled(options: BugseeEdgeLaunchOptions): void { if (options.partitionCaptureByTenant !== false) { @@ -58,9 +57,9 @@ export function launch(appToken: string, options: BugseeEdgeLaunchOptions = {}): asyncLocalStorage: new AsyncLocalStorage(), ...options, // Durable Objects for different customers share one isolate and therefore one capture ring; partition - // by tenant so an incident in one cannot upload another's data (SEV1 #2). The default applies ONLY for an + // by tenant so an incident in one cannot upload another's data. The default applies ONLY for an // absent/`undefined` value and is placed AFTER the spread: an explicit `{partitionCaptureByTenant: undefined}` - // would otherwise override it (R4V-1). Any other value is judged by launchEdge (fail ON + onError). + // would otherwise override it. Any other value is judged by launchEdge (fail ON + onError). partitionCaptureByTenant: options.partitionCaptureByTenant === undefined ? true : options.partitionCaptureByTenant, }); diff --git a/packages/cloudflare/src/node-async-hooks.d.ts b/packages/cloudflare/src/node-async-hooks.d.ts index 680615e3..b3562364 100644 --- a/packages/cloudflare/src/node-async-hooks.d.ts +++ b/packages/cloudflare/src/node-async-hooks.d.ts @@ -6,8 +6,7 @@ // guardrail intact and documents the exact dependency. // // Why the dependency exists at all: `globalThis.AsyncLocalStorage` does not exist on workerd under any -// compatibility flag; ALS is reachable only through `node:async_hooks` (docs/review/cloudflare.md SEV1 #3, -// docs/design/cloudflare-tenant-isolation.md Β§7). Requires the `nodejs_compat` flag at deploy time. +// compatibility flag; ALS is reachable only through `node:async_hooks`. Requires the `nodejs_compat` flag at deploy time. declare module 'node:async_hooks' { /** The run()-scoped subset used by the edge context store β€” no `enterWith`/`disable` (workerd lacks them). */ export class AsyncLocalStorage { diff --git a/packages/cloudflare/src/relaunch.test.ts b/packages/cloudflare/src/relaunch.test.ts index 96976f2a..3c782611 100644 --- a/packages/cloudflare/src/relaunch.test.ts +++ b/packages/cloudflare/src/relaunch.test.ts @@ -8,7 +8,7 @@ import { import { afterEach, describe, expect, it, vi } from 'vitest'; // `relaunch` must re-run THE CLOUDFLARE launch (workers identity, AsyncLocalStorage, tenant partitioning), not -// the plain edge one re-exported from `@bugsee/vercel-edge` (review R2D-4). The Cloudflare `launch` is replaced +// the plain edge one re-exported from `@bugsee/vercel-edge`. The Cloudflare `launch` is replaced // by a spy that forwards to the real edge launch; the first client is a real one so the carrier holds a real // remembered token and a real client to stop. const { launch } = vi.hoisted(() => ({ launch: vi.fn() })); diff --git a/packages/cloudflare/src/request-cf.ts b/packages/cloudflare/src/request-cf.ts index 35461170..713587a0 100644 --- a/packages/cloudflare/src/request-cf.ts +++ b/packages/cloudflare/src/request-cf.ts @@ -1,6 +1,6 @@ import { type AttributeValue, requestAttributes } from '@bugsee/vercel-edge'; -// Cloudflare `request.cf` enrichment (docs/design/edge-runtime.md C3, decision D9). Behind the edge, every +// Cloudflare `request.cf` enrichment. Behind the edge, every // incoming fetch Request carries a `cf` object of FREE geo/network metadata. We stamp a CURATED, low-PII subset // (the design's colo / country / city / timezone / asn / tls β€” NOT latitude/longitude) onto the request's // context, so a fetch incident report carries where/how the request arrived. Every field is optional (absent in diff --git a/packages/cloudflare/src/with-bugsee.test.ts b/packages/cloudflare/src/with-bugsee.test.ts index 5138c06e..480212b0 100644 --- a/packages/cloudflare/src/with-bugsee.test.ts +++ b/packages/cloudflare/src/with-bugsee.test.ts @@ -51,7 +51,7 @@ describe('withBugsee', () => { const res = await handler.fetch?.(request, {}, ctx); expect(await res?.text()).toBe('ok'); expect(attrs?.['http.url']).toBe('/p/1'); // shared route attrs - expect(attrs?.['cf.colo']).toBe('SJC'); // C3 request.cf enrichment flows through withBugsee + expect(attrs?.['cf.colo']).toBe('SJC'); // request.cf enrichment flows through withBugsee expect(attrs?.['cf.country']).toBe('US'); expect(ctx.waitUntil).toHaveBeenCalledTimes(1); expect(flush).toHaveBeenCalledTimes(1); diff --git a/packages/cloudflare/src/with-bugsee.ts b/packages/cloudflare/src/with-bugsee.ts index 9db21873..8d4a68dc 100644 --- a/packages/cloudflare/src/with-bugsee.ts +++ b/packages/cloudflare/src/with-bugsee.ts @@ -15,7 +15,7 @@ import { instrumentEdgeClass } from './instrument-class'; import { type BugseeWorkerConfig, createLazyLauncher } from './launch-config'; import { cloudflareRequestAttributes } from './request-cf'; -// The unified Cloudflare Workers instrumentation wrapper (docs/design/edge-runtime.md C2). Cloudflare module +// The unified Cloudflare Workers instrumentation wrapper. Cloudflare module // Workers export a handler OBJECT (`export default { fetch, scheduled, queue, email, tail }`); each method is a // distinct invocation type, and the non-fetch ones have NO incoming Request, so a fetch-only SDK misses cron / // queue / email / tail entirely. `withBugsee` wraps every PRESENT method so it runs in its own Bugsee context diff --git a/packages/cloudflare/vitest.config.ts b/packages/cloudflare/vitest.config.ts index 88178df3..d887da61 100644 --- a/packages/cloudflare/vitest.config.ts +++ b/packages/cloudflare/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config'; -// Coverage gate per docs/implementation-standards.md Β§4: 100% line/functions/statements, >=90% branch. +// Coverage gate: 100% line/functions/statements, >=90% branch. // @bugsee/cloudflare is the @bugsee/vercel-edge composition with platformType defaulted to 'workers'; its own // code is the thin launch wrapper + the re-export index, tested via injection (spy on launchEdge) β€” the heavy // edge behavior is covered in @bugsee/vercel-edge. environment 'node' (no real workerd isolate). diff --git a/packages/core/src/attachments.ts b/packages/core/src/attachments.ts index 6cc2d9fd..0c3f7405 100644 --- a/packages/core/src/attachments.ts +++ b/packages/core/src/attachments.ts @@ -75,7 +75,7 @@ export function resolveUserAttachments( generateName: () => string = randomBundleName, ): ResolvedUserAttachment[] { // The sink is the application's callback: a throw from it must never cost the report these attachments - // ride on (R2P-6), whether we are reporting a malformed one or the catch below is. + // ride on, whether we are reporting a malformed one or the catch below is. const onError = (error: unknown): void => { try { rawOnError(error); @@ -204,7 +204,7 @@ export function normalizeReportAttachments( /** * A COPY of `request` whose attachments have the limits applied, for a request that could not be normalised in * place (a handler froze the report). The marker must never encode attachments the limits would drop - 3 x 3 MB - * is the bound, not "whatever the app attached" (R2P-9). Silent: assembly reports the drops. The request itself + * is the bound, not "whatever the app attached". Silent: assembly reports the drops. The request itself * is returned when it carries none. */ export function limitAttachmentsForMarker(request: ReportingRequest): ReportingRequest { @@ -233,7 +233,7 @@ export function limitAttachmentsForMarker(request: ReportingRequest): ReportingR } // The crash is the part of a report an `after` handler most plausibly SCRUBS (frame locals, source context, the -// cause chain, the message), so it must be in the signature (R4H-1). The WHOLE crash is serialised (R5H-1): +// cause chain, the message), so it must be in the signature. The WHOLE crash is serialised: // listing the fields that matter missed `exception.cause`, `exception.name` and `domain`, so scrubbing a cause // chain left the marker holding the unscrubbed one. Measured 0.05 ms for a 10-frame crash and 1.1 ms for a 1 MB one. function crashFingerprint(crash: Report['crash']): unknown { @@ -241,7 +241,7 @@ function crashFingerprint(crash: Report['crash']): unknown { } // A 32-bit FNV-1a over the content plus its length: O(n), allocation-free, so a same-length rewrite (mask with -// `*`, `fill(0)`) cannot hide behind an unchanged size (R5H-2). A collision needs an edit that keeps both the length +// `*`, `fill(0)`) cannot hide behind an unchanged size. A collision needs an edit that keeps both the length // and the hash (about 1 in 4 billion); the cost of one is a marker that keeps the pre-edit attachment. function contentHash(data: Uint8Array | string): string { let hash = 0x811c9dc5; diff --git a/packages/core/src/bounded.fuzz.test.ts b/packages/core/src/bounded.fuzz.test.ts index a4e82a25..0e53e40e 100644 --- a/packages/core/src/bounded.fuzz.test.ts +++ b/packages/core/src/bounded.fuzz.test.ts @@ -7,7 +7,7 @@ import { createRingBuffer } from './ring-buffer'; /** * Property-based tests for the two structures that bound what the SDK costs its host. * - * Both are self-protection (design Β§7.7): the ring buffer caps retained capture, and the rate limiter + * Both are self-protection: the ring buffer caps retained capture, and the rate limiter * caps admissions during an error storm. Their guarantees are the kind that hold "for any sequence of * operations", which is exactly what an example test cannot say β€” so both are checked against a MODEL * rather than against remembered outputs. diff --git a/packages/core/src/bugsee-api.ts b/packages/core/src/bugsee-api.ts index 5e7f7bf6..9d51b24b 100644 --- a/packages/core/src/bugsee-api.ts +++ b/packages/core/src/bugsee-api.ts @@ -11,13 +11,13 @@ import type { NotifyResult, } from './transport'; -// The control-plane BugseeApi (design Β§7.5/Β§8.1/Β§8.2) β€” platform-agnostic logic over an injected +// The control-plane BugseeApi β€” platform-agnostic logic over an injected // HttpTransport. Lazily creates a session (POST /v2/sessions), memoizes the access token, and // authenticates issue creation / renewal with it. Non-2xx responses throw a BugseeError carrying the // status as `code` β€” plus the collector's own `error.code` on `serverCode` when the failed body carried // one, which is the only thing the UploadPipeline reads as a verdict about the payload. The // UploadPipeline catches the throw, calls invalidateSession() and retries (so a stale-token 401 is -// re-acquired). `app_token` travels in the X-App-Token header (Β§8.1 v3). The +// re-acquired). `app_token` travels in the X-App-Token header (v3). The // only platform-specific piece is the transport, supplied by the platform. export interface BugseeApiOptions { @@ -215,7 +215,7 @@ export function createBugseeApi(transport: HttpTransport, options: BugseeApiOpti const sessionId = options.sessionId ?? randomId(); let accessToken: AccessToken | null = null; - // Standard SDK headers on every control-plane call (Β§8.2). X-Bugsee-Internal lets the network + // Standard SDK headers on every control-plane call. X-Bugsee-Internal lets the network // capture integration skip the SDK's own traffic. const baseHeaders = (): Record => ({ 'content-type': 'application/json', @@ -224,7 +224,7 @@ export function createBugseeApi(transport: HttpTransport, options: BugseeApiOpti // and a JS SDK application is type `javascript` β€” sending `web` had every session rejected with // ApplicationTypeMismatchError. `javascript` also stays off the dashboard cookie-auth path, which // only `web`/`unknown`/absent take (`populate.middleware.js`); that risk was the reason `web` was - // chosen, and it does not apply. Resolves design open question #1 (sdk-design.md Β§wire C3). + // chosen, and it does not apply. 'x-client-type': 'javascript', ...(options.sendUserAgent === false ? {} : { 'user-agent': `BugseeJS/${sdkVersion}` }), 'x-bugsee-internal': '1', @@ -265,7 +265,7 @@ export function createBugseeApi(transport: HttpTransport, options: BugseeApiOpti const response = await transport(`${baseUrl}/v2/sessions`, { method: 'POST', headers: baseHeaders(), - // `session_id`: the client-minted correlation id the collector joins to traces (Profile v1 Β§17). + // `session_id`: the client-minted correlation id the collector joins to traces. body: JSON.stringify({ app_token: appToken, environment, session_id: sessionId }), }); if (!isOk(response.status)) { diff --git a/packages/core/src/bundle-assembler.test.ts b/packages/core/src/bundle-assembler.test.ts index 6840a6c2..ba42d50c 100644 --- a/packages/core/src/bundle-assembler.test.ts +++ b/packages/core/src/bundle-assembler.test.ts @@ -574,7 +574,7 @@ describe('assembleBundle β€” crash.json', () => { const request = createReportingRequest({ source: { type: 'error' }, id: 'r1', crash }); const z = unzip(assembleBundle(request, new Map(), context()).body); expect(z.names).toContain('crash.json'); - // The written document is the report's crash PLUS the provenance the assembler stamps (Wave 5.4a). + // The written document is the report's crash PLUS the provenance the assembler stamps. expect(JSON.parse(z.text('crash.json'))).toEqual({ ...crash, source_sdk: 'javascript', @@ -645,7 +645,7 @@ describe('assembleBundle β€” native crash (minidump + attachments)', () => { const bundle = assembleBundle(request, new Map(), context()); const z = unzip(bundle.body); // Native shape (minidumpFile) + the stamped provenance β€” a JS/Electron Crashpad dump has to route to - // the javascript processor exactly like a managed one (Wave 5.4a). + // the javascript processor exactly like a managed one. expect(JSON.parse(z.text('crash.json'))).toEqual({ ...nativeCrash, source_sdk: 'javascript', diff --git a/packages/core/src/bundle-assembler.ts b/packages/core/src/bundle-assembler.ts index 556eaf17..8b92935f 100644 --- a/packages/core/src/bundle-assembler.ts +++ b/packages/core/src/bundle-assembler.ts @@ -24,7 +24,7 @@ import type { ReportingRequest } from './reporting'; import type { RequestContext } from './request-context'; import type { Bundle } from './transport'; -// Report assembly (design Β§7.7 trigger path, Β§8.4/Β§8.5 bundle layout, Android CaptureExporter). Turns +// Report assembly (trigger path, bundle layout, Android CaptureExporter). Turns // a ReportingRequest + the captured data (grouped by file type, from CaptureExporter.drain()) into a // Bundle: request.json (from the Report + Environment + source mechanism), manifest.json (file // inventory + time bounds + attributes), the apptoken file, and one JSON file per captured file-type, @@ -43,7 +43,7 @@ export interface BundleAssemblyContext { */ userIdentifier?: string | null; /** - * The active request context captured at report-submit time (framework adapters). When present, its + * The active request context captured at report-submit time. When present, its * `contextId` becomes request.json `context_id`, and its `user`/`attributes` OVERRIDE the global ones * (the report reflects the request it fired in). Absent β†’ global behavior unchanged. */ @@ -87,14 +87,13 @@ function fileNameForType(type: FileType): string { } // Per-type JSON shape. Most file types serialize to a top-level ARRAY of payloads. Four exceptions: -// `performance.json` = `{ transactions: [...] }` (Β§711/Β§8.8); `profile.json` = the SINGLE bare V8 CPU +// `performance.json` = `{ transactions: [...] }`; `profile.json` = the SINGLE bare V8 CPU // profile object (.cpuprofile β€” DevTools/speedscope-loadable); and `input.json` and `video.aux.json` // carry the wire spec's own versioned envelope, `{ version, events: [...] }` // (`specs/sdk/reporting/bundle/{input,video-aux}.md`). Those two need the version for the same reason: // a consumer keys its decoding on it β€” `button`/`buttonMask`/`metaState`/scroll for `input`, and for // `video.aux` whether the frame is in display pixels (version 1) or in the coordinates' own units -// (version 2), which are the same numbers meaning different things. Binary streams (Β§8.4: -// replay/screenshot/attachment) are platform-tier provider concerns; when those land the assembler +// (version 2), which are the same numbers meaning different things. Binary streams (replay, screenshots, attachments) are platform-tier provider concerns; when those land the assembler // branches further (passing a Uint8Array `data` through). function serializeFileData(type: FileType, payloads: unknown[]): unknown { if (type === 'performance') { @@ -132,7 +131,7 @@ export function assembleBundle( const email = report.email ?? (requestContext?.user || undefined) ?? context.userIdentifier ?? undefined; - // request.json (Β§8.5): metadata from the Report + the wire source mechanism + the environment. + // request.json: metadata from the Report + the wire source mechanism + the environment. const requestJson: RequestJson = { type: report.type, summary: report.summary ?? defaultSummary(report.type), @@ -146,7 +145,7 @@ export function assembleBundle( created_on: new Date(now).toISOString(), environment: context.environment, ...(requestContext !== undefined ? { context_id: requestContext.contextId } : {}), - // The active trace β†’ the cross-project join key on the report envelope (Profile v1 Β§16, T8). + // The active trace β†’ the cross-project join key on the report envelope. ...(requestContext?.trace !== undefined ? { trace_id: requestContext.trace.traceId, span_id: requestContext.trace.spanId } : {}), @@ -156,7 +155,7 @@ export function assembleBundle( ...(report.signatures.length > 0 ? { signatures: report.signatures } : {}), }; - // Time bounds (Β§7.7): earliest captured entry β†’ trigger moment. + // Time bounds: earliest captured entry β†’ trigger moment. let start = now; const files: ManifestFileEntry[] = []; const typedFiles: BundleFile[] = []; @@ -186,7 +185,7 @@ export function assembleBundle( typedFiles.push({ name: filename, data }); } - // Structured crash detail (Β§8.4 `crash.json`) β€” a per-report file (not a rolling capture stream) built + // Structured crash detail (`crash.json`) β€” a per-report file (not a rolling capture stream) built // from the thrown Error at report time. The backend crash pipeline reads it for stack symbolication. // // The `source_*` provenance is stamped HERE rather than in `buildCrashJson`, because this is the one @@ -201,7 +200,7 @@ export function assembleBundle( typedFiles.push({ name: crashFilename, data: JSON.stringify(crash) }); } - // Report-level attachments (Β§8.4 `attachment`) β€” extra binary/text files written verbatim (e.g. a + // Report-level attachments (`attachment`) β€” extra binary/text files written verbatim (e.g. a // harvested native-crash `.dmp` referenced by `crash.minidumpFile`). Listed in the manifest by their own // filename so the backend can enumerate them. // The name is a zip member name and must be unique among the bundle's members: a clash (a `before` handler diff --git a/packages/core/src/bundle-uploader.ts b/packages/core/src/bundle-uploader.ts index b0d099b0..651a2ec0 100644 --- a/packages/core/src/bundle-uploader.ts +++ b/packages/core/src/bundle-uploader.ts @@ -7,7 +7,7 @@ import { type PutResult, } from './transport'; -// The data-plane BundleUploader (design Β§7.5/Β§8.3) β€” platform-agnostic logic over an injected +// The data-plane BundleUploader β€” platform-agnostic logic over an injected // HttpTransport. It builds the signed-PUT headers (Content-Length, fileName; NO Authorization β€” the // signed URL self-auths β€” and NO Content-Type, and NO `x-amz-*`, see below) and maps the response to // a PutResult: 2xx β†’ ok; a network error (transport reject) β†’ retryable; anything else is classified diff --git a/packages/core/src/bundle-writer.ts b/packages/core/src/bundle-writer.ts index 675bcd63..094248d6 100644 --- a/packages/core/src/bundle-writer.ts +++ b/packages/core/src/bundle-writer.ts @@ -1,11 +1,11 @@ import { strToU8, zipSync } from '@bugsee/util'; -// Bundle ZIP assembly (design Β§7.7 trigger path, Β§8.4 layout). Given the root-level files the +// Bundle ZIP assembly for the trigger path. Given the root-level files the // pipeline has already serialized (request.json, manifest.json, apptoken, and the typed buffer // files), produce the `*.bundle.zip` byte payload for the signed-URL PUT. export interface BundleFile { - /** Root-level filename within the zip (Β§8.4). */ + /** Root-level filename within the zip. */ name: string; /** File content; strings are UTF-8 encoded, byte arrays are stored as-is. */ data: Uint8Array | string; diff --git a/packages/core/src/capture-aggregator.test.ts b/packages/core/src/capture-aggregator.test.ts index 7dcadde4..449173f8 100644 --- a/packages/core/src/capture-aggregator.test.ts +++ b/packages/core/src/capture-aggregator.test.ts @@ -225,10 +225,10 @@ describe('createCaptureAggregator (exporter round-trip)', () => { }); }); -// S1 (docs/design/cloudflare-tenant-isolation.md Β§4.2): the tenant OWNER travels out-of-band. +// the tenant OWNER travels out-of-band. // // Durable Objects for different tenants share one isolate, one client and one capture ring, so an incident -// in one DO uploads every other DO's data (docs/review/cloudflare.md SEV1 #2, proven on real workerd). +// in one DO uploads every other DO's data (proven on real workerd). // Partitioning the store needs an owner key AT WRITE TIME. It cannot be recovered from `context_id`: that is // stamped INSIDE entry.data and then serialized, and StoredEntry deliberately keeps queryable fields // out-of-band "so ordering/time-bounds need no deserialize" (contracts.ts). So `owner` rides alongside diff --git a/packages/core/src/capture-aggregator.ts b/packages/core/src/capture-aggregator.ts index b5c9a387..5d080676 100644 --- a/packages/core/src/capture-aggregator.ts +++ b/packages/core/src/capture-aggregator.ts @@ -1,14 +1,14 @@ import type { CaptureAggregator, CaptureDataEntry, CaptureStore } from './contracts'; import type { RequestContext } from './request-context'; -// The single data adapter every provider feeds (Android BugseeCaptureAggregator parity, design Β§7.7). +// The single data adapter every provider feeds (Android BugseeCaptureAggregator parity). // Data flows ONE direction: accept an entry β†’ (optionally stamp the active request context) β†’ transform // (entry.serialize()) β†’ route the serialized record to the configurable CaptureStore (in-memory / disk / // IndexedDB). Read-back is NOT here β€” it belongs to the CaptureExporter (capture-exporter.ts). export interface CaptureAggregatorOptions { /** - * Reads the active request context (design: framework-adapters.md, "correlation, not isolation"). When + * Reads the active request context ("correlation, not isolation"). When * a context is active, each entry's wire payload is stamped with its correlation ids so the recording * can later be focused on one request. Absent (default) β†’ no stamping; behavior is byte-identical. */ @@ -32,8 +32,7 @@ export function createCaptureAggregator( // // Stamp onto a COPY, never the caller's object: a provider may hand us the very object the source // emitter broadcast to all its subscribers (and that app code may still reference), and the correlation - // ids are an internal concern that must not leak onto it (observe-only β€” see the design doc / the - // "interceptors must not alter app behavior" principle). We replace entry.data with the stamped copy. + // ids are an internal concern that must not leak onto it (observe-only, per the principle that "interceptors must not alter app behavior"). We replace entry.data with the stamped copy. const stamp = (entry: CaptureDataEntry): void => { const context = options.getContext?.(); if (context === undefined) { diff --git a/packages/core/src/capture-coordinator.ts b/packages/core/src/capture-coordinator.ts index 183618f2..2eea1736 100644 --- a/packages/core/src/capture-coordinator.ts +++ b/packages/core/src/capture-coordinator.ts @@ -1,14 +1,14 @@ import type { CaptureProvider, CaptureProviderInit, OptionsContainer } from './contracts'; -// Capture-provider lifecycle manager (design Β§16.3, Android BugseeCaptureCoordinator parity). Holds +// Capture-provider lifecycle manager (Android BugseeCaptureCoordinator parity). Holds // the capture-pipeline init (hubs/operations/aggregator) and injects it into each provider ONCE at // registration (Android constructs providers with the init); then start(options) starts every // enabled provider for the launch (the provider subscribes to its hub and buffers entries), stop() // stops the started ones, and providers may be added after start. The trigger-time bufferβ†’serialize -// wiring lives in the event pipeline (Β§7.7). +// wiring lives in the event pipeline. // // Deterministic by design: a throwing provider.start propagates. The Client wraps start() to honor -// the "launch never throws" guarantee (Β§15.1). +// the "launch never throws" guarantee. /** Returns whether a launch option (by key) is enabled. */ export type OptionGate = (option: string) => boolean; diff --git a/packages/core/src/capture-exporter.test.ts b/packages/core/src/capture-exporter.test.ts index 202eff53..5fa8b602 100644 --- a/packages/core/src/capture-exporter.test.ts +++ b/packages/core/src/capture-exporter.test.ts @@ -157,9 +157,9 @@ describe('createCaptureExporter β€” factory', () => { }); }); -// S3 (docs/design/cloudflare-tenant-isolation.md Β§4.3): the exporter scopes the snapshot to one tenant. +// the exporter scopes the snapshot to one tenant. // -// The partitioned store (S2) can already isolate; this is what ASKS it to. Without the owner reaching +// The partitioned store can already isolate; this is what ASKS it to. Without the owner reaching // snapshot(), every incident still drains every tenant β€” the leak, unchanged. describe('createCaptureExporter β€” owner-scoped export', () => { const snapshotOf = (records: StoredEntry[]) => ({ diff --git a/packages/core/src/capture-exporter.ts b/packages/core/src/capture-exporter.ts index e1e0d1d8..c5244a1c 100644 --- a/packages/core/src/capture-exporter.ts +++ b/packages/core/src/capture-exporter.ts @@ -13,7 +13,7 @@ import type { // keeps writing during export β€” reads the snapshot's records, reconstructs entries (factory(type) + // instance deserialize), and RELEASES the snapshot when done (try/finally). stream() reads one-by-one // (memory-light); drain() reads all-at-once grouped by file type. The aggregator writes; the exporter -// reads β€” the two directions are separate (Android-derived Β§16). +// reads β€” the two directions are separate (Android-derived). function reify(record: StoredEntry, factory: CaptureEntryFactory): CaptureDataEntry { const entry = factory(record.type); diff --git a/packages/core/src/capture-recovery.test.ts b/packages/core/src/capture-recovery.test.ts index bd6ccd27..5c159149 100644 --- a/packages/core/src/capture-recovery.test.ts +++ b/packages/core/src/capture-recovery.test.ts @@ -464,7 +464,7 @@ describe('recoverReports', () => { expect(pipe.enqueue).not.toHaveBeenCalled(); }); - // SEV1 (recovery double-upload): a bundle that already reached the durable queue was staged only AFTER + // (recovery double-upload): a bundle that already reached the durable queue was staged only AFTER // its incident's marker was written (client.ts submitReport) and cleared only once that upload SETTLED, // so a process dying inside that window leaves BOTH traces of the SAME incident. `skipReportIds` is how // the bundle-queue leg tells this pass which incidents it already settled with. diff --git a/packages/core/src/capture-recovery.ts b/packages/core/src/capture-recovery.ts index 507f3ca8..0b6b9836 100644 --- a/packages/core/src/capture-recovery.ts +++ b/packages/core/src/capture-recovery.ts @@ -18,7 +18,7 @@ import { // Capture recovery: on the next launch, rebuild + upload a detected-incident report from a PRIOR // generation's preserved capture chunks β€” the gap the durable bundle queue does NOT cover (incident // detected, but the process died before its bundle reached the queue). For each pending marker -// (persisted at incident time β€” R1), read its generation's chunks via the backend, reassemble the +// (persisted at incident time), read its generation's chunks via the backend, reassemble the // bundle with the marker's incident-time metadata, and enqueue it through the (durable) upload // pipeline. A marker + its generation are removed only after delivery succeeds; preserved generations // that carry no incident are swept. Pure over injected ports (no fs/runtime), best-effort β€” every @@ -29,7 +29,7 @@ import { * delivered exactly once. The single definition of that policy β€” node (`recoverInstances`) and the * browser/worker tiers (`browser-utils`'s `Coexistence`) both drive their own queue replay through it. * - * ## Why the two legs collide (the SEV1: `events_count +2` per incident, confirmed on 4 samples) + * ## Why the two legs collide (`events_count +2` per incident, confirmed on 4 samples) * * `client.ts`'s `submitReport` writes an incident's report marker BEFORE assembly and clears it only when * the upload SETTLES, while `durable-upload-pipeline.ts` stages the assembled bundle BEFORE that upload. diff --git a/packages/core/src/carrier.test.ts b/packages/core/src/carrier.test.ts index c7a1b448..37ec2b5f 100644 --- a/packages/core/src/carrier.test.ts +++ b/packages/core/src/carrier.test.ts @@ -224,7 +224,7 @@ describe('setCarrierLastLaunch', () => { expect(getCarrier(g).lastLaunch?.appToken).toBe('tok'); }); - // L18: the slot lives on globalThis, readable by any script in the process. It keeps only what + // the slot lives on globalThis, readable by any script in the process. It keeps only what // relaunch() needs to find its way back, never the host's options wholesale. it('keeps the integration seams relaunch needs, never a host Bugsee option, header or identifier', () => { const g = {}; @@ -249,7 +249,7 @@ describe('setCarrierLastLaunch', () => { expect(Object.keys(retained).sort()).toEqual([ 'carrier', 'clock', - 'endpoint', // deployment identity (R2P-3): a relaunch must not fall back to the production endpoint + 'endpoint', // deployment identity: a relaunch must not fall back to the production endpoint 'onError', 'scheduler', 'transport', diff --git a/packages/core/src/carrier.ts b/packages/core/src/carrier.ts index d79a0055..5917af9c 100644 --- a/packages/core/src/carrier.ts +++ b/packages/core/src/carrier.ts @@ -20,7 +20,7 @@ import type { NotifyItem } from './transport'; */ export type ServiceManifest = (internal: ServiceRegistrar & ServiceResolver) => void; -// Process-global Carrier (design Β§4.2 Β§214): a version-keyed slot on `globalThis.__BUGSEE__` holding +// Process-global Carrier: a version-keyed slot on `globalThis.__BUGSEE__` holding // the SDK's process-global singletons β€” currently the interceptor registry. Routing interceptor // creation through it means that even if a bundler emits multiple copies of @bugsee/capture / // @bugsee/core (mixed dep ranges, framework adapters), all copies WITHIN ONE SDK VERSION converge on @@ -34,7 +34,7 @@ export const BUGSEE_SDK_VERSION = '0.1.0-beta.2'; const CARRIER_PROPERTY = '__BUGSEE__'; -/** What a stale client handle needs of the client that owns the process blackout (R3L-4). */ +/** What a stale client handle needs of the client that owns the process blackout. */ export interface CarrierBlackoutOwner { /** Start the blackout on the owner (refused by the owner itself when it is not launched). */ start(): void; @@ -48,13 +48,13 @@ export interface BugseeCarrier { readonly version: string; /** Interceptor singletons keyed by interceptor `name` β€” one global patch per name. */ readonly interceptors: Map>; - /** The process-global launched client (Bugsee is a per-process singleton, Β§1497/Β§473); else undefined. */ + /** The process-global launched client (Bugsee is a per-process singleton); else undefined. */ client?: unknown; /** The process lifecycle state; absent means {@link BugseeStatus.Stopped}. */ status?: BugseeStatus; /** * On the DEFAULT (`globalThis`) slot only: the custom carrier host the last launch used, so a plain - * `relaunch()` (which is not told the host) finds the client and the remembered launch there (R2L-12). + * `relaunch()` (which is not told the host) finds the client and the remembered launch there. */ lastHost?: object; /** What the last launch that built a client leaves for `relaunch()`: the token + only the integration seams (never secrets or the full options). */ @@ -65,7 +65,7 @@ export interface BugseeCarrier { * survives `relaunch()` (a new client has a new emitter, but reads the same slot). */ lifecycleListener?: LifecycleEventsListener | null; - /** The delivery queue of that listener (R2L-5): shared by every emitter of the process, like the slot. */ + /** The delivery queue of that listener: shared by every emitter of the process, like the slot. */ lifecycleDelivery?: LifecycleDeliveryState; /** * Whether a blackout is in force. Process-level, like mobile: a relaunch builds a new client, and @@ -73,36 +73,36 @@ export interface BugseeCarrier { */ blackout?: boolean; /** - * The client that currently owns the process-wide blackout (R3L-4): the newest one created or launched on + * The client that currently owns the process-wide blackout: the newest one created or launched on * this carrier. Only it may change {@link BugseeCarrier.blackout}; a stale handle's start is forwarded to it. */ blackoutOwner?: CarrierBlackoutOwner; /** True while `relaunch()` is stopping the old client and building the new one (notify backlog handover). */ relaunching?: boolean; /** - * Bumped each time a `relaunch()` attempt FAILS (R4L-2). A client that handed its notifications over records the + * Bumped each time a `relaunch()` attempt FAILS. A client that handed its notifications over records the * value when its stop() was issued; a hand-back that arrives after the value moved belongs to an attempt nobody * will complete, so it is dropped instead of parked on `globalThis` (user-identifying titles/e-mail). */ relaunchEpoch?: number; - /** Notifications the old client could not deliver, waiting for the client `relaunch()` builds (R2L-6). */ + /** Notifications the old client could not deliver, waiting for the client `relaunch()` builds. */ notifyBacklog?: NotifyItem[]; /** - * The launched client that takes over notifications another client hands back (R3L-5): a late hand-back + * The launched client that takes over notifications another client hands back: a late hand-back * after the new client already adopted the backlog goes straight to it instead of parking on the carrier. * `owner` is the registering client's own token, so a client never hands items to itself. */ notifyAdopter?: { owner: object; adopt(items: NotifyItem[]): void }; /** * The launch function an EDGE entry (`@bugsee/{sveltekit,astro,nuxt}/edge`) was installed with, so its - * `relaunch()` re-runs the same seam by default (R4H-4). Typed `unknown` here (core does not know the + * `relaunch()` re-runs the same seam by default. Typed `unknown` here (core does not know the * edge launch signature); owned by `@bugsee/vercel-edge`'s `rememberEdgeLaunch`. */ edgeLaunch?: unknown; /** The launch currently being built (async platforms), so `relaunch()` can wait for it. */ pendingLaunch?: Promise; /** - * Node: how many LIVE launches write into each on-disk subtree root (R4L-1/R5L-2), keyed by root path. On the + * Node: how many LIVE launches write into each on-disk subtree root, keyed by root path. On the * carrier (not module state) so the ESM and CJS copies of `@bugsee/node` count the same launches. */ liveInstanceRoots?: Map; @@ -309,7 +309,7 @@ export function setCarrierLastLaunch( } } getCarrier(globalObj).lastLaunch = { appToken, options: retained }; - // Remember the custom host on the default slot, so a plain relaunch() can find it (R2L-12). + // Remember the custom host on the default slot, so a plain relaunch() can find it. const host = retained.carrier; getCarrier().lastHost = typeof host === 'object' && host !== null && host !== globalThis ? host : undefined; @@ -331,7 +331,7 @@ export function getFilters(globalObj: object = globalThis): FilterStore | undefi return getInternal(globalObj)?.getService(FiltersToken); } -// Lifecycle state of SEALED carriers (R5L-1). Module-private on purpose: a property on the carrier is page- +// Lifecycle state of SEALED carriers. Module-private on purpose: a property on the carrier is page- // reachable in a WebView (the carrier is `window.__BUGSEE__`), and a listener there both observes the session // and receives events page script could forge. A WebView is one self-contained bundle, so one module copy // holds this; node/browser never seal and keep the cross-copy carrier slot. @@ -445,7 +445,7 @@ export function getCarrierRelaunching(globalObj: object = globalThis): boolean { return getCarrier(globalObj).relaunching === true; } -/** The failed-relaunch counter a notify hand-over is tied to (R4L-2). */ +/** The failed-relaunch counter a notify hand-over is tied to. */ export function getCarrierRelaunchEpoch(globalObj: object = globalThis): number { return getCarrier(globalObj).relaunchEpoch ?? 0; } diff --git a/packages/core/src/chunk-backend.ts b/packages/core/src/chunk-backend.ts index 64a2e4ee..9424ea4c 100644 --- a/packages/core/src/chunk-backend.ts +++ b/packages/core/src/chunk-backend.ts @@ -1,7 +1,6 @@ import type { CaptureSnapshot, StoredEntry } from './contracts'; -// The storage seam beneath the chunk-based capture store (design: durable-as-captured, metadata-only in -// memory; Android `CaptureFileStorage` analog). The ChunkCaptureStore keeps only a part metadata INDEX +// The storage seam beneath the chunk-based capture store (metadata-only in memory; Android `CaptureFileStorage` analog). The ChunkCaptureStore keeps only a part metadata INDEX // in RAM and writes every entry through to a ChunkBackend β€” in-memory (ephemeral), filesystem (node), // or IndexedDB (browser). Writes are SYNC-ISSUE / async-complete: openPart/appendEntry/closePart return // immediately so the sync CaptureStore contract holds; a durable backend may complete the write @@ -60,7 +59,7 @@ export interface ChunkBackend { /** Every generation with persisted parts (recovery). */ listGenerations(): number[] | Promise; /** - * Commit everything captured so far (Wave 6.2). Optional: only a backend that DEFERS physical writes has + * Commit everything captured so far. Optional: only a backend that DEFERS physical writes has * anything to commit, and one that writes synchronously must not advertise a guarantee it does not add. * * A durable backend keeps `appendEntry` non-blocking by queueing the write, so at any instant some diff --git a/packages/core/src/chunk-capture-store.test.ts b/packages/core/src/chunk-capture-store.test.ts index 5ce8fcf1..e072c0c3 100644 --- a/packages/core/src/chunk-capture-store.test.ts +++ b/packages/core/src/chunk-capture-store.test.ts @@ -42,19 +42,19 @@ describe('createChunkCaptureStore β€” backend protocol', () => { clock: clockAt(10_000), maxRecordingTimeMs: 2000, }); - store.add(recBytes(5)); // part 0 - store.tick(11_000); // part 0 closes @11000 - store.tick(15_000); // cutting 12000 β†’ part 0 (end 11000) evicted β†’ removePart - expect((await backend.listParts(0)).map((p) => p.number)).toEqual([1, 2]); // part 0 gone + store.add(recBytes(5)); + store.tick(11_000); // closes @11000 + store.tick(15_000); // cutting 12000 β†’ (end 11000) evicted β†’ removePart + expect((await backend.listParts(0)).map((p) => p.number)).toEqual([1, 2]); // gone }); it('removes a byte-cap-evicted part from the backend', async () => { const backend = createMemoryChunkBackend(); const store = createChunkCaptureStore(backend, { clock: clockAt(0), maxDataSizeBytes: 10 }); - store.add(recBytes(8)); // part 0 - store.tick(1000); // close part 0 - store.add(recBytes(8)); // part 1 β†’ total 16 > 10 β†’ evict part 0 (closed) β†’ removePart - expect((await backend.listParts(0)).map((p) => p.number)).toEqual([1]); // part 0 gone + store.add(recBytes(8)); + store.tick(1000); // close + store.add(recBytes(8)); // β†’ total 16 > 10 β†’ evict (closed) β†’ removePart + expect((await backend.listParts(0)).map((p) => p.number)).toEqual([1]); // gone }); it('removes the whole generation on clear, then opens a fresh part', async () => { diff --git a/packages/core/src/chunk-storage.ts b/packages/core/src/chunk-storage.ts index 3203bef6..4ea74889 100644 --- a/packages/core/src/chunk-storage.ts +++ b/packages/core/src/chunk-storage.ts @@ -35,7 +35,7 @@ export interface ChunkStorage { /** Flush everything + close all handles (on stop()). */ dispose?(): void; /** - * Bytes currently held UNWRITTEN by a batched writer (Wave 6.3), or absent where nothing is buffered. + * Bytes currently held UNWRITTEN by a batched writer, or absent where nothing is buffered. * * This is the quantity a back-pressure bound exists to cap: a storage that defers physical writes holds * captured records in memory until they land, and a persistently failing disk otherwise turns that hold diff --git a/packages/core/src/client-blackout-ownership.test.ts b/packages/core/src/client-blackout-ownership.test.ts index c011e18d..3775d1a0 100644 --- a/packages/core/src/client-blackout-ownership.test.ts +++ b/packages/core/src/client-blackout-ownership.test.ts @@ -9,7 +9,7 @@ import { import { createClient } from './client'; import { LifecycleEvents } from './lifecycle-events'; -// Review round 3 (H1): R3L-4 / R3V-1. Only the client that currently owns the carrier may change the +// Only the client that currently owns the carrier may change the // process-wide blackout flag; a stale handle's start/end is forwarded to the current client or reported. const noScheduler = { setInterval: () => 'h', clearInterval: () => {} }; @@ -68,7 +68,7 @@ describe('R3L-4 β€” a stale handle cannot change the process blackout', () => { expect(b.isBlackout()).toBe(true); expect(getCarrierBlackout(host)).toBe(true); expect(types(events)).toEqual([LifecycleEvents.BlackoutStarted]); - expect(a.isBlackout()).toBe(true); // R4L-5: a stale handle answers with the process flag, not its own state + expect(a.isBlackout()).toBe(true); // a stale handle answers with the process flag, not its own state }); it('R4L-5: a stale handle reflects the carrier flag both ways', async () => { @@ -171,8 +171,8 @@ describe('R3L-4 β€” a stale handle cannot change the process blackout', () => { expect(getCarrierBlackout(host)).toBe(true); b.startBlackout(); // never launched, stale: forwarded to a (already dark), never applied to b itself expect(getCarrierBlackout(host)).toBe(true); - expect(b.isBlackout()).toBe(true); // a stale handle answers with the process flag (R4L-5) - // R5L-4: b's OWN state (read from its service, not through the carrier-reading isBlackout) stays untouched + expect(b.isBlackout()).toBe(true); // a stale handle answers with the process flag + // b's OWN state (read from its service, not through the carrier-reading isBlackout) stays untouched expect(b.getService(BlackoutToken).isActive()).toBe(false); expect(a.getService(BlackoutToken).isActive()).toBe(true); }); diff --git a/packages/core/src/client-blackout-started-deferred.test.ts b/packages/core/src/client-blackout-started-deferred.test.ts index 4112aaec..04e8abd4 100644 --- a/packages/core/src/client-blackout-started-deferred.test.ts +++ b/packages/core/src/client-blackout-started-deferred.test.ts @@ -3,7 +3,7 @@ import { BlackoutToken } from './blackout'; import { asInternalClient, createClient } from './client'; import { LifecycleEvents } from './lifecycle-events'; -// Review round 3 (H1): R3L-7. A blackout raised silently before launch (`startInBlackout`) owes the application +// A blackout raised silently before launch (`startInBlackout`) owes the application // ONE BlackoutStarted, after Launching: but never after a client that is no longer launched, and never AFTER an // Ended that a Launching listener caused. diff --git a/packages/core/src/client-lifecycle-state.test.ts b/packages/core/src/client-lifecycle-state.test.ts index cfe1a569..3b0f588e 100644 --- a/packages/core/src/client-lifecycle-state.test.ts +++ b/packages/core/src/client-lifecycle-state.test.ts @@ -11,8 +11,7 @@ import { LifecycleEvents } from './lifecycle-events'; import { createOptionsContainer } from './options'; import type { UploadPipeline, UploadResult } from './transport'; -// Review round 1, group G1: lifecycle-event delivery (L1, L2), stop generations (L4), the kill state (L12), -// blackout state (L3, L16, V2), `getLaunchOptions` (L11). +// Lifecycle-event delivery, stop generations, the kill state, blackout state, and `getLaunchOptions`. const getEnvironment = (): EnvironmentEnvelope => ({ platform: { type: 'web', version: '1' }, diff --git a/packages/core/src/client-notify-handover.test.ts b/packages/core/src/client-notify-handover.test.ts index afa3857b..c1a47056 100644 --- a/packages/core/src/client-notify-handover.test.ts +++ b/packages/core/src/client-notify-handover.test.ts @@ -13,7 +13,7 @@ import { createMemoryCaptureStore } from './memory-capture-store'; import { relaunchWith } from './relaunch'; import type { NotifyItem, NotifyResult, UploadPipeline } from './transport'; -// Review round 3 (H1): R3L-5 / R3P-4. The notify hand-over of relaunch(): a batch in flight at take() that later +// The notify hand-over of relaunch(): a batch in flight at take() that later // fails, a late old-stop `.finally`, and the relaunch failure path. const getEnvironment = (): EnvironmentEnvelope => ({ diff --git a/packages/core/src/client-notify.test.ts b/packages/core/src/client-notify.test.ts index 710a4b64..3e4d5fb5 100644 --- a/packages/core/src/client-notify.test.ts +++ b/packages/core/src/client-notify.test.ts @@ -7,7 +7,7 @@ import { createMemoryCaptureStore } from './memory-capture-store'; import { createOptionsContainer } from './options'; import type { NotifyItem, NotifyResult, UploadPipeline } from './transport'; -// `client.notify()` (T7) through the real client: the launch gate, option parsing, the snapshot taken at +// `client.notify()` through the real client: the launch gate, option parsing, the snapshot taken at // the call, and the flush/stop drain. The relay's own batching and retry rules are in notify-relay.test.ts. const env: EnvironmentEnvelope = { diff --git a/packages/core/src/client-stop-safety.test.ts b/packages/core/src/client-stop-safety.test.ts index 9a3ec220..3eea5001 100644 --- a/packages/core/src/client-stop-safety.test.ts +++ b/packages/core/src/client-stop-safety.test.ts @@ -18,9 +18,9 @@ import type { ReportingRequest } from './reporting'; import type { NotifyItem, NotifyResult, UploadPipeline, UploadResult } from './transport'; import type { TriggerPipeline } from './trigger-pipeline'; -// Review round 2, group F1 (core half): stop() safety (R2L-1), ReportHandler.after on injected pipelines -// (R2L-2), refusing launch() on a stopped platform client (R2L-3), blackout end while stopped (R2L-7), -// the kill-state generation (R2L-13) and the notify backlog handover across relaunch() (R2L-6). +// Core half: stop() safety, ReportHandler.after on injected pipelines +// refusing launch() on a stopped platform client, blackout end while stopped, +// the kill-state generation and the notify backlog handover across relaunch(). const getEnvironment = (): EnvironmentEnvelope => ({ platform: { type: 'web', version: '1' }, diff --git a/packages/core/src/client-stop-shared-drain.test.ts b/packages/core/src/client-stop-shared-drain.test.ts index 20c2c8fb..9b1ac58e 100644 --- a/packages/core/src/client-stop-shared-drain.test.ts +++ b/packages/core/src/client-stop-shared-drain.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, vi } from 'vitest'; import { createClient } from './client'; import type { UploadPipeline } from './transport'; -// Review round 3 (H1): R3L-2. The shared stop drain runs UNBOUNDED; each caller's timeout only bounds its own wait. +// The shared stop drain runs UNBOUNDED; each caller's timeout only bounds its own wait. const getEnvironment = (): EnvironmentEnvelope => ({ platform: { type: 'web', version: '1' }, @@ -67,7 +67,7 @@ describe('R3L-2 β€” the shared stop drain is not bounded by the first caller', ( settled = true; }); // The drain is gated on a promise (no timer involved), so "still pending" is decided by microtasks alone: - // flush every continuation that could have settled it, deterministically (R4H-8). + // flush every continuation that could have settled it, deterministically. await tick(); await tick(); expect(settled).toBe(false); diff --git a/packages/core/src/client.test.ts b/packages/core/src/client.test.ts index 935fea4b..a2df048f 100644 --- a/packages/core/src/client.test.ts +++ b/packages/core/src/client.test.ts @@ -409,10 +409,9 @@ describe('createClient β€” capture entry points', () => { expect((entry?.data as { timestamp: number }).timestamp).toBe(42); }); - // The wire level is NUMERIC (design Β§8.9, mobile parity: 1=Error … 5=Verbose). Wave 5.1 fixed the - // console-CAPTURE path and left `client.log()` β€” the manual API users are told to call β€” shipping the - // string name. These two tests asserted `level: 'info'` and `level: 'error'`, so they pinned the defect - // in place; the e2e upload contract added in Wave 3b.2 is what finally named it. + // The wire level is NUMERIC (mobile parity: 1=Error … 5=Verbose). `client.log()` β€” the manual API + // users are told to call β€” once shipped the string name. These two tests asserted `level: 'info'` and + // `level: 'error'`, so they pinned the defect in place; the e2e upload contract is what finally named it. it('log pushes a log entry whose level is the NUMERIC wire value', async () => { const store = createMemoryCaptureStore({ maxRecordingTimeMs: Number.POSITIVE_INFINITY }); const client = createClient({ captureStore: store, clock: fixedClock(1000) }); @@ -607,7 +606,7 @@ describe('createClient β€” crash.json (SC3)', () => { (f: { data?: { member?: string } }) => f.data?.member, ); expect(members).toContain('applicationCode'); - // R2P-1: the PUBLIC wrapper (`Object.logException`) used to be frame 0. Frame 0 IS the application. + // the PUBLIC wrapper (`Object.logException`) used to be frame 0. Frame 0 IS the application. expect(members[0]).toBe('applicationCode'); expect(members.some((m: string | undefined) => m?.includes('logException'))).toBe(false); }); @@ -1451,7 +1450,7 @@ describe('createClient β€” lifecycle', () => { const drained = await client.stop(50); expect(drained).toBe(true); expect(provider.stop).toHaveBeenCalledTimes(1); - expect(flush).toHaveBeenCalledWith(undefined); // the shared drain is unbounded (R3L-2); 50 only bounds this wait + expect(flush).toHaveBeenCalledWith(undefined); // the shared drain is unbounded; 50 only bounds this wait expect(client.isLaunched()).toBe(false); }); @@ -1712,7 +1711,7 @@ describe('createClient β€” logException', () => { }); client.launch(); await client.stop(); - expect(await client.logException(new Error('after-stop'))).toEqual({ ok: false }); // Β§1501 + expect(await client.logException(new Error('after-stop'))).toEqual({ ok: false }); expect(report).not.toHaveBeenCalled(); client.launch(); // re-launch restores capture expect(await client.logException(new Error('after-relaunch'))).toEqual({ ok: true }); @@ -2204,10 +2203,10 @@ describe('createClient β€” capture-store tick timer', () => { }); }); -// S3 (docs/design/cloudflare-tenant-isolation.md Β§4.3): an incident drains only the faulting TENANT. +// an incident drains only the faulting TENANT. // // The client-level statement of the leak proven on real workerd, where tenant C's bundle carried tenant -// A's and B's secrets (docs/review/cloudflare.md SEV1 #2). Store, aggregator and exporter are all REAL +// A's and B's secrets. Store, aggregator and exporter are all REAL // here β€” only the context provider is driven, standing in for per-DO contexts in one isolate. describe('createClient β€” per-tenant capture isolation', () => { it('an incident in one tenant enqueues that tenant only', async () => { @@ -3454,7 +3453,7 @@ describe('createClient β€” mobile-parity options (T6)', () => { expect(severities(report)).toEqual(['high']); }); - // R2D-3: Android applies ReportingDefaultBugPriority to EVERY bug report, not only the browser dialog's. + // Android applies ReportingDefaultBugPriority to EVERY bug report, not only the browser dialog's. const BUG = 'com.bugsee.option.reporting.defaults.bug-priority'; it('applies reportDefaultBugPriority to client.upload() and to a detected bug-type report', async () => { diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 94b532db..9468c1e9 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -111,9 +111,9 @@ import { } from './transport'; import { createTriggerPipeline, type TriggerPipeline } from './trigger-pipeline'; -// The Client facade (design Β§7.1) β€” the runtime-agnostic composition root that wires the kernel -// together: registration seams (Β§16.3), identity/attribute delegation to the single global -// Environment (Β§7.2), manual capture entry points, lifecycle (launch/stop/flush), and the +// The Client facade β€” the runtime-agnostic composition root that wires the kernel +// together: registration seams, identity/attribute delegation to the single global +// Environment, manual capture entry points, lifecycle (launch/stop/flush), and the // report/upload path. When given a report assembler's inputs (appToken + environment + upload // pipeline), the Client builds the trigger pipeline itself (assembleBundle over the exporter // drain); logException instance-dedups + rate-limits, then reports. A dropped result is @@ -213,7 +213,7 @@ export interface InternalLogExceptionOptions extends LogExceptionOptions { /** * The function whose caller is the application, when it is NOT the method the crash is built in: the * public `logException` wrapper supplies itself here so a non-Error throwable's synthetic stack starts - * at the app's call site and `skipFrames` counts from there (R2P-1). The wrapper always overwrites it. + * at the app's call site and `skipFrames` counts from there. The wrapper always overwrites it. * `sdkFrames` is the frames above the application when `Error.captureStackTrace` is unavailable. */ callSite?: { boundary: (...args: never[]) => unknown; sdkFrames: number }; @@ -281,7 +281,7 @@ export interface BugseeClient extends Client, ServiceResolver, ServiceRegistrar clearAllAttributes(): void; getAllAttributes(): Record; - // Redaction filters (Β§4.1#4 / Β§7.1): each runs per captured event to mutate it or DROP it (return + // Redaction filters: each runs per captured event to mutate it or DROP it (return // null). A network filter REPLACES the built-in sanitizer. Pass null to clear. Settable any time. /** * Filter captured network events: mutate one, or return null to drop it. @@ -335,7 +335,7 @@ export interface BugseeClient extends Client, ServiceResolver, ServiceRegistrar */ setLifecycleEventsListener(listener: LifecycleEventsListener | null): void; - // Manual capture entry points (Android parity, Β§7.1). Each pushes a CaptureDataEntry. + // Manual capture entry points (Android parity). Each pushes a CaptureDataEntry. addBreadcrumb(breadcrumb: BreadcrumbInput): void; log(message: string, level?: LogLevel | LogLevelName, timestamp?: number): void; event(name: string, params?: Record): void; @@ -381,7 +381,7 @@ export interface BugseeClient extends Client, ServiceResolver, ServiceRegistrar * Suppress ALL visual / interaction capture (session replay, input presses, `ui.*` interaction * breadcrumbs, the report-time view hierarchy) until {@link endBlackout}. Logs, network, traces and * events continue (so do request/response bodies, navigation URLs and `video.aux` geometry: see - * docs/design/blackout.md for the exact list). Idempotent and NOT counted: one `endBlackout()` ends any + * for the exact list). Idempotent and NOT counted: one `endBlackout()` ends any * number of calls, as on mobile. * * A NO-OP while the client is not launched (before it is launched, after `stop()`, or once the SDK has been @@ -400,7 +400,7 @@ export interface BugseeClient extends Client, ServiceResolver, ServiceRegistrar /** Whether a blackout is currently in force. */ isBlackout(): boolean; - // Lifecycle (Β§7.1). + // Lifecycle. /** Start capture + detection. Idempotent β€” a second call while launched is ignored. */ launch(): void; isLaunched(): boolean; @@ -436,7 +436,7 @@ export interface InternalBugseeClient extends BugseeClient { * A blackout raised SILENTLY before `launch()` (`startInBlackout`) owes the application one BlackoutStarted: owe * it. It is announced after Launching/Launched when the client is still launched and still dark, and BEFORE * the BlackoutEnded of a Launching listener that lifts the blackout at once, so Started always precedes Ended - * (R3L-7). Without this call a silent hold never announces itself. + * Without this call a silent hold never announces itself. */ deferBlackoutStarted(): void; } @@ -505,9 +505,9 @@ function exposePublicSurface(client: InternalBugseeClient): CreatedClient { ...publicMethods, logException: function logException(error, options) { // The wrapper is itself an SDK frame: it names itself as the boundary so the application, not - // `Object.logException`, is frame 0 of a non-Error's synthetic stack (R2P-1). + // `Object.logException`, is frame 0 of a non-Error's synthetic stack. // NOT a tail call: JavaScriptCore drops the frame of a function that tail-calls, which would make - // `sdkFrames: 2` remove the application frame in the fallback path (R3P-1). + // `sdkFrames: 2` remove the application frame in the fallback path. const result = internal.logException(error, { ...withoutWireOptions(options, 'mechanism'), callSite: { boundary: logException, sdkFrames: 2 }, @@ -575,7 +575,7 @@ export interface CreateClientOptions { captureStore?: CaptureStore; /** The internal service container (the per-process DI registry). Default a fresh one. */ services?: ServiceContainer; - /** Recording window in seconds for the default in-memory store (design maxRecordingTime). Default 60. */ + /** Recording window in seconds for the default in-memory store (maxRecordingTime). Default 60. */ maxRecordingTime?: number; /** Scheduler for the capture-store tick; injectable for tests/edge. Default global timers. */ scheduler?: Scheduler; @@ -593,7 +593,7 @@ export interface CreateClientOptions { effective: Readonly>; host: Readonly>; }; - /** Capture-storm rate limit (Β§7.7). Default 100 / 60s. */ + /** Capture-storm rate limit. Default 100 / 60s. */ captureRateLimit?: RateLimiterOptions; /** Upload pipeline (built by the platform from BugseeApi/BundleUploader); enables flush/stop drain. */ uploadPipeline?: UploadPipeline; @@ -631,7 +631,7 @@ export interface CreateClientOptions { */ reportSnapshots?: readonly ReportSnapshotSource[]; /** - * The active-request context provider (design: framework-adapters.md). When present, the capture + * The active-request context provider. When present, the capture * aggregator stamps each entry's payload with the active context's correlation ids (contextId + trace * ids), and it is registered as a container service (ContextProviderToken) for process-wide resolution. * Absent (default) β†’ no stamping; behavior is byte-identical to today for non-adapter users. @@ -639,7 +639,7 @@ export interface CreateClientOptions { contextProvider?: ContextProvider; /** * The process carrier host (default none). When given, PROCESS-level state lives on it, so it outlives a - * single client and `relaunch()` (which builds a new client) keeps it: the lifecycle listener (L1) and + * single client and `relaunch()` (which builds a new client) keeps it: the lifecycle listener and * the blackout state (V2). Without it both stay private to this client. */ carrier?: object; @@ -659,7 +659,7 @@ export interface CreateClientOptions { */ restartable?: boolean; /** - * Internal error sink (Β§15.1). Receives provider-start failures (so launch() never throws) and + * Internal error sink. Receives provider-start failures (so launch() never throws) and * hub-listener / operation-observer failures. Platform tiers wire this to debug.warn. Default no-op. */ onError?: (error: unknown) => void; @@ -686,10 +686,10 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { // a throwing sink must not defeat the guard either } }; - // The internal service container (the "BugseeInternal" β€” the per-process DI registry, Β§7.4). Phase 1 + // The internal service container (the "BugseeInternal" β€” the per-process DI registry). // stands it up; later phases migrate the hand-wired seams into it as registered services. const services = options.services ?? createServiceContainer(); - // Redaction filters: the first real service (Β§4.1#4). The facade's set* mutate this store; the + // Redaction filters: the first real service. The facade's set* mutate this store; the // capture pipeline reads the same instance via the container (getFilters). Registered eagerly so a // pipeline resolve always finds it once a client exists. const filters = createFilterStore(onError); @@ -713,7 +713,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { const rateLimiter = createRateLimiter(clock, options.captureRateLimit); const environment = createEnvironment(); const operations = createOperationDispatcher(onError); - // One store, two directions (Β§16): the aggregator writes (serialize + route), the exporter reads + // One store, two directions: the aggregator writes (serialize + route), the exporter reads // (drain + deserialize) at trigger time. The default in-memory store shares the Client's clock and // recording window so entries and the time-based retention agree on "now". const captureStore = @@ -736,7 +736,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { processCarrier !== undefined && getCarrierBlackout(processCarrier), ); services.addService(defineService(BlackoutToken, () => blackout)); - // Who may change the PROCESS-wide blackout (R3L-4): the newest client created or launched on the carrier. An + // Who may change the PROCESS-wide blackout: the newest client created or launched on the carrier. An // older handle (stopped, or replaced by a relaunch) would otherwise clear the flag under the live client, // so the next relaunch would start un-dark. A client without a carrier is its own owner. const blackoutOwner: CarrierBlackoutOwner = { @@ -763,12 +763,12 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { setCarrierBlackout(active, processCarrier); } // A silent change (a launch hold, a handshake release) is bookkeeping the application never asked for, and - // a blackout can only be ENDED while the client is not launched (R2L-7): that clears the carried state + // a blackout can only be ENDED while the client is not launched: that clears the carried state // quietly, since nothing is being captured for the events to describe. if (launched && !blackout.isChangeSilent()) { if (blackoutStartedOwed) { // The owed announcement is ahead of whatever the application did to the blackout since: an Ended - // must never be heard for a blackout whose Started was not (R3L-7). + // must never be heard for a blackout whose Started was not. blackoutStartedOwed = false; if (!active) { lifecycle.emit(LifecycleEvents.BlackoutStarted); @@ -777,7 +777,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { lifecycle.emit(active ? LifecycleEvents.BlackoutStarted : LifecycleEvents.BlackoutEnded); } }); - // The active-request context provider (framework adapters). When injected, the aggregator stamps each + // The active-request context provider. When injected, the aggregator stamps each // entry's correlation ids and the provider is resolvable process-wide; absent β†’ no-op. const contextProvider = options.contextProvider; if (contextProvider !== undefined) { @@ -851,7 +851,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { 'high', ); // Android applies `ReportingDefaultBugPriority` to every bug report (an upload, a shake, the report - // dialog), so it is applied here for every platform, not only the browser dialog (R2D-3). + // dialog), so it is applied here for every platform, not only the browser dialog. const bugPriority = resolvePriority( BugseeOption.ReportingDefaultBugPriority, 'reportDefaultBugPriority', @@ -863,7 +863,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { services.addService(defineService(SchedulerToken, () => scheduler)); const tickIntervalMs = options.tickIntervalMs ?? 1000; let launched = false; - // True once stop() has run (until a re-launch): manual captures that upload become no-ops (Β§1501). + // True once stop() has run (until a re-launch): manual captures that upload become no-ops. // Distinct from `!launched` so capturing BEFORE the first launch is unaffected. let stopped = false; // True from stop() until its upload drain settles (BugseeStatus.Stopping). @@ -871,9 +871,9 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { // True from launch() until providers are started (BugseeStatus.Launching). let launching = false; // Bumped by every launch(), stop() and kill: a drain that finishes after a newer transition is STALE and - // must not emit Stopped, suspend the notify relay or clear `stopping` for the launch that replaced it (L4). + // must not emit Stopped, suspend the notify relay or clear `stopping` for the launch that replaced it. let generation = 0; - // Permanent kill-state (Β§1435/Β§1504): set when a report comes back with the collector's KILL_SDK + // Permanent kill-state: set when a report comes back with the collector's KILL_SDK // verdict (code 99099) β€” the app token has been switched OFF at the server. All capture goes no-op, // capture+detection halt, onError fires ONCE. An INVALID app token is a different thing entirely: it // classifies as `permanent` (drop this bundle) and leaves the client recording. @@ -890,7 +890,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { // Build the trigger pipeline from the report assembler when its inputs are present (unless an // override is injected). assemble reads the exporter drain + the live environment/attributes. const { uploadPipeline, appToken, getEnvironment } = options; - // `notify()` (T7). The window is read once: a non-number, negative or non-finite value is treated as the + // `notify()`. The window is read once: a non-number, negative or non-finite value is treated as the // default 0 rather than reported, because a typo here only changes batching, never what is sent. const notifyDelayOption = launchOptions.get(BugseeOption.NotifyFlushDelay, 0); const notifyFlushDelayMs = @@ -907,7 +907,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { clock, flushDelayMs: notifyFlushDelayMs, onError: onErrorSafe, - // A batch still in a request when relaunch() took the queue, failing later (R3L-5). + // A batch still in a request when relaunch() took the queue, failing later. onHandBack: (items) => handBackNotify(items), // The relay's own `403 KillSdk` verdict switches the SDK off exactly like a report's. onKill: () => @@ -935,21 +935,21 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { services.addService(defineService(UploadPipelineToken, () => uploadPipeline)); } // relaunch() hands what the old client could not deliver (an offline notify() queue) to the client it builds - // (R2L-6). The decision is taken when stop() is CALLED (`getCarrierRelaunching` is true only for the stop that + // The decision is taken when stop() is CALLED (`getCarrierRelaunching` is true only for the stop that // relaunch() itself issues), never when the drain finishes: a slow old stop can settle after the new client // is up. Where the items go, in order: the client that has already adopted (it registered as the carrier's // adopter), else the carrier backlog its launch() will drain. A plain stop() keeps nothing on the carrier, // which sits on `globalThis` and would otherwise hold user-identifying notifications for a process that never // relaunches. const notifyOwner = {}; - /** The carrier's failed-relaunch counter when the handing-over stop() was issued (R4L-2). */ + /** The carrier's failed-relaunch counter when the handing-over stop() was issued. */ let handoverEpoch = 0; const handBackNotify = (items: NotifyItem[]): void => { if (processCarrier === undefined || items.length === 0) { return; } if (getCarrierRelaunchEpoch(processCarrier) !== handoverEpoch) { - // The relaunch attempt this hand-over belonged to failed: nobody will adopt these (R4L-2). + // The relaunch attempt this hand-over belonged to failed: nobody will adopt these. onErrorSafe( new Error( `notify: dropped ${items.length} notification(s); the relaunch they were handed to failed`, @@ -990,7 +990,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { // // Scope the drain to the TENANT that faulted. On a multi-tenant isolate (Cloudflare Durable Objects) // an unscoped drain would put every other tenant's capture into this bundle β€” the leak proven on real - // workerd (docs/review/cloudflare.md SEV1 #2). `owner` is undefined everywhere else, where the + // workerd. `owner` is undefined everywhere else, where the // partitioned store is a no-op and this is byte-identical to an unscoped drain. collectCapture = async (owner?: string): Promise> => { const capturedByType = await captureExporter.drain( @@ -1035,7 +1035,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { const assembleReport = async (request: ReportingRequest): Promise => { // Scope the drain to the TENANT that faulted. On a multi-tenant isolate (Cloudflare Durable // Objects) an unscoped drain would put every other tenant's capture into this bundle β€” the leak - // proven on real workerd (docs/review/cloudflare.md SEV1 #2). `owner` is undefined everywhere else, + // proven on real workerd. `owner` is undefined everywhere else, // where the partitioned store is a no-op and this is byte-identical to an unscoped drain. const reportContext = reportContexts.get(request); // A prepared upload carries the capture it froze; anything else drains live. A frozen capture that @@ -1057,12 +1057,12 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { // come through submitReport, where reading the Environment live is the previous behaviour and a // strictly better degrade than shipping no attributes at all. const identity = reportIdentity.get(request) ?? liveIdentity(); - // What `after` just changed is part of the report, so it belongs in the recovery marker too (R2P-7). + // What `after` just changed is part of the report, so it belongs in the recovery marker too. // The marker is written at SUBMIT, before assembly - the crash-safety point - and `after` runs at // assembly, so it is REPLACED here (same id) rather than the handler being run earlier, which would // change when `after` fires for a refused or failed report. The remaining difference is a crash in the // window between submit and this point, which recovers the report as `before` left it. - // Only when `after` actually changed the report (R3P-7): an unreadable report counts as changed. + // Only when `after` actually changed the report: an unreadable report counts as changed. if ( afterHandler !== undefined && reportMarkers !== undefined && @@ -1201,7 +1201,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { timeout === undefined ? drained : Promise.race([drained, sleep(timeout).then(() => false)]); const drainPending = (timeout?: number): Promise => boundDrain(startDrain(timeout), timeout); - // The REAL drain of the latest stop(), kept after it settles (R2L-1): a repeat stop() while it is still + // The REAL drain of the latest stop(), kept after it settles: a repeat stop() while it is still // running, or after a bounded stop() gave up on it, must answer for THAT drain. Answering "drained" at once // let a platform mark the instance's data recoverable (node `.stopped`) or release its lock (browser, // worker) while the first stop's upload was still going. Cleared by launch(). @@ -1228,7 +1228,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { onError(error); notifyRelay?.suspend(); // a switched-off SDK sends nothing more, notifications included if (launched) { - // The same transition a stop() makes, announced the same way (L12): Stopping, then Stopped, with the + // The same transition a stop() makes, announced the same way: Stopping, then Stopped, with the // state already settled when each is delivered. There is no drain to wait for. generation += 1; // a transition like stop(): any drain started before it is stale launched = false; @@ -1300,14 +1300,14 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { } catch (error) { onErrorSafe(error); // The limits could not be applied in place (a handler froze the report): the marker still must not - // persist what they would drop, so it encodes a limited COPY (R2P-9). Assembly applies them again. + // persist what they would drop, so it encodes a limited COPY. Assembly applies them again. // A hostile report (throwing Proxy/getter) can throw here too: the encode below is guarded and the - // report must never be lost to its own recovery marker (R3P-8). + // report must never be lost to its own recovery marker. try { markerRequest = limitAttachmentsForMarker(handled); } catch (limitError) { onErrorSafe(limitError); - // Fail CLOSED (R4H-6): the unlimited request must not reach the marker, so it persists no + // Fail CLOSED: the unlimited request must not reach the marker, so it persists no // user attachments at all. Assembly still limits and ships them on the live path. try { markerRequest = { ...handled, report: { ...handled.report, userAttachments: [] } }; @@ -1319,7 +1319,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { } } if (markerRequest === undefined) { - markerDurable = false; // no marker is persisted at all: the "unrecoverable" warning must still fire (R5H-3) + markerDurable = false; // no marker is persisted at all: the "unrecoverable" warning must still fire } if (reportMarkers !== undefined && markerRequest !== undefined) { try { @@ -1345,7 +1345,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { } if (options.triggerPipeline !== undefined) { // An injected pipeline (Electron renderer, WebView) never goes through `assembleReport`, which is where - // `after` runs for the built one (R2L-2). The report exists and `before` has had its say: same moment. + // `after` runs for the built one. The report exists and `before` has had its say: same moment. neverThrow(() => filters.report?.after?.(handled), onErrorSafe); } const result = track(triggerPipeline?.report(handled) ?? Promise.resolve({ ok: false })); @@ -1431,7 +1431,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { return result; }; - // The provider/extension-facing surface (Β§16.3) passed to providers at start(). + // The provider/extension-facing surface passed to providers at start(). const context: Client = { operations, captureAggregator, @@ -1440,7 +1440,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { }; // `getLaunchOptions()` / `getHostLaunchOptions()` report the values the client actually applies, not the - // raw host input (L11): an unrecognised priority or log level reads as the fallback it was replaced by, + // raw host input: an unrecognised priority or log level reads as the fallback it was replaced by, // and the notify window as the whole-millisecond figure in force. Only keys the view already carries // are rewritten, so the host view still lists only what the host supplied. const inEffect = ( @@ -1538,9 +1538,9 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { if (filtered === null) { return; // dropped by the log filter } - // The wire level is NUMERIC (design Β§8.9, mobile parity). Encoded AFTER the filter so a user's + // The wire level is NUMERIC (mobile parity). Encoded AFTER the filter so a user's // `logFilter` still sees the friendly name it was written against β€” the same ordering the capture - // provider uses. Wave 5.1 fixed that provider and left this path, the MANUAL API, shipping the string. + // provider uses. This path, the MANUAL API, was left unfixed and kept shipping the string. const wire: LogEvent = typeof filtered.level === 'string' ? { ...filtered, level: logLevelToWire(filtered.level) } @@ -1576,15 +1576,15 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { error: unknown, exceptionOptions?: InternalLogExceptionOptions, ): Promise { - // After stop() (Β§1501) or in the kill-state (Β§1435), logException is a silent no-op. + // After stop() or in the kill-state, logException is a silent no-op. if (stopped || killed) { return Promise.resolve({ ok: false }); } - // Instance dedup: a re-capture of the same thrown object is a no-op (Β§7.7). + // Instance dedup: a re-capture of the same thrown object is a no-op. if (checkOrSetAlreadyCaught(error)) { return Promise.resolve({ ok: false }); } - // Storm self-protection: drop beyond the rolling capture rate (Β§7.7). + // Storm self-protection: drop beyond the rolling capture rate. if (!rateLimiter.tryAcquire()) { return Promise.resolve({ ok: false }); } @@ -1748,11 +1748,11 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { } }, - // Allowed while stopped (R2L-7), unlike start: an app that stopped during a blackout must be able to lift + // Allowed while stopped, unlike start: an app that stopped during a blackout must be able to lift // it, or the carried process flag would make the NEXT launch start dark with no event to say so. endBlackout(): void { if (!ownsBlackout()) { - // Never lifted from a stale handle (R3L-4): that would clear the process flag under the live client. + // Never lifted from a stale handle: that would clear the process flag under the live client. onErrorSafe( new Error( 'Bugsee: endBlackout() on a stale client handle ignored; end the blackout through the current client', @@ -1764,7 +1764,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { }, isBlackout(): boolean { - // A stale handle answers with the PROCESS flag (R4L-5): its own state is a stopped client's memory. + // A stale handle answers with the PROCESS flag: its own state is a stopped client's memory. return !ownsBlackout() && processCarrier !== undefined ? getCarrierBlackout(processCarrier) : blackout.isActive(); @@ -1803,11 +1803,11 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { } if (stopping) { // A launch while the previous stop() is still draining ends that stop: from here on the client is - // running, so the stale drain must not report Stopped (or suspend anything) when it finishes (L4). + // running, so the stale drain must not report Stopped (or suspend anything) when it finishes. stopping = false; lifecycle.emit(LifecycleEvents.Stopped); } - // State first, events second (L2): by the time a listener hears Launching the client already reads as + // State first, events second: by the time a listener hears Launching the client already reads as // launching, so a listener that calls launch() again is a no-op and one that calls stop() aborts this // launch cleanly instead of racing it. generation += 1; @@ -1825,7 +1825,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { stopDrain = undefined; // this launch starts a fresh life: a repeat stop() answers for ITS drain notifyRelay?.resume(); // a stop() suspended it; what was kept goes out now adoptNotifyBacklog(); - // launch() must never throw (Β§15.1): a throwing provider.start is isolated per coordinator + // launch() must never throw: a throwing provider.start is isolated per coordinator // (a failed capture start must not prevent detection from starting) and routed to onError. try { captureCoordinator.start(launchOptions, isEnabled); @@ -1864,7 +1864,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { stop(timeout?: number): Promise { if (!launched) { - // Already stopped, or stopping: answer for the drain of THAT stop (R2L-1), within this call's own bound. + // Already stopped, or stopping: answer for the drain of THAT stop, within this call's own bound. // Never started, or killed (no drain was started): there is nothing left to wait for. return stopDrain !== undefined ? boundDrain(stopDrain, timeout) : Promise.resolve(true); } @@ -1881,7 +1881,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { haltCapture(); // also valid mid-launch: a stop from the Launching listener has nothing started yet stopping = true; lifecycle.emit(LifecycleEvents.Stopping); - // The shared drain is UNBOUNDED (R3L-2): it is the one real drain every stop() answers for, so a first + // The shared drain is UNBOUNDED: it is the one real drain every stop() answers for, so a first // caller's deadline must not cap it (a repeat stop() could then never report true). Each caller applies // its own timeout only when racing it. const realDrain = startDrain(); @@ -1889,7 +1889,7 @@ export function createClient(options: CreateClientOptions = {}): CreatedClient { return boundDrain(realDrain, timeout) .finally(() => { // Only the LATEST transition owns these: a launch() that superseded this stop has already cleared - // `stopping`, and its notify relay must keep running (L4). + // `stopping`, and its notify relay must keep running. if (mine === generation) { stopping = false; // A notification still waiting (offline) keeps its place for a re-launch but stops retrying: a diff --git a/packages/core/src/clock.ts b/packages/core/src/clock.ts index cf1f3f19..f99babee 100644 --- a/packages/core/src/clock.ts +++ b/packages/core/src/clock.ts @@ -1,6 +1,6 @@ import { serviceToken } from '@bugsee/service'; -// Clock model (design Β§7.7). Two distinct time sources, injectable so downstream components can use +// Clock model. Two distinct time sources, injectable so downstream components can use // a deterministic fake in tests: // - wallNow(): Date.now() unix-ms, for wire `timestamp` fields (matches mobile). // - monotonicNow(): performance.now() + performance.timeOrigin, for INTERNAL ordering & durations @@ -32,7 +32,7 @@ export function createSystemClock(): Clock { // // Unlike the browser UI-breadcrumb source (and the render-timing helpers below the core tier), a literal // `0` is NOT special-cased here: monotonicNow() is consumed only as a DIFFERENCE of two readings - // (RateLimiter interval math, Span duration math β€” see `docs/design/sdk-design.md` Β§7.7) and never + // (RateLimiter interval math, Span duration math) and never // surfaced as an absolute wire timestamp, so a constant `0` offset cancels exactly and is fully usable. const usable = !!perf && typeof perf.now === 'function' && Number.isFinite(perf.timeOrigin); const monotonicNow = usable diff --git a/packages/core/src/contracts.test-d.ts b/packages/core/src/contracts.test-d.ts index d279ae66..65870fef 100644 --- a/packages/core/src/contracts.test-d.ts +++ b/packages/core/src/contracts.test-d.ts @@ -1,4 +1,4 @@ -// Type-level tests for the Β§16.2 contracts, checked by `tsc --noEmit`. Valid example implementations +// Type-level tests for the contracts, checked by `tsc --noEmit`. Valid example implementations // must type-check; @ts-expect-error negatives pin required members. Example object literals are // type-checked (excess-property + missing-property checks). diff --git a/packages/core/src/contracts.ts b/packages/core/src/contracts.ts index 887aae08..a9b819b5 100644 --- a/packages/core/src/contracts.ts +++ b/packages/core/src/contracts.ts @@ -3,9 +3,9 @@ import { serviceToken } from '@bugsee/service'; import type { EventSubscribable } from './emitter'; import type { ReportingRequest } from './reporting'; -// The Android-derived extension contracts (design Β§16.2). Sources (Interceptor / adapters via +// The Android-derived extension contracts. Sources (Interceptor / adapters via // OperationDispatcher) emit to hubs; consumers (CaptureProvider / DetectionProvider) and feature -// modules (Extension) plug in through these seams (Β§16.3). Type-only; validated by contracts.test-d.ts. +// modules (Extension) plug in through these seams. Type-only; validated by contracts.test-d.ts. // // Operation / OperationObserver are unspecified in the design, so they get MINIMAL cross-runtime base // shapes (adapters refine them), matching the InputEvent approach. Detection submits a @@ -13,7 +13,7 @@ import type { ReportingRequest } from './reporting'; /** * A build-injected / framework-adapter operation (DB, HTTP, file, …) fed in via OperationDispatcher - * for APM and custom observers to consume (Β§4.1#4, Β§16). Minimal base; adapters refine `data`. + * for APM and custom observers to consume. Minimal base; adapters refine `data`. */ export interface Operation { /** Operation kind, e.g. 'http' | 'db' | 'file' (adapter-defined). */ @@ -28,7 +28,7 @@ export interface Operation { export type OperationObserver = (operation: Operation) => void; -/** Bridge for external libs / build-time injection; fans out operations to its own observers (Β§16.2). */ +/** Bridge for external libs / build-time injection; fans out operations to its own observers. */ export interface OperationDispatcher { /** Subscribe an observer; returns an unsubscribe function. */ registerObserver(observer: OperationObserver): () => void; @@ -37,7 +37,7 @@ export interface OperationDispatcher { } /** - * A source that owns a runtime hook and emits captured events to its listeners (Β§16.2). It is a + * A source that owns a runtime hook and emits captured events to its listeners. It is a * listenable emitter: by extending {@link EventSubscribable} it lets other components subscribe to its * processing STAGES (`StageMap` maps stage name β†’ payload β€” e.g. the network interceptor keys by * NetworkStage) through the contract alone, without the concrete impl. Observe-only: the contract @@ -91,7 +91,6 @@ export interface StoredEntry { * single-tenant path, where partitioning is a no-op. * * Exists because Durable Objects for different tenants share one isolate, one client and one capture ring - * (docs/design/cloudflare-tenant-isolation.md). */ owner?: string; } @@ -118,13 +117,13 @@ export interface CaptureStore { * * `options.owner` scopes the snapshot to ONE tenant. Stores that do not partition may ignore it; the * partitioned store uses it to keep one Durable Object's incident bundle free of every other tenant's - * capture (docs/design/cloudflare-tenant-isolation.md). + * capture. */ snapshot(options?: { owner?: string }): CaptureSnapshot; /** Discard all live records. */ clear(): void; /** - * Commit everything captured so far (Wave 6.2). Optional, and present only on stores whose backing + * Commit everything captured so far. Optional, and present only on stores whose backing * write is DEFERRED β€” an in-memory store has nothing pending, and a store that advertised a no-op flush * would turn `store.flush?.()` from a real question into a meaningless one. * @@ -176,7 +175,7 @@ export interface CaptureExporter { /** Snapshot the store, stream deserialized entries one-by-one, then release the snapshot. * `options.owner` scopes the export to one tenant (see CaptureStore.snapshot). */ stream(options?: { owner?: string }): AsyncIterableIterator; - /** Snapshot the store, read + deserialize all entries grouped by file type, then release (Β§7.7). + /** Snapshot the store, read + deserialize all entries grouped by file type, then release. * `options.owner` scopes the export to one tenant (see CaptureStore.snapshot). */ drain(options?: { owner?: string }): Promise>; } @@ -190,9 +189,9 @@ export interface CaptureExporter { * source(s) separately. Deliberately a subset of {@link Client} that EXCLUDES the registration seams. */ export interface CaptureProviderInit { - /** Operation bridge for adapters/build injection (Β§16.2). */ + /** Operation bridge for adapters/build injection. */ readonly operations: OperationDispatcher; - /** The single data adapter the provider pushes captured entries to (Β§7.7). */ + /** The single data adapter the provider pushes captured entries to. */ readonly captureAggregator: CaptureAggregator; } @@ -222,7 +221,7 @@ export interface OptionsContainer { export type ControllingOption = keyof BugseeOptionTypes | (string & Record); /** - * Capture-pipeline data source (Β§16.2), Android BugseeCaptureDataProvider parity. Lifecycle splits + * Capture-pipeline data source, Android BugseeCaptureDataProvider parity. Lifecycle splits * dependency wiring from per-launch configuration: * - init(init): ONCE at registration β€” capture the pipeline deps (hubs/operations/aggregator). * - start(options): per launch β€” (re)configure from launch options, subscribe to its hub, and @@ -230,8 +229,7 @@ export type ControllingOption = keyof BugseeOptionTypes | (string & Record