diff --git a/scripts/bench/results/v1.45.0-boot-after.json b/scripts/bench/results/v1.45.0-boot-after.json new file mode 100644 index 000000000..cb435b3cb --- /dev/null +++ b/scripts/bench/results/v1.45.0-boot-after.json @@ -0,0 +1,1648 @@ +{ + "meta": { + "label": "v1.45.0-boot-after", + "commit": "a859adb59", + "date": "2026-10-22T04:49:50.419Z", + "node": "v22.23.2", + "cpus": 14, + "loadAverage": [ + 8.6, + 9.4, + 10.7 + ], + "heapMb": 792, + "idleSeconds": 300, + "healthyAtMs": 850, + "readyAtMs": 3038, + "stallMaxMs": 0 + }, + "bootLines": [ + "3038 ms: boot.steps {\"boot_queue_count\":122,\"boot_steps\":\"readiness_summary=2,key_check=187,time_zone_check=1,pulse_method_sync=25,monitors=3,worker_import=63,boss_start=266,load_worker_lifecycle=47,load_integration_sync=119,register_integration_sync=100,load_status=52,register_status=70,load_rollup=12,register_rollup=27,load_reminders=11,register_reminders=37,load_maintenance=50,register_maintena" + ], + "stalls": [], + "heapLines": [ + { + "atMs": 5524, + "heap_used_mb": 166, + "heap_total_mb": 192, + "heap_limit_mb": 840, + "heap_used_ratio": 0.197, + "new_space_mb": 12, + "old_space_mb": 91, + "code_space_mb": 3, + "large_object_space_mb": 53, + "new_large_object_space_mb": 0, + "code_large_object_space_mb": 0, + "trusted_space_mb": 6, + "read_only_space_mb": 0, + "other_spaces_mb": 0, + "rss_mb": 430, + "external_mb": 12, + "array_buffers_mb": 1, + "active_requests": 0, + "active_jobs": 0, + "in_flight_dropped": 0, + "uptime_s": 6 + }, + { + "atMs": 305529, + "heap_used_mb": 153, + "heap_total_mb": 163, + "heap_limit_mb": 840, + "heap_used_ratio": 0.182, + "new_space_mb": 1, + "old_space_mb": 90, + "code_space_mb": 4, + "large_object_space_mb": 53, + "new_large_object_space_mb": 0, + "code_large_object_space_mb": 0, + "trusted_space_mb": 5, + "read_only_space_mb": 0, + "other_spaces_mb": 0, + "rss_mb": 599, + "external_mb": 11, + "array_buffers_mb": 0, + "active_requests": 0, + "active_jobs": 0, + "in_flight_dropped": 0, + "uptime_s": 306 + } + ], + "markCompacts": [ + { + "atMs": 189, + "beforeMb": 20.2, + "afterMb": 17.1 + }, + { + "atMs": 374, + "beforeMb": 55.3, + "afterMb": 52.6 + }, + { + "atMs": 458, + "beforeMb": 83.2, + "afterMb": 81.1 + }, + { + "atMs": 1036, + "beforeMb": 148.4, + "afterMb": 132.7 + }, + { + "atMs": 16410, + "beforeMb": 159.6, + "afterMb": 153.2 + }, + { + "atMs": 17036, + "beforeMb": 153.4, + "afterMb": 151 + }, + { + "atMs": 61902, + "beforeMb": 167.5, + "afterMb": 157.5 + }, + { + "atMs": 245952, + "beforeMb": 183.4, + "afterMb": 152.6 + }, + { + "atMs": 303192, + "beforeMb": 165.6, + "afterMb": 151.8 + }, + { + "atMs": 303207, + "beforeMb": 151.8, + "afterMb": 151.8 + } + ], + "tasks": [ + { + "startMs": 6, + "durationMs": 175, + "byModule": [ + [ + "npm:next", + 47.2 + ], + [ + "node:internal/modules/cjs/loader", + 34.8 + ], + [ + "(native)", + 34.5 + ], + [ + "node:internal/bootstrap/realm", + 25.7 + ], + [ + "node:internal/deps/undici/undici", + 8.8 + ], + [ + "node:path", + 6.3 + ], + [ + "node:internal/modules/package_json_reader", + 3 + ], + [ + "node:internal/net", + 3 + ], + [ + "node:internal/modules/esm/resolve", + 2.5 + ], + [ + "node:fs", + 1.5 + ], + [ + "node:buffer", + 1.5 + ], + [ + "node:internal/deps/cjs-module-lexer/lexer", + 1.5 + ], + [ + "node:internal/util", + 1.5 + ], + [ + "node:events", + 1.5 + ], + [ + "node:internal/modules/helpers", + 1.1 + ] + ], + "byFunction": [ + [ + "wrapSafe node:internal/modules/cjs/loader", + 32.2 + ], + [ + "compileForInternalLoader node:internal/bootstrap/realm", + 25.7 + ], + [ + "startServer npm:next", + 10.5 + ], + [ + "internalModuleStat (native)", + 9.5 + ], + [ + "_export npm:next", + 7.5 + ], + [ + "__require node:internal/deps/undici/undici", + 7.3 + ], + [ + "(garbage collector) (native)", + 5.8 + ], + [ + "readFileUtf8 (native)", + 5.8 + ], + [ + "resolve node:path", + 4.5 + ], + [ + "__nccwpck_require__ npm:next", + 4.5 + ], + [ + "(anonymous) npm:next", + 4.2 + ], + [ + "compileFunction (native)", + 3 + ], + [ + "ZodType npm:next", + 3 + ], + [ + "dlopen (native)", + 3 + ], + [ + "(anonymous) node:internal/modules/cjs/loader", + 2.6 + ] + ], + "byEntry": [] + }, + { + "startMs": 203, + "durationMs": 241, + "byModule": [ + [ + "(native)", + 52.7 + ], + [ + "node:internal/modules/cjs/loader", + 40.1 + ], + [ + "turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 31.7 + ], + [ + "wasm://wasm/00d1cb12", + 28.7 + ], + [ + "npm:@prisma/client", + 28 + ], + [ + "npm:next", + 16.3 + ], + [ + "node:internal/modules/esm/utils", + 12 + ], + [ + "node:fs", + 4.5 + ], + [ + "npm:zod", + 3 + ], + [ + "node:internal/modules/helpers", + 3 + ], + [ + "npm:pg", + 3 + ], + [ + "node:internal/fs/utils", + 1.5 + ], + [ + "src/lib/validations/entry-instant.ts", + 1.5 + ], + [ + "npm:undici", + 1.5 + ], + [ + "node:internal/bootstrap/realm", + 1.5 + ] + ], + "byFunction": [ + [ + "wrapSafe node:internal/modules/cjs/loader", + 37.1 + ], + [ + "instantiateModule turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 31.7 + ], + [ + "(garbage collector) (native)", + 30.6 + ], + [ + "#u npm:@prisma/client", + 22.4 + ], + [ + "compileSourceTextModule node:internal/modules/esm/utils", + 12 + ], + [ + "wasm-function[1454] wasm://wasm/00d1cb12", + 6.1 + ], + [ + "internalModuleStat (native)", + 5.8 + ], + [ + "readFileUtf8 (native)", + 4.5 + ], + [ + "js-to-wasm (native)", + 4.5 + ], + [ + "wasm-function[2550] wasm://wasm/00d1cb12", + 4.5 + ], + [ + "(anonymous) npm:next", + 4.3 + ], + [ + "readFileSync node:fs", + 3 + ], + [ + "toRealPath node:internal/modules/helpers", + 3 + ], + [ + "(anonymous) npm:pg", + 3 + ], + [ + "Module (native)", + 3 + ] + ], + "byEntry": [ + [ + "src/instrumentation.ts", + 80.8 + ], + [ + "src/lib/db-session.ts", + 49.5 + ], + [ + "src/lib/boot/startup-state.ts", + 25.6 + ], + [ + "src/lib/boot/boot-steps.ts", + 25.6 + ], + [ + "src/lib/logging/background.ts", + 25.6 + ], + [ + "src/lib/process-type.ts", + 25.5 + ], + [ + "src/lib/logging/context.ts", + 13.6 + ], + [ + "src/lib/validations/measurement.ts", + 12.1 + ], + [ + "src/lib/logging/redact.ts", + 10.5 + ], + [ + "src/lib/validations/entry-instant.ts", + 4.6 + ], + [ + "src/generated/prisma/internal/class.ts", + 3 + ], + [ + "src/lib/medications/scheduling/hhmm.ts", + 1.5 + ], + [ + "src/lib/env.ts", + 1.5 + ] + ] + }, + { + "startMs": 501, + "durationMs": 178, + "byModule": [ + [ + "node:internal/modules/cjs/loader", + 53.9 + ], + [ + "turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 37.5 + ], + [ + "npm:next", + 18 + ], + [ + "npm:zod", + 12 + ], + [ + "node:internal/modules/esm/utils", + 10.5 + ], + [ + "(native)", + 8.7 + ], + [ + "node:fs", + 6 + ], + [ + "turbopack:///[turbopack]/shared/runtime/async-module.ts", + 4.5 + ], + [ + "npm:undici", + 4.5 + ], + [ + "node:internal/url", + 1.8 + ], + [ + "node:internal/modules/esm/resolve", + 1.5 + ], + [ + "node:internal/deps/cjs-module-lexer/lexer", + 1.5 + ], + [ + "node:internal/modules/esm/module_job", + 1.5 + ], + [ + "node:internal/deps/undici/undici", + 1.5 + ], + [ + "node:internal/promise_hooks", + 1.5 + ] + ], + "byFunction": [ + [ + "wrapSafe node:internal/modules/cjs/loader", + 52.4 + ], + [ + "instantiateModule turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 37.5 + ], + [ + "compileSourceTextModule node:internal/modules/esm/utils", + 10.5 + ], + [ + "(anonymous) npm:zod", + 6.1 + ], + [ + "(anonymous) npm:next", + 6 + ], + [ + "asyncModule turbopack:///[turbopack]/shared/runtime/async-module.ts", + 4.5 + ], + [ + "readFileSync node:fs", + 3 + ], + [ + "(garbage collector) (native)", + 3 + ], + [ + "realpathSync node:fs", + 2.9 + ], + [ + "Z npm:zod", + 2.9 + ], + [ + "finalizeResolution node:internal/modules/esm/resolve", + 1.5 + ], + [ + "pathToFileURL node:internal/url", + 1.5 + ], + [ + "a npm:next", + 1.5 + ], + [ + "./dist/esm/server/app-render/manifests-singleton.js npm:next", + 1.5 + ], + [ + "a.d npm:next", + 1.5 + ] + ], + "byEntry": [ + [ + "src/app/api/health/route.ts", + 61.4 + ], + [ + "src/lib/api-errors.ts", + 27 + ], + [ + "src/lib/jobs/job-failures.ts", + 14.9 + ], + [ + "src/lib/jobs/offhost-backup.ts", + 14.9 + ], + [ + "src/lib/managed-profiles/handover-access.ts", + 14.9 + ], + [ + "src/lib/sharing/acting-session.ts", + 14.9 + ], + [ + "src/lib/db-session.ts", + 13.4 + ], + [ + "src/lib/export/environment-backup.ts", + 13.4 + ], + [ + "src/lib/export/full-backup-payload.ts", + 13.4 + ], + [ + "src/lib/logging/background.ts", + 10.5 + ], + [ + "src/lib/auth/secure-cookie.ts", + 10.5 + ], + [ + "src/lib/notifications/types.ts", + 10.5 + ], + [ + "src/lib/logging/redact.ts", + 10.5 + ], + [ + "src/lib/validations/measurement.ts", + 9 + ], + [ + "src/instrumentation.ts", + 8.7 + ] + ] + }, + { + "startMs": 833, + "durationMs": 184, + "byModule": [ + [ + "turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 38.9 + ], + [ + "node:internal/modules/cjs/loader", + 28.6 + ], + [ + "(native)", + 22.8 + ], + [ + "src/lib/i18n/server-catalogs.ts", + 15.1 + ], + [ + "npm:yaml", + 15 + ], + [ + "node:fs", + 12.1 + ], + [ + "npm:zod", + 9 + ], + [ + "turbopack:///[turbopack]/shared/runtime/async-module.ts", + 5.9 + ], + [ + "turbopack:///[turbopack]/shared/runtime/runtime-utils.ts", + 4.5 + ], + [ + "npm:semver", + 3 + ], + [ + "npm:next", + 2.3 + ], + [ + "turbopack:///[turbopack]/nodejs/runtime/runtime-base.ts", + 1.5 + ], + [ + "node:internal/bootstrap/realm", + 1.5 + ], + [ + "src/lib/labs/unit-conversion/registry.ts", + 1.5 + ], + [ + "src/lib/validations/user-prefs.ts", + 1.5 + ] + ], + "byFunction": [ + [ + "instantiateModule turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 38.9 + ], + [ + "wrapSafe node:internal/modules/cjs/loader", + 28.6 + ], + [ + "(garbage collector) (native)", + 18.5 + ], + [ + "(anonymous) src/lib/i18n/server-catalogs.ts", + 15.1 + ], + [ + "readFileSync node:fs", + 12.1 + ], + [ + "z npm:zod", + 4.5 + ], + [ + "asyncModule turbopack:///[turbopack]/shared/runtime/async-module.ts", + 4.5 + ], + [ + "readFileUtf8 (native)", + 4.3 + ], + [ + "(anonymous) npm:zod", + 3 + ], + [ + "next npm:yaml", + 3 + ], + [ + "loadChunkAsync turbopack:///[turbopack]/nodejs/runtime/runtime-base.ts", + 1.5 + ], + [ + "compileForInternalLoader node:internal/bootstrap/realm", + 1.5 + ], + [ + "esmExport turbopack:///[turbopack]/shared/runtime/runtime-utils.ts", + 1.5 + ], + [ + "(anonymous) src/lib/labs/unit-conversion/registry.ts", + 1.5 + ], + [ + "(anonymous) src/lib/validations/user-prefs.ts", + 1.5 + ] + ], + "byEntry": [ + [ + "src/lib/jobs/reminder-worker.ts", + 129.1 + ], + [ + "src/lib/jobs/reminder/google-health-sync.ts", + 88.5 + ], + [ + "src/lib/whoop/sync.ts", + 79.5 + ], + [ + "src/lib/whoop/sync-body.ts", + 63 + ], + [ + "src/lib/insights/bucket-series.ts", + 60 + ], + [ + "src/lib/whoop/oauth-descriptor.ts", + 60 + ], + [ + "src/lib/whoop/sync-core.ts", + 60 + ], + [ + "src/instrumentation.ts", + 43.4 + ], + [ + "src/lib/import/unzip-export-xml.ts", + 31.4 + ], + [ + "src/lib/jobs/integration-status-null-probe.ts", + 31.4 + ], + [ + "src/lib/ai/coach/memory/propose-plan.ts", + 30.1 + ], + [ + "src/lib/ai/coach/refusal.ts", + 27.1 + ], + [ + "src/lib/ai/coach/outbound-guard.ts", + 25.6 + ], + [ + "src/lib/i18n/server-catalogs.ts", + 24.1 + ], + [ + "src/lib/insights/snapshot-hash.ts", + 23.9 + ] + ] + }, + { + "startMs": 1099, + "durationMs": 54, + "byModule": [ + [ + "node:internal/modules/cjs/loader", + 23.9 + ], + [ + "turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 10.6 + ], + [ + "turbopack:///[turbopack]/shared/runtime/async-module.ts", + 5.8 + ], + [ + "src/lib/daily/today-overview.ts", + 4.5 + ], + [ + "src/lib/jobs/reminder-worker.ts", + 3 + ], + [ + "node:fs", + 3 + ], + [ + "npm:pg-boss", + 1.5 + ], + [ + "npm:zod", + 1.5 + ] + ], + "byFunction": [ + [ + "wrapSafe node:internal/modules/cjs/loader", + 23.9 + ], + [ + "instantiateModule turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 10.6 + ], + [ + "asyncModule turbopack:///[turbopack]/shared/runtime/async-module.ts", + 5.8 + ], + [ + "w src/lib/daily/today-overview.ts", + 4.5 + ], + [ + "readFileSync node:fs", + 3 + ], + [ + "work npm:pg-boss", + 1.5 + ], + [ + "(anonymous) src/lib/jobs/reminder-worker.ts", + 1.5 + ], + [ + "d9 npm:zod", + 1.5 + ], + [ + "v src/lib/jobs/reminder-worker.ts", + 1.5 + ] + ], + "byEntry": [ + [ + "src/lib/jobs/reminder-worker.ts", + 52.3 + ], + [ + "src/lib/jobs/reminder/insights-handlers.ts", + 22.3 + ], + [ + "src/lib/ai/prompts/arrival-reaction.ts", + 12 + ], + [ + "src/lib/daily/score-steady.ts", + 10.5 + ], + [ + "src/lib/dashboard/bands.ts", + 6 + ], + [ + "src/lib/jobs/data-arrival.ts", + 4.5 + ], + [ + "src/lib/dashboard/snapshot-read.ts", + 4.5 + ], + [ + "src/lib/daily/today-overview.ts", + 4.5 + ], + [ + "src/lib/daily/priority-item-key.ts", + 4.5 + ], + [ + "src/lib/ai/prompts/status-batch.ts", + 3 + ], + [ + "src/lib/jobs/reminder/registrar-shared.ts", + 1.5 + ], + [ + "src/lib/jobs/reminder/register-integration-sync.ts", + 1.5 + ], + [ + "src/lib/ai/prompts/base-system.ts", + 1.5 + ], + [ + "src/lib/insights/derived/derived-assessment-ai.ts", + 1.5 + ], + [ + "src/lib/measurements/list/sleep-nights.ts", + 1.5 + ] + ] + }, + { + "startMs": 1309, + "durationMs": 51, + "byModule": [ + [ + "node:internal/modules/cjs/loader", + 28 + ], + [ + "turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 6 + ], + [ + "node:fs", + 4.5 + ], + [ + "turbopack:///[turbopack]/shared/runtime/async-module.ts", + 3 + ], + [ + "turbopack:///[turbopack]/nodejs/runtime/runtime-base.ts", + 1.5 + ], + [ + "src/lib/jobs/failure-condition.ts", + 1.5 + ], + [ + "src/lib/labs/parse-reference-range.ts", + 1.5 + ], + [ + "npm:zod", + 1.5 + ], + [ + "node:internal/linkedlist", + 1.5 + ], + [ + "(native)", + 0.9 + ], + [ + "node:buffer", + 0.6 + ] + ], + "byFunction": [ + [ + "wrapSafe node:internal/modules/cjs/loader", + 28 + ], + [ + "instantiateModule turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 6 + ], + [ + "readFileSync node:fs", + 4.5 + ], + [ + "asyncModule turbopack:///[turbopack]/shared/runtime/async-module.ts", + 3 + ], + [ + "loadChunkAsync turbopack:///[turbopack]/nodejs/runtime/runtime-base.ts", + 1.5 + ], + [ + "(anonymous) src/lib/jobs/failure-condition.ts", + 1.5 + ], + [ + "r src/lib/labs/parse-reference-range.ts", + 1.5 + ], + [ + "u npm:zod", + 1.5 + ], + [ + "isEmpty node:internal/linkedlist", + 1.5 + ], + [ + "(garbage collector) (native)", + 0.9 + ], + [ + "getEncodingOps node:buffer", + 0.6 + ] + ], + "byEntry": [ + [ + "src/lib/jobs/reminder-worker.ts", + 48.2 + ], + [ + "src/lib/jobs/reminder/ops-handlers.ts", + 14.1 + ], + [ + "src/lib/medications/ingredients/label-extract.ts", + 3 + ], + [ + "src/lib/export/restore-backup.ts", + 2.1 + ], + [ + "src/lib/jobs/failure-condition.ts", + 1.5 + ], + [ + "src/lib/documents/ai-runs/summary-run.ts", + 1.5 + ], + [ + "src/lib/documents/auto-stage-labs.ts", + 1.5 + ], + [ + "src/lib/documents/ai-runs/store.ts", + 1.5 + ], + [ + "src/lib/labs/parse-reference-range.ts", + 1.5 + ], + [ + "src/lib/labs/ocr-upload.ts", + 1.5 + ], + [ + "src/lib/jobs/document-content-index-backfill.ts", + 1.5 + ], + [ + "src/lib/labs/ocr-extract.ts", + 1.5 + ], + [ + "src/lib/dawarich/sync.ts", + 1.5 + ], + [ + "src/lib/validations/dawarich.ts", + 1.5 + ], + [ + "src/lib/jobs/auth-ledger-cleanup.ts", + 1.5 + ] + ] + } + ], + "bootSelf": [ + [ + "(native)", + 230.8 + ], + [ + "node:internal/modules/cjs/loader", + 223.6 + ], + [ + "turbopack:///[turbopack]/nodejs/runtime/build-base.ts", + 124.7 + ], + [ + "npm:next", + 98.8 + ], + [ + "wasm://wasm/00d1cb12", + 62.6 + ], + [ + "npm:@prisma/client", + 56.2 + ], + [ + "node:fs", + 34.6 + ], + [ + "node:internal/bootstrap/realm", + 28.7 + ], + [ + "npm:pg", + 28.3 + ], + [ + "npm:zod", + 27 + ], + [ + "npm:pg-boss", + 24.6 + ], + [ + "node:internal/modules/esm/utils", + 22.5 + ], + [ + "turbopack:///[turbopack]/shared/runtime/async-module.ts", + 20.6 + ], + [ + "npm:luxon", + 17 + ], + [ + "src/lib/i18n/server-catalogs.ts", + 15.1 + ], + [ + "npm:yaml", + 15 + ], + [ + "npm:pg-pool", + 13 + ], + [ + "node:internal/deps/undici/undici", + 10.4 + ], + [ + "node:path", + 7.8 + ], + [ + "turbopack:///[turbopack]/shared/runtime/runtime-utils.ts", + 7.5 + ], + [ + "node:internal/async_hooks", + 7.5 + ], + [ + "node:internal/process/task_queues", + 7.3 + ], + [ + "node:internal/modules/package_json_reader", + 6 + ], + [ + "npm:undici", + 6 + ], + [ + "npm:pg-protocol", + 6 + ], + [ + "npm:cron-parser", + 6 + ], + [ + "node:internal/stream_base_commons", + 5.7 + ], + [ + "node:events", + 5.5 + ], + [ + "node:internal/timers", + 5.5 + ], + [ + "node:internal/streams/writable", + 5.3 + ], + [ + "node:internal/async_local_storage/async_hooks", + 5.3 + ], + [ + "node:buffer", + 5.2 + ], + [ + "node:internal/util", + 4.5 + ], + [ + "npm:%40prisma", + 4.5 + ], + [ + "node:internal/crypto/random", + 4.5 + ], + [ + "src/lib/daily/today-overview.ts", + 4.5 + ], + [ + "node:internal/modules/helpers", + 4.2 + ], + [ + "node:internal/modules/esm/resolve", + 4 + ], + [ + "node:internal/deps/cjs-module-lexer/lexer", + 3 + ], + [ + "node:internal/net", + 3 + ] + ], + "heap": { + "probes": { + "parsed locale catalogs": { + "count": 8, + "retainedMb": 7 + } + }, + "totalMb": 160.1, + "nodeCount": 1275329, + "byType": [ + [ + "string", + 68.7 + ], + [ + "array", + 26.4 + ], + [ + "code", + 21.1 + ], + [ + "object", + 14.1 + ], + [ + "hidden", + 8.9 + ], + [ + "native", + 8 + ], + [ + "object shape", + 5.9 + ], + [ + "closure", + 5.2 + ], + [ + "concatenated string", + 1.3 + ], + [ + "regexp", + 0.2 + ], + [ + "sliced string", + 0.2 + ], + [ + "number", + 0.1 + ], + [ + "synthetic", + 0 + ], + [ + "symbol", + 0 + ], + [ + "bigint", + 0 + ] + ], + "byConstructor": [ + [ + "array:(object elements)", + 20.4, + 23797 + ], + [ + "object:Object", + 10.2, + 274231 + ], + [ + "native:system / JSArrayBufferData", + 7.6, + 171 + ], + [ + "array:(object properties)", + 3.6, + 6512 + ], + [ + "array:", + 2.4, + 10502 + ], + [ + "closure:", + 1.6, + 29042 + ], + [ + "object:system / Context", + 1.6, + 28873 + ], + [ + "closure:native_bind", + 1, + 20782 + ], + [ + "object:Array", + 0.6, + 19679 + ], + [ + "object:a", + 0.4, + 4438 + ], + [ + "native:Node / histogram", + 0.4, + 1 + ], + [ + "closure:get", + 0.3, + 4736 + ], + [ + "object:d", + 0.3, + 4425 + ], + [ + "object:Set", + 0.2, + 5673 + ], + [ + "closure:set", + 0.2, + 3432 + ], + [ + "object:Module", + 0.2, + 1958 + ], + [ + "closure:i", + 0.1, + 1581 + ], + [ + "closure:n", + 0.1, + 1572 + ], + [ + "object:Promise", + 0.1, + 1277 + ], + [ + "object:n", + 0.1, + 1881 + ], + [ + "object:ZodOptional", + 0.1, + 236 + ], + [ + "object:_TstNode", + 0.1, + 939 + ], + [ + "native:Node / parser_buffer", + 0.1, + 1 + ], + [ + "object:TCP", + 0, + 8 + ], + [ + "closure:callbackTrampoline", + 0, + 1 + ], + [ + "closure:promiseInitHook", + 0, + 1 + ], + [ + "closure:promiseBeforeHook", + 0, + 1 + ], + [ + "closure:promiseAfterHook", + 0, + 1 + ], + [ + "object:Signal", + 0, + 4 + ], + [ + "object:HTTPParser", + 0, + 2 + ] + ], + "largestStrings": [ + [ + 5.6, + "module.exports=[234494,A=>{\"use strict\";var e,C,g,I,Q=Object.defineProperty,B={},s={$:()=>i,bgBlack:" + ], + [ + 5.6, + "module.exports=[17856,A=>{\"use strict\";var e,C,g,I,Q=Object.defineProperty,B={},s={$:()=>i,bgBlack:(" + ], + [ + 5.6, + "module.exports=[17856,A=>{\"use strict\";var e,C,g,I,Q=Object.defineProperty,B={},s={$:()=>i,bgBlack:(" + ], + [ + 4.4, + "const wasm = \"AGFzbQEAAAABtgRLYAN/f38Bf2ACf38Bf2ACf38AYAF/AGADf39/AGAEf39/fwBgBX9/f39/AGABfwF/YAZ/f3" + ], + [ + 4.4, + "AGFzbQEAAAABtgRLYAN/f38Bf2ACf38Bf2ACf38AYAF/AGADf39/AGAEf39/fwBgBX9/f39/AGABfwF/YAZ/f39/f38AYAR/f39/" + ], + [ + 1.9, + "lliADOAUngIEAACgKwAgBgAAoSsAIAcAAKErACAIAACiKwAgCQAAoysAIAoAAKQrACALAAClKwAgDAAApisAIA0AAKcrACARAACS" + ], + [ + 1.3, + "(()=>{var e,t,r={\"./dist/build/webpack/alias/react-dom-server.js\"(e,t,r){\"use strict\";var n;function" + ], + [ + 1.2, + "module.exports=[842865,(e,t,r)=>{let i=e.r(522734);t.exports=function(e){return e?/-----BEGIN ([A-Z\\" + ], + [ + 1.2, + "module.exports=[842865,(e,t,r)=>{let i=e.r(522734);t.exports=function(e){return e?/-----BEGIN ([A-Z\\" + ], + [ + 1.1, + "module.exports=[799092,(e,t,n)=>{\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),Objec" + ], + [ + 1.1, + "// v1.4.34 web-freeze. Schema is locked additive-only until iOS launch // + v1.5.0 marker. See .plan" + ], + [ + 1.1, + "module.exports=[971743,(A,e,t)=>{\"use strict\";let r=A.r(57328),{addAbortListener:s}=A.r(687769),{Rea" + ], + [ + 1, + "module.exports=[835717,(A,e,t)=>{\"use strict\";let r=A.r(57328),{addAbortListener:s}=A.r(687769),{Rea" + ], + [ + 0.8, + "module.exports=[767189,e=>e.a(async(t,a)=>{try{var n=e.i(64105),r=e.i(563616),i=t([n]);[n]=i.then?(a" + ], + [ + 0.7, + "module.exports=[594108,806176,674750,98746,t=>{\"use strict\";var i;let r;function n(t){return t}funct" + ], + [ + 0.7, + "module.exports=[144056,t=>{\"use strict\";let i,n;function r(t){return t}function a(t){return t}functi" + ], + [ + 0.6, + "module.exports=[992657,e=>e.a(async(t,a)=>{try{var r=e.i(851258),n=t([r]);async function i(e,t,a,n){" + ], + [ + 0.6, + "module.exports=[388046,e=>{\"use strict\";class t{value;next;constructor(e){this.value=e}}class n{#e;#" + ], + [ + 0.6, + "module.exports=[388046,e=>{\"use strict\";class t{value;next;constructor(e){this.value=e}}class n{#e;#" + ], + [ + 0.5, + "module.exports=[120630,(e,t,n)=>{\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.def" + ], + [ + 0.5, + "module.exports=[120630,(e,t,n)=>{\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.def" + ], + [ + 0.5, + "module.exports=[120630,(e,t,n)=>{\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.def" + ], + [ + 0.5, + "module.exports=[120630,(e,t,n)=>{\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.def" + ], + [ + 0.5, + "module.exports=[120630,(e,t,n)=>{\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.def" + ], + [ + 0.5, + "module.exports=[120630,(e,t,n)=>{\"use strict\";Object.defineProperty(n,\"__esModule\",{value:!0}),n.def" + ] + ], + "topRetainers": [ + { + "mb": 33.9, + "name": "object:t", + "path": "[1] > [10] > 60 > extension > global > prisma > [1]" + }, + { + "mb": 7.9, + "name": "object:global", + "path": "[1] > [10] > 60 > extension" + }, + { + "mb": 7.4, + "name": "object:system / Context", + "path": "Node / PrincipalRealm > base_object_list > Node / BaseObjectList > [25] > Node / ModuleWrap > native_to_javascript > ModuleWrap > 2 > [4] > context" + }, + { + "mb": 5.6, + "name": "object:Module", + "path": "[1] > [10] > 97 > process > nextTick > patchedNextTick > context > system / Context > require > require > cache > .next/server/chunks/_1mg8fms._.js" + }, + { + "mb": 5.6, + "name": "code:.next/server/chu", + "path": "require > cache > Object > .next/server/chunks/_1kwfnjg._.js > Module > exports > Array > [1] > > shared > script" + }, + { + "mb": 5.6, + "name": "code:.next/server/chu", + "path": "global > prisma > [1] > t > _engineConfig > Object > compilerWasm > Object > getRuntime > getRuntime > shared > script" + }, + { + "mb": 4.4, + "name": "object:Generator", + "path": "[6] > principal_realm > Node / PrincipalRealm > base_object_list > Node / BaseObjectList > [26] > Node / ModuleWrap > native_to_javascript > ModuleWrap > 2 > [4]" + }, + { + "mb": 4.4, + "name": "string:AGFzbQEAAAABtgRLYAN/f38Bf2ACf38Bf2ACf38AYAF/AGADf39/AGAEf39/", + "path": "[1] > [21] > 62203" + }, + { + "mb": 3.3, + "name": "object:Object", + "path": "system / Context > require > require > cache > Object > .next/server/chunks/[turbopack]_runtime.js > Module > exports > > context > moduleCache" + } + ], + "sourceStringsMb": 39.8 + } +} diff --git a/scripts/bench/results/v1.45.0-boot-after.md b/scripts/bench/results/v1.45.0-boot-after.md new file mode 100644 index 000000000..4ec08b032 --- /dev/null +++ b/scripts/bench/results/v1.45.0-boot-after.md @@ -0,0 +1,298 @@ +# Boot profile v1.45.0-boot-after + +Commit a859adb59, 2026-10-22T04:49:50.419Z. Production build, every role, heap ceiling 792 MB, 300 s idle. 14 cores, load average 8.6,9.4,10.7 at the end. + +- first answer from /api/health: 850 ms; background role ready: 3038 ms +- event-loop stalls logged: 0, max 0 ms + +## Stalls + + +## Heap samples + +| at s | used MB | old | large obj | code | external | array buffers | rss | +| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| 6 | 166 | 91 | 53 | 3 | 12 | 1 | 430 | +| 306 | 153 | 90 | 53 | 4 | 11 | 0 | 599 | + +## Full collections (mark-compact) + +10 mark-compacts during the run. Used heap before the collection, and what survived it (the live heap): + +| at s | before MB | after MB | +| ---: | ---: | ---: | +| 0 | 20.2 | 17.1 | +| 0 | 55.3 | 52.6 | +| 0 | 83.2 | 81.1 | +| 1 | 148.4 | 132.7 | +| 16 | 159.6 | 153.2 | +| 17 | 153.4 | 151 | +| 62 | 167.5 | 157.5 | +| 246 | 183.4 | 152.6 | +| 303 | 165.6 | 151.8 | +| 303 | 151.8 | 151.8 | + +## Main-thread busy stretches over 50 ms without an idle sample, first two minutes + +### at 6 ms, 175 ms + +| self time by module | ms | +| --- | ---: | +| npm:next | 47.2 | +| node:internal/modules/cjs/loader | 34.8 | +| (native) | 34.5 | +| node:internal/bootstrap/realm | 25.7 | +| node:internal/deps/undici/undici | 8.8 | +| node:path | 6.3 | +| node:internal/modules/package_json_reader | 3 | +| node:internal/net | 3 | +| node:internal/modules/esm/resolve | 2.5 | +| node:fs | 1.5 | + +| inclusive, application modules | ms | +| --- | ---: | + +### at 203 ms, 241 ms + +| self time by module | ms | +| --- | ---: | +| (native) | 52.7 | +| node:internal/modules/cjs/loader | 40.1 | +| turbopack:///[turbopack]/nodejs/runtime/build-base.ts | 31.7 | +| wasm://wasm/00d1cb12 | 28.7 | +| npm:@prisma/client | 28 | +| npm:next | 16.3 | +| node:internal/modules/esm/utils | 12 | +| node:fs | 4.5 | +| npm:zod | 3 | +| node:internal/modules/helpers | 3 | + +| inclusive, application modules | ms | +| --- | ---: | +| src/instrumentation.ts | 80.8 | +| src/lib/db-session.ts | 49.5 | +| src/lib/boot/startup-state.ts | 25.6 | +| src/lib/boot/boot-steps.ts | 25.6 | +| src/lib/logging/background.ts | 25.6 | +| src/lib/process-type.ts | 25.5 | +| src/lib/logging/context.ts | 13.6 | +| src/lib/validations/measurement.ts | 12.1 | +| src/lib/logging/redact.ts | 10.5 | +| src/lib/validations/entry-instant.ts | 4.6 | + +### at 501 ms, 178 ms + +| self time by module | ms | +| --- | ---: | +| node:internal/modules/cjs/loader | 53.9 | +| turbopack:///[turbopack]/nodejs/runtime/build-base.ts | 37.5 | +| npm:next | 18 | +| npm:zod | 12 | +| node:internal/modules/esm/utils | 10.5 | +| (native) | 8.7 | +| node:fs | 6 | +| turbopack:///[turbopack]/shared/runtime/async-module.ts | 4.5 | +| npm:undici | 4.5 | +| node:internal/url | 1.8 | + +| inclusive, application modules | ms | +| --- | ---: | +| src/app/api/health/route.ts | 61.4 | +| src/lib/api-errors.ts | 27 | +| src/lib/jobs/job-failures.ts | 14.9 | +| src/lib/jobs/offhost-backup.ts | 14.9 | +| src/lib/managed-profiles/handover-access.ts | 14.9 | +| src/lib/sharing/acting-session.ts | 14.9 | +| src/lib/db-session.ts | 13.4 | +| src/lib/export/environment-backup.ts | 13.4 | +| src/lib/export/full-backup-payload.ts | 13.4 | +| src/lib/logging/background.ts | 10.5 | + +### at 833 ms, 184 ms + +| self time by module | ms | +| --- | ---: | +| turbopack:///[turbopack]/nodejs/runtime/build-base.ts | 38.9 | +| node:internal/modules/cjs/loader | 28.6 | +| (native) | 22.8 | +| src/lib/i18n/server-catalogs.ts | 15.1 | +| npm:yaml | 15 | +| node:fs | 12.1 | +| npm:zod | 9 | +| turbopack:///[turbopack]/shared/runtime/async-module.ts | 5.9 | +| turbopack:///[turbopack]/shared/runtime/runtime-utils.ts | 4.5 | +| npm:semver | 3 | + +| inclusive, application modules | ms | +| --- | ---: | +| src/lib/jobs/reminder-worker.ts | 129.1 | +| src/lib/jobs/reminder/google-health-sync.ts | 88.5 | +| src/lib/whoop/sync.ts | 79.5 | +| src/lib/whoop/sync-body.ts | 63 | +| src/lib/insights/bucket-series.ts | 60 | +| src/lib/whoop/oauth-descriptor.ts | 60 | +| src/lib/whoop/sync-core.ts | 60 | +| src/instrumentation.ts | 43.4 | +| src/lib/import/unzip-export-xml.ts | 31.4 | +| src/lib/jobs/integration-status-null-probe.ts | 31.4 | + +### at 1099 ms, 54 ms + +| self time by module | ms | +| --- | ---: | +| node:internal/modules/cjs/loader | 23.9 | +| turbopack:///[turbopack]/nodejs/runtime/build-base.ts | 10.6 | +| turbopack:///[turbopack]/shared/runtime/async-module.ts | 5.8 | +| src/lib/daily/today-overview.ts | 4.5 | +| src/lib/jobs/reminder-worker.ts | 3 | +| node:fs | 3 | +| npm:pg-boss | 1.5 | +| npm:zod | 1.5 | + +| inclusive, application modules | ms | +| --- | ---: | +| src/lib/jobs/reminder-worker.ts | 52.3 | +| src/lib/jobs/reminder/insights-handlers.ts | 22.3 | +| src/lib/ai/prompts/arrival-reaction.ts | 12 | +| src/lib/daily/score-steady.ts | 10.5 | +| src/lib/dashboard/bands.ts | 6 | +| src/lib/jobs/data-arrival.ts | 4.5 | +| src/lib/dashboard/snapshot-read.ts | 4.5 | +| src/lib/daily/today-overview.ts | 4.5 | +| src/lib/daily/priority-item-key.ts | 4.5 | +| src/lib/ai/prompts/status-batch.ts | 3 | + +### at 1309 ms, 51 ms + +| self time by module | ms | +| --- | ---: | +| node:internal/modules/cjs/loader | 28 | +| turbopack:///[turbopack]/nodejs/runtime/build-base.ts | 6 | +| node:fs | 4.5 | +| turbopack:///[turbopack]/shared/runtime/async-module.ts | 3 | +| turbopack:///[turbopack]/nodejs/runtime/runtime-base.ts | 1.5 | +| src/lib/jobs/failure-condition.ts | 1.5 | +| src/lib/labs/parse-reference-range.ts | 1.5 | +| npm:zod | 1.5 | +| node:internal/linkedlist | 1.5 | +| (native) | 0.9 | + +| inclusive, application modules | ms | +| --- | ---: | +| src/lib/jobs/reminder-worker.ts | 48.2 | +| src/lib/jobs/reminder/ops-handlers.ts | 14.1 | +| src/lib/medications/ingredients/label-extract.ts | 3 | +| src/lib/export/restore-backup.ts | 2.1 | +| src/lib/jobs/failure-condition.ts | 1.5 | +| src/lib/documents/ai-runs/summary-run.ts | 1.5 | +| src/lib/documents/auto-stage-labs.ts | 1.5 | +| src/lib/documents/ai-runs/store.ts | 1.5 | +| src/lib/labs/parse-reference-range.ts | 1.5 | +| src/lib/labs/ocr-upload.ts | 1.5 | + +## Self time by module until the worker is ready + +| module | ms | +| --- | ---: | +| (native) | 230.8 | +| node:internal/modules/cjs/loader | 223.6 | +| turbopack:///[turbopack]/nodejs/runtime/build-base.ts | 124.7 | +| npm:next | 98.8 | +| wasm://wasm/00d1cb12 | 62.6 | +| npm:@prisma/client | 56.2 | +| node:fs | 34.6 | +| node:internal/bootstrap/realm | 28.7 | +| npm:pg | 28.3 | +| npm:zod | 27 | +| npm:pg-boss | 24.6 | +| node:internal/modules/esm/utils | 22.5 | +| turbopack:///[turbopack]/shared/runtime/async-module.ts | 20.6 | +| npm:luxon | 17 | +| src/lib/i18n/server-catalogs.ts | 15.1 | +| npm:yaml | 15 | +| npm:pg-pool | 13 | +| node:internal/deps/undici/undici | 10.4 | +| node:path | 7.8 | +| turbopack:///[turbopack]/shared/runtime/runtime-utils.ts | 7.5 | +| node:internal/async_hooks | 7.5 | +| node:internal/process/task_queues | 7.3 | +| node:internal/modules/package_json_reader | 6 | +| npm:undici | 6 | +| npm:pg-protocol | 6 | + +## Heap snapshot after 300 s idle (live heap after a full GC) + +160.1 MB in 1275329 nodes; module source strings over 50 KB: 39.8 MB. + +- parsed locale catalogs: 8 objects, 7 MB retained + +| node type | MB | +| --- | ---: | +| string | 68.7 | +| array | 26.4 | +| code | 21.1 | +| object | 14.1 | +| hidden | 8.9 | +| native | 8 | +| object shape | 5.9 | +| closure | 5.2 | +| concatenated string | 1.3 | +| regexp | 0.2 | +| sliced string | 0.2 | +| number | 0.1 | +| synthetic | 0 | +| symbol | 0 | +| bigint | 0 | + +| constructor | MB | count | +| --- | ---: | ---: | +| array:(object elements) | 20.4 | 23797 | +| object:Object | 10.2 | 274231 | +| native:system / JSArrayBufferData | 7.6 | 171 | +| array:(object properties) | 3.6 | 6512 | +| array: | 2.4 | 10502 | +| closure: | 1.6 | 29042 | +| object:system / Context | 1.6 | 28873 | +| closure:native_bind | 1 | 20782 | +| object:Array | 0.6 | 19679 | +| object:a | 0.4 | 4438 | +| native:Node / histogram | 0.4 | 1 | +| closure:get | 0.3 | 4736 | +| object:d | 0.3 | 4425 | +| object:Set | 0.2 | 5673 | +| closure:set | 0.2 | 3432 | +| object:Module | 0.2 | 1958 | +| closure:i | 0.1 | 1581 | +| closure:n | 0.1 | 1572 | +| object:Promise | 0.1 | 1277 | +| object:n | 0.1 | 1881 | + +| largest retained subtrees | MB | retaining path | +| --- | ---: | --- | +| object:t | 33.9 | [1] > [10] > 60 > extension > global > prisma > [1] | +| object:global | 7.9 | [1] > [10] > 60 > extension | +| object:system / Context | 7.4 | Node / PrincipalRealm > base_object_list > Node / BaseObjectList > [25] > Node / ModuleWrap > native_to_javascript > ModuleWrap > 2 > [4] > context | +| object:Module | 5.6 | [1] > [10] > 97 > process > nextTick > patchedNextTick > context > system / Context > require > require > cache > .n | +| code:.next/server/chu | 5.6 | require > cache > Object > .next/server/chunks/_1kwfnjg._.js > Module > exports > Array > [1] > > shared > script | +| code:.next/server/chu | 5.6 | global > prisma > [1] > t > _engineConfig > Object > compilerWasm > Object > getRuntime > getRuntime > shared > script | +| object:Generator | 4.4 | [6] > principal_realm > Node / PrincipalRealm > base_object_list > Node / BaseObjectList > [26] > Node / ModuleWrap > native_to_javascript > ModuleWrap > 2 > [4 | +| string:AGFzbQEAAAABtgRLYAN/f38Bf2ACf38Bf2ACf38AYAF/AGADf39/AGAEf39/ | 4.4 | [1] > [21] > 62203 | +| object:Object | 3.3 | system / Context > require > require > cache > Object > .next/server/chunks/[turbopack]_runtime.js > Module > export | + +| largest strings | MB | +| --- | ---: | +| module.exports=[234494,A=>{"use strict";var e,C,g,I,Q=Object.defineProperty,B={},s={$:()=> | 5.6 | +| module.exports=[17856,A=>{"use strict";var e,C,g,I,Q=Object.defineProperty,B={},s={$:()=>i | 5.6 | +| module.exports=[17856,A=>{"use strict";var e,C,g,I,Q=Object.defineProperty,B={},s={$:()=>i | 5.6 | +| const wasm = "AGFzbQEAAAABtgRLYAN/f38Bf2ACf38Bf2ACf38AYAF/AGADf39/AGAEf39/fwBgBX9/f39/AGAB | 4.4 | +| AGFzbQEAAAABtgRLYAN/f38Bf2ACf38Bf2ACf38AYAF/AGADf39/AGAEf39/fwBgBX9/f39/AGABfwF/YAZ/f39/f3 | 4.4 | +| lliADOAUngIEAACgKwAgBgAAoSsAIAcAAKErACAIAACiKwAgCQAAoysAIAoAAKQrACALAAClKwAgDAAApisAIA0AAK | 1.9 | +| (()=>{var e,t,r={"./dist/build/webpack/alias/react-dom-server.js"(e,t,r){"use strict";var | 1.3 | +| module.exports=[842865,(e,t,r)=>{let i=e.r(522734);t.exports=function(e){return e?/-----BE | 1.2 | +| module.exports=[842865,(e,t,r)=>{let i=e.r(522734);t.exports=function(e){return e?/-----BE | 1.2 | +| module.exports=[799092,(e,t,n)=>{"use strict";Object.defineProperty(n,"__esModule",{value: | 1.1 | +| // v1.4.34 web-freeze. Schema is locked additive-only until iOS launch // + v1.5.0 marker. | 1.1 | +| module.exports=[971743,(A,e,t)=>{"use strict";let r=A.r(57328),{addAbortListener:s}=A.r(68 | 1.1 | +| module.exports=[835717,(A,e,t)=>{"use strict";let r=A.r(57328),{addAbortListener:s}=A.r(68 | 1 | +| module.exports=[767189,e=>e.a(async(t,a)=>{try{var n=e.i(64105),r=e.i(563616),i=t([n]);[n] | 0.8 | +| module.exports=[594108,806176,674750,98746,t=>{"use strict";var i;let r;function n(t){retu | 0.7 | diff --git a/src/lib/ai/coach/refusal.ts b/src/lib/ai/coach/refusal.ts index 2a0607496..90a053109 100644 --- a/src/lib/ai/coach/refusal.ts +++ b/src/lib/ai/coach/refusal.ts @@ -388,7 +388,17 @@ function buildDerivedHealthTokens(): ReadonlySet { return tokens; } -const DERIVED_HEALTH_TOKENS = buildDerivedHealthTokens(); +let memoDerivedHealthTokensCache: + ReturnType | undefined; +/** Built on first use, not at module load: it reads every shipped catalog. */ +function memoDerivedHealthTokens(): ReturnType< + typeof memoDerivedHealthTokensBuild +> { + return (memoDerivedHealthTokensCache ??= memoDerivedHealthTokensBuild()); +} +function memoDerivedHealthTokensBuild() { + return buildDerivedHealthTokens(); +} /** * Test seam: the guard suite asserts the derived set is non-empty and that @@ -396,13 +406,13 @@ const DERIVED_HEALTH_TOKENS = buildDerivedHealthTokens(); * metric labels fails the suite rather than quietly narrowing the allow-list. */ export function derivedHealthTokens(): ReadonlySet { - return DERIVED_HEALTH_TOKENS; + return memoDerivedHealthTokens(); } /** True when any whole word of the message is a health noun from the bundles. */ function matchesDerivedHealthToken(message: string): boolean { for (const word of healthWordTokens(message)) { - if (DERIVED_HEALTH_TOKENS.has(word)) return true; + if (memoDerivedHealthTokens().has(word)) return true; } return false; } diff --git a/src/lib/documents/auto-stage-labs.ts b/src/lib/documents/auto-stage-labs.ts index 4250be7df..013e0d9b1 100644 --- a/src/lib/documents/auto-stage-labs.ts +++ b/src/lib/documents/auto-stage-labs.ts @@ -152,11 +152,19 @@ function buildAnalyteNamePattern(): RegExp { ); } -const DERIVED_ANALYTE_NAMES = buildAnalyteNamePattern(); +let derivedAnalyteNamesCache: + ReturnType | undefined; +/** Built on first use, not at module load: it reads every shipped catalog. */ +function derivedAnalyteNames(): ReturnType { + return (derivedAnalyteNamesCache ??= derivedAnalyteNamesBuild()); +} +function derivedAnalyteNamesBuild() { + return buildAnalyteNamePattern(); +} /** Test seam: the guard suite pins that the derived pattern covers every locale. */ export function derivedAnalyteNamePattern(): RegExp { - return DERIVED_ANALYTE_NAMES; + return derivedAnalyteNames(); } /** @@ -175,7 +183,7 @@ export function looksLikeLabDocument(text: string): boolean { for (const signal of LAB_SIGNALS) { const hit = signal.test(folded) || - (signal === ANALYTE_SIGNAL && DERIVED_ANALYTE_NAMES.test(folded)); + (signal === ANALYTE_SIGNAL && derivedAnalyteNames().test(folded)); if (hit && ++hits >= 2) return true; } return false; diff --git a/src/lib/fhir/lab-loinc.ts b/src/lib/fhir/lab-loinc.ts index 8b2a51158..af29c284c 100644 --- a/src/lib/fhir/lab-loinc.ts +++ b/src/lib/fhir/lab-loinc.ts @@ -355,14 +355,24 @@ function buildLocalisedAliases(): Record { return index; } -const LOCALISED_LAB_ALIASES = buildLocalisedAliases(); +let memoLocalisedLabAliasesCache: + ReturnType | undefined; +/** Built on first use, not at module load: it reads every shipped catalog. */ +function memoLocalisedLabAliases(): ReturnType< + typeof memoLocalisedLabAliasesBuild +> { + return (memoLocalisedLabAliasesCache ??= memoLocalisedLabAliasesBuild()); +} +function memoLocalisedLabAliasesBuild() { + return buildLocalisedAliases(); +} /** * Test seam: the guard suite asserts the derived index covers every shipped * locale and contradicts no hand-written alias. Production reads the constant. */ export function localisedLabAliases(): Readonly> { - return LOCALISED_LAB_ALIASES; + return memoLocalisedLabAliases(); } /** @@ -462,7 +472,7 @@ export function resolveLabCoding( // every resolution that worked before working the same way. const resolvedKey = LAB_LOINC_BY_KEY[key] ? key - : (LAB_ALIASES[key] ?? LOCALISED_LAB_ALIASES[key]); + : (LAB_ALIASES[key] ?? memoLocalisedLabAliases()[key]); if (!resolvedKey) return null; const mapping = LAB_LOINC_BY_KEY[resolvedKey]; if (!mapping) return null; diff --git a/src/lib/fhir/lab-qualitative.ts b/src/lib/fhir/lab-qualitative.ts index 7b5448ddc..02ea7bdda 100644 --- a/src/lib/fhir/lab-qualitative.ts +++ b/src/lib/fhir/lab-qualitative.ts @@ -110,7 +110,14 @@ function buildLocalisedIndex(): Record { return index; } -const LOCALISED_SNOMED = buildLocalisedIndex(); +let localisedSnomedCache: ReturnType | undefined; +/** Built on first use, not at module load: it reads every shipped catalog. */ +function localisedSnomed(): ReturnType { + return (localisedSnomedCache ??= localisedSnomedBuild()); +} +function localisedSnomedBuild() { + return buildLocalisedIndex(); +} /** * Test seam: the guard suite asserts the derived index covers every shipped @@ -120,7 +127,7 @@ const LOCALISED_SNOMED = buildLocalisedIndex(); export function localisedQualitativeIndex(): Readonly< Record > { - return LOCALISED_SNOMED; + return localisedSnomed(); } /** @@ -144,7 +151,7 @@ export function qualitativeValueConcept( valueText: string, ): FhirCodeableConcept { const key = normaliseQualitativeKey(valueText); - const concept = HAND_WRITTEN_SNOMED[key] ?? LOCALISED_SNOMED[key]; + const concept = HAND_WRITTEN_SNOMED[key] ?? localisedSnomed()[key]; if (!concept) { return { text: valueText }; } diff --git a/src/lib/mcp/__tests__/rich-reads.test.ts b/src/lib/mcp/__tests__/rich-reads.test.ts index 92f9e1a8b..10273c96e 100644 --- a/src/lib/mcp/__tests__/rich-reads.test.ts +++ b/src/lib/mcp/__tests__/rich-reads.test.ts @@ -42,7 +42,7 @@ import { getLabHistory, resolveRichMetric, MCP_METRIC_STATUS_DISCOVERY, - LOCALISED_METRIC_NAME_COLLISIONS, + localisedMetricNameCollisions, metricStatusDiscoveryRows, } from "../rich-reads"; import { locales } from "@/lib/i18n/config"; @@ -292,7 +292,7 @@ describe("resolveRichMetric — localised metric names", () => { // the set is empty and pinned empty: a NEW collision fails here instead of // silently resolving to whichever entry happened to be built first. it("refuses a word two different metrics claim, and only those words", () => { - expect([...LOCALISED_METRIC_NAME_COLLISIONS].sort()).toEqual([]); + expect([...localisedMetricNameCollisions()].sort()).toEqual([]); }); // The property, not a sample: over every metric and every shipped locale, diff --git a/src/lib/mcp/nutrients-read.ts b/src/lib/mcp/nutrients-read.ts index 9b9cd33d2..cd2e4e365 100644 --- a/src/lib/mcp/nutrients-read.ts +++ b/src/lib/mcp/nutrients-read.ts @@ -85,13 +85,21 @@ export const NUTRIENT_LABELS: Readonly> = * `locales`, so a seventh language starts resolving the moment its bundle * lands, with no edit here. */ -const NUTRIENT_NAME_INDEX = buildLocalisedLabelIndex( - NUTRIENT_CODES.map((code) => ({ - id: code, - messageKey: nutrientLabelKey(code), - value: code, - })), -); +let nutrientNameIndexCache: + ReturnType | undefined; +/** Built on first use, not at module load: it reads every shipped catalog. */ +function nutrientNameIndex(): ReturnType { + return (nutrientNameIndexCache ??= nutrientNameIndexBuild()); +} +function nutrientNameIndexBuild() { + return buildLocalisedLabelIndex( + NUTRIENT_CODES.map((code) => ({ + id: code, + messageKey: nutrientLabelKey(code), + value: code, + })), + ); +} /** Overview mode (no `nutrient` arg) mirrors `GET /api/nutrients`'s bounds. */ const OVERVIEW_DEFAULT_DAYS = 14; @@ -148,7 +156,7 @@ export function resolveNutrientCode(input: string): NutrientCode | null { if (!raw) return null; const key = raw.toLowerCase().replace(/[\s-]+/g, "_"); if (isNutrientCode(key)) return key; - return NUTRIENT_NAME_INDEX.index.get(foldLabel(raw)) ?? null; + return nutrientNameIndex().index.get(foldLabel(raw)) ?? null; } /** diff --git a/src/lib/mcp/rich-reads.ts b/src/lib/mcp/rich-reads.ts index 9d15570fa..a7b8cb000 100644 --- a/src/lib/mcp/rich-reads.ts +++ b/src/lib/mcp/rich-reads.ts @@ -560,7 +560,13 @@ export async function metricStatusDiscoveryRows(userId: string): Promise< // // The alias tables stay as they are and stay English: `resting_hr`, `spo2`, // `vo2max` are protocol slugs, not translations of anything. -const LOCALISED_METRIC_INDEX = (() => { +let localisedMetricIndexCache: + ReturnType | undefined; +/** Built on first use, not at module load: it reads every shipped catalog. */ +function localisedMetricIndex(): ReturnType { + return (localisedMetricIndexCache ??= buildLocalisedMetricIndex()); +} +function buildLocalisedMetricIndex() { const exposed = new Map(); const claim = (metric: RichMetric | null) => { if (metric && !exposed.has(metric.measurementType)) { @@ -579,7 +585,7 @@ const LOCALISED_METRIC_INDEX = (() => { value: exposed.get(type as MeasurementType) ?? null, })); return buildLocalisedLabelIndex(entries); -})(); +} /** * Words two different measurement types claim in some bundle, and which @@ -587,8 +593,9 @@ const LOCALISED_METRIC_INDEX = (() => { * is empty: a NEW member is a regression to look at rather than something to * resolve by coin flip. */ -export const LOCALISED_METRIC_NAME_COLLISIONS: ReadonlySet = - LOCALISED_METRIC_INDEX.ambiguous; +export function localisedMetricNameCollisions(): ReadonlySet { + return localisedMetricIndex().ambiguous; +} /** * Resolve a free-text metric name to a single scalar series. Forgiving for an @@ -632,7 +639,7 @@ export function resolveRichMetric(input: string): RichMetric | null { // on PULSE_WAVE_VELOCITY, because "pulse-wave velocity" contains "puls". // A value-less hit (a metric this surface does not expose) falls through // rather than short-circuiting, so the English behaviour below is untouched. - const localised = LOCALISED_METRIC_INDEX.index.get(foldLabel(raw)); + const localised = localisedMetricIndex().index.get(foldLabel(raw)); if (localised) return localised; // 5. English display-name substring match (e.g. "heart-rate variability"). diff --git a/src/lib/medications/glp1-side-effect-tag-match.ts b/src/lib/medications/glp1-side-effect-tag-match.ts index 1dcd7d880..4ce95264d 100644 --- a/src/lib/medications/glp1-side-effect-tag-match.ts +++ b/src/lib/medications/glp1-side-effect-tag-match.ts @@ -64,7 +64,14 @@ function buildIndex(): Map { return index; } -const TAG_INDEX = buildIndex(); +let tagIndexCache: ReturnType | undefined; +/** Built on first use, not at module load: it reads every shipped catalog. */ +function tagIndex(): ReturnType { + return (tagIndexCache ??= tagIndexBuild()); +} +function tagIndexBuild() { + return buildIndex(); +} /** * Test seam: the guard suite asserts the index covers every locale and that no @@ -74,7 +81,7 @@ export function glp1SideEffectTagIndex(): ReadonlyMap< string, Glp1SideEffectTag > { - return TAG_INDEX; + return tagIndex(); } /** @@ -89,7 +96,7 @@ export function glp1SideEffectTagIndex(): ReadonlyMap< export function resolveGlp1SideEffectTag( raw: string, ): Glp1SideEffectTag | null { - return TAG_INDEX.get(normaliseSideEffectTag(raw)) ?? null; + return tagIndex().get(normaliseSideEffectTag(raw)) ?? null; } /** What one `MoodEntry.tags` column contributed. */