target-supabase-sdk 0.2.12 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +1 -1
- package/dist/chunks/enable-DqnfLPsQ.js +874 -0
- package/dist/chunks/enable-DqnfLPsQ.js.map +1 -0
- package/dist/chunks/list.api-BzbTJBsM.js.map +1 -1
- package/dist/chunks/{task-queue-H4mpSW8R.js → task-queue-6X3aglJh.js} +108 -3
- package/dist/chunks/task-queue-6X3aglJh.js.map +1 -0
- package/dist/node/preload/run-service-preload.d.ts.map +1 -1
- package/dist/node/preload/service-preload.interface.d.ts +3 -1
- package/dist/node/preload/service-preload.interface.d.ts.map +1 -1
- package/dist/node/process/managed-child-processes.d.ts.map +1 -1
- package/dist/node/service-guard/service-guard-node.d.ts.map +1 -1
- package/dist/node/service-host/index.d.ts +1 -0
- package/dist/node/service-host/index.d.ts.map +1 -1
- package/dist/node/service-host/service-host.d.ts +5 -1
- package/dist/node/service-host/service-host.d.ts.map +1 -1
- package/dist/node.d.ts +10 -5
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +492 -47
- package/dist/node.js.map +1 -1
- package/dist/preload.js +9 -6
- package/dist/preload.js.map +1 -1
- package/dist/service/index.d.ts +2 -2
- package/dist/service/index.d.ts.map +1 -1
- package/dist/service/registry.service.d.ts +19 -0
- package/dist/service/registry.service.d.ts.map +1 -1
- package/dist/shared/log/core/create-logger.d.ts.map +1 -0
- package/dist/shared/log/{log-batch.d.ts → core/log-batch.d.ts} +4 -4
- package/dist/shared/log/core/log-batch.d.ts.map +1 -0
- package/dist/shared/log/core/log-manager.d.ts.map +1 -0
- package/dist/shared/log/core/log-min-level.d.ts.map +1 -0
- package/dist/shared/log/core/log-scope.d.ts.map +1 -0
- package/dist/shared/log/index.d.ts +13 -13
- package/dist/shared/log/index.d.ts.map +1 -1
- package/dist/shared/log/spool/collector.d.ts +21 -0
- package/dist/shared/log/spool/collector.d.ts.map +1 -0
- package/dist/shared/log/spool/config.d.ts +7 -0
- package/dist/shared/log/spool/config.d.ts.map +1 -0
- package/dist/shared/log/spool/coordinator.d.ts +18 -0
- package/dist/shared/log/spool/coordinator.d.ts.map +1 -0
- package/dist/shared/log/spool/enable.d.ts +23 -0
- package/dist/shared/log/spool/enable.d.ts.map +1 -0
- package/dist/shared/log/spool/file.d.ts +21 -0
- package/dist/shared/log/spool/file.d.ts.map +1 -0
- package/dist/shared/log/spool/interface.d.ts +55 -0
- package/dist/shared/log/spool/interface.d.ts.map +1 -0
- package/dist/shared/log/spool/paths.d.ts +6 -0
- package/dist/shared/log/spool/paths.d.ts.map +1 -0
- package/dist/shared/log/spool/process-roles.d.ts +17 -0
- package/dist/shared/log/spool/process-roles.d.ts.map +1 -0
- package/dist/shared/log/spool/register-collect-log-runner.d.ts +15 -0
- package/dist/shared/log/spool/register-collect-log-runner.d.ts.map +1 -0
- package/dist/shared/log/spool/service-lifecycle.d.ts +13 -0
- package/dist/shared/log/spool/service-lifecycle.d.ts.map +1 -0
- package/dist/shared/log/spool/writer.d.ts +32 -0
- package/dist/shared/log/spool/writer.d.ts.map +1 -0
- package/dist/shared/log/{log-persist-batch-id.d.ts → upload/batch-id.d.ts} +3 -3
- package/dist/shared/log/upload/batch-id.d.ts.map +1 -0
- package/dist/shared/log/upload/env.d.ts +7 -0
- package/dist/shared/log/upload/env.d.ts.map +1 -0
- package/dist/shared/log/{log-persist-flush.d.ts → upload/flush.d.ts} +3 -3
- package/dist/shared/log/upload/flush.d.ts.map +1 -0
- package/dist/shared/log/{log-persist-hook.d.ts → upload/hook.d.ts} +2 -2
- package/dist/shared/log/upload/hook.d.ts.map +1 -0
- package/dist/shared/log/upload/interface.d.ts +33 -0
- package/dist/shared/log/upload/interface.d.ts.map +1 -0
- package/dist/shared/log/upload/logger.d.ts +22 -0
- package/dist/shared/log/upload/logger.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/browser.ts +1 -0
- package/src/node/preload/run-service-preload.ts +11 -6
- package/src/node/preload/service-preload.interface.ts +3 -1
- package/src/node/process/create-managed-child-processes.ts +1 -1
- package/src/node/process/managed-child-processes.ts +8 -1
- package/src/node/service-guard/service-guard-node.ts +5 -0
- package/src/node/service-host/index.ts +4 -0
- package/src/node/service-host/service-host.ts +22 -3
- package/src/node.ts +26 -19
- package/src/service/index.ts +3 -0
- package/src/service/registry.service.ts +142 -0
- package/src/shared/log/README.md +206 -0
- package/src/shared/log/{log-batch.ts → core/log-batch.ts} +4 -4
- package/src/shared/log/{log-manager.ts → core/log-manager.ts} +3 -3
- package/src/shared/log/{log-min-level.ts → core/log-min-level.ts} +1 -1
- package/src/shared/log/{log-scope.ts → core/log-scope.ts} +1 -1
- package/src/shared/log/index.ts +13 -18
- package/src/shared/log/spool/collector.ts +330 -0
- package/src/shared/log/spool/config.ts +56 -0
- package/src/shared/log/spool/coordinator.ts +55 -0
- package/src/shared/log/spool/enable.ts +136 -0
- package/src/shared/log/spool/file.ts +75 -0
- package/src/shared/log/spool/interface.ts +62 -0
- package/src/shared/log/spool/paths.ts +30 -0
- package/src/shared/log/spool/process-roles.ts +75 -0
- package/src/shared/log/spool/register-collect-log-runner.ts +63 -0
- package/src/shared/log/spool/service-lifecycle.ts +35 -0
- package/src/shared/log/spool/writer.ts +273 -0
- package/src/shared/log/{log-persist-batch-id.ts → upload/batch-id.ts} +2 -2
- package/src/shared/log/upload/env.ts +26 -0
- package/src/shared/log/{log-persist-flush.ts → upload/flush.ts} +8 -7
- package/src/shared/log/{log-persist-hook.ts → upload/hook.ts} +1 -1
- package/src/shared/log/upload/interface.ts +38 -0
- package/src/shared/log/upload/logger.ts +37 -0
- package/dist/chunks/enable-log-persist-Dl55_mv2.js +0 -1674
- package/dist/chunks/enable-log-persist-Dl55_mv2.js.map +0 -1
- package/dist/chunks/task-queue-H4mpSW8R.js.map +0 -1
- package/dist/shared/log/create-logger.d.ts.map +0 -1
- package/dist/shared/log/enable-log-persist.d.ts +0 -28
- package/dist/shared/log/enable-log-persist.d.ts.map +0 -1
- package/dist/shared/log/log-batch.d.ts.map +0 -1
- package/dist/shared/log/log-manager.d.ts.map +0 -1
- package/dist/shared/log/log-min-level.d.ts.map +0 -1
- package/dist/shared/log/log-persist-batch-id.d.ts.map +0 -1
- package/dist/shared/log/log-persist-coordinator.d.ts +0 -37
- package/dist/shared/log/log-persist-coordinator.d.ts.map +0 -1
- package/dist/shared/log/log-persist-errors.d.ts +0 -4
- package/dist/shared/log/log-persist-errors.d.ts.map +0 -1
- package/dist/shared/log/log-persist-flush.d.ts.map +0 -1
- package/dist/shared/log/log-persist-hook.d.ts.map +0 -1
- package/dist/shared/log/log-persist-logger.d.ts +0 -16
- package/dist/shared/log/log-persist-logger.d.ts.map +0 -1
- package/dist/shared/log/log-persist-registry.d.ts +0 -18
- package/dist/shared/log/log-persist-registry.d.ts.map +0 -1
- package/dist/shared/log/log-persist.config.d.ts +0 -9
- package/dist/shared/log/log-persist.config.d.ts.map +0 -1
- package/dist/shared/log/log-persist.d.ts +0 -62
- package/dist/shared/log/log-persist.d.ts.map +0 -1
- package/dist/shared/log/log-persist.interface.d.ts +0 -132
- package/dist/shared/log/log-persist.interface.d.ts.map +0 -1
- package/dist/shared/log/log-scope.d.ts.map +0 -1
- package/src/shared/log/enable-log-persist.ts +0 -225
- package/src/shared/log/log-persist-coordinator.ts +0 -107
- package/src/shared/log/log-persist-errors.ts +0 -16
- package/src/shared/log/log-persist-logger.ts +0 -61
- package/src/shared/log/log-persist-registry.ts +0 -252
- package/src/shared/log/log-persist.config.ts +0 -115
- package/src/shared/log/log-persist.interface.ts +0 -150
- package/src/shared/log/log-persist.ts +0 -822
- /package/dist/shared/log/{create-logger.d.ts → core/create-logger.d.ts} +0 -0
- /package/dist/shared/log/{log-manager.d.ts → core/log-manager.d.ts} +0 -0
- /package/dist/shared/log/{log-min-level.d.ts → core/log-min-level.d.ts} +0 -0
- /package/dist/shared/log/{log-scope.d.ts → core/log-scope.d.ts} +0 -0
- /package/src/shared/log/{create-logger.ts → core/create-logger.ts} +0 -0
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
disableLogPersist,
|
|
3
|
-
ensureLogPersistFromEnv,
|
|
4
|
-
getLogPersistStats,
|
|
5
|
-
logPersistEnabledFromEnv,
|
|
6
|
-
snapshotLogPersistReady,
|
|
7
|
-
waitForLogPersistReady,
|
|
8
|
-
} from "./enable-log-persist";
|
|
9
|
-
import type { LogPersistReadySnapshot, LogPersistStats } from "./log-persist.interface";
|
|
10
|
-
|
|
11
|
-
export interface LogPersistCoordinatorOptions {
|
|
12
|
-
/** Service identity written into each registry shard (e.g. `log-service`). */
|
|
13
|
-
service: string;
|
|
14
|
-
/** Directory holding one JSON shard per process (`main.json`, `guard.json`, …). */
|
|
15
|
-
registryFilePath: string;
|
|
16
|
-
/** Process labels main waits for before accepting traffic (must match `LOG_PERSIST_PROCESS` per child). */
|
|
17
|
-
expectedProcesses: readonly string[];
|
|
18
|
-
/** Max wait for {@link LogPersistCoordinator.waitForAllProcessesReady} (default 60_000 ms). */
|
|
19
|
-
readyTimeoutMs?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Service-bound facade over env-driven log-persist registration and readiness gates. */
|
|
23
|
-
export interface LogPersistCoordinator {
|
|
24
|
-
/** Whether `LOG_PERSIST_ENABLED` is active in this process. */
|
|
25
|
-
isEnabled(): boolean;
|
|
26
|
-
|
|
27
|
-
/** Absolute path to the multi-process registry directory configured at construction. */
|
|
28
|
-
getRegistryFilePath(): string;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Enable log-persist for this process and write/update its registry shard.
|
|
32
|
-
* @param process Optional label; defaults to `LOG_PERSIST_PROCESS` env.
|
|
33
|
-
*/
|
|
34
|
-
registerProcess(process?: string): Promise<boolean>;
|
|
35
|
-
|
|
36
|
-
/** Block until every {@link LogPersistCoordinatorOptions.expectedProcesses} entry is registered and fresh. */
|
|
37
|
-
waitForAllProcessesReady(): Promise<LogPersistReadySnapshot>;
|
|
38
|
-
|
|
39
|
-
/** Immediate readiness snapshot without blocking; `null` when log-persist is disabled. */
|
|
40
|
-
snapshotProcessesReady(): Promise<LogPersistReadySnapshot | null>;
|
|
41
|
-
|
|
42
|
-
/** Flush-queue stats for observability; `null` when log-persist is disabled. */
|
|
43
|
-
getPersistStats(): LogPersistStats | null;
|
|
44
|
-
|
|
45
|
-
/** Flush pending logs and tear down log-persist (call from process shutdown hooks). */
|
|
46
|
-
shutdownLogPersist(): Promise<void>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Bind service-specific log-persist registry settings (process list, paths, timeouts).
|
|
51
|
-
* Each L3 service exports one instance from `src/startup/log-persist.ts`.
|
|
52
|
-
*/
|
|
53
|
-
export function createLogPersistCoordinator(options: LogPersistCoordinatorOptions): LogPersistCoordinator {
|
|
54
|
-
const { service, registryFilePath, expectedProcesses, readyTimeoutMs } = options;
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
isEnabled() {
|
|
58
|
-
return logPersistEnabledFromEnv();
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
getRegistryFilePath() {
|
|
62
|
-
return registryFilePath;
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
registerProcess(process?: string) {
|
|
66
|
-
return ensureLogPersistFromEnv({
|
|
67
|
-
process,
|
|
68
|
-
service,
|
|
69
|
-
registryFilePath,
|
|
70
|
-
});
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
waitForAllProcessesReady() {
|
|
74
|
-
return waitForLogPersistReady({
|
|
75
|
-
registryFilePath,
|
|
76
|
-
service,
|
|
77
|
-
expectedProcesses: [...expectedProcesses],
|
|
78
|
-
timeoutMs: readyTimeoutMs,
|
|
79
|
-
});
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
async snapshotProcessesReady() {
|
|
83
|
-
if (!logPersistEnabledFromEnv()) {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
return snapshotLogPersistReady({
|
|
87
|
-
registryFilePath,
|
|
88
|
-
service,
|
|
89
|
-
expectedProcesses: [...expectedProcesses],
|
|
90
|
-
});
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
getPersistStats() {
|
|
94
|
-
if (!logPersistEnabledFromEnv()) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
return getLogPersistStats();
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
async shutdownLogPersist() {
|
|
101
|
-
if (!logPersistEnabledFromEnv()) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
await disableLogPersist();
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
const PERMANENT_LOG_PERSIST_ERROR_PATTERNS = [
|
|
2
|
-
/not initialized/i,
|
|
3
|
-
/call initialize\(\) first/i,
|
|
4
|
-
/missing required supabase environment/i,
|
|
5
|
-
/preload env incomplete/i,
|
|
6
|
-
];
|
|
7
|
-
|
|
8
|
-
export function formatLogPersistError(error: unknown): string {
|
|
9
|
-
return error instanceof Error ? error.message : String(error);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/** Errors that will not succeed on retry without fixing process configuration. */
|
|
13
|
-
export function isPermanentLogPersistError(error: unknown): boolean {
|
|
14
|
-
const message = formatLogPersistError(error);
|
|
15
|
-
return PERMANENT_LOG_PERSIST_ERROR_PATTERNS.some((pattern) => pattern.test(message));
|
|
16
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { createLogger } from "./create-logger";
|
|
2
|
-
|
|
3
|
-
/** User-facing / lifecycle logs — may be persisted like normal app logs. */
|
|
4
|
-
export const LOG_PERSIST_TOPIC = "log-persist";
|
|
5
|
-
|
|
6
|
-
/** Operational logs from the persist pipeline — never re-offered to avoid feedback loops. */
|
|
7
|
-
export const LOG_PERSIST_INTERNAL_TOPIC = "log-persist-internal";
|
|
8
|
-
|
|
9
|
-
const logger = createLogger({ module: "log-persist" });
|
|
10
|
-
|
|
11
|
-
let errorLogRateLimitMs = 5_000;
|
|
12
|
-
const lastLoggedAt = new Map<string, number>();
|
|
13
|
-
|
|
14
|
-
export function configurePersistLoggerRateLimit(ms: number): void {
|
|
15
|
-
errorLogRateLimitMs = Math.max(0, ms);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function isLogPersistInternalTopic(topic: string | undefined): boolean {
|
|
19
|
-
return topic === LOG_PERSIST_INTERNAL_TOPIC;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
type PersistLogData = Record<string, unknown>;
|
|
23
|
-
|
|
24
|
-
function shouldEmit(level: "debug" | "info" | "warn" | "error", message: string): boolean {
|
|
25
|
-
if (level !== "error" && level !== "warn") {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
if (errorLogRateLimitMs <= 0) {
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const key = `${level}:${message}`;
|
|
33
|
-
const now = Date.now();
|
|
34
|
-
const last = lastLoggedAt.get(key);
|
|
35
|
-
if (last != null && now - last < errorLogRateLimitMs) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
lastLoggedAt.set(key, now);
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function write(level: "debug" | "info" | "warn" | "error", message: string, data?: PersistLogData): void {
|
|
43
|
-
if (!shouldEmit(level, message)) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
logger[level](message, {
|
|
47
|
-
topic: LOG_PERSIST_INTERNAL_TOPIC,
|
|
48
|
-
...(data != null ? { data } : {}),
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export const persistLogger = {
|
|
53
|
-
debug: (message: string, data?: PersistLogData) => write("debug", message, data),
|
|
54
|
-
info: (message: string, data?: PersistLogData) => write("info", message, data),
|
|
55
|
-
warn: (message: string, data?: PersistLogData) => write("warn", message, data),
|
|
56
|
-
error: (message: string, data?: PersistLogData) => write("error", message, data),
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export function patchPersistLoggerScope(labels: Record<string, string>): void {
|
|
60
|
-
logger.resetScope({ labels });
|
|
61
|
-
}
|
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import { mkdir, readdir, readFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { createJsonFileStateStore } from "../../node/fs/json-state-store";
|
|
4
|
-
import type { LogPersistProcessRecord, LogPersistRegistryState } from "./log-persist.interface";
|
|
5
|
-
import { persistLogger } from "./log-persist-logger";
|
|
6
|
-
|
|
7
|
-
const DEFAULT_REGISTRY: LogPersistRegistryState = {
|
|
8
|
-
updatedAt: new Date(0).toISOString(),
|
|
9
|
-
service: "",
|
|
10
|
-
processes: {},
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/** Per-process shard — each process owns one file; no cross-process RMW. */
|
|
14
|
-
interface LogPersistProcessShard extends LogPersistProcessRecord {
|
|
15
|
-
service: string;
|
|
16
|
-
updatedAt: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const DEFAULT_SHARD: LogPersistProcessShard = {
|
|
20
|
-
service: "",
|
|
21
|
-
updatedAt: new Date(0).toISOString(),
|
|
22
|
-
pid: 0,
|
|
23
|
-
enabledAt: new Date(0).toISOString(),
|
|
24
|
-
lastHeartbeatAt: new Date(0).toISOString(),
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const legacyStores = new Map<
|
|
28
|
-
string,
|
|
29
|
-
ReturnType<typeof createJsonFileStateStore<LogPersistRegistryState, "processes">>
|
|
30
|
-
>();
|
|
31
|
-
|
|
32
|
-
const shardStores = new Map<string, ReturnType<typeof createJsonFileStateStore<LogPersistProcessShard>>>();
|
|
33
|
-
|
|
34
|
-
function isLegacyRegistryFile(registryPath: string): boolean {
|
|
35
|
-
return registryPath.endsWith(".json");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function legacyStoreFor(filePath: string) {
|
|
39
|
-
let store = legacyStores.get(filePath);
|
|
40
|
-
if (store == null) {
|
|
41
|
-
store = createJsonFileStateStore<LogPersistRegistryState, "processes">({
|
|
42
|
-
filePath,
|
|
43
|
-
defaultState: DEFAULT_REGISTRY,
|
|
44
|
-
nestedKeys: ["processes"],
|
|
45
|
-
updatedAtKey: "updatedAt",
|
|
46
|
-
});
|
|
47
|
-
legacyStores.set(filePath, store);
|
|
48
|
-
}
|
|
49
|
-
return store;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function shardStoreFor(shardFilePath: string) {
|
|
53
|
-
let store = shardStores.get(shardFilePath);
|
|
54
|
-
if (store == null) {
|
|
55
|
-
store = createJsonFileStateStore<LogPersistProcessShard>({
|
|
56
|
-
filePath: shardFilePath,
|
|
57
|
-
defaultState: DEFAULT_SHARD,
|
|
58
|
-
updatedAtKey: "updatedAt",
|
|
59
|
-
});
|
|
60
|
-
shardStores.set(shardFilePath, store);
|
|
61
|
-
}
|
|
62
|
-
return store;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function shardFilePath(registryDir: string, process: string): string {
|
|
66
|
-
return join(registryDir, `${process}.json`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** Registry root directory — one shard file per process under this path. */
|
|
70
|
-
export function defaultLogPersistRegistryPath(registryDir: string): string {
|
|
71
|
-
return join(registryDir, "log-persist-registry");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async function readShardedLogPersistRegistry(registryDir: string): Promise<LogPersistRegistryState> {
|
|
75
|
-
await mkdir(registryDir, { recursive: true });
|
|
76
|
-
|
|
77
|
-
let entries: string[];
|
|
78
|
-
try {
|
|
79
|
-
entries = await readdir(registryDir);
|
|
80
|
-
} catch {
|
|
81
|
-
return { ...DEFAULT_REGISTRY };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const processes: Record<string, LogPersistProcessRecord> = {};
|
|
85
|
-
let service = "";
|
|
86
|
-
let updatedAt = new Date(0).toISOString();
|
|
87
|
-
|
|
88
|
-
for (const name of entries) {
|
|
89
|
-
if (!name.endsWith(".json")) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const processName = name.slice(0, -".json".length);
|
|
94
|
-
if (processName === "" || processName.startsWith("_")) {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
const raw = await readFile(join(registryDir, name), "utf8");
|
|
100
|
-
const shard = JSON.parse(raw) as Partial<LogPersistProcessShard>;
|
|
101
|
-
if (shard.service != null && shard.service !== "") {
|
|
102
|
-
service = shard.service;
|
|
103
|
-
}
|
|
104
|
-
if (shard.updatedAt != null && shard.updatedAt > updatedAt) {
|
|
105
|
-
updatedAt = shard.updatedAt;
|
|
106
|
-
}
|
|
107
|
-
processes[processName] = {
|
|
108
|
-
pid: shard.pid ?? 0,
|
|
109
|
-
enabledAt: shard.enabledAt ?? new Date(0).toISOString(),
|
|
110
|
-
lastHeartbeatAt: shard.lastHeartbeatAt ?? new Date(0).toISOString(),
|
|
111
|
-
};
|
|
112
|
-
} catch {
|
|
113
|
-
persistLogger.warn("registry shard read skipped — corrupt or missing", {
|
|
114
|
-
registryDir,
|
|
115
|
-
shard: name,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return { updatedAt, service, processes };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export async function registerLogPersistProcess(input: {
|
|
124
|
-
registryFilePath: string;
|
|
125
|
-
service: string;
|
|
126
|
-
process: string;
|
|
127
|
-
}): Promise<void> {
|
|
128
|
-
const now = new Date().toISOString();
|
|
129
|
-
const record: LogPersistProcessRecord = {
|
|
130
|
-
pid: process.pid,
|
|
131
|
-
enabledAt: now,
|
|
132
|
-
lastHeartbeatAt: now,
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
if (isLegacyRegistryFile(input.registryFilePath)) {
|
|
136
|
-
await legacyStoreFor(input.registryFilePath).write({
|
|
137
|
-
service: input.service,
|
|
138
|
-
processes: {
|
|
139
|
-
[input.process]: record,
|
|
140
|
-
},
|
|
141
|
-
});
|
|
142
|
-
} else {
|
|
143
|
-
await shardStoreFor(shardFilePath(input.registryFilePath, input.process)).write({
|
|
144
|
-
service: input.service,
|
|
145
|
-
pid: record.pid,
|
|
146
|
-
enabledAt: record.enabledAt,
|
|
147
|
-
lastHeartbeatAt: record.lastHeartbeatAt,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
persistLogger.info("registry process registered", {
|
|
152
|
-
registryFilePath: input.registryFilePath,
|
|
153
|
-
service: input.service,
|
|
154
|
-
process: input.process,
|
|
155
|
-
pid: record.pid,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export async function heartbeatLogPersistProcess(input: { registryFilePath: string; process: string }): Promise<void> {
|
|
160
|
-
if (isLegacyRegistryFile(input.registryFilePath)) {
|
|
161
|
-
const current = await legacyStoreFor(input.registryFilePath).read();
|
|
162
|
-
const existing = current.processes[input.process];
|
|
163
|
-
if (existing == null) {
|
|
164
|
-
persistLogger.warn("registry heartbeat skipped — process not registered", {
|
|
165
|
-
registryFilePath: input.registryFilePath,
|
|
166
|
-
process: input.process,
|
|
167
|
-
});
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
await legacyStoreFor(input.registryFilePath).write({
|
|
172
|
-
processes: {
|
|
173
|
-
[input.process]: {
|
|
174
|
-
...existing,
|
|
175
|
-
lastHeartbeatAt: new Date().toISOString(),
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
});
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const store = shardStoreFor(shardFilePath(input.registryFilePath, input.process));
|
|
183
|
-
const current = await store.read();
|
|
184
|
-
if (current.pid === 0) {
|
|
185
|
-
persistLogger.warn("registry heartbeat skipped — process not registered", {
|
|
186
|
-
registryFilePath: input.registryFilePath,
|
|
187
|
-
process: input.process,
|
|
188
|
-
});
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
await store.write({
|
|
193
|
-
lastHeartbeatAt: new Date().toISOString(),
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export async function unregisterLogPersistProcess(input: { registryFilePath: string; process: string }): Promise<void> {
|
|
198
|
-
if (isLegacyRegistryFile(input.registryFilePath)) {
|
|
199
|
-
const current = await legacyStoreFor(input.registryFilePath).read();
|
|
200
|
-
const existing = current.processes[input.process];
|
|
201
|
-
if (existing == null) {
|
|
202
|
-
persistLogger.warn("registry unregister skipped — process not found", {
|
|
203
|
-
registryFilePath: input.registryFilePath,
|
|
204
|
-
process: input.process,
|
|
205
|
-
});
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
await legacyStoreFor(input.registryFilePath).write({
|
|
210
|
-
processes: {
|
|
211
|
-
[input.process]: {
|
|
212
|
-
...existing,
|
|
213
|
-
pid: 0,
|
|
214
|
-
lastHeartbeatAt: new Date(0).toISOString(),
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
persistLogger.info("registry process unregistered", {
|
|
220
|
-
registryFilePath: input.registryFilePath,
|
|
221
|
-
process: input.process,
|
|
222
|
-
});
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const store = shardStoreFor(shardFilePath(input.registryFilePath, input.process));
|
|
227
|
-
const current = await store.read();
|
|
228
|
-
if (current.pid === 0) {
|
|
229
|
-
persistLogger.warn("registry unregister skipped — process not found", {
|
|
230
|
-
registryFilePath: input.registryFilePath,
|
|
231
|
-
process: input.process,
|
|
232
|
-
});
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
await store.write({
|
|
237
|
-
pid: 0,
|
|
238
|
-
lastHeartbeatAt: new Date(0).toISOString(),
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
persistLogger.info("registry process unregistered", {
|
|
242
|
-
registryFilePath: input.registryFilePath,
|
|
243
|
-
process: input.process,
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export async function readLogPersistRegistry(registryFilePath: string): Promise<LogPersistRegistryState> {
|
|
248
|
-
if (isLegacyRegistryFile(registryFilePath)) {
|
|
249
|
-
return legacyStoreFor(registryFilePath).read();
|
|
250
|
-
}
|
|
251
|
-
return readShardedLogPersistRegistry(registryFilePath);
|
|
252
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { envBool, envInt, envMs } from "../../node/env/env-parsers";
|
|
2
|
-
import type { LogPersistConfig } from "./log-persist.interface";
|
|
3
|
-
|
|
4
|
-
export const DEFAULT_LOG_PERSIST_CONFIG: LogPersistConfig = {
|
|
5
|
-
fast: {
|
|
6
|
-
maxBufferEntries: 500,
|
|
7
|
-
},
|
|
8
|
-
medium: {
|
|
9
|
-
debounceMs: 5_000,
|
|
10
|
-
maxEntries: 20,
|
|
11
|
-
maxBytes: 512 * 1024,
|
|
12
|
-
maxBufferEntries: 1_000,
|
|
13
|
-
},
|
|
14
|
-
slow: {
|
|
15
|
-
maxEntries: 400,
|
|
16
|
-
maxBytes: 512 * 1024,
|
|
17
|
-
maxAgeMs: 120_000,
|
|
18
|
-
maxBufferEntries: 2_000,
|
|
19
|
-
},
|
|
20
|
-
postTimeoutMs: 30_000,
|
|
21
|
-
shutdownDrainTimeoutMs: 15_000,
|
|
22
|
-
laneRetryIntervalMs: 5_000,
|
|
23
|
-
errorLogRateLimitMs: 5_000,
|
|
24
|
-
circuitBreakerPermanentFailureThreshold: 3,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export function resolveLogPersistConfig(partial?: Partial<LogPersistConfig>): LogPersistConfig {
|
|
28
|
-
const defaults = DEFAULT_LOG_PERSIST_CONFIG;
|
|
29
|
-
return {
|
|
30
|
-
fast: { ...defaults.fast, ...partial?.fast },
|
|
31
|
-
medium: { ...defaults.medium, ...partial?.medium },
|
|
32
|
-
slow: { ...defaults.slow, ...partial?.slow },
|
|
33
|
-
postTimeoutMs: partial?.postTimeoutMs ?? defaults.postTimeoutMs,
|
|
34
|
-
shutdownDrainTimeoutMs: partial?.shutdownDrainTimeoutMs ?? defaults.shutdownDrainTimeoutMs,
|
|
35
|
-
laneRetryIntervalMs: partial?.laneRetryIntervalMs ?? defaults.laneRetryIntervalMs,
|
|
36
|
-
errorLogRateLimitMs: partial?.errorLogRateLimitMs ?? defaults.errorLogRateLimitMs,
|
|
37
|
-
circuitBreakerPermanentFailureThreshold:
|
|
38
|
-
partial?.circuitBreakerPermanentFailureThreshold ?? defaults.circuitBreakerPermanentFailureThreshold,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function logPersistEnabledFromEnv(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
43
|
-
return envBool("LOG_PERSIST_ENABLED", false, env);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function logPersistServiceFromEnv(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
47
|
-
const raw = env.LOG_PERSIST_SERVICE?.trim();
|
|
48
|
-
return raw != null && raw !== "" ? raw : undefined;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function logPersistProcessFromEnv(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
52
|
-
const raw = env.LOG_PERSIST_PROCESS?.trim();
|
|
53
|
-
return raw != null && raw !== "" ? raw : undefined;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function logPersistRegistryDirFromEnv(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
57
|
-
const configured = env.LOG_PERSIST_REGISTRY_DIR?.trim();
|
|
58
|
-
if (configured != null && configured !== "") {
|
|
59
|
-
return configured;
|
|
60
|
-
}
|
|
61
|
-
const runtime = env.RUNTIME_DATA_DIR?.trim();
|
|
62
|
-
return runtime != null && runtime !== "" ? runtime : undefined;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function logPersistConfigFromEnv(env: NodeJS.ProcessEnv = process.env): LogPersistConfig {
|
|
66
|
-
return resolveLogPersistConfig({
|
|
67
|
-
fast: {
|
|
68
|
-
maxBufferEntries: envInt("LOG_FAST_MAX_BUFFER_ENTRIES", DEFAULT_LOG_PERSIST_CONFIG.fast.maxBufferEntries, {
|
|
69
|
-
env,
|
|
70
|
-
min: 1,
|
|
71
|
-
}),
|
|
72
|
-
},
|
|
73
|
-
medium: {
|
|
74
|
-
debounceMs: envMs("LOG_MEDIUM_DEBOUNCE_MS", DEFAULT_LOG_PERSIST_CONFIG.medium.debounceMs, { env }),
|
|
75
|
-
maxEntries: envInt("LOG_MEDIUM_MAX_ENTRIES", DEFAULT_LOG_PERSIST_CONFIG.medium.maxEntries, {
|
|
76
|
-
env,
|
|
77
|
-
min: 1,
|
|
78
|
-
}),
|
|
79
|
-
maxBytes: envInt("LOG_MEDIUM_MAX_BYTES", DEFAULT_LOG_PERSIST_CONFIG.medium.maxBytes, { env, min: 1 }),
|
|
80
|
-
maxBufferEntries: envInt(
|
|
81
|
-
"LOG_MEDIUM_MAX_BUFFER_ENTRIES",
|
|
82
|
-
DEFAULT_LOG_PERSIST_CONFIG.medium.maxBufferEntries,
|
|
83
|
-
{ env, min: 1 },
|
|
84
|
-
),
|
|
85
|
-
},
|
|
86
|
-
slow: {
|
|
87
|
-
maxEntries: envInt("LOG_SLOW_MAX_ENTRIES", DEFAULT_LOG_PERSIST_CONFIG.slow.maxEntries, { env, min: 1 }),
|
|
88
|
-
maxBytes: envInt("LOG_SLOW_MAX_BYTES", DEFAULT_LOG_PERSIST_CONFIG.slow.maxBytes, { env, min: 1 }),
|
|
89
|
-
maxAgeMs: envMs("LOG_SLOW_MAX_AGE_MS", DEFAULT_LOG_PERSIST_CONFIG.slow.maxAgeMs, { env }),
|
|
90
|
-
maxBufferEntries: envInt("LOG_SLOW_MAX_BUFFER_ENTRIES", DEFAULT_LOG_PERSIST_CONFIG.slow.maxBufferEntries, {
|
|
91
|
-
env,
|
|
92
|
-
min: 1,
|
|
93
|
-
}),
|
|
94
|
-
},
|
|
95
|
-
postTimeoutMs: envMs("LOG_PERSIST_POST_TIMEOUT_MS", DEFAULT_LOG_PERSIST_CONFIG.postTimeoutMs, { env }),
|
|
96
|
-
shutdownDrainTimeoutMs: envMs(
|
|
97
|
-
"LOG_PERSIST_SHUTDOWN_DRAIN_MS",
|
|
98
|
-
DEFAULT_LOG_PERSIST_CONFIG.shutdownDrainTimeoutMs,
|
|
99
|
-
{ env },
|
|
100
|
-
),
|
|
101
|
-
laneRetryIntervalMs: envMs("LOG_PERSIST_LANE_RETRY_MS", DEFAULT_LOG_PERSIST_CONFIG.laneRetryIntervalMs, {
|
|
102
|
-
env,
|
|
103
|
-
}),
|
|
104
|
-
errorLogRateLimitMs: envMs(
|
|
105
|
-
"LOG_PERSIST_ERROR_LOG_RATE_LIMIT_MS",
|
|
106
|
-
DEFAULT_LOG_PERSIST_CONFIG.errorLogRateLimitMs,
|
|
107
|
-
{ env },
|
|
108
|
-
),
|
|
109
|
-
circuitBreakerPermanentFailureThreshold: envInt(
|
|
110
|
-
"LOG_PERSIST_CIRCUIT_BREAKER_THRESHOLD",
|
|
111
|
-
DEFAULT_LOG_PERSIST_CONFIG.circuitBreakerPermanentFailureThreshold,
|
|
112
|
-
{ env, min: 1 },
|
|
113
|
-
),
|
|
114
|
-
});
|
|
115
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import type { TargetDraft } from "../../core.interface";
|
|
2
|
-
import type { List } from "../../list/list.interface";
|
|
3
|
-
import type { LogEntry } from "./log-manager";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* `ListDetails.loaderKey` stamped on every persisted log batch (write + read side).
|
|
7
|
-
* Business type: **LogBatch** (transport flush). Pairs with log-service **LogTrace**
|
|
8
|
-
* (`LogTrace.0`) after rekeying by `traceId`.
|
|
9
|
-
*/
|
|
10
|
-
export const LOG_PERSIST_LOADER_KEY = "LogBatch.0";
|
|
11
|
-
|
|
12
|
-
/** Alias for {@link LOG_PERSIST_LOADER_KEY} — preferred when contrasting with LogTrace. */
|
|
13
|
-
export const LOG_BATCH_LOADER_KEY = LOG_PERSIST_LOADER_KEY;
|
|
14
|
-
|
|
15
|
-
/** Persistence lane — maps to List tagList and flush policy. */
|
|
16
|
-
export type LogPersistLane = "fast" | "medium" | "slow";
|
|
17
|
-
|
|
18
|
-
export interface LogPersistFastConfig {
|
|
19
|
-
maxBufferEntries: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface LogPersistMediumConfig {
|
|
23
|
-
debounceMs: number;
|
|
24
|
-
maxEntries: number;
|
|
25
|
-
maxBytes: number;
|
|
26
|
-
maxBufferEntries: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface LogPersistSlowConfig {
|
|
30
|
-
maxEntries: number;
|
|
31
|
-
maxBytes: number;
|
|
32
|
-
maxAgeMs: number;
|
|
33
|
-
maxBufferEntries: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface LogPersistConfig {
|
|
37
|
-
fast: LogPersistFastConfig;
|
|
38
|
-
medium: LogPersistMediumConfig;
|
|
39
|
-
slow: LogPersistSlowConfig;
|
|
40
|
-
/** postListCreate / network ceiling per batch */
|
|
41
|
-
postTimeoutMs: number;
|
|
42
|
-
/** Graceful shutdown drain ceiling */
|
|
43
|
-
shutdownDrainTimeoutMs: number;
|
|
44
|
-
/** Delay before medium/slow lane flush retries after failure (fast lane uses the same interval). */
|
|
45
|
-
laneRetryIntervalMs: number;
|
|
46
|
-
/** Suppress duplicate persistLogger warn/error lines with the same message within this window. */
|
|
47
|
-
errorLogRateLimitMs: number;
|
|
48
|
-
/** Disable log-persist after this many consecutive permanent flush errors. */
|
|
49
|
-
circuitBreakerPermanentFailureThreshold: number;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface EnableLogPersistOptions {
|
|
53
|
-
service: string;
|
|
54
|
-
process: string;
|
|
55
|
-
/** Shared registry root directory — all processes use the same path; each writes its own shard file. */
|
|
56
|
-
registryFilePath?: string;
|
|
57
|
-
config?: Partial<LogPersistConfig>;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface EnsureLogPersistFromEnvOptions {
|
|
61
|
-
/** Overrides LOG_PERSIST_PROCESS when set. */
|
|
62
|
-
process?: string;
|
|
63
|
-
/** Overrides LOG_PERSIST_SERVICE when set. */
|
|
64
|
-
service?: string;
|
|
65
|
-
registryFilePath?: string;
|
|
66
|
-
config?: Partial<LogPersistConfig>;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface LogPersistProcessRecord {
|
|
70
|
-
pid: number;
|
|
71
|
-
enabledAt: string;
|
|
72
|
-
lastHeartbeatAt: string;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface LogPersistRegistryState {
|
|
76
|
-
updatedAt: string;
|
|
77
|
-
service: string;
|
|
78
|
-
processes: Record<string, LogPersistProcessRecord>;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface LogPersistQueueStats {
|
|
82
|
-
fast: number;
|
|
83
|
-
fastRetry: number;
|
|
84
|
-
medium: number;
|
|
85
|
-
slow: number;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export interface LogPersistQueueBytesStats {
|
|
89
|
-
fast: number;
|
|
90
|
-
fastRetry: number;
|
|
91
|
-
medium: number;
|
|
92
|
-
slow: number;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface LogPersistStats {
|
|
96
|
-
enabled: boolean;
|
|
97
|
-
draining: boolean;
|
|
98
|
-
circuitBreakerTripped: boolean;
|
|
99
|
-
permanentFailureCount: number;
|
|
100
|
-
service: string | null;
|
|
101
|
-
process: string | null;
|
|
102
|
-
queues: LogPersistQueueStats;
|
|
103
|
-
queueBytes: LogPersistQueueBytesStats;
|
|
104
|
-
mediumDebouncePending: boolean;
|
|
105
|
-
laneFlushing: Record<LogPersistLane, boolean>;
|
|
106
|
-
consecutiveFailures: Record<LogPersistLane, number>;
|
|
107
|
-
config: LogPersistConfig;
|
|
108
|
-
lastFlushAt: Record<LogPersistLane, string | null>;
|
|
109
|
-
lastError: string | null;
|
|
110
|
-
lastErrorAt: string | null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export interface WaitForLogPersistReadyOptions {
|
|
114
|
-
registryFilePath: string;
|
|
115
|
-
service: string;
|
|
116
|
-
expectedProcesses: string[];
|
|
117
|
-
timeoutMs?: number;
|
|
118
|
-
pollIntervalMs?: number;
|
|
119
|
-
/** Process heartbeat older than this is treated as missing. */
|
|
120
|
-
staleMs?: number;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export interface LogPersistReadySnapshot {
|
|
124
|
-
ok: boolean;
|
|
125
|
-
missing: string[];
|
|
126
|
-
stale: string[];
|
|
127
|
-
registered: string[];
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export interface LogBatchMeta {
|
|
131
|
-
service: string;
|
|
132
|
-
process: string;
|
|
133
|
-
pid: number;
|
|
134
|
-
lane: LogPersistLane;
|
|
135
|
-
count: number;
|
|
136
|
-
from: number | null;
|
|
137
|
-
to: number | null;
|
|
138
|
-
flushedAt: number;
|
|
139
|
-
idempotencyKey: string;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export interface BuildLogListDraftInput {
|
|
143
|
-
service: string;
|
|
144
|
-
process: string;
|
|
145
|
-
lane: LogPersistLane;
|
|
146
|
-
idempotencyKey: string;
|
|
147
|
-
entries: LogEntry[];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export type LogListDraft = TargetDraft<List>;
|