target-supabase-sdk 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -11
- package/dist/browser.d.ts +44 -36
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +6 -45
- package/dist/browser.js.map +1 -1
- package/dist/chunks/create-logger-DyH3mZ8p.js +1028 -0
- package/dist/chunks/create-logger-DyH3mZ8p.js.map +1 -0
- package/dist/chunks/enable-log-persist-BH-tbKEj.js +1481 -0
- package/dist/chunks/enable-log-persist-BH-tbKEj.js.map +1 -0
- package/dist/chunks/word.interface-DLgRobKV.js +1973 -0
- package/dist/chunks/word.interface-DLgRobKV.js.map +1 -0
- package/dist/command/command.api.d.ts +1 -1
- package/dist/command/command.api.d.ts.map +1 -1
- package/dist/command/command.interface.d.ts +1 -1
- package/dist/command/command.interface.d.ts.map +1 -1
- package/dist/core.api.d.ts +10 -6
- package/dist/core.api.d.ts.map +1 -1
- package/dist/core.interface.d.ts +3 -2
- package/dist/core.interface.d.ts.map +1 -1
- package/dist/core.utils.d.ts +1 -1
- package/dist/core.utils.d.ts.map +1 -1
- package/dist/idea/idea.interface.d.ts +1 -1
- package/dist/idea/idea.interface.d.ts.map +1 -1
- package/dist/link/link.api.d.ts +1 -1
- package/dist/link/link.api.d.ts.map +1 -1
- package/dist/link/link.interface.d.ts +2 -2
- package/dist/link/link.interface.d.ts.map +1 -1
- package/dist/list/list.api.d.ts +3 -1
- package/dist/list/list.api.d.ts.map +1 -1
- package/dist/list/list.interface.d.ts +2 -2
- package/dist/list/list.interface.d.ts.map +1 -1
- package/dist/node/env/env-parsers.d.ts +19 -0
- package/dist/node/env/env-parsers.d.ts.map +1 -0
- package/dist/node/env/index.d.ts +11 -0
- package/dist/node/env/index.d.ts.map +1 -0
- package/dist/node/env/init-supabase-from-env.d.ts +17 -0
- package/dist/node/env/init-supabase-from-env.d.ts.map +1 -0
- package/dist/node/env/load-env.d.ts +20 -0
- package/dist/node/env/load-env.d.ts.map +1 -0
- package/dist/node/env/project-root.d.ts +11 -0
- package/dist/node/env/project-root.d.ts.map +1 -0
- package/dist/node/env/public-base-url.d.ts +10 -0
- package/dist/node/env/public-base-url.d.ts.map +1 -0
- package/dist/node/env/require-env.d.ts +3 -0
- package/dist/node/env/require-env.d.ts.map +1 -0
- package/dist/node/fs/json-state-store.d.ts +19 -0
- package/dist/node/fs/json-state-store.d.ts.map +1 -0
- package/dist/node/fs/read-source-file.d.ts +19 -0
- package/dist/node/fs/read-source-file.d.ts.map +1 -0
- package/dist/node/node-liveness.d.ts +30 -0
- package/dist/node/node-liveness.d.ts.map +1 -0
- package/dist/node/node-runtime.base.d.ts +21 -3
- package/dist/node/node-runtime.base.d.ts.map +1 -1
- package/dist/node/node.api.d.ts +1 -1
- package/dist/node/node.api.d.ts.map +1 -1
- package/dist/node/node.interface.d.ts +1 -6
- package/dist/node/node.interface.d.ts.map +1 -1
- package/dist/node/preload/run-service-preload.d.ts +14 -0
- package/dist/node/preload/run-service-preload.d.ts.map +1 -0
- package/dist/node/preload/service-preload.interface.d.ts +15 -0
- package/dist/node/preload/service-preload.interface.d.ts.map +1 -0
- package/dist/node/process/index.d.ts +5 -0
- package/dist/node/process/index.d.ts.map +1 -0
- package/dist/node/process/managed-child-processes.d.ts +37 -0
- package/dist/node/process/managed-child-processes.d.ts.map +1 -0
- package/dist/node/process/process-spawn.d.ts +27 -0
- package/dist/node/process/process-spawn.d.ts.map +1 -0
- package/dist/node/readiness/index.d.ts +9 -0
- package/dist/node/readiness/index.d.ts.map +1 -0
- package/dist/node/readiness/poll-until.d.ts +11 -0
- package/dist/node/readiness/poll-until.d.ts.map +1 -0
- package/dist/node/readiness/readiness-checks.d.ts +22 -0
- package/dist/node/readiness/readiness-checks.d.ts.map +1 -0
- package/dist/node/readiness/readiness.types.d.ts +12 -0
- package/dist/node/readiness/readiness.types.d.ts.map +1 -0
- package/dist/node/readiness/run-readiness-checks.d.ts +4 -0
- package/dist/node/readiness/run-readiness-checks.d.ts.map +1 -0
- package/dist/node/readiness/service-ready-gate.d.ts +26 -0
- package/dist/node/readiness/service-ready-gate.d.ts.map +1 -0
- package/dist/node.d.ts +26 -12
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +2482 -18
- package/dist/node.js.map +1 -1
- package/dist/parcel/chunk-fetch-registry.d.ts +14 -0
- package/dist/parcel/chunk-fetch-registry.d.ts.map +1 -0
- package/dist/parcel/chunk-url.utils.d.ts +9 -0
- package/dist/parcel/chunk-url.utils.d.ts.map +1 -0
- package/dist/parcel/parcel-manager.d.ts +44 -20
- package/dist/parcel/parcel-manager.d.ts.map +1 -1
- package/dist/parcel/parcel.api.d.ts +306 -0
- package/dist/parcel/parcel.api.d.ts.map +1 -0
- package/dist/parcel/parcel.interface.d.ts +26 -5
- package/dist/parcel/parcel.interface.d.ts.map +1 -1
- package/dist/parcel/parcel.service.d.ts +38 -0
- package/dist/parcel/parcel.service.d.ts.map +1 -0
- package/dist/parcel/storage-provider-registry.d.ts +17 -0
- package/dist/parcel/storage-provider-registry.d.ts.map +1 -0
- package/dist/parcel/storage-provider.types.d.ts +31 -0
- package/dist/parcel/storage-provider.types.d.ts.map +1 -0
- package/dist/preload.d.ts +7 -0
- package/dist/preload.d.ts.map +1 -0
- package/dist/preload.js +47 -0
- package/dist/preload.js.map +1 -0
- package/dist/repo/repo-context.utils.d.ts.map +1 -1
- package/dist/repo/repo-manager.d.ts +1 -1
- package/dist/repo/repo-manager.d.ts.map +1 -1
- package/dist/repo/repo.api.d.ts +1 -1
- package/dist/repo/repo.api.d.ts.map +1 -1
- package/dist/repo/repo.interface.d.ts +1 -1
- package/dist/repo/repo.interface.d.ts.map +1 -1
- package/dist/repo/repo.script-loader.d.ts +3 -3
- package/dist/repo/repo.script-loader.d.ts.map +1 -1
- package/dist/service/base.interface.d.ts +54 -57
- package/dist/service/base.interface.d.ts.map +1 -1
- package/dist/service/index.d.ts +21 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/service.api.d.ts +503 -0
- package/dist/service/service.api.d.ts.map +1 -0
- package/dist/service/service.interface.d.ts +36 -9
- package/dist/service/service.interface.d.ts.map +1 -1
- package/dist/shared/http/fetch-retry.d.ts +21 -0
- package/dist/shared/http/fetch-retry.d.ts.map +1 -0
- package/dist/shared/log/create-logger.d.ts +27 -0
- package/dist/shared/log/create-logger.d.ts.map +1 -0
- package/dist/shared/log/enable-log-persist.d.ts +28 -0
- package/dist/shared/log/enable-log-persist.d.ts.map +1 -0
- package/dist/shared/log/index.d.ts +6 -6
- package/dist/shared/log/index.d.ts.map +1 -1
- package/dist/shared/log/log-manager.d.ts +25 -27
- package/dist/shared/log/log-manager.d.ts.map +1 -1
- package/dist/shared/log/log-persist-batch-id.d.ts +13 -0
- package/dist/shared/log/log-persist-batch-id.d.ts.map +1 -0
- package/dist/shared/log/log-persist-flush.d.ts +8 -0
- package/dist/shared/log/log-persist-flush.d.ts.map +1 -0
- package/dist/shared/log/log-persist-hook.d.ts +6 -0
- package/dist/shared/log/log-persist-hook.d.ts.map +1 -0
- package/dist/shared/log/log-persist-logger.d.ts +15 -0
- package/dist/shared/log/log-persist-logger.d.ts.map +1 -0
- package/dist/shared/log/log-persist-registry.d.ts +18 -0
- package/dist/shared/log/log-persist-registry.d.ts.map +1 -0
- package/dist/shared/log/log-persist.config.d.ts +10 -0
- package/dist/shared/log/log-persist.config.d.ts.map +1 -0
- package/dist/shared/log/log-persist.d.ts +55 -0
- package/dist/shared/log/log-persist.d.ts.map +1 -0
- package/dist/shared/log/log-persist.interface.d.ts +116 -0
- package/dist/shared/log/log-persist.interface.d.ts.map +1 -0
- package/dist/shared/log/log-scope.d.ts +25 -18
- package/dist/shared/log/log-scope.d.ts.map +1 -1
- package/dist/shared/utils/fetch-url.d.ts +3 -0
- package/dist/shared/utils/fetch-url.d.ts.map +1 -0
- package/dist/shared/utils/network-error.d.ts +14 -0
- package/dist/shared/utils/network-error.d.ts.map +1 -0
- package/dist/shared/utils/safe-file-name.d.ts +14 -0
- package/dist/shared/utils/safe-file-name.d.ts.map +1 -0
- package/dist/shared/utils/sha256.d.ts +3 -0
- package/dist/shared/utils/sha256.d.ts.map +1 -0
- package/dist/supabase.d.ts +7 -2
- package/dist/supabase.d.ts.map +1 -1
- package/dist/task/index.d.ts +8 -7
- package/dist/task/index.d.ts.map +1 -1
- package/dist/task/local-task-registry.d.ts +1 -1
- package/dist/task/local-task-registry.d.ts.map +1 -1
- package/dist/task/task-manager.d.ts +7 -9
- package/dist/task/task-manager.d.ts.map +1 -1
- package/dist/{node → task}/task-node.d.ts +2 -3
- package/dist/task/task-node.d.ts.map +1 -0
- package/dist/task/task-post-validated.api.d.ts +19 -0
- package/dist/task/task-post-validated.api.d.ts.map +1 -0
- package/dist/task/task-post.api.d.ts +10 -5
- package/dist/task/task-post.api.d.ts.map +1 -1
- package/dist/task/task-queue.d.ts +7 -0
- package/dist/task/task-queue.d.ts.map +1 -0
- package/dist/task/task-repo-context.d.ts.map +1 -1
- package/dist/task/task-repo-validation.d.ts +2 -2
- package/dist/task/task-repo-validation.d.ts.map +1 -1
- package/dist/task/task.api.d.ts +13 -13
- package/dist/task/task.api.d.ts.map +1 -1
- package/dist/task/task.constant.d.ts +4 -0
- package/dist/task/task.constant.d.ts.map +1 -0
- package/dist/task/task.interface.d.ts +1 -1
- package/dist/task/task.interface.d.ts.map +1 -1
- package/dist/trigger/trigger-manager.d.ts +25 -0
- package/dist/trigger/trigger-manager.d.ts.map +1 -0
- package/dist/trigger/trigger-node.d.ts +23 -0
- package/dist/trigger/trigger-node.d.ts.map +1 -0
- package/dist/trigger/trigger.api.d.ts +4 -4
- package/dist/trigger/trigger.api.d.ts.map +1 -1
- package/dist/trigger/trigger.constant.d.ts +8 -0
- package/dist/trigger/trigger.constant.d.ts.map +1 -0
- package/dist/trigger/trigger.interface.d.ts +46 -2
- package/dist/trigger/trigger.interface.d.ts.map +1 -1
- package/dist/word/word.api.d.ts +1 -1
- package/dist/word/word.api.d.ts.map +1 -1
- package/dist/word/word.interface.d.ts +2 -2
- package/dist/word/word.interface.d.ts.map +1 -1
- package/package.json +84 -74
- package/src/browser.ts +144 -79
- package/src/command/command.api.ts +19 -17
- package/src/command/command.interface.ts +2 -3
- package/src/core.api.ts +466 -477
- package/src/core.interface.ts +63 -62
- package/src/core.utils.ts +64 -64
- package/src/idea/idea.interface.ts +3 -3
- package/src/link/link.api.ts +28 -30
- package/src/link/link.interface.ts +3 -4
- package/src/list/list.api.ts +50 -29
- package/src/list/list.interface.ts +3 -4
- package/src/node/env/env-parsers.ts +77 -0
- package/src/node/env/index.ts +10 -0
- package/src/node/env/init-supabase-from-env.ts +38 -0
- package/src/node/env/load-env.ts +67 -0
- package/src/node/env/project-root.ts +34 -0
- package/src/node/env/public-base-url.ts +18 -0
- package/src/node/env/require-env.ts +15 -0
- package/src/node/fs/json-state-store.ts +97 -0
- package/src/node/fs/read-source-file.ts +75 -0
- package/src/node/node-liveness.ts +80 -0
- package/src/node/node-runtime.base.ts +135 -104
- package/src/node/node.api.ts +11 -10
- package/src/node/node.interface.ts +1 -8
- package/src/node/node.utils.ts +1 -1
- package/src/node/preload/run-service-preload.ts +48 -0
- package/src/node/preload/service-preload.interface.ts +14 -0
- package/src/node/process/index.ts +8 -0
- package/src/node/process/managed-child-processes.ts +121 -0
- package/src/node/process/process-spawn.ts +61 -0
- package/src/node/readiness/index.ts +20 -0
- package/src/node/readiness/poll-until.ts +27 -0
- package/src/node/readiness/readiness-checks.ts +79 -0
- package/src/node/readiness/readiness.types.ts +13 -0
- package/src/node/readiness/run-readiness-checks.ts +20 -0
- package/src/node/readiness/service-ready-gate.ts +65 -0
- package/src/node.ts +138 -47
- package/src/parcel/chunk-fetch-registry.ts +96 -0
- package/src/parcel/chunk-url.utils.ts +30 -0
- package/src/parcel/parcel-manager.ts +431 -173
- package/src/parcel/parcel.api.ts +103 -0
- package/src/parcel/parcel.interface.ts +44 -23
- package/src/parcel/parcel.service.ts +79 -0
- package/src/parcel/storage-provider-registry.ts +49 -0
- package/src/parcel/storage-provider.types.ts +45 -0
- package/src/preload.ts +7 -0
- package/src/repo/repo-context.utils.ts +3 -4
- package/src/repo/repo-manager.ts +7 -20
- package/src/repo/repo.api.ts +5 -5
- package/src/repo/repo.interface.ts +1 -2
- package/src/repo/repo.script-loader.ts +4 -8
- package/src/service/base.interface.ts +81 -90
- package/src/service/index.ts +173 -0
- package/src/service/service.api.ts +294 -0
- package/src/service/service.interface.ts +49 -17
- package/src/shared/http/fetch-retry.ts +145 -0
- package/src/shared/log/create-logger.ts +51 -0
- package/src/shared/log/enable-log-persist.ts +225 -0
- package/src/shared/log/index.ts +19 -11
- package/src/shared/log/log-manager.ts +65 -69
- package/src/shared/log/log-persist-batch-id.ts +43 -0
- package/src/shared/log/log-persist-flush.ts +128 -0
- package/src/shared/log/log-persist-hook.ts +13 -0
- package/src/shared/log/log-persist-logger.ts +33 -0
- package/src/shared/log/log-persist-registry.ts +252 -0
- package/src/shared/log/log-persist.config.ts +97 -0
- package/src/shared/log/log-persist.interface.ts +132 -0
- package/src/shared/log/log-persist.ts +704 -0
- package/src/shared/log/log-scope.ts +46 -46
- package/src/shared/utils/config-path.utils.ts +1 -1
- package/src/shared/utils/fetch-url.ts +13 -0
- package/src/shared/utils/network-error.ts +152 -0
- package/src/shared/utils/safe-file-name.ts +37 -0
- package/src/shared/utils/sha256.ts +15 -0
- package/src/supabase.ts +125 -119
- package/src/task/index.ts +31 -34
- package/src/task/local-task-registry.ts +9 -17
- package/src/task/task-manager.ts +34 -35
- package/src/{node → task}/task-node.ts +91 -73
- package/src/task/task-post-validated.api.ts +47 -0
- package/src/task/task-post.api.ts +81 -98
- package/src/task/task-queue.ts +18 -0
- package/src/task/task-repo-context.ts +2 -1
- package/src/task/task-repo-validation.ts +6 -8
- package/src/task/task.api.ts +61 -25
- package/src/task/task.constant.ts +4 -0
- package/src/task/task.interface.ts +4 -7
- package/src/trigger/trigger-manager.ts +287 -0
- package/src/trigger/trigger-node.ts +80 -0
- package/src/trigger/trigger.api.ts +11 -11
- package/src/trigger/trigger.constant.ts +10 -0
- package/src/trigger/trigger.interface.ts +52 -2
- package/src/trigger/trigger.utils.ts +1 -1
- package/src/word/word.api.ts +28 -30
- package/src/word/word.interface.ts +3 -4
- package/dist/auth/auth.api.d.ts +0 -28
- package/dist/auth/auth.api.d.ts.map +0 -1
- package/dist/auth/auth.api.js +0 -51
- package/dist/auth/auth.api.js.map +0 -1
- package/dist/command/command.api.js +0 -58
- package/dist/command/command.api.js.map +0 -1
- package/dist/command/command.interface.js +0 -9
- package/dist/command/command.interface.js.map +0 -1
- package/dist/core.api.js +0 -412
- package/dist/core.api.js.map +0 -1
- package/dist/core.interface.js +0 -46
- package/dist/core.interface.js.map +0 -1
- package/dist/core.utils.js +0 -81
- package/dist/core.utils.js.map +0 -1
- package/dist/feed/feed.interface.d.ts +0 -27
- package/dist/feed/feed.interface.d.ts.map +0 -1
- package/dist/feed/feed.interface.js +0 -17
- package/dist/feed/feed.interface.js.map +0 -1
- package/dist/file/file-manager.d.ts +0 -24
- package/dist/file/file-manager.d.ts.map +0 -1
- package/dist/file/file-manager.js +0 -109
- package/dist/file/file-manager.js.map +0 -1
- package/dist/file/file.api.d.ts +0 -15
- package/dist/file/file.api.d.ts.map +0 -1
- package/dist/file/file.api.js +0 -30
- package/dist/file/file.api.js.map +0 -1
- package/dist/file/file.interface.d.ts +0 -25
- package/dist/file/file.interface.d.ts.map +0 -1
- package/dist/file/file.interface.js +0 -9
- package/dist/file/file.interface.js.map +0 -1
- package/dist/file-list/file-list.api.d.ts +0 -15
- package/dist/file-list/file-list.api.d.ts.map +0 -1
- package/dist/file-list/file-list.api.js +0 -54
- package/dist/file-list/file-list.api.js.map +0 -1
- package/dist/file-list/file-list.interface.d.ts +0 -22
- package/dist/file-list/file-list.interface.d.ts.map +0 -1
- package/dist/file-list/file-list.interface.js +0 -5
- package/dist/file-list/file-list.interface.js.map +0 -1
- package/dist/idea/idea.interface.js +0 -15
- package/dist/idea/idea.interface.js.map +0 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +0 -1
- package/dist/link/link.api.js +0 -30
- package/dist/link/link.api.js.map +0 -1
- package/dist/link/link.interface.js +0 -7
- package/dist/link/link.interface.js.map +0 -1
- package/dist/list/list.api.js +0 -30
- package/dist/list/list.api.js.map +0 -1
- package/dist/list/list.interface.js +0 -6
- package/dist/list/list.interface.js.map +0 -1
- package/dist/node/node-log-scope.d.ts +0 -11
- package/dist/node/node-log-scope.d.ts.map +0 -1
- package/dist/node/node-log-scope.js +0 -7
- package/dist/node/node-log-scope.js.map +0 -1
- package/dist/node/node-runtime.base.js +0 -397
- package/dist/node/node-runtime.base.js.map +0 -1
- package/dist/node/node-task.preset.d.ts +0 -1
- package/dist/node/node-task.preset.d.ts.map +0 -1
- package/dist/node/node-task.preset.js +0 -14
- package/dist/node/node-task.preset.js.map +0 -1
- package/dist/node/node.api.js +0 -100
- package/dist/node/node.api.js.map +0 -1
- package/dist/node/node.interface.js +0 -19
- package/dist/node/node.interface.js.map +0 -1
- package/dist/node/node.utils.js +0 -36
- package/dist/node/node.utils.js.map +0 -1
- package/dist/node/task-node.d.ts.map +0 -1
- package/dist/node/task-node.js +0 -245
- package/dist/node/task-node.js.map +0 -1
- package/dist/node/trigger-node.d.ts +0 -18
- package/dist/node/trigger-node.d.ts.map +0 -1
- package/dist/node/trigger-node.js +0 -150
- package/dist/node/trigger-node.js.map +0 -1
- package/dist/parcel/parcel-manager.js +0 -156
- package/dist/parcel/parcel-manager.js.map +0 -1
- package/dist/parcel/parcel.interface.js +0 -11
- package/dist/parcel/parcel.interface.js.map +0 -1
- package/dist/question-list/question-list.api.d.ts +0 -13
- package/dist/question-list/question-list.api.d.ts.map +0 -1
- package/dist/question-list/question-list.api.js +0 -35
- package/dist/question-list/question-list.api.js.map +0 -1
- package/dist/question-list/question-list.interface.d.ts +0 -18
- package/dist/question-list/question-list.interface.d.ts.map +0 -1
- package/dist/question-list/question-list.interface.js +0 -5
- package/dist/question-list/question-list.interface.js.map +0 -1
- package/dist/repo/repo-context.utils.js +0 -81
- package/dist/repo/repo-context.utils.js.map +0 -1
- package/dist/repo/repo-manager.js +0 -166
- package/dist/repo/repo-manager.js.map +0 -1
- package/dist/repo/repo.api.js +0 -40
- package/dist/repo/repo.api.js.map +0 -1
- package/dist/repo/repo.interface.js +0 -8
- package/dist/repo/repo.interface.js.map +0 -1
- package/dist/repo/repo.script-loader.js +0 -108
- package/dist/repo/repo.script-loader.js.map +0 -1
- package/dist/review/review.api.d.ts +0 -14
- package/dist/review/review.api.d.ts.map +0 -1
- package/dist/review/review.api.js +0 -133
- package/dist/review/review.api.js.map +0 -1
- package/dist/review/review.interface.d.ts +0 -24
- package/dist/review/review.interface.d.ts.map +0 -1
- package/dist/review/review.interface.js +0 -10
- package/dist/review/review.interface.js.map +0 -1
- package/dist/review-v2/apis/post-review-setting.api.d.ts +0 -12
- package/dist/review-v2/apis/post-review-setting.api.d.ts.map +0 -1
- package/dist/review-v2/apis/post-review-setting.api.js +0 -27
- package/dist/review-v2/apis/post-review-setting.api.js.map +0 -1
- package/dist/review-v2/apis/post-review.api.d.ts +0 -13
- package/dist/review-v2/apis/post-review.api.d.ts.map +0 -1
- package/dist/review-v2/apis/post-review.api.js +0 -46
- package/dist/review-v2/apis/post-review.api.js.map +0 -1
- package/dist/review-v2/review-setting.interface.d.ts +0 -23
- package/dist/review-v2/review-setting.interface.d.ts.map +0 -1
- package/dist/review-v2/review-setting.interface.js +0 -5
- package/dist/review-v2/review-setting.interface.js.map +0 -1
- package/dist/review-v2/review.interface.d.ts +0 -20
- package/dist/review-v2/review.interface.d.ts.map +0 -1
- package/dist/review-v2/review.interface.js +0 -5
- package/dist/review-v2/review.interface.js.map +0 -1
- package/dist/service/api.interface.d.ts +0 -35
- package/dist/service/api.interface.d.ts.map +0 -1
- package/dist/service/api.interface.js +0 -12
- package/dist/service/api.interface.js.map +0 -1
- package/dist/service/base.interface.js +0 -7
- package/dist/service/base.interface.js.map +0 -1
- package/dist/service/service.interface.js +0 -11
- package/dist/service/service.interface.js.map +0 -1
- package/dist/shared/log/create-api-logger.d.ts +0 -21
- package/dist/shared/log/create-api-logger.d.ts.map +0 -1
- package/dist/shared/log/create-api-logger.js +0 -19
- package/dist/shared/log/create-api-logger.js.map +0 -1
- package/dist/shared/log/index.js +0 -10
- package/dist/shared/log/index.js.map +0 -1
- package/dist/shared/log/log-manager.js +0 -237
- package/dist/shared/log/log-manager.js.map +0 -1
- package/dist/shared/log/log-scope.js +0 -70
- package/dist/shared/log/log-scope.js.map +0 -1
- package/dist/shared/utils/config-path.utils.js +0 -85
- package/dist/shared/utils/config-path.utils.js.map +0 -1
- package/dist/shared/utils/error.utils.js +0 -15
- package/dist/shared/utils/error.utils.js.map +0 -1
- package/dist/shared/utils/id.utils.js +0 -5
- package/dist/shared/utils/id.utils.js.map +0 -1
- package/dist/supabase.js +0 -100
- package/dist/supabase.js.map +0 -1
- package/dist/task/index.js +0 -14
- package/dist/task/index.js.map +0 -1
- package/dist/task/local-task-registry.js +0 -214
- package/dist/task/local-task-registry.js.map +0 -1
- package/dist/task/task-manager.js +0 -196
- package/dist/task/task-manager.js.map +0 -1
- package/dist/task/task-post.api.js +0 -91
- package/dist/task/task-post.api.js.map +0 -1
- package/dist/task/task-repo-context.js +0 -12
- package/dist/task/task-repo-context.js.map +0 -1
- package/dist/task/task-repo-validation.js +0 -96
- package/dist/task/task-repo-validation.js.map +0 -1
- package/dist/task/task.api.js +0 -303
- package/dist/task/task.api.js.map +0 -1
- package/dist/task/task.interface.js +0 -36
- package/dist/task/task.interface.js.map +0 -1
- package/dist/task/task.utils.js +0 -2
- package/dist/task/task.utils.js.map +0 -1
- package/dist/trigger/trigger.api.js +0 -125
- package/dist/trigger/trigger.api.js.map +0 -1
- package/dist/trigger/trigger.interface.js +0 -11
- package/dist/trigger/trigger.interface.js.map +0 -1
- package/dist/trigger/trigger.utils.js +0 -39
- package/dist/trigger/trigger.utils.js.map +0 -1
- package/dist/word/word.api.js +0 -30
- package/dist/word/word.api.js.map +0 -1
- package/dist/word/word.interface.js +0 -6
- package/dist/word/word.interface.js.map +0 -1
- package/src/auth/auth.api.ts +0 -87
- package/src/feed/feed.interface.ts +0 -33
- package/src/file/file-manager.ts +0 -145
- package/src/file/file.api.ts +0 -43
- package/src/file/file.interface.ts +0 -30
- package/src/file-list/file-list.api.ts +0 -64
- package/src/file-list/file-list.interface.ts +0 -24
- package/src/node/node-log-scope.ts +0 -15
- package/src/node/node-task.preset.ts +0 -13
- package/src/node/trigger-node.ts +0 -180
- package/src/question-list/question-list.api.ts +0 -42
- package/src/question-list/question-list.interface.ts +0 -21
- package/src/review/review.api.ts +0 -145
- package/src/review/review.interface.ts +0 -26
- package/src/review-v2/apis/post-review-setting.api.ts +0 -35
- package/src/review-v2/apis/post-review.api.ts +0 -59
- package/src/review-v2/review-setting.interface.ts +0 -26
- package/src/review-v2/review.interface.ts +0 -23
- package/src/service/api.interface.ts +0 -47
- package/src/shared/log/create-api-logger.ts +0 -36
|
@@ -0,0 +1,1481 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve, dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { readFile, mkdir, writeFile, rename, readdir } from 'node:fs/promises';
|
|
5
|
+
import { d as createLogger, a as CategoryList, w as postLogListCreate, m as isCreateTargetAlreadyExistsError, L as LogLevel, F as registerLogPersistOffer } from './create-logger-DyH3mZ8p.js';
|
|
6
|
+
import 'lodash-es';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
|
|
9
|
+
function readEnv(name, env = process.env) {
|
|
10
|
+
const value = env[name]?.trim();
|
|
11
|
+
if (value == null || value === "") {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function requireEnv(name, env = process.env) {
|
|
17
|
+
const value = readEnv(name, env);
|
|
18
|
+
if (value == null) {
|
|
19
|
+
throw new Error(`Missing required environment variable: ${name}`);
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function envInt(name, defaultValue, options) {
|
|
25
|
+
const env = options?.env ?? process.env;
|
|
26
|
+
const raw = readEnv(name, env);
|
|
27
|
+
if (raw == null) {
|
|
28
|
+
return defaultValue;
|
|
29
|
+
}
|
|
30
|
+
const value = Number(raw);
|
|
31
|
+
if (!Number.isInteger(value)) {
|
|
32
|
+
throw new Error(`Invalid ${name}: ${raw}`);
|
|
33
|
+
}
|
|
34
|
+
const min = options?.min;
|
|
35
|
+
if (min != null && value < min) {
|
|
36
|
+
throw new Error(`Invalid ${name}: ${raw}`);
|
|
37
|
+
}
|
|
38
|
+
const max = options?.max;
|
|
39
|
+
if (max != null && value > max) {
|
|
40
|
+
throw new Error(`Invalid ${name}: ${raw}`);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function envMs(name, defaultMs, options) {
|
|
45
|
+
return envInt(name, defaultMs, { ...options, min: options?.min ?? 1 });
|
|
46
|
+
}
|
|
47
|
+
function envPort(defaultPort = 3100, options) {
|
|
48
|
+
const envKey = options?.envKey ?? "PORT";
|
|
49
|
+
return envInt(envKey, defaultPort, { ...options, min: 1, max: 65535 });
|
|
50
|
+
}
|
|
51
|
+
function envNumber(name, defaultValue, options) {
|
|
52
|
+
const env = options?.env ?? process.env;
|
|
53
|
+
const raw = readEnv(name, env);
|
|
54
|
+
if (raw == null) {
|
|
55
|
+
return defaultValue;
|
|
56
|
+
}
|
|
57
|
+
const value = Number(raw);
|
|
58
|
+
if (!Number.isFinite(value)) {
|
|
59
|
+
throw new Error(`Invalid ${name}: ${raw}`);
|
|
60
|
+
}
|
|
61
|
+
const min = options?.min;
|
|
62
|
+
const resolved = options?.floor === true ? Math.floor(value) : value;
|
|
63
|
+
if (min != null && resolved < min) {
|
|
64
|
+
throw new Error(`Invalid ${name}: ${raw}`);
|
|
65
|
+
}
|
|
66
|
+
return resolved;
|
|
67
|
+
}
|
|
68
|
+
function envBool(name, defaultValue = false, env = process.env) {
|
|
69
|
+
const raw = readEnv(name, env)?.toLowerCase();
|
|
70
|
+
if (raw == null) {
|
|
71
|
+
return defaultValue;
|
|
72
|
+
}
|
|
73
|
+
return raw === "1" || raw === "true" || raw === "yes";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const DEFAULT_ENV_FILES = [".env.local", ".env"];
|
|
77
|
+
function stripQuotes(value) {
|
|
78
|
+
return value.replace(/^["']|["']$/g, "");
|
|
79
|
+
}
|
|
80
|
+
/** Parse one KEY=VALUE line; returns null if line should be skipped. */
|
|
81
|
+
function parseEnvLine(line) {
|
|
82
|
+
const trimmed = line.trim();
|
|
83
|
+
if (trimmed === "" || trimmed.startsWith("#")) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const eq = trimmed.indexOf("=");
|
|
87
|
+
if (eq === -1) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const key = trimmed.slice(0, eq).trim();
|
|
91
|
+
const raw = trimmed.slice(eq + 1).trim();
|
|
92
|
+
return { key, value: stripQuotes(raw) };
|
|
93
|
+
}
|
|
94
|
+
function parseEnvFileContent(content, env) {
|
|
95
|
+
for (const line of content.split("\n")) {
|
|
96
|
+
const parsed = parseEnvLine(line);
|
|
97
|
+
if (parsed == null) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (env[parsed.key] == null || env[parsed.key] === "") {
|
|
101
|
+
env[parsed.key] = parsed.value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function parseEnvFile(filePath, env) {
|
|
106
|
+
if (!existsSync(filePath)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
parseEnvFileContent(readFileSync(filePath, "utf8"), env);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Load `.env.local` then `.env` into `process.env` (or `options.env`).
|
|
113
|
+
* Existing non-empty env values are not overwritten.
|
|
114
|
+
*/
|
|
115
|
+
function loadEnvFiles(root, options) {
|
|
116
|
+
const env = options?.env ?? process.env;
|
|
117
|
+
const files = options?.files ?? DEFAULT_ENV_FILES;
|
|
118
|
+
for (const name of files) {
|
|
119
|
+
parseEnvFile(resolve(root, name), env);
|
|
120
|
+
}
|
|
121
|
+
options?.afterLoad?.();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Resolve project root from a module URL (typically `import.meta.url`).
|
|
126
|
+
* @param relativePath Path relative to the module file (default `".."` = parent directory).
|
|
127
|
+
*/
|
|
128
|
+
function resolveProjectRootFromModule(importMetaUrl, relativePath = "..") {
|
|
129
|
+
return resolve(fileURLToPath(new URL(relativePath, importMetaUrl)));
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Walk upward from `importMetaUrl` until a `package.json` with matching `name` is found.
|
|
133
|
+
* Use for esbuild bundles under `dist/` where a single `..` hop lands in `dist/`, not repo root.
|
|
134
|
+
*/
|
|
135
|
+
function resolveProjectRootByPackageName(importMetaUrl, packageName) {
|
|
136
|
+
let dir = dirname(fileURLToPath(importMetaUrl));
|
|
137
|
+
for (;;) {
|
|
138
|
+
const packageJsonPath = resolve(dir, "package.json");
|
|
139
|
+
if (existsSync(packageJsonPath)) {
|
|
140
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
141
|
+
if (pkg.name === packageName) {
|
|
142
|
+
return dir;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const parent = dirname(dir);
|
|
146
|
+
if (parent === dir) {
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
dir = parent;
|
|
150
|
+
}
|
|
151
|
+
throw new Error(`Could not find package root for "${packageName}" from ${importMetaUrl}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function mergeWithNested(defaults, current, patch, nestedKeys) {
|
|
155
|
+
const merged = {
|
|
156
|
+
...defaults,
|
|
157
|
+
...current,
|
|
158
|
+
...patch,
|
|
159
|
+
};
|
|
160
|
+
for (const key of nestedKeys) {
|
|
161
|
+
const defaultNested = defaults[key];
|
|
162
|
+
if (defaultNested != null && typeof defaultNested === "object") {
|
|
163
|
+
merged[key] = {
|
|
164
|
+
...defaultNested,
|
|
165
|
+
...current?.[key],
|
|
166
|
+
...patch[key],
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return merged;
|
|
171
|
+
}
|
|
172
|
+
async function atomicWriteJson(filePath, data) {
|
|
173
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
174
|
+
const content = `${JSON.stringify(data, null, 2)}\n`;
|
|
175
|
+
const tempPath = `${filePath}.${process.pid}.tmp`;
|
|
176
|
+
await writeFile(tempPath, content, "utf8");
|
|
177
|
+
await rename(tempPath, filePath);
|
|
178
|
+
}
|
|
179
|
+
/** JSON file-backed state with optional nested key merge and atomic writes. */
|
|
180
|
+
function createJsonFileStateStore(options) {
|
|
181
|
+
const { filePath, defaultState, nestedKeys = [], updatedAtKey } = options;
|
|
182
|
+
function touchUpdatedAt(state) {
|
|
183
|
+
if (updatedAtKey == null) {
|
|
184
|
+
return state;
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
...state,
|
|
188
|
+
[updatedAtKey]: new Date().toISOString(),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
async read() {
|
|
193
|
+
try {
|
|
194
|
+
const raw = await readFile(filePath, "utf8");
|
|
195
|
+
const parsed = JSON.parse(raw);
|
|
196
|
+
return mergeWithNested(defaultState, parsed, {}, nestedKeys);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return { ...defaultState };
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
async write(patch) {
|
|
203
|
+
const current = await this.read();
|
|
204
|
+
const next = touchUpdatedAt(mergeWithNested(defaultState, current, patch, nestedKeys));
|
|
205
|
+
await atomicWriteJson(filePath, next);
|
|
206
|
+
return next;
|
|
207
|
+
},
|
|
208
|
+
async reset() {
|
|
209
|
+
const next = touchUpdatedAt({ ...defaultState });
|
|
210
|
+
await atomicWriteJson(filePath, next);
|
|
211
|
+
return next;
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function sleep(ms) {
|
|
217
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Poll `until()` until it returns true or `timeoutMs` elapses.
|
|
221
|
+
* @throws Error on timeout
|
|
222
|
+
*/
|
|
223
|
+
async function pollUntil(options) {
|
|
224
|
+
const intervalMs = options.intervalMs ?? 500;
|
|
225
|
+
const deadline = Date.now() + options.timeoutMs;
|
|
226
|
+
while (Date.now() < deadline) {
|
|
227
|
+
if (await options.until()) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
await sleep(intervalMs);
|
|
231
|
+
}
|
|
232
|
+
throw new Error(`pollUntil timeout after ${options.timeoutMs}ms`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const LOG_PERSIST_LOADER_KEY = "LogBatch.0";
|
|
236
|
+
const DEFAULT_LOG_PERSIST_CONFIG = {
|
|
237
|
+
fast: {
|
|
238
|
+
maxBufferEntries: 500,
|
|
239
|
+
},
|
|
240
|
+
medium: {
|
|
241
|
+
debounceMs: 5_000,
|
|
242
|
+
maxEntries: 20,
|
|
243
|
+
maxBytes: 512 * 1024,
|
|
244
|
+
maxBufferEntries: 1_000,
|
|
245
|
+
},
|
|
246
|
+
slow: {
|
|
247
|
+
maxEntries: 400,
|
|
248
|
+
maxBytes: 512 * 1024,
|
|
249
|
+
maxAgeMs: 120_000,
|
|
250
|
+
maxBufferEntries: 2_000,
|
|
251
|
+
},
|
|
252
|
+
postTimeoutMs: 30_000,
|
|
253
|
+
shutdownDrainTimeoutMs: 15_000,
|
|
254
|
+
};
|
|
255
|
+
function resolveLogPersistConfig(partial) {
|
|
256
|
+
const defaults = DEFAULT_LOG_PERSIST_CONFIG;
|
|
257
|
+
return {
|
|
258
|
+
fast: { ...defaults.fast, ...partial?.fast },
|
|
259
|
+
medium: { ...defaults.medium, ...partial?.medium },
|
|
260
|
+
slow: { ...defaults.slow, ...partial?.slow },
|
|
261
|
+
postTimeoutMs: partial?.postTimeoutMs ?? defaults.postTimeoutMs,
|
|
262
|
+
shutdownDrainTimeoutMs: partial?.shutdownDrainTimeoutMs ?? defaults.shutdownDrainTimeoutMs,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function logPersistEnabledFromEnv(env = process.env) {
|
|
266
|
+
return envBool("LOG_PERSIST_ENABLED", false, env);
|
|
267
|
+
}
|
|
268
|
+
function logPersistServiceFromEnv(env = process.env) {
|
|
269
|
+
const raw = env.LOG_PERSIST_SERVICE?.trim();
|
|
270
|
+
return raw != null && raw !== "" ? raw : undefined;
|
|
271
|
+
}
|
|
272
|
+
function logPersistProcessFromEnv(env = process.env) {
|
|
273
|
+
const raw = env.LOG_PERSIST_PROCESS?.trim();
|
|
274
|
+
return raw != null && raw !== "" ? raw : undefined;
|
|
275
|
+
}
|
|
276
|
+
function logPersistRegistryDirFromEnv(env = process.env) {
|
|
277
|
+
const configured = env.LOG_PERSIST_REGISTRY_DIR?.trim();
|
|
278
|
+
if (configured != null && configured !== "") {
|
|
279
|
+
return configured;
|
|
280
|
+
}
|
|
281
|
+
const runtime = env.RUNTIME_DATA_DIR?.trim();
|
|
282
|
+
return runtime != null && runtime !== "" ? runtime : undefined;
|
|
283
|
+
}
|
|
284
|
+
function logPersistConfigFromEnv(env = process.env) {
|
|
285
|
+
return resolveLogPersistConfig({
|
|
286
|
+
fast: {
|
|
287
|
+
maxBufferEntries: envInt("LOG_FAST_MAX_BUFFER_ENTRIES", DEFAULT_LOG_PERSIST_CONFIG.fast.maxBufferEntries, {
|
|
288
|
+
env,
|
|
289
|
+
min: 1,
|
|
290
|
+
}),
|
|
291
|
+
},
|
|
292
|
+
medium: {
|
|
293
|
+
debounceMs: envMs("LOG_MEDIUM_DEBOUNCE_MS", DEFAULT_LOG_PERSIST_CONFIG.medium.debounceMs, { env }),
|
|
294
|
+
maxEntries: envInt("LOG_MEDIUM_MAX_ENTRIES", DEFAULT_LOG_PERSIST_CONFIG.medium.maxEntries, {
|
|
295
|
+
env,
|
|
296
|
+
min: 1,
|
|
297
|
+
}),
|
|
298
|
+
maxBytes: envInt("LOG_MEDIUM_MAX_BYTES", DEFAULT_LOG_PERSIST_CONFIG.medium.maxBytes, { env, min: 1 }),
|
|
299
|
+
maxBufferEntries: envInt("LOG_MEDIUM_MAX_BUFFER_ENTRIES", DEFAULT_LOG_PERSIST_CONFIG.medium.maxBufferEntries, { env, min: 1 }),
|
|
300
|
+
},
|
|
301
|
+
slow: {
|
|
302
|
+
maxEntries: envInt("LOG_SLOW_MAX_ENTRIES", DEFAULT_LOG_PERSIST_CONFIG.slow.maxEntries, { env, min: 1 }),
|
|
303
|
+
maxBytes: envInt("LOG_SLOW_MAX_BYTES", DEFAULT_LOG_PERSIST_CONFIG.slow.maxBytes, { env, min: 1 }),
|
|
304
|
+
maxAgeMs: envMs("LOG_SLOW_MAX_AGE_MS", DEFAULT_LOG_PERSIST_CONFIG.slow.maxAgeMs, { env }),
|
|
305
|
+
maxBufferEntries: envInt("LOG_SLOW_MAX_BUFFER_ENTRIES", DEFAULT_LOG_PERSIST_CONFIG.slow.maxBufferEntries, {
|
|
306
|
+
env,
|
|
307
|
+
min: 1,
|
|
308
|
+
}),
|
|
309
|
+
},
|
|
310
|
+
postTimeoutMs: envMs("LOG_PERSIST_POST_TIMEOUT_MS", DEFAULT_LOG_PERSIST_CONFIG.postTimeoutMs, { env }),
|
|
311
|
+
shutdownDrainTimeoutMs: envMs("LOG_PERSIST_SHUTDOWN_DRAIN_MS", DEFAULT_LOG_PERSIST_CONFIG.shutdownDrainTimeoutMs, { env }),
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const BATCH_ID_VERSION = 1;
|
|
316
|
+
/** Stable subset of LogEntry for idempotency hashing (same batch → same key). */
|
|
317
|
+
function canonicalLogEntry(entry) {
|
|
318
|
+
return {
|
|
319
|
+
timestamp: entry.timestamp,
|
|
320
|
+
level: entry.level,
|
|
321
|
+
message: entry.message,
|
|
322
|
+
topic: entry.topic,
|
|
323
|
+
module: entry.module,
|
|
324
|
+
traceId: entry.traceId,
|
|
325
|
+
traceParentId: entry.traceParentId ?? null,
|
|
326
|
+
extra: entry.extra ?? null,
|
|
327
|
+
data: entry.data ?? null,
|
|
328
|
+
labels: entry.labels ?? null,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Deterministic List.value for a log batch — retries after timeout map to the same row key.
|
|
333
|
+
* SHA-256 over canonical JSON (service + process + lane + entries in order).
|
|
334
|
+
*/
|
|
335
|
+
function computeLogBatchIdempotencyKey(input) {
|
|
336
|
+
const payload = {
|
|
337
|
+
v: BATCH_ID_VERSION,
|
|
338
|
+
service: input.service,
|
|
339
|
+
process: input.process,
|
|
340
|
+
lane: input.lane,
|
|
341
|
+
entries: input.entries.map(canonicalLogEntry),
|
|
342
|
+
};
|
|
343
|
+
const hash = createHash("sha256").update(JSON.stringify(payload), "utf8").digest("hex");
|
|
344
|
+
return `${hash.slice(0, 32)}`;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** User-facing / lifecycle logs — may be persisted like normal app logs. */
|
|
348
|
+
const LOG_PERSIST_TOPIC = "log-persist";
|
|
349
|
+
/** Operational logs from the persist pipeline — never re-offered to avoid feedback loops. */
|
|
350
|
+
const LOG_PERSIST_INTERNAL_TOPIC = "log-persist-internal";
|
|
351
|
+
const logger = createLogger({ module: "log-persist" });
|
|
352
|
+
function isLogPersistInternalTopic(topic) {
|
|
353
|
+
return topic === LOG_PERSIST_INTERNAL_TOPIC;
|
|
354
|
+
}
|
|
355
|
+
function write(level, message, data) {
|
|
356
|
+
logger[level](message, {
|
|
357
|
+
topic: LOG_PERSIST_INTERNAL_TOPIC,
|
|
358
|
+
...(data != null ? { data } : {}),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
const persistLogger = {
|
|
362
|
+
debug: (message, data) => write("debug", message, data),
|
|
363
|
+
info: (message, data) => write("info", message, data),
|
|
364
|
+
warn: (message, data) => write("warn", message, data),
|
|
365
|
+
error: (message, data) => write("error", message, data),
|
|
366
|
+
};
|
|
367
|
+
function patchPersistLoggerScope(labels) {
|
|
368
|
+
logger.resetScope({ labels });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function estimateEntriesBytes(entries) {
|
|
372
|
+
if (entries.length === 0) {
|
|
373
|
+
return 0;
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
return Buffer.byteLength(JSON.stringify(entries), "utf8");
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
return entries.length * 512;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function buildLogListDraft(input) {
|
|
383
|
+
const { service, process, lane, idempotencyKey, entries } = input;
|
|
384
|
+
const first = entries[0];
|
|
385
|
+
const last = entries[entries.length - 1];
|
|
386
|
+
return {
|
|
387
|
+
name: `log-batch:${service}:${process}:${lane}`,
|
|
388
|
+
value: idempotencyKey,
|
|
389
|
+
category: CategoryList.LIST,
|
|
390
|
+
tagList: ["log", lane, service],
|
|
391
|
+
details: {
|
|
392
|
+
manifestVersion: 0,
|
|
393
|
+
loaderKey: LOG_PERSIST_LOADER_KEY,
|
|
394
|
+
meta: JSON.stringify({
|
|
395
|
+
service,
|
|
396
|
+
process,
|
|
397
|
+
pid: globalThis.process.pid,
|
|
398
|
+
lane,
|
|
399
|
+
count: entries.length,
|
|
400
|
+
from: first?.timestamp ?? null,
|
|
401
|
+
to: last?.timestamp ?? null,
|
|
402
|
+
flushedAt: Date.now(),
|
|
403
|
+
idempotencyKey,
|
|
404
|
+
}),
|
|
405
|
+
preview: first?.message ?? "",
|
|
406
|
+
items: entries,
|
|
407
|
+
},
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
async function withTimeout(promise, timeoutMs, label) {
|
|
411
|
+
let timer = null;
|
|
412
|
+
try {
|
|
413
|
+
return await Promise.race([
|
|
414
|
+
promise,
|
|
415
|
+
new Promise((_resolve, reject) => {
|
|
416
|
+
timer = setTimeout(() => {
|
|
417
|
+
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
418
|
+
}, timeoutMs);
|
|
419
|
+
}),
|
|
420
|
+
]);
|
|
421
|
+
}
|
|
422
|
+
finally {
|
|
423
|
+
if (timer != null) {
|
|
424
|
+
clearTimeout(timer);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
async function postLogBatch(draft, postTimeoutMs) {
|
|
429
|
+
try {
|
|
430
|
+
const result = await withTimeout(postLogListCreate({
|
|
431
|
+
name: draft.name,
|
|
432
|
+
value: draft.value,
|
|
433
|
+
category: draft.category,
|
|
434
|
+
details: draft.details,
|
|
435
|
+
}), postTimeoutMs, "postLogListCreate");
|
|
436
|
+
if (!result.success) {
|
|
437
|
+
const message = result.error?.message ?? "postLogListCreate failed";
|
|
438
|
+
persistLogger.error("post log batch failed", {
|
|
439
|
+
idempotencyKey: draft.value,
|
|
440
|
+
name: draft.name,
|
|
441
|
+
error: message,
|
|
442
|
+
});
|
|
443
|
+
throw new Error(message);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
if (isCreateTargetAlreadyExistsError(error)) {
|
|
448
|
+
persistLogger.error("post log batch idempotent duplicate — treating as success", {
|
|
449
|
+
idempotencyKey: draft.value,
|
|
450
|
+
name: draft.name,
|
|
451
|
+
});
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/** Take a prefix of entries that fits within maxBytes. */
|
|
458
|
+
function takeEntriesWithinBytes(entries, maxBytes) {
|
|
459
|
+
if (entries.length === 0) {
|
|
460
|
+
return [];
|
|
461
|
+
}
|
|
462
|
+
const taken = [];
|
|
463
|
+
for (const entry of entries) {
|
|
464
|
+
const candidate = [...taken, entry];
|
|
465
|
+
if (taken.length > 0 && estimateEntriesBytes(candidate) > maxBytes) {
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
taken.push(entry);
|
|
469
|
+
}
|
|
470
|
+
const result = taken.length > 0 ? taken : [entries[0]];
|
|
471
|
+
if (result.length < entries.length) {
|
|
472
|
+
persistLogger.warn("trimmed batch to maxBytes", {
|
|
473
|
+
requested: entries.length,
|
|
474
|
+
taken: result.length,
|
|
475
|
+
maxBytes,
|
|
476
|
+
takenBytes: estimateEntriesBytes(result),
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const DEFAULT_REGISTRY = {
|
|
483
|
+
updatedAt: new Date(0).toISOString(),
|
|
484
|
+
service: "",
|
|
485
|
+
processes: {},
|
|
486
|
+
};
|
|
487
|
+
const DEFAULT_SHARD = {
|
|
488
|
+
service: "",
|
|
489
|
+
updatedAt: new Date(0).toISOString(),
|
|
490
|
+
pid: 0,
|
|
491
|
+
enabledAt: new Date(0).toISOString(),
|
|
492
|
+
lastHeartbeatAt: new Date(0).toISOString(),
|
|
493
|
+
};
|
|
494
|
+
const legacyStores = new Map();
|
|
495
|
+
const shardStores = new Map();
|
|
496
|
+
function isLegacyRegistryFile(registryPath) {
|
|
497
|
+
return registryPath.endsWith(".json");
|
|
498
|
+
}
|
|
499
|
+
function legacyStoreFor(filePath) {
|
|
500
|
+
let store = legacyStores.get(filePath);
|
|
501
|
+
if (store == null) {
|
|
502
|
+
store = createJsonFileStateStore({
|
|
503
|
+
filePath,
|
|
504
|
+
defaultState: DEFAULT_REGISTRY,
|
|
505
|
+
nestedKeys: ["processes"],
|
|
506
|
+
updatedAtKey: "updatedAt",
|
|
507
|
+
});
|
|
508
|
+
legacyStores.set(filePath, store);
|
|
509
|
+
}
|
|
510
|
+
return store;
|
|
511
|
+
}
|
|
512
|
+
function shardStoreFor(shardFilePath) {
|
|
513
|
+
let store = shardStores.get(shardFilePath);
|
|
514
|
+
if (store == null) {
|
|
515
|
+
store = createJsonFileStateStore({
|
|
516
|
+
filePath: shardFilePath,
|
|
517
|
+
defaultState: DEFAULT_SHARD,
|
|
518
|
+
updatedAtKey: "updatedAt",
|
|
519
|
+
});
|
|
520
|
+
shardStores.set(shardFilePath, store);
|
|
521
|
+
}
|
|
522
|
+
return store;
|
|
523
|
+
}
|
|
524
|
+
function shardFilePath(registryDir, process) {
|
|
525
|
+
return join(registryDir, `${process}.json`);
|
|
526
|
+
}
|
|
527
|
+
/** Registry root directory — one shard file per process under this path. */
|
|
528
|
+
function defaultLogPersistRegistryPath(registryDir) {
|
|
529
|
+
return join(registryDir, "log-persist-registry");
|
|
530
|
+
}
|
|
531
|
+
async function readShardedLogPersistRegistry(registryDir) {
|
|
532
|
+
await mkdir(registryDir, { recursive: true });
|
|
533
|
+
let entries;
|
|
534
|
+
try {
|
|
535
|
+
entries = await readdir(registryDir);
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
return { ...DEFAULT_REGISTRY };
|
|
539
|
+
}
|
|
540
|
+
const processes = {};
|
|
541
|
+
let service = "";
|
|
542
|
+
let updatedAt = new Date(0).toISOString();
|
|
543
|
+
for (const name of entries) {
|
|
544
|
+
if (!name.endsWith(".json")) {
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
const processName = name.slice(0, -".json".length);
|
|
548
|
+
if (processName === "" || processName.startsWith("_")) {
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
try {
|
|
552
|
+
const raw = await readFile(join(registryDir, name), "utf8");
|
|
553
|
+
const shard = JSON.parse(raw);
|
|
554
|
+
if (shard.service != null && shard.service !== "") {
|
|
555
|
+
service = shard.service;
|
|
556
|
+
}
|
|
557
|
+
if (shard.updatedAt != null && shard.updatedAt > updatedAt) {
|
|
558
|
+
updatedAt = shard.updatedAt;
|
|
559
|
+
}
|
|
560
|
+
processes[processName] = {
|
|
561
|
+
pid: shard.pid ?? 0,
|
|
562
|
+
enabledAt: shard.enabledAt ?? new Date(0).toISOString(),
|
|
563
|
+
lastHeartbeatAt: shard.lastHeartbeatAt ?? new Date(0).toISOString(),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
persistLogger.warn("registry shard read skipped — corrupt or missing", {
|
|
568
|
+
registryDir,
|
|
569
|
+
shard: name,
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return { updatedAt, service, processes };
|
|
574
|
+
}
|
|
575
|
+
async function registerLogPersistProcess(input) {
|
|
576
|
+
const now = new Date().toISOString();
|
|
577
|
+
const record = {
|
|
578
|
+
pid: process.pid,
|
|
579
|
+
enabledAt: now,
|
|
580
|
+
lastHeartbeatAt: now,
|
|
581
|
+
};
|
|
582
|
+
if (isLegacyRegistryFile(input.registryFilePath)) {
|
|
583
|
+
await legacyStoreFor(input.registryFilePath).write({
|
|
584
|
+
service: input.service,
|
|
585
|
+
processes: {
|
|
586
|
+
[input.process]: record,
|
|
587
|
+
},
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
await shardStoreFor(shardFilePath(input.registryFilePath, input.process)).write({
|
|
592
|
+
service: input.service,
|
|
593
|
+
pid: record.pid,
|
|
594
|
+
enabledAt: record.enabledAt,
|
|
595
|
+
lastHeartbeatAt: record.lastHeartbeatAt,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
persistLogger.info("registry process registered", {
|
|
599
|
+
registryFilePath: input.registryFilePath,
|
|
600
|
+
service: input.service,
|
|
601
|
+
process: input.process,
|
|
602
|
+
pid: record.pid,
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
async function heartbeatLogPersistProcess(input) {
|
|
606
|
+
if (isLegacyRegistryFile(input.registryFilePath)) {
|
|
607
|
+
const current = await legacyStoreFor(input.registryFilePath).read();
|
|
608
|
+
const existing = current.processes[input.process];
|
|
609
|
+
if (existing == null) {
|
|
610
|
+
persistLogger.warn("registry heartbeat skipped — process not registered", {
|
|
611
|
+
registryFilePath: input.registryFilePath,
|
|
612
|
+
process: input.process,
|
|
613
|
+
});
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
await legacyStoreFor(input.registryFilePath).write({
|
|
617
|
+
processes: {
|
|
618
|
+
[input.process]: {
|
|
619
|
+
...existing,
|
|
620
|
+
lastHeartbeatAt: new Date().toISOString(),
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
});
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const store = shardStoreFor(shardFilePath(input.registryFilePath, input.process));
|
|
627
|
+
const current = await store.read();
|
|
628
|
+
if (current.pid === 0) {
|
|
629
|
+
persistLogger.warn("registry heartbeat skipped — process not registered", {
|
|
630
|
+
registryFilePath: input.registryFilePath,
|
|
631
|
+
process: input.process,
|
|
632
|
+
});
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
await store.write({
|
|
636
|
+
lastHeartbeatAt: new Date().toISOString(),
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
async function unregisterLogPersistProcess(input) {
|
|
640
|
+
if (isLegacyRegistryFile(input.registryFilePath)) {
|
|
641
|
+
const current = await legacyStoreFor(input.registryFilePath).read();
|
|
642
|
+
const existing = current.processes[input.process];
|
|
643
|
+
if (existing == null) {
|
|
644
|
+
persistLogger.warn("registry unregister skipped — process not found", {
|
|
645
|
+
registryFilePath: input.registryFilePath,
|
|
646
|
+
process: input.process,
|
|
647
|
+
});
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
await legacyStoreFor(input.registryFilePath).write({
|
|
651
|
+
processes: {
|
|
652
|
+
[input.process]: {
|
|
653
|
+
...existing,
|
|
654
|
+
pid: 0,
|
|
655
|
+
lastHeartbeatAt: new Date(0).toISOString(),
|
|
656
|
+
},
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
persistLogger.info("registry process unregistered", {
|
|
660
|
+
registryFilePath: input.registryFilePath,
|
|
661
|
+
process: input.process,
|
|
662
|
+
});
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const store = shardStoreFor(shardFilePath(input.registryFilePath, input.process));
|
|
666
|
+
const current = await store.read();
|
|
667
|
+
if (current.pid === 0) {
|
|
668
|
+
persistLogger.warn("registry unregister skipped — process not found", {
|
|
669
|
+
registryFilePath: input.registryFilePath,
|
|
670
|
+
process: input.process,
|
|
671
|
+
});
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
await store.write({
|
|
675
|
+
pid: 0,
|
|
676
|
+
lastHeartbeatAt: new Date(0).toISOString(),
|
|
677
|
+
});
|
|
678
|
+
persistLogger.info("registry process unregistered", {
|
|
679
|
+
registryFilePath: input.registryFilePath,
|
|
680
|
+
process: input.process,
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
async function readLogPersistRegistry(registryFilePath) {
|
|
684
|
+
if (isLegacyRegistryFile(registryFilePath)) {
|
|
685
|
+
return legacyStoreFor(registryFilePath).read();
|
|
686
|
+
}
|
|
687
|
+
return readShardedLogPersistRegistry(registryFilePath);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
var logPersistRegistry = /*#__PURE__*/Object.freeze({
|
|
691
|
+
__proto__: null,
|
|
692
|
+
defaultLogPersistRegistryPath: defaultLogPersistRegistryPath,
|
|
693
|
+
heartbeatLogPersistProcess: heartbeatLogPersistProcess,
|
|
694
|
+
readLogPersistRegistry: readLogPersistRegistry,
|
|
695
|
+
registerLogPersistProcess: registerLogPersistProcess,
|
|
696
|
+
unregisterLogPersistProcess: unregisterLogPersistProcess
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
const FAST_RETRY_INTERVAL_MS = 5_000;
|
|
700
|
+
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
701
|
+
function routeLane(level) {
|
|
702
|
+
if (level === LogLevel.CRITICAL) {
|
|
703
|
+
return "fast";
|
|
704
|
+
}
|
|
705
|
+
if (level === LogLevel.ERROR || level === LogLevel.SUCCESS) {
|
|
706
|
+
return "medium";
|
|
707
|
+
}
|
|
708
|
+
return "slow";
|
|
709
|
+
}
|
|
710
|
+
class LogPersist {
|
|
711
|
+
static instance = null;
|
|
712
|
+
enabled = false;
|
|
713
|
+
draining = false;
|
|
714
|
+
service = null;
|
|
715
|
+
processName = null;
|
|
716
|
+
registryFilePath = null;
|
|
717
|
+
config = DEFAULT_LOG_PERSIST_CONFIG;
|
|
718
|
+
fastQueue = [];
|
|
719
|
+
fastRetryQueue = [];
|
|
720
|
+
mediumQueue = [];
|
|
721
|
+
slowQueue = [];
|
|
722
|
+
mediumDebounceTimer = null;
|
|
723
|
+
slowQueueStartedAt = null;
|
|
724
|
+
fastRetryTimer = null;
|
|
725
|
+
heartbeatTimer = null;
|
|
726
|
+
lastHeartbeatAt = 0;
|
|
727
|
+
laneFlushing = {
|
|
728
|
+
fast: false,
|
|
729
|
+
medium: false,
|
|
730
|
+
slow: false,
|
|
731
|
+
};
|
|
732
|
+
consecutiveFailures = {
|
|
733
|
+
fast: 0,
|
|
734
|
+
medium: 0,
|
|
735
|
+
slow: 0,
|
|
736
|
+
};
|
|
737
|
+
lastFlushAt = {
|
|
738
|
+
fast: null,
|
|
739
|
+
medium: null,
|
|
740
|
+
slow: null,
|
|
741
|
+
};
|
|
742
|
+
lastError = null;
|
|
743
|
+
lastErrorAt = null;
|
|
744
|
+
shutdownHookInstalled = false;
|
|
745
|
+
shutdownPromise = null;
|
|
746
|
+
static getInstance() {
|
|
747
|
+
LogPersist.instance ??= new LogPersist();
|
|
748
|
+
return LogPersist.instance;
|
|
749
|
+
}
|
|
750
|
+
async enable(options) {
|
|
751
|
+
if (this.enabled) {
|
|
752
|
+
persistLogger.warn("log persist core enable skipped — already enabled", {
|
|
753
|
+
service: this.service,
|
|
754
|
+
process: this.processName,
|
|
755
|
+
});
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
this.service = options.service;
|
|
759
|
+
this.processName = options.process;
|
|
760
|
+
this.registryFilePath = options.registryFilePath ?? null;
|
|
761
|
+
this.config = resolveLogPersistConfig(options.config);
|
|
762
|
+
this.draining = false;
|
|
763
|
+
this.shutdownPromise = null;
|
|
764
|
+
this.consecutiveFailures = { fast: 0, medium: 0, slow: 0 };
|
|
765
|
+
this.lastError = null;
|
|
766
|
+
this.lastErrorAt = null;
|
|
767
|
+
this.enabled = true;
|
|
768
|
+
patchPersistLoggerScope({
|
|
769
|
+
service: options.service,
|
|
770
|
+
process: options.process,
|
|
771
|
+
});
|
|
772
|
+
persistLogger.info("log persist core enabled", {
|
|
773
|
+
service: options.service,
|
|
774
|
+
process: options.process,
|
|
775
|
+
registryFilePath: this.registryFilePath,
|
|
776
|
+
config: this.config,
|
|
777
|
+
pid: process.pid,
|
|
778
|
+
});
|
|
779
|
+
registerLogPersistOffer((entry) => {
|
|
780
|
+
this.offer(entry);
|
|
781
|
+
});
|
|
782
|
+
if (this.registryFilePath != null) {
|
|
783
|
+
const { registerLogPersistProcess } = await Promise.resolve().then(function () { return logPersistRegistry; });
|
|
784
|
+
await registerLogPersistProcess({
|
|
785
|
+
registryFilePath: this.registryFilePath,
|
|
786
|
+
service: options.service,
|
|
787
|
+
process: options.process,
|
|
788
|
+
});
|
|
789
|
+
this.startHeartbeat();
|
|
790
|
+
}
|
|
791
|
+
this.installShutdownHook();
|
|
792
|
+
}
|
|
793
|
+
async disable() {
|
|
794
|
+
if (!this.enabled && !this.draining) {
|
|
795
|
+
persistLogger.debug("disable skipped — not enabled");
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
persistLogger.info("log persist core disabling", this.queueSnapshot());
|
|
799
|
+
this.cancelMediumDebounce();
|
|
800
|
+
this.stopHeartbeat();
|
|
801
|
+
await this.shutdown();
|
|
802
|
+
persistLogger.info("log persist core disabled");
|
|
803
|
+
}
|
|
804
|
+
offer(entry) {
|
|
805
|
+
if (isLogPersistInternalTopic(entry.topic)) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (!this.enabled || this.draining || this.service == null || this.processName == null) {
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
void this.touchRegistryHeartbeat();
|
|
812
|
+
const lane = routeLane(entry.level);
|
|
813
|
+
if (lane === "fast") {
|
|
814
|
+
this.fastQueue.push(entry);
|
|
815
|
+
this.evictFastOverflow();
|
|
816
|
+
void this.flushFast();
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
if (lane === "medium") {
|
|
820
|
+
this.mediumQueue.push(entry);
|
|
821
|
+
this.evictMediumOverflow();
|
|
822
|
+
this.scheduleMediumFlush();
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
this.slowQueue.push(entry);
|
|
826
|
+
if (this.slowQueueStartedAt == null) {
|
|
827
|
+
this.slowQueueStartedAt = Date.now();
|
|
828
|
+
}
|
|
829
|
+
this.evictSlowOverflow();
|
|
830
|
+
this.scheduleSlowFlush();
|
|
831
|
+
}
|
|
832
|
+
getStats() {
|
|
833
|
+
return {
|
|
834
|
+
enabled: this.enabled,
|
|
835
|
+
draining: this.draining,
|
|
836
|
+
service: this.service,
|
|
837
|
+
process: this.processName,
|
|
838
|
+
queues: {
|
|
839
|
+
fast: this.fastQueue.length,
|
|
840
|
+
fastRetry: this.fastRetryQueue.length,
|
|
841
|
+
medium: this.mediumQueue.length,
|
|
842
|
+
slow: this.slowQueue.length,
|
|
843
|
+
},
|
|
844
|
+
queueBytes: {
|
|
845
|
+
fast: estimateEntriesBytes(this.fastQueue),
|
|
846
|
+
fastRetry: estimateEntriesBytes(this.fastRetryQueue),
|
|
847
|
+
medium: estimateEntriesBytes(this.mediumQueue),
|
|
848
|
+
slow: estimateEntriesBytes(this.slowQueue),
|
|
849
|
+
},
|
|
850
|
+
mediumDebouncePending: this.mediumDebounceTimer != null,
|
|
851
|
+
laneFlushing: { ...this.laneFlushing },
|
|
852
|
+
consecutiveFailures: { ...this.consecutiveFailures },
|
|
853
|
+
config: this.config,
|
|
854
|
+
lastFlushAt: { ...this.lastFlushAt },
|
|
855
|
+
lastError: this.lastError,
|
|
856
|
+
lastErrorAt: this.lastErrorAt,
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
async shutdown() {
|
|
860
|
+
if (this.shutdownPromise != null) {
|
|
861
|
+
return this.shutdownPromise;
|
|
862
|
+
}
|
|
863
|
+
this.shutdownPromise = this.shutdownInner();
|
|
864
|
+
return this.shutdownPromise;
|
|
865
|
+
}
|
|
866
|
+
async shutdownInner() {
|
|
867
|
+
if (this.draining) {
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
this.draining = true;
|
|
871
|
+
this.enabled = false;
|
|
872
|
+
registerLogPersistOffer(null);
|
|
873
|
+
persistLogger.info("shutdown flush started", this.queueSnapshot());
|
|
874
|
+
this.cancelMediumDebounce();
|
|
875
|
+
await this.drainQueues(this.config.shutdownDrainTimeoutMs);
|
|
876
|
+
if (this.registryFilePath != null && this.processName != null) {
|
|
877
|
+
const { unregisterLogPersistProcess } = await Promise.resolve().then(function () { return logPersistRegistry; });
|
|
878
|
+
await unregisterLogPersistProcess({
|
|
879
|
+
registryFilePath: this.registryFilePath,
|
|
880
|
+
process: this.processName,
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
if (this.hasQueued()) {
|
|
884
|
+
persistLogger.warn("shutdown drain incomplete — queued logs dropped", {
|
|
885
|
+
...this.queueSnapshot(),
|
|
886
|
+
shutdownDrainTimeoutMs: this.config.shutdownDrainTimeoutMs,
|
|
887
|
+
});
|
|
888
|
+
this.fastQueue = [];
|
|
889
|
+
this.fastRetryQueue = [];
|
|
890
|
+
this.mediumQueue = [];
|
|
891
|
+
this.slowQueue = [];
|
|
892
|
+
this.slowQueueStartedAt = null;
|
|
893
|
+
}
|
|
894
|
+
persistLogger.info("shutdown flush completed", this.queueSnapshot());
|
|
895
|
+
}
|
|
896
|
+
hasQueued() {
|
|
897
|
+
return (this.fastQueue.length > 0 ||
|
|
898
|
+
this.fastRetryQueue.length > 0 ||
|
|
899
|
+
this.mediumQueue.length > 0 ||
|
|
900
|
+
this.slowQueue.length > 0);
|
|
901
|
+
}
|
|
902
|
+
async drainQueues(timeoutMs) {
|
|
903
|
+
const deadline = Date.now() + timeoutMs;
|
|
904
|
+
while (this.hasQueued() && Date.now() < deadline) {
|
|
905
|
+
await this.flushFast();
|
|
906
|
+
await this.flushMedium();
|
|
907
|
+
await this.flushSlow();
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
recordLaneError(lane, error) {
|
|
911
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
912
|
+
this.lastError = message;
|
|
913
|
+
this.lastErrorAt = new Date().toISOString();
|
|
914
|
+
this.consecutiveFailures[lane] += 1;
|
|
915
|
+
}
|
|
916
|
+
recordLaneSuccess(lane) {
|
|
917
|
+
this.lastError = null;
|
|
918
|
+
this.consecutiveFailures[lane] = 0;
|
|
919
|
+
}
|
|
920
|
+
queueSnapshot() {
|
|
921
|
+
return {
|
|
922
|
+
queues: {
|
|
923
|
+
fast: this.fastQueue.length,
|
|
924
|
+
fastRetry: this.fastRetryQueue.length,
|
|
925
|
+
medium: this.mediumQueue.length,
|
|
926
|
+
slow: this.slowQueue.length,
|
|
927
|
+
},
|
|
928
|
+
queueBytes: {
|
|
929
|
+
fast: estimateEntriesBytes(this.fastQueue),
|
|
930
|
+
fastRetry: estimateEntriesBytes(this.fastRetryQueue),
|
|
931
|
+
medium: estimateEntriesBytes(this.mediumQueue),
|
|
932
|
+
slow: estimateEntriesBytes(this.slowQueue),
|
|
933
|
+
},
|
|
934
|
+
laneFlushing: { ...this.laneFlushing },
|
|
935
|
+
consecutiveFailures: { ...this.consecutiveFailures },
|
|
936
|
+
lastError: this.lastError,
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
installShutdownHook() {
|
|
940
|
+
if (this.shutdownHookInstalled) {
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
this.shutdownHookInstalled = true;
|
|
944
|
+
const handler = (signal) => {
|
|
945
|
+
persistLogger.info("shutdown signal received", { signal });
|
|
946
|
+
void this.shutdown();
|
|
947
|
+
};
|
|
948
|
+
process.once("SIGTERM", handler);
|
|
949
|
+
process.once("SIGINT", handler);
|
|
950
|
+
}
|
|
951
|
+
startHeartbeat() {
|
|
952
|
+
this.stopHeartbeat();
|
|
953
|
+
this.heartbeatTimer = setInterval(() => {
|
|
954
|
+
void this.touchRegistryHeartbeat(true);
|
|
955
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
956
|
+
this.heartbeatTimer.unref?.();
|
|
957
|
+
}
|
|
958
|
+
stopHeartbeat() {
|
|
959
|
+
if (this.heartbeatTimer != null) {
|
|
960
|
+
clearInterval(this.heartbeatTimer);
|
|
961
|
+
this.heartbeatTimer = null;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
async touchRegistryHeartbeat(force = false) {
|
|
965
|
+
if (this.registryFilePath == null || this.processName == null) {
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
const now = Date.now();
|
|
969
|
+
if (!force && now - this.lastHeartbeatAt < HEARTBEAT_INTERVAL_MS) {
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
this.lastHeartbeatAt = now;
|
|
973
|
+
await heartbeatLogPersistProcess({
|
|
974
|
+
registryFilePath: this.registryFilePath,
|
|
975
|
+
process: this.processName,
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
scheduleMediumFlush() {
|
|
979
|
+
const { debounceMs, maxEntries, maxBytes } = this.config.medium;
|
|
980
|
+
const queueBytes = estimateEntriesBytes(this.mediumQueue);
|
|
981
|
+
if (this.mediumQueue.length >= maxEntries || queueBytes >= maxBytes) {
|
|
982
|
+
persistLogger.warn("medium flush scheduled — threshold", {
|
|
983
|
+
reason: this.mediumQueue.length >= maxEntries ? "maxEntries" : "maxBytes",
|
|
984
|
+
queueLength: this.mediumQueue.length,
|
|
985
|
+
maxEntries,
|
|
986
|
+
queueBytes,
|
|
987
|
+
maxBytes,
|
|
988
|
+
});
|
|
989
|
+
this.cancelMediumDebounce();
|
|
990
|
+
void this.flushMedium();
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
if (this.mediumDebounceTimer == null && this.mediumQueue.length > 0) {
|
|
994
|
+
persistLogger.debug("medium flush debounce scheduled", {
|
|
995
|
+
debounceMs,
|
|
996
|
+
queueLength: this.mediumQueue.length,
|
|
997
|
+
queueBytes,
|
|
998
|
+
});
|
|
999
|
+
this.mediumDebounceTimer = setTimeout(() => {
|
|
1000
|
+
this.mediumDebounceTimer = null;
|
|
1001
|
+
persistLogger.debug("medium flush debounce fired");
|
|
1002
|
+
void this.flushMedium();
|
|
1003
|
+
}, debounceMs);
|
|
1004
|
+
this.mediumDebounceTimer.unref?.();
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
cancelMediumDebounce() {
|
|
1008
|
+
if (this.mediumDebounceTimer != null) {
|
|
1009
|
+
clearTimeout(this.mediumDebounceTimer);
|
|
1010
|
+
this.mediumDebounceTimer = null;
|
|
1011
|
+
persistLogger.debug("medium flush debounce cancelled");
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
scheduleSlowFlush() {
|
|
1015
|
+
const { maxEntries, maxBytes, maxAgeMs } = this.config.slow;
|
|
1016
|
+
const queueBytes = estimateEntriesBytes(this.slowQueue);
|
|
1017
|
+
const ageMs = this.slowQueueStartedAt != null ? Date.now() - this.slowQueueStartedAt : 0;
|
|
1018
|
+
if (this.slowQueue.length >= maxEntries || queueBytes >= maxBytes || ageMs >= maxAgeMs) {
|
|
1019
|
+
persistLogger.debug("slow flush scheduled — threshold", {
|
|
1020
|
+
reason: this.slowQueue.length >= maxEntries
|
|
1021
|
+
? "maxEntries"
|
|
1022
|
+
: queueBytes >= maxBytes
|
|
1023
|
+
? "maxBytes"
|
|
1024
|
+
: "maxAgeMs",
|
|
1025
|
+
queueLength: this.slowQueue.length,
|
|
1026
|
+
maxEntries,
|
|
1027
|
+
queueBytes,
|
|
1028
|
+
maxBytes,
|
|
1029
|
+
ageMs,
|
|
1030
|
+
maxAgeMs,
|
|
1031
|
+
});
|
|
1032
|
+
void this.flushSlow();
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
evictFastOverflow() {
|
|
1036
|
+
const { maxBufferEntries } = this.config.fast;
|
|
1037
|
+
let evicted = 0;
|
|
1038
|
+
while (this.fastQueue.length + this.fastRetryQueue.length > maxBufferEntries) {
|
|
1039
|
+
const removed = this.fastQueue.length > 0 ? this.fastQueue.shift() : this.fastRetryQueue.shift();
|
|
1040
|
+
evicted += 1;
|
|
1041
|
+
persistLogger.warn("fast queue overflow — evicted entry", {
|
|
1042
|
+
evictedLevel: removed?.level,
|
|
1043
|
+
evictedTopic: removed?.topic,
|
|
1044
|
+
fromRetryQueue: this.fastQueue.length === 0,
|
|
1045
|
+
maxBufferEntries,
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
if (evicted > 0) {
|
|
1049
|
+
persistLogger.warn("fast queue overflow summary", { evicted, maxBufferEntries });
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
evictMediumOverflow() {
|
|
1053
|
+
const { maxBufferEntries } = this.config.medium;
|
|
1054
|
+
let evicted = 0;
|
|
1055
|
+
while (this.mediumQueue.length > maxBufferEntries) {
|
|
1056
|
+
const successIndex = this.mediumQueue.findIndex((entry) => entry.level === LogLevel.SUCCESS);
|
|
1057
|
+
const removed = successIndex >= 0 ? this.mediumQueue.splice(successIndex, 1)[0] : this.mediumQueue.shift();
|
|
1058
|
+
evicted += 1;
|
|
1059
|
+
persistLogger.warn("medium queue overflow — evicted entry", {
|
|
1060
|
+
evictedLevel: removed?.level,
|
|
1061
|
+
evictedTopic: removed?.topic,
|
|
1062
|
+
queueLength: this.mediumQueue.length,
|
|
1063
|
+
maxBufferEntries,
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
if (evicted > 0) {
|
|
1067
|
+
persistLogger.warn("medium queue overflow summary", {
|
|
1068
|
+
evicted,
|
|
1069
|
+
queueLength: this.mediumQueue.length,
|
|
1070
|
+
maxBufferEntries,
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
evictSlowOverflow() {
|
|
1075
|
+
const { maxBufferEntries } = this.config.slow;
|
|
1076
|
+
let evicted = 0;
|
|
1077
|
+
while (this.slowQueue.length > maxBufferEntries) {
|
|
1078
|
+
const debugIndex = this.slowQueue.findIndex((entry) => entry.level === LogLevel.DEBUG);
|
|
1079
|
+
const removed = debugIndex >= 0 ? this.slowQueue.splice(debugIndex, 1)[0] : this.slowQueue.shift();
|
|
1080
|
+
evicted += 1;
|
|
1081
|
+
persistLogger.warn("slow queue overflow — evicted entry", {
|
|
1082
|
+
evictedLevel: removed?.level,
|
|
1083
|
+
evictedTopic: removed?.topic,
|
|
1084
|
+
queueLength: this.slowQueue.length,
|
|
1085
|
+
maxBufferEntries,
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
if (evicted > 0) {
|
|
1089
|
+
persistLogger.warn("slow queue overflow summary", {
|
|
1090
|
+
evicted,
|
|
1091
|
+
queueLength: this.slowQueue.length,
|
|
1092
|
+
maxBufferEntries,
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
if (this.slowQueue.length === 0) {
|
|
1096
|
+
this.slowQueueStartedAt = null;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
async flushFast() {
|
|
1100
|
+
if (this.laneFlushing.fast) {
|
|
1101
|
+
persistLogger.debug("fast flush skipped — already flushing");
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
if (this.service == null || this.processName == null) {
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
this.laneFlushing.fast = true;
|
|
1108
|
+
try {
|
|
1109
|
+
while (this.fastQueue.length > 0 || this.fastRetryQueue.length > 0) {
|
|
1110
|
+
const fromRetry = this.fastQueue.length === 0;
|
|
1111
|
+
const entry = this.fastQueue.shift() ?? this.fastRetryQueue.shift();
|
|
1112
|
+
if (entry == null) {
|
|
1113
|
+
break;
|
|
1114
|
+
}
|
|
1115
|
+
const idempotencyKey = computeLogBatchIdempotencyKey({
|
|
1116
|
+
service: this.service,
|
|
1117
|
+
process: this.processName,
|
|
1118
|
+
lane: "fast",
|
|
1119
|
+
entries: [entry],
|
|
1120
|
+
});
|
|
1121
|
+
try {
|
|
1122
|
+
const draft = buildLogListDraft({
|
|
1123
|
+
service: this.service,
|
|
1124
|
+
process: this.processName,
|
|
1125
|
+
lane: "fast",
|
|
1126
|
+
idempotencyKey,
|
|
1127
|
+
entries: [entry],
|
|
1128
|
+
});
|
|
1129
|
+
persistLogger.debug("fast flush posting batch", {
|
|
1130
|
+
idempotencyKey,
|
|
1131
|
+
fromRetry,
|
|
1132
|
+
level: entry.level,
|
|
1133
|
+
topic: entry.topic,
|
|
1134
|
+
});
|
|
1135
|
+
await postLogBatch(draft, this.config.postTimeoutMs);
|
|
1136
|
+
this.lastFlushAt.fast = new Date().toISOString();
|
|
1137
|
+
this.recordLaneSuccess("fast");
|
|
1138
|
+
persistLogger.info("fast lane flushed", {
|
|
1139
|
+
idempotencyKey,
|
|
1140
|
+
level: entry.level,
|
|
1141
|
+
topic: entry.topic,
|
|
1142
|
+
fastRetryRemaining: this.fastRetryQueue.length,
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
catch (error) {
|
|
1146
|
+
this.recordLaneError("fast", error);
|
|
1147
|
+
this.fastRetryQueue.push(entry);
|
|
1148
|
+
persistLogger.error("fast flush failed — queued for retry", {
|
|
1149
|
+
idempotencyKey,
|
|
1150
|
+
error: this.lastError,
|
|
1151
|
+
retryInMs: FAST_RETRY_INTERVAL_MS,
|
|
1152
|
+
fastRetryQueue: this.fastRetryQueue.length,
|
|
1153
|
+
});
|
|
1154
|
+
this.scheduleFastRetry();
|
|
1155
|
+
break;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
finally {
|
|
1160
|
+
this.laneFlushing.fast = false;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
scheduleFastRetry() {
|
|
1164
|
+
if (this.fastRetryTimer != null) {
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
persistLogger.warn("fast flush retry scheduled", { retryInMs: FAST_RETRY_INTERVAL_MS });
|
|
1168
|
+
this.fastRetryTimer = setTimeout(() => {
|
|
1169
|
+
this.fastRetryTimer = null;
|
|
1170
|
+
persistLogger.debug("fast flush retry timer fired");
|
|
1171
|
+
void this.flushFast();
|
|
1172
|
+
}, FAST_RETRY_INTERVAL_MS);
|
|
1173
|
+
this.fastRetryTimer.unref?.();
|
|
1174
|
+
}
|
|
1175
|
+
async flushMedium() {
|
|
1176
|
+
if (this.laneFlushing.medium) {
|
|
1177
|
+
persistLogger.debug("medium flush skipped — already flushing");
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
if (this.mediumQueue.length === 0) {
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
if (this.service == null || this.processName == null) {
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
this.laneFlushing.medium = true;
|
|
1187
|
+
try {
|
|
1188
|
+
while (this.mediumQueue.length > 0) {
|
|
1189
|
+
const batch = takeEntriesWithinBytes(this.mediumQueue, this.config.medium.maxBytes);
|
|
1190
|
+
if (batch.length === 0) {
|
|
1191
|
+
break;
|
|
1192
|
+
}
|
|
1193
|
+
const idempotencyKey = computeLogBatchIdempotencyKey({
|
|
1194
|
+
service: this.service,
|
|
1195
|
+
process: this.processName,
|
|
1196
|
+
lane: "medium",
|
|
1197
|
+
entries: batch,
|
|
1198
|
+
});
|
|
1199
|
+
const batchBytes = estimateEntriesBytes(batch);
|
|
1200
|
+
try {
|
|
1201
|
+
persistLogger.debug("medium flush posting batch", {
|
|
1202
|
+
idempotencyKey,
|
|
1203
|
+
batchSize: batch.length,
|
|
1204
|
+
batchBytes,
|
|
1205
|
+
queueRemaining: this.mediumQueue.length,
|
|
1206
|
+
});
|
|
1207
|
+
const draft = buildLogListDraft({
|
|
1208
|
+
service: this.service,
|
|
1209
|
+
process: this.processName,
|
|
1210
|
+
lane: "medium",
|
|
1211
|
+
idempotencyKey,
|
|
1212
|
+
entries: batch,
|
|
1213
|
+
});
|
|
1214
|
+
await postLogBatch(draft, this.config.postTimeoutMs);
|
|
1215
|
+
this.mediumQueue.splice(0, batch.length);
|
|
1216
|
+
this.lastFlushAt.medium = new Date().toISOString();
|
|
1217
|
+
this.recordLaneSuccess("medium");
|
|
1218
|
+
persistLogger.info("medium lane flushed", {
|
|
1219
|
+
idempotencyKey,
|
|
1220
|
+
batchSize: batch.length,
|
|
1221
|
+
batchBytes,
|
|
1222
|
+
queueRemaining: this.mediumQueue.length,
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
catch (error) {
|
|
1226
|
+
this.recordLaneError("medium", error);
|
|
1227
|
+
persistLogger.error("medium flush failed — entries retained", {
|
|
1228
|
+
idempotencyKey,
|
|
1229
|
+
batchSize: batch.length,
|
|
1230
|
+
batchBytes,
|
|
1231
|
+
error: this.lastError,
|
|
1232
|
+
queueRemaining: this.mediumQueue.length,
|
|
1233
|
+
});
|
|
1234
|
+
this.scheduleMediumFlush();
|
|
1235
|
+
break;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
finally {
|
|
1240
|
+
this.laneFlushing.medium = false;
|
|
1241
|
+
}
|
|
1242
|
+
if (this.mediumQueue.length > 0 && !this.laneFlushing.medium) {
|
|
1243
|
+
this.scheduleMediumFlush();
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
async flushSlow() {
|
|
1247
|
+
if (this.laneFlushing.slow) {
|
|
1248
|
+
persistLogger.debug("slow flush skipped — already flushing");
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
if (this.slowQueue.length === 0) {
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
if (this.service == null || this.processName == null) {
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
this.laneFlushing.slow = true;
|
|
1258
|
+
try {
|
|
1259
|
+
while (this.slowQueue.length > 0) {
|
|
1260
|
+
const batch = takeEntriesWithinBytes(this.slowQueue, this.config.slow.maxBytes);
|
|
1261
|
+
if (batch.length === 0) {
|
|
1262
|
+
break;
|
|
1263
|
+
}
|
|
1264
|
+
const idempotencyKey = computeLogBatchIdempotencyKey({
|
|
1265
|
+
service: this.service,
|
|
1266
|
+
process: this.processName,
|
|
1267
|
+
lane: "slow",
|
|
1268
|
+
entries: batch,
|
|
1269
|
+
});
|
|
1270
|
+
const batchBytes = estimateEntriesBytes(batch);
|
|
1271
|
+
try {
|
|
1272
|
+
persistLogger.debug("slow flush posting batch", {
|
|
1273
|
+
idempotencyKey,
|
|
1274
|
+
batchSize: batch.length,
|
|
1275
|
+
batchBytes,
|
|
1276
|
+
queueRemaining: this.slowQueue.length,
|
|
1277
|
+
});
|
|
1278
|
+
const draft = buildLogListDraft({
|
|
1279
|
+
service: this.service,
|
|
1280
|
+
process: this.processName,
|
|
1281
|
+
lane: "slow",
|
|
1282
|
+
idempotencyKey,
|
|
1283
|
+
entries: batch,
|
|
1284
|
+
});
|
|
1285
|
+
await postLogBatch(draft, this.config.postTimeoutMs);
|
|
1286
|
+
this.slowQueue.splice(0, batch.length);
|
|
1287
|
+
this.lastFlushAt.slow = new Date().toISOString();
|
|
1288
|
+
this.recordLaneSuccess("slow");
|
|
1289
|
+
persistLogger.info("slow lane flushed", {
|
|
1290
|
+
idempotencyKey,
|
|
1291
|
+
batchSize: batch.length,
|
|
1292
|
+
batchBytes,
|
|
1293
|
+
queueRemaining: this.slowQueue.length,
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
catch (error) {
|
|
1297
|
+
this.recordLaneError("slow", error);
|
|
1298
|
+
persistLogger.error("slow flush failed — entries retained", {
|
|
1299
|
+
idempotencyKey,
|
|
1300
|
+
batchSize: batch.length,
|
|
1301
|
+
batchBytes,
|
|
1302
|
+
error: this.lastError,
|
|
1303
|
+
queueRemaining: this.slowQueue.length,
|
|
1304
|
+
});
|
|
1305
|
+
this.scheduleSlowFlush();
|
|
1306
|
+
break;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
if (this.slowQueue.length === 0) {
|
|
1310
|
+
this.slowQueueStartedAt = null;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
finally {
|
|
1314
|
+
this.laneFlushing.slow = false;
|
|
1315
|
+
}
|
|
1316
|
+
if (this.slowQueue.length > 0 && !this.laneFlushing.slow) {
|
|
1317
|
+
this.scheduleSlowFlush();
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
function resolveRegistryFilePath(explicit) {
|
|
1323
|
+
const dir = logPersistRegistryDirFromEnv();
|
|
1324
|
+
if (dir == null) {
|
|
1325
|
+
throw new Error("[LogPersist] registry path required: set registryFilePath option or LOG_PERSIST_REGISTRY_DIR / RUNTIME_DATA_DIR");
|
|
1326
|
+
}
|
|
1327
|
+
return defaultLogPersistRegistryPath(resolve(dir));
|
|
1328
|
+
}
|
|
1329
|
+
async function enableLogPersist(options) {
|
|
1330
|
+
const registryFilePath = options.registryFilePath != null ? resolve(options.registryFilePath) : undefined;
|
|
1331
|
+
const resolvedRegistry = registryFilePath ?? resolveRegistryFilePath();
|
|
1332
|
+
const envConfig = logPersistConfigFromEnv();
|
|
1333
|
+
await LogPersist.getInstance().enable({
|
|
1334
|
+
...options,
|
|
1335
|
+
registryFilePath: resolvedRegistry,
|
|
1336
|
+
config: resolveLogPersistConfig({
|
|
1337
|
+
fast: { ...envConfig.fast, ...options.config?.fast },
|
|
1338
|
+
medium: { ...envConfig.medium, ...options.config?.medium },
|
|
1339
|
+
slow: { ...envConfig.slow, ...options.config?.slow },
|
|
1340
|
+
postTimeoutMs: options.config?.postTimeoutMs ?? envConfig.postTimeoutMs,
|
|
1341
|
+
shutdownDrainTimeoutMs: options.config?.shutdownDrainTimeoutMs ?? envConfig.shutdownDrainTimeoutMs,
|
|
1342
|
+
}),
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
async function disableLogPersist() {
|
|
1346
|
+
await LogPersist.getInstance().disable();
|
|
1347
|
+
}
|
|
1348
|
+
function getLogPersistStats() {
|
|
1349
|
+
return LogPersist.getInstance().getStats();
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Enable log persistence when LOG_PERSIST_ENABLED=true.
|
|
1353
|
+
* `process` / `service` can be passed explicitly or via LOG_PERSIST_PROCESS / LOG_PERSIST_SERVICE.
|
|
1354
|
+
*/
|
|
1355
|
+
async function ensureLogPersistFromEnv(options = {}) {
|
|
1356
|
+
if (!logPersistEnabledFromEnv()) {
|
|
1357
|
+
registerLogPersistOffer(null);
|
|
1358
|
+
return false;
|
|
1359
|
+
}
|
|
1360
|
+
const service = options.service ?? logPersistServiceFromEnv();
|
|
1361
|
+
const processName = options.process ?? logPersistProcessFromEnv();
|
|
1362
|
+
if (service == null || service === "") {
|
|
1363
|
+
throw new Error("[LogPersist] LOG_PERSIST_SERVICE is required when LOG_PERSIST_ENABLED=true");
|
|
1364
|
+
}
|
|
1365
|
+
if (processName == null || processName === "") {
|
|
1366
|
+
throw new Error("[LogPersist] LOG_PERSIST_PROCESS is required when LOG_PERSIST_ENABLED=true (or pass process option)");
|
|
1367
|
+
}
|
|
1368
|
+
const registryFilePath = options.registryFilePath != null ? resolve(options.registryFilePath) : resolveRegistryFilePath();
|
|
1369
|
+
await enableLogPersist({
|
|
1370
|
+
service,
|
|
1371
|
+
process: processName,
|
|
1372
|
+
registryFilePath,
|
|
1373
|
+
config: options.config,
|
|
1374
|
+
});
|
|
1375
|
+
const lifecycleLogger = createLogger({ module: "log-persist" });
|
|
1376
|
+
lifecycleLogger.info("log persist enabled from env", {
|
|
1377
|
+
topic: LOG_PERSIST_TOPIC,
|
|
1378
|
+
data: { service, process: processName, registryFilePath },
|
|
1379
|
+
});
|
|
1380
|
+
return true;
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Preload guard: when persistence is enabled, required env must be present before app code runs.
|
|
1384
|
+
*/
|
|
1385
|
+
function validateLogPersistPreloadEnv(env = process.env) {
|
|
1386
|
+
if (!envBool("LOG_PERSIST_ENABLED", false, env)) {
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
const missing = [];
|
|
1390
|
+
if (readEnv("LOG_PERSIST_SERVICE", env) == null) {
|
|
1391
|
+
missing.push("LOG_PERSIST_SERVICE");
|
|
1392
|
+
}
|
|
1393
|
+
const registryDir = logPersistRegistryDirFromEnv(env);
|
|
1394
|
+
if (registryDir == null) {
|
|
1395
|
+
missing.push("LOG_PERSIST_REGISTRY_DIR or RUNTIME_DATA_DIR");
|
|
1396
|
+
}
|
|
1397
|
+
if (missing.length > 0) {
|
|
1398
|
+
throw new Error(`[LogPersist] preload env incomplete: ${missing.join(", ")}`);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
function evaluateLogPersistReady(state, expectedProcesses, staleMs, now) {
|
|
1402
|
+
const missing = [];
|
|
1403
|
+
const stale = [];
|
|
1404
|
+
const registered = [];
|
|
1405
|
+
for (const processName of expectedProcesses) {
|
|
1406
|
+
const record = state.processes[processName];
|
|
1407
|
+
if (record == null) {
|
|
1408
|
+
missing.push(processName);
|
|
1409
|
+
continue;
|
|
1410
|
+
}
|
|
1411
|
+
const heartbeatAt = Date.parse(record.lastHeartbeatAt);
|
|
1412
|
+
if (!Number.isFinite(heartbeatAt) || now - heartbeatAt > staleMs) {
|
|
1413
|
+
stale.push(processName);
|
|
1414
|
+
continue;
|
|
1415
|
+
}
|
|
1416
|
+
registered.push(processName);
|
|
1417
|
+
}
|
|
1418
|
+
return {
|
|
1419
|
+
ok: missing.length === 0 && stale.length === 0,
|
|
1420
|
+
missing,
|
|
1421
|
+
stale,
|
|
1422
|
+
registered,
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Wait until every expected process has registered in the shared registry with a fresh heartbeat.
|
|
1427
|
+
* Call from the service main process after spawning children.
|
|
1428
|
+
*/
|
|
1429
|
+
async function waitForLogPersistReady(options) {
|
|
1430
|
+
const timeoutMs = options.timeoutMs ?? 60_000;
|
|
1431
|
+
const pollIntervalMs = options.pollIntervalMs ?? 500;
|
|
1432
|
+
const staleMs = options.staleMs ?? 90_000;
|
|
1433
|
+
let last = {
|
|
1434
|
+
ok: false,
|
|
1435
|
+
missing: [...options.expectedProcesses],
|
|
1436
|
+
stale: [],
|
|
1437
|
+
registered: [],
|
|
1438
|
+
};
|
|
1439
|
+
try {
|
|
1440
|
+
await pollUntil({
|
|
1441
|
+
intervalMs: pollIntervalMs,
|
|
1442
|
+
timeoutMs,
|
|
1443
|
+
until: async () => {
|
|
1444
|
+
const state = await readLogPersistRegistry(options.registryFilePath);
|
|
1445
|
+
if (state.service !== options.service) {
|
|
1446
|
+
last = {
|
|
1447
|
+
ok: false,
|
|
1448
|
+
missing: options.expectedProcesses,
|
|
1449
|
+
stale: [],
|
|
1450
|
+
registered: [],
|
|
1451
|
+
};
|
|
1452
|
+
return false;
|
|
1453
|
+
}
|
|
1454
|
+
last = evaluateLogPersistReady(state, options.expectedProcesses, staleMs, Date.now());
|
|
1455
|
+
return last.ok;
|
|
1456
|
+
},
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
catch {
|
|
1460
|
+
persistLogger.warn("waitForLogPersistReady timed out", {
|
|
1461
|
+
timeoutMs,
|
|
1462
|
+
expectedProcesses: options.expectedProcesses,
|
|
1463
|
+
...last,
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
return last;
|
|
1467
|
+
}
|
|
1468
|
+
async function snapshotLogPersistReady(options) {
|
|
1469
|
+
const state = await readLogPersistRegistry(options.registryFilePath);
|
|
1470
|
+
return evaluateLogPersistReady(state, options.expectedProcesses, options.staleMs ?? 90_000, Date.now());
|
|
1471
|
+
}
|
|
1472
|
+
function resolveLogPersistRegistryPath(registryDir) {
|
|
1473
|
+
const dir = registryDir ?? logPersistRegistryDirFromEnv();
|
|
1474
|
+
if (dir == null) {
|
|
1475
|
+
requireEnv("LOG_PERSIST_REGISTRY_DIR");
|
|
1476
|
+
}
|
|
1477
|
+
return defaultLogPersistRegistryPath(resolve(dir ?? ""));
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
export { logPersistEnabledFromEnv as a, readEnv as b, requireEnv as c, createJsonFileStateStore as d, disableLogPersist as e, enableLogPersist as f, ensureLogPersistFromEnv as g, envBool as h, envInt as i, envMs as j, envNumber as k, loadEnvFiles as l, envPort as m, getLogPersistStats as n, parseEnvLine as o, parseEnvFile as p, pollUntil as q, resolveProjectRootByPackageName as r, resolveLogPersistRegistryPath as s, resolveProjectRootFromModule as t, snapshotLogPersistReady as u, validateLogPersistPreloadEnv as v, waitForLogPersistReady as w };
|
|
1481
|
+
//# sourceMappingURL=enable-log-persist-BH-tbKEj.js.map
|