target-supabase-sdk 0.2.11 → 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.
Files changed (154) hide show
  1. package/dist/browser.d.ts +2 -3
  2. package/dist/browser.d.ts.map +1 -1
  3. package/dist/browser.js +2 -2
  4. package/dist/chunks/enable-DqnfLPsQ.js +874 -0
  5. package/dist/chunks/enable-DqnfLPsQ.js.map +1 -0
  6. package/dist/chunks/{list.api-DSTOHzOC.js → list.api-BzbTJBsM.js} +35 -2
  7. package/dist/chunks/list.api-BzbTJBsM.js.map +1 -0
  8. package/dist/chunks/{task-queue-BHvNgb-X.js → task-queue-6X3aglJh.js} +110 -17
  9. package/dist/chunks/task-queue-6X3aglJh.js.map +1 -0
  10. package/dist/node/preload/run-service-preload.d.ts +1 -0
  11. package/dist/node/preload/run-service-preload.d.ts.map +1 -1
  12. package/dist/node/preload/service-preload.interface.d.ts +7 -0
  13. package/dist/node/preload/service-preload.interface.d.ts.map +1 -1
  14. package/dist/node/process/managed-child-processes.d.ts.map +1 -1
  15. package/dist/node/service-guard/service-guard-node.d.ts.map +1 -1
  16. package/dist/node/service-host/index.d.ts +1 -0
  17. package/dist/node/service-host/index.d.ts.map +1 -1
  18. package/dist/node/service-host/service-host.d.ts +5 -1
  19. package/dist/node/service-host/service-host.d.ts.map +1 -1
  20. package/dist/node.d.ts +10 -5
  21. package/dist/node.d.ts.map +1 -1
  22. package/dist/node.js +494 -49
  23. package/dist/node.js.map +1 -1
  24. package/dist/preload.js +20 -7
  25. package/dist/preload.js.map +1 -1
  26. package/dist/resource/resource.interface.d.ts +20 -0
  27. package/dist/resource/resource.interface.d.ts.map +1 -0
  28. package/dist/service/config.api.d.ts.map +1 -1
  29. package/dist/service/index.d.ts +2 -2
  30. package/dist/service/index.d.ts.map +1 -1
  31. package/dist/service/registry.service.d.ts +19 -0
  32. package/dist/service/registry.service.d.ts.map +1 -1
  33. package/dist/shared/log/core/create-logger.d.ts.map +1 -0
  34. package/dist/shared/log/{log-batch.d.ts → core/log-batch.d.ts} +4 -4
  35. package/dist/shared/log/core/log-batch.d.ts.map +1 -0
  36. package/dist/shared/log/core/log-manager.d.ts.map +1 -0
  37. package/dist/shared/log/core/log-min-level.d.ts +15 -0
  38. package/dist/shared/log/core/log-min-level.d.ts.map +1 -0
  39. package/dist/shared/log/core/log-scope.d.ts.map +1 -0
  40. package/dist/shared/log/index.d.ts +13 -11
  41. package/dist/shared/log/index.d.ts.map +1 -1
  42. package/dist/shared/log/spool/collector.d.ts +21 -0
  43. package/dist/shared/log/spool/collector.d.ts.map +1 -0
  44. package/dist/shared/log/spool/config.d.ts +7 -0
  45. package/dist/shared/log/spool/config.d.ts.map +1 -0
  46. package/dist/shared/log/spool/coordinator.d.ts +18 -0
  47. package/dist/shared/log/spool/coordinator.d.ts.map +1 -0
  48. package/dist/shared/log/spool/enable.d.ts +23 -0
  49. package/dist/shared/log/spool/enable.d.ts.map +1 -0
  50. package/dist/shared/log/spool/file.d.ts +21 -0
  51. package/dist/shared/log/spool/file.d.ts.map +1 -0
  52. package/dist/shared/log/spool/interface.d.ts +55 -0
  53. package/dist/shared/log/spool/interface.d.ts.map +1 -0
  54. package/dist/shared/log/spool/paths.d.ts +6 -0
  55. package/dist/shared/log/spool/paths.d.ts.map +1 -0
  56. package/dist/shared/log/spool/process-roles.d.ts +17 -0
  57. package/dist/shared/log/spool/process-roles.d.ts.map +1 -0
  58. package/dist/shared/log/spool/register-collect-log-runner.d.ts +15 -0
  59. package/dist/shared/log/spool/register-collect-log-runner.d.ts.map +1 -0
  60. package/dist/shared/log/spool/service-lifecycle.d.ts +13 -0
  61. package/dist/shared/log/spool/service-lifecycle.d.ts.map +1 -0
  62. package/dist/shared/log/spool/writer.d.ts +32 -0
  63. package/dist/shared/log/spool/writer.d.ts.map +1 -0
  64. package/dist/shared/log/{log-persist-batch-id.d.ts → upload/batch-id.d.ts} +3 -3
  65. package/dist/shared/log/upload/batch-id.d.ts.map +1 -0
  66. package/dist/shared/log/upload/env.d.ts +7 -0
  67. package/dist/shared/log/upload/env.d.ts.map +1 -0
  68. package/dist/shared/log/{log-persist-flush.d.ts → upload/flush.d.ts} +3 -3
  69. package/dist/shared/log/upload/flush.d.ts.map +1 -0
  70. package/dist/shared/log/{log-persist-hook.d.ts → upload/hook.d.ts} +2 -2
  71. package/dist/shared/log/upload/hook.d.ts.map +1 -0
  72. package/dist/shared/log/upload/interface.d.ts +33 -0
  73. package/dist/shared/log/upload/interface.d.ts.map +1 -0
  74. package/dist/shared/log/upload/logger.d.ts +22 -0
  75. package/dist/shared/log/upload/logger.d.ts.map +1 -0
  76. package/package.json +1 -1
  77. package/src/browser.ts +4 -1
  78. package/src/node/preload/run-service-preload.ts +23 -4
  79. package/src/node/preload/service-preload.interface.ts +8 -0
  80. package/src/node/process/create-managed-child-processes.ts +1 -1
  81. package/src/node/process/managed-child-processes.ts +8 -1
  82. package/src/node/service-guard/service-guard-node.ts +5 -0
  83. package/src/node/service-host/index.ts +4 -0
  84. package/src/node/service-host/service-host.ts +22 -3
  85. package/src/node.ts +26 -19
  86. package/src/resource/resource.interface.ts +37 -0
  87. package/src/service/config.api.ts +1 -0
  88. package/src/service/index.ts +3 -0
  89. package/src/service/registry.service.ts +142 -0
  90. package/src/shared/log/README.md +206 -0
  91. package/src/shared/log/{log-batch.ts → core/log-batch.ts} +4 -4
  92. package/src/shared/log/{log-manager.ts → core/log-manager.ts} +3 -3
  93. package/src/shared/log/core/log-min-level.ts +46 -0
  94. package/src/shared/log/{log-scope.ts → core/log-scope.ts} +1 -1
  95. package/src/shared/log/index.ts +17 -15
  96. package/src/shared/log/spool/collector.ts +330 -0
  97. package/src/shared/log/spool/config.ts +56 -0
  98. package/src/shared/log/spool/coordinator.ts +55 -0
  99. package/src/shared/log/spool/enable.ts +136 -0
  100. package/src/shared/log/spool/file.ts +75 -0
  101. package/src/shared/log/spool/interface.ts +62 -0
  102. package/src/shared/log/spool/paths.ts +30 -0
  103. package/src/shared/log/spool/process-roles.ts +75 -0
  104. package/src/shared/log/spool/register-collect-log-runner.ts +63 -0
  105. package/src/shared/log/spool/service-lifecycle.ts +35 -0
  106. package/src/shared/log/spool/writer.ts +273 -0
  107. package/src/shared/log/{log-persist-batch-id.ts → upload/batch-id.ts} +2 -2
  108. package/src/shared/log/upload/env.ts +26 -0
  109. package/src/shared/log/{log-persist-flush.ts → upload/flush.ts} +8 -7
  110. package/src/shared/log/{log-persist-hook.ts → upload/hook.ts} +1 -1
  111. package/src/shared/log/upload/interface.ts +38 -0
  112. package/src/shared/log/upload/logger.ts +37 -0
  113. package/dist/chunks/enable-log-persist-Cu3SGxK7.js +0 -1674
  114. package/dist/chunks/enable-log-persist-Cu3SGxK7.js.map +0 -1
  115. package/dist/chunks/list.api-DSTOHzOC.js.map +0 -1
  116. package/dist/chunks/task-queue-BHvNgb-X.js.map +0 -1
  117. package/dist/idea/idea.interface.d.ts +0 -25
  118. package/dist/idea/idea.interface.d.ts.map +0 -1
  119. package/dist/shared/log/create-logger.d.ts.map +0 -1
  120. package/dist/shared/log/enable-log-persist.d.ts +0 -28
  121. package/dist/shared/log/enable-log-persist.d.ts.map +0 -1
  122. package/dist/shared/log/log-batch.d.ts.map +0 -1
  123. package/dist/shared/log/log-manager.d.ts.map +0 -1
  124. package/dist/shared/log/log-persist-batch-id.d.ts.map +0 -1
  125. package/dist/shared/log/log-persist-coordinator.d.ts +0 -37
  126. package/dist/shared/log/log-persist-coordinator.d.ts.map +0 -1
  127. package/dist/shared/log/log-persist-errors.d.ts +0 -4
  128. package/dist/shared/log/log-persist-errors.d.ts.map +0 -1
  129. package/dist/shared/log/log-persist-flush.d.ts.map +0 -1
  130. package/dist/shared/log/log-persist-hook.d.ts.map +0 -1
  131. package/dist/shared/log/log-persist-logger.d.ts +0 -16
  132. package/dist/shared/log/log-persist-logger.d.ts.map +0 -1
  133. package/dist/shared/log/log-persist-registry.d.ts +0 -18
  134. package/dist/shared/log/log-persist-registry.d.ts.map +0 -1
  135. package/dist/shared/log/log-persist.config.d.ts +0 -9
  136. package/dist/shared/log/log-persist.config.d.ts.map +0 -1
  137. package/dist/shared/log/log-persist.d.ts +0 -62
  138. package/dist/shared/log/log-persist.d.ts.map +0 -1
  139. package/dist/shared/log/log-persist.interface.d.ts +0 -132
  140. package/dist/shared/log/log-persist.interface.d.ts.map +0 -1
  141. package/dist/shared/log/log-scope.d.ts.map +0 -1
  142. package/src/idea/idea.interface.ts +0 -28
  143. package/src/shared/log/enable-log-persist.ts +0 -225
  144. package/src/shared/log/log-persist-coordinator.ts +0 -107
  145. package/src/shared/log/log-persist-errors.ts +0 -16
  146. package/src/shared/log/log-persist-logger.ts +0 -61
  147. package/src/shared/log/log-persist-registry.ts +0 -252
  148. package/src/shared/log/log-persist.config.ts +0 -115
  149. package/src/shared/log/log-persist.interface.ts +0 -150
  150. package/src/shared/log/log-persist.ts +0 -822
  151. /package/dist/shared/log/{create-logger.d.ts → core/create-logger.d.ts} +0 -0
  152. /package/dist/shared/log/{log-manager.d.ts → core/log-manager.d.ts} +0 -0
  153. /package/dist/shared/log/{log-scope.d.ts → core/log-scope.d.ts} +0 -0
  154. /package/src/shared/log/{create-logger.ts → core/create-logger.ts} +0 -0
@@ -1,225 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { envBool } from "../../node/env/env-parsers";
3
- import { readEnv, requireEnv } from "../../node/env/require-env";
4
- import { pollUntil } from "../../node/readiness";
5
- import { createLogger } from "./create-logger";
6
- import { LogPersist } from "./log-persist";
7
- import {
8
- logPersistConfigFromEnv,
9
- logPersistEnabledFromEnv,
10
- logPersistProcessFromEnv,
11
- logPersistRegistryDirFromEnv,
12
- logPersistServiceFromEnv,
13
- resolveLogPersistConfig,
14
- } from "./log-persist.config";
15
- import type {
16
- EnableLogPersistOptions,
17
- EnsureLogPersistFromEnvOptions,
18
- LogPersistReadySnapshot,
19
- LogPersistStats,
20
- WaitForLogPersistReadyOptions,
21
- } from "./log-persist.interface";
22
- import { registerLogPersistOffer } from "./log-persist-hook";
23
- import { LOG_PERSIST_TOPIC, persistLogger } from "./log-persist-logger";
24
- import { defaultLogPersistRegistryPath, readLogPersistRegistry } from "./log-persist-registry";
25
-
26
- export { logPersistEnabledFromEnv };
27
-
28
- function resolveRegistryFilePath(explicit?: string): string {
29
- if (explicit != null && explicit.trim() !== "") {
30
- return resolve(explicit);
31
- }
32
- const dir = logPersistRegistryDirFromEnv();
33
- if (dir == null) {
34
- throw new Error(
35
- "[LogPersist] registry path required: set registryFilePath option or LOG_PERSIST_REGISTRY_DIR / RUNTIME_DATA_DIR",
36
- );
37
- }
38
- return defaultLogPersistRegistryPath(resolve(dir));
39
- }
40
-
41
- export async function enableLogPersist(options: EnableLogPersistOptions): Promise<void> {
42
- const registryFilePath = options.registryFilePath != null ? resolve(options.registryFilePath) : undefined;
43
- const resolvedRegistry = registryFilePath ?? resolveRegistryFilePath();
44
-
45
- const envConfig = logPersistConfigFromEnv();
46
- await LogPersist.getInstance().enable({
47
- ...options,
48
- registryFilePath: resolvedRegistry,
49
- config: resolveLogPersistConfig({
50
- fast: { ...envConfig.fast, ...options.config?.fast },
51
- medium: { ...envConfig.medium, ...options.config?.medium },
52
- slow: { ...envConfig.slow, ...options.config?.slow },
53
- postTimeoutMs: options.config?.postTimeoutMs ?? envConfig.postTimeoutMs,
54
- shutdownDrainTimeoutMs: options.config?.shutdownDrainTimeoutMs ?? envConfig.shutdownDrainTimeoutMs,
55
- }),
56
- });
57
- }
58
-
59
- export async function disableLogPersist(): Promise<void> {
60
- await LogPersist.getInstance().disable();
61
- }
62
-
63
- export function getLogPersistStats(): LogPersistStats {
64
- return LogPersist.getInstance().getStats();
65
- }
66
-
67
- /**
68
- * Enable log persistence when LOG_PERSIST_ENABLED=true.
69
- * `process` / `service` can be passed explicitly or via LOG_PERSIST_PROCESS / LOG_PERSIST_SERVICE.
70
- */
71
- export async function ensureLogPersistFromEnv(options: EnsureLogPersistFromEnvOptions = {}): Promise<boolean> {
72
- if (!logPersistEnabledFromEnv()) {
73
- registerLogPersistOffer(null);
74
- return false;
75
- }
76
-
77
- const service = options.service ?? logPersistServiceFromEnv();
78
- const processName = options.process ?? logPersistProcessFromEnv();
79
- if (service == null || service === "") {
80
- throw new Error("[LogPersist] LOG_PERSIST_SERVICE is required when LOG_PERSIST_ENABLED=true");
81
- }
82
- if (processName == null || processName === "") {
83
- throw new Error(
84
- "[LogPersist] LOG_PERSIST_PROCESS is required when LOG_PERSIST_ENABLED=true (or pass process option)",
85
- );
86
- }
87
-
88
- const registryFilePath =
89
- options.registryFilePath != null ? resolve(options.registryFilePath) : resolveRegistryFilePath();
90
-
91
- await enableLogPersist({
92
- service,
93
- process: processName,
94
- registryFilePath,
95
- config: options.config,
96
- });
97
-
98
- const lifecycleLogger = createLogger({ module: "log-persist" });
99
- lifecycleLogger.info("log persist enabled from env", {
100
- topic: LOG_PERSIST_TOPIC,
101
- data: { service, process: processName, registryFilePath },
102
- });
103
-
104
- return true;
105
- }
106
-
107
- /**
108
- * Preload guard: when persistence is enabled, required env must be present before app code runs.
109
- */
110
- export function validateLogPersistPreloadEnv(env: NodeJS.ProcessEnv = process.env): void {
111
- if (!envBool("LOG_PERSIST_ENABLED", false, env)) {
112
- return;
113
- }
114
-
115
- const missing: string[] = [];
116
- if (readEnv("LOG_PERSIST_SERVICE", env) == null) {
117
- missing.push("LOG_PERSIST_SERVICE");
118
- }
119
- const registryDir = logPersistRegistryDirFromEnv(env);
120
- if (registryDir == null) {
121
- missing.push("LOG_PERSIST_REGISTRY_DIR or RUNTIME_DATA_DIR");
122
- }
123
-
124
- if (missing.length > 0) {
125
- throw new Error(`[LogPersist] preload env incomplete: ${missing.join(", ")}`);
126
- }
127
- }
128
-
129
- function evaluateLogPersistReady(
130
- state: Awaited<ReturnType<typeof readLogPersistRegistry>>,
131
- expectedProcesses: string[],
132
- staleMs: number,
133
- now: number,
134
- ): LogPersistReadySnapshot {
135
- const missing: string[] = [];
136
- const stale: string[] = [];
137
- const registered: string[] = [];
138
-
139
- for (const processName of expectedProcesses) {
140
- const record = state.processes[processName];
141
- if (record == null) {
142
- missing.push(processName);
143
- continue;
144
- }
145
-
146
- const heartbeatAt = Date.parse(record.lastHeartbeatAt);
147
- if (!Number.isFinite(heartbeatAt) || now - heartbeatAt > staleMs) {
148
- stale.push(processName);
149
- continue;
150
- }
151
-
152
- registered.push(processName);
153
- }
154
-
155
- return {
156
- ok: missing.length === 0 && stale.length === 0,
157
- missing,
158
- stale,
159
- registered,
160
- };
161
- }
162
-
163
- /**
164
- * Wait until every expected process has registered in the shared registry with a fresh heartbeat.
165
- * Call from the service main process after spawning children.
166
- */
167
- export async function waitForLogPersistReady(options: WaitForLogPersistReadyOptions): Promise<LogPersistReadySnapshot> {
168
- const timeoutMs = options.timeoutMs ?? 60_000;
169
- const pollIntervalMs = options.pollIntervalMs ?? 500;
170
- const staleMs = options.staleMs ?? 90_000;
171
-
172
- let last: LogPersistReadySnapshot = {
173
- ok: false,
174
- missing: [...options.expectedProcesses],
175
- stale: [],
176
- registered: [],
177
- };
178
-
179
- try {
180
- await pollUntil({
181
- intervalMs: pollIntervalMs,
182
- timeoutMs,
183
- until: async () => {
184
- const state = await readLogPersistRegistry(options.registryFilePath);
185
- if (state.service !== options.service) {
186
- last = {
187
- ok: false,
188
- missing: options.expectedProcesses,
189
- stale: [],
190
- registered: [],
191
- };
192
- return false;
193
- }
194
- last = evaluateLogPersistReady(state, options.expectedProcesses, staleMs, Date.now());
195
- return last.ok;
196
- },
197
- });
198
- } catch {
199
- persistLogger.warn("waitForLogPersistReady timed out", {
200
- timeoutMs,
201
- expectedProcesses: options.expectedProcesses,
202
- ...last,
203
- });
204
- }
205
-
206
- return last;
207
- }
208
-
209
- export async function snapshotLogPersistReady(options: {
210
- registryFilePath: string;
211
- service: string;
212
- expectedProcesses: string[];
213
- staleMs?: number;
214
- }): Promise<LogPersistReadySnapshot> {
215
- const state = await readLogPersistRegistry(options.registryFilePath);
216
- return evaluateLogPersistReady(state, options.expectedProcesses, options.staleMs ?? 90_000, Date.now());
217
- }
218
-
219
- export function resolveLogPersistRegistryPath(registryDir?: string): string {
220
- const dir = registryDir ?? logPersistRegistryDirFromEnv();
221
- if (dir == null) {
222
- requireEnv("LOG_PERSIST_REGISTRY_DIR");
223
- }
224
- return defaultLogPersistRegistryPath(resolve(dir ?? ""));
225
- }
@@ -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
- }