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,822 +0,0 @@
1
- import { type LogEntry, LogLevel } from "./log-manager";
2
- import { DEFAULT_LOG_PERSIST_CONFIG, resolveLogPersistConfig } from "./log-persist.config";
3
- import type {
4
- EnableLogPersistOptions,
5
- LogPersistConfig,
6
- LogPersistLane,
7
- LogPersistStats,
8
- } from "./log-persist.interface";
9
- import { computeLogBatchIdempotencyKey } from "./log-persist-batch-id";
10
- import { formatLogPersistError, isPermanentLogPersistError } from "./log-persist-errors";
11
- import { buildLogListDraft, estimateEntriesBytes, postLogBatch, takeEntriesWithinBytes } from "./log-persist-flush";
12
- import { registerLogPersistOffer } from "./log-persist-hook";
13
- import {
14
- configurePersistLoggerRateLimit,
15
- isLogPersistInternalTopic,
16
- patchPersistLoggerScope,
17
- persistLogger,
18
- } from "./log-persist-logger";
19
- import { heartbeatLogPersistProcess } from "./log-persist-registry";
20
-
21
- const HEARTBEAT_INTERVAL_MS = 30_000;
22
-
23
- function routeLane(level: LogLevel): LogPersistLane {
24
- if (level === LogLevel.CRITICAL) {
25
- return "fast";
26
- }
27
- if (level === LogLevel.ERROR || level === LogLevel.SUCCESS) {
28
- return "medium";
29
- }
30
- return "slow";
31
- }
32
-
33
- class LogPersist {
34
- private static instance: LogPersist | null = null;
35
-
36
- private enabled = false;
37
- private draining = false;
38
- private service: string | null = null;
39
- private processName: string | null = null;
40
- private registryFilePath: string | null = null;
41
- private config: LogPersistConfig = DEFAULT_LOG_PERSIST_CONFIG;
42
-
43
- private fastQueue: LogEntry[] = [];
44
- private fastRetryQueue: LogEntry[] = [];
45
- private mediumQueue: LogEntry[] = [];
46
- private slowQueue: LogEntry[] = [];
47
-
48
- private mediumDebounceTimer: ReturnType<typeof setTimeout> | null = null;
49
- private slowQueueStartedAt: number | null = null;
50
- private fastRetryTimer: ReturnType<typeof setTimeout> | null = null;
51
- private mediumRetryTimer: ReturnType<typeof setTimeout> | null = null;
52
- private slowRetryTimer: ReturnType<typeof setTimeout> | null = null;
53
- private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
54
- private lastHeartbeatAt = 0;
55
- private permanentFailureCount = 0;
56
- private circuitBreakerTripped = false;
57
-
58
- private laneFlushing: Record<LogPersistLane, boolean> = {
59
- fast: false,
60
- medium: false,
61
- slow: false,
62
- };
63
-
64
- private consecutiveFailures: Record<LogPersistLane, number> = {
65
- fast: 0,
66
- medium: 0,
67
- slow: 0,
68
- };
69
-
70
- private lastFlushAt: Record<LogPersistLane, string | null> = {
71
- fast: null,
72
- medium: null,
73
- slow: null,
74
- };
75
-
76
- private lastError: string | null = null;
77
- private lastErrorAt: string | null = null;
78
- private shutdownHookInstalled = false;
79
- private shutdownPromise: Promise<void> | null = null;
80
-
81
- static getInstance(): LogPersist {
82
- LogPersist.instance ??= new LogPersist();
83
- return LogPersist.instance;
84
- }
85
-
86
- async enable(options: EnableLogPersistOptions): Promise<void> {
87
- if (this.enabled) {
88
- persistLogger.warn("log persist core enable skipped — already enabled", {
89
- service: this.service,
90
- process: this.processName,
91
- });
92
- return;
93
- }
94
-
95
- this.service = options.service;
96
- this.processName = options.process;
97
- this.registryFilePath = options.registryFilePath ?? null;
98
- this.config = resolveLogPersistConfig(options.config);
99
- configurePersistLoggerRateLimit(this.config.errorLogRateLimitMs);
100
- this.draining = false;
101
- this.shutdownPromise = null;
102
- this.consecutiveFailures = { fast: 0, medium: 0, slow: 0 };
103
- this.permanentFailureCount = 0;
104
- this.circuitBreakerTripped = false;
105
- this.lastError = null;
106
- this.lastErrorAt = null;
107
- this.enabled = true;
108
-
109
- patchPersistLoggerScope({
110
- service: options.service,
111
- process: options.process,
112
- });
113
-
114
- persistLogger.info("log persist core enabled", {
115
- service: options.service,
116
- process: options.process,
117
- registryFilePath: this.registryFilePath,
118
- config: this.config,
119
- pid: process.pid,
120
- });
121
-
122
- registerLogPersistOffer((entry) => {
123
- this.offer(entry);
124
- });
125
-
126
- if (this.registryFilePath != null) {
127
- const { registerLogPersistProcess } = await import("./log-persist-registry");
128
- await registerLogPersistProcess({
129
- registryFilePath: this.registryFilePath,
130
- service: options.service,
131
- process: options.process,
132
- });
133
- this.startHeartbeat();
134
- }
135
-
136
- this.installShutdownHook();
137
- }
138
-
139
- async disable(): Promise<void> {
140
- if (!this.enabled && !this.draining) {
141
- persistLogger.debug("disable skipped — not enabled");
142
- return;
143
- }
144
-
145
- persistLogger.info("log persist core disabling", this.queueSnapshot());
146
- this.cancelMediumDebounce();
147
- this.cancelLaneRetryTimers();
148
- this.stopHeartbeat();
149
- await this.shutdown();
150
- persistLogger.info("log persist core disabled");
151
- }
152
-
153
- offer(entry: LogEntry): void {
154
- if (isLogPersistInternalTopic(entry.topic)) {
155
- return;
156
- }
157
-
158
- if (!this.enabled || this.draining || this.service == null || this.processName == null) {
159
- return;
160
- }
161
-
162
- void this.touchRegistryHeartbeat();
163
-
164
- const lane = routeLane(entry.level);
165
-
166
- if (lane === "fast") {
167
- this.fastQueue.push(entry);
168
- this.evictFastOverflow();
169
- void this.flushFast();
170
- return;
171
- }
172
-
173
- if (lane === "medium") {
174
- this.mediumQueue.push(entry);
175
- this.evictMediumOverflow();
176
- this.scheduleMediumFlush();
177
- return;
178
- }
179
-
180
- this.slowQueue.push(entry);
181
- if (this.slowQueueStartedAt == null) {
182
- this.slowQueueStartedAt = Date.now();
183
- }
184
- this.evictSlowOverflow();
185
- this.scheduleSlowFlush();
186
- }
187
-
188
- getStats(): LogPersistStats {
189
- return {
190
- enabled: this.enabled,
191
- draining: this.draining,
192
- circuitBreakerTripped: this.circuitBreakerTripped,
193
- permanentFailureCount: this.permanentFailureCount,
194
- service: this.service,
195
- process: this.processName,
196
- queues: {
197
- fast: this.fastQueue.length,
198
- fastRetry: this.fastRetryQueue.length,
199
- medium: this.mediumQueue.length,
200
- slow: this.slowQueue.length,
201
- },
202
- queueBytes: {
203
- fast: estimateEntriesBytes(this.fastQueue),
204
- fastRetry: estimateEntriesBytes(this.fastRetryQueue),
205
- medium: estimateEntriesBytes(this.mediumQueue),
206
- slow: estimateEntriesBytes(this.slowQueue),
207
- },
208
- mediumDebouncePending: this.mediumDebounceTimer != null,
209
- laneFlushing: { ...this.laneFlushing },
210
- consecutiveFailures: { ...this.consecutiveFailures },
211
- config: this.config,
212
- lastFlushAt: { ...this.lastFlushAt },
213
- lastError: this.lastError,
214
- lastErrorAt: this.lastErrorAt,
215
- };
216
- }
217
-
218
- async shutdown(): Promise<void> {
219
- if (this.shutdownPromise != null) {
220
- return this.shutdownPromise;
221
- }
222
-
223
- this.shutdownPromise = this.shutdownInner();
224
- return this.shutdownPromise;
225
- }
226
-
227
- private async shutdownInner(): Promise<void> {
228
- if (this.draining) {
229
- return;
230
- }
231
-
232
- this.draining = true;
233
- this.enabled = false;
234
- registerLogPersistOffer(null);
235
-
236
- persistLogger.info("shutdown flush started", this.queueSnapshot());
237
-
238
- this.cancelMediumDebounce();
239
- await this.drainQueues(this.config.shutdownDrainTimeoutMs);
240
-
241
- if (this.registryFilePath != null && this.processName != null) {
242
- const { unregisterLogPersistProcess } = await import("./log-persist-registry");
243
- await unregisterLogPersistProcess({
244
- registryFilePath: this.registryFilePath,
245
- process: this.processName,
246
- });
247
- }
248
-
249
- if (this.hasQueued()) {
250
- persistLogger.warn("shutdown drain incomplete — queued logs dropped", {
251
- ...this.queueSnapshot(),
252
- shutdownDrainTimeoutMs: this.config.shutdownDrainTimeoutMs,
253
- });
254
- this.fastQueue = [];
255
- this.fastRetryQueue = [];
256
- this.mediumQueue = [];
257
- this.slowQueue = [];
258
- this.slowQueueStartedAt = null;
259
- }
260
-
261
- persistLogger.info("shutdown flush completed", this.queueSnapshot());
262
- }
263
-
264
- private hasQueued(): boolean {
265
- return (
266
- this.fastQueue.length > 0 ||
267
- this.fastRetryQueue.length > 0 ||
268
- this.mediumQueue.length > 0 ||
269
- this.slowQueue.length > 0
270
- );
271
- }
272
-
273
- private async drainQueues(timeoutMs: number): Promise<void> {
274
- const deadline = Date.now() + timeoutMs;
275
- while (this.hasQueued() && Date.now() < deadline) {
276
- await this.flushFast();
277
- await this.flushMedium();
278
- await this.flushSlow();
279
- }
280
- }
281
-
282
- private recordLaneError(lane: LogPersistLane, error: unknown): void {
283
- const message = error instanceof Error ? error.message : String(error);
284
- this.lastError = message;
285
- this.lastErrorAt = new Date().toISOString();
286
- this.consecutiveFailures[lane] += 1;
287
- }
288
-
289
- private recordLaneSuccess(lane: LogPersistLane): void {
290
- this.lastError = null;
291
- this.consecutiveFailures[lane] = 0;
292
- this.permanentFailureCount = 0;
293
- }
294
-
295
- private handleLaneFlushFailure(
296
- lane: LogPersistLane,
297
- error: unknown,
298
- scheduleRetry: () => void,
299
- logMessage: string,
300
- context: Record<string, unknown>,
301
- ): void {
302
- this.recordLaneError(lane, error);
303
- const message = this.lastError ?? formatLogPersistError(error);
304
-
305
- if (isPermanentLogPersistError(error)) {
306
- this.permanentFailureCount += 1;
307
- if (this.permanentFailureCount >= this.config.circuitBreakerPermanentFailureThreshold) {
308
- this.tripCircuitBreaker(message);
309
- return;
310
- }
311
- }
312
-
313
- persistLogger.error(logMessage, {
314
- ...context,
315
- error: message,
316
- retryInMs: this.config.laneRetryIntervalMs,
317
- permanentFailureCount: this.permanentFailureCount,
318
- });
319
- scheduleRetry();
320
- }
321
-
322
- private tripCircuitBreaker(reason: string): void {
323
- if (this.circuitBreakerTripped) {
324
- return;
325
- }
326
- this.circuitBreakerTripped = true;
327
-
328
- this.cancelMediumDebounce();
329
- this.cancelLaneRetryTimers();
330
- this.stopHeartbeat();
331
-
332
- const dropped = this.queueSnapshot();
333
- this.fastQueue = [];
334
- this.fastRetryQueue = [];
335
- this.mediumQueue = [];
336
- this.slowQueue = [];
337
- this.slowQueueStartedAt = null;
338
-
339
- this.enabled = false;
340
- registerLogPersistOffer(null);
341
-
342
- persistLogger.error("circuit breaker tripped — log persist disabled", {
343
- reason,
344
- permanentFailureCount: this.permanentFailureCount,
345
- threshold: this.config.circuitBreakerPermanentFailureThreshold,
346
- dropped,
347
- });
348
- }
349
-
350
- private cancelLaneRetryTimers(): void {
351
- if (this.fastRetryTimer != null) {
352
- clearTimeout(this.fastRetryTimer);
353
- this.fastRetryTimer = null;
354
- }
355
- if (this.mediumRetryTimer != null) {
356
- clearTimeout(this.mediumRetryTimer);
357
- this.mediumRetryTimer = null;
358
- }
359
- if (this.slowRetryTimer != null) {
360
- clearTimeout(this.slowRetryTimer);
361
- this.slowRetryTimer = null;
362
- }
363
- }
364
-
365
- private scheduleLaneRetry(lane: "fast" | "medium" | "slow", flush: () => void): void {
366
- const retryMs = this.config.laneRetryIntervalMs;
367
- const existing =
368
- lane === "fast" ? this.fastRetryTimer : lane === "medium" ? this.mediumRetryTimer : this.slowRetryTimer;
369
- if (existing != null) {
370
- return;
371
- }
372
-
373
- persistLogger.warn(`${lane} flush retry scheduled`, { retryInMs: retryMs });
374
- const timer = setTimeout(() => {
375
- if (lane === "fast") {
376
- this.fastRetryTimer = null;
377
- } else if (lane === "medium") {
378
- this.mediumRetryTimer = null;
379
- } else {
380
- this.slowRetryTimer = null;
381
- }
382
- persistLogger.debug(`${lane} flush retry timer fired`);
383
- flush();
384
- }, retryMs);
385
- timer.unref?.();
386
-
387
- if (lane === "fast") {
388
- this.fastRetryTimer = timer;
389
- } else if (lane === "medium") {
390
- this.mediumRetryTimer = timer;
391
- } else {
392
- this.slowRetryTimer = timer;
393
- }
394
- }
395
-
396
- private queueSnapshot(): Record<string, unknown> {
397
- return {
398
- queues: {
399
- fast: this.fastQueue.length,
400
- fastRetry: this.fastRetryQueue.length,
401
- medium: this.mediumQueue.length,
402
- slow: this.slowQueue.length,
403
- },
404
- queueBytes: {
405
- fast: estimateEntriesBytes(this.fastQueue),
406
- fastRetry: estimateEntriesBytes(this.fastRetryQueue),
407
- medium: estimateEntriesBytes(this.mediumQueue),
408
- slow: estimateEntriesBytes(this.slowQueue),
409
- },
410
- laneFlushing: { ...this.laneFlushing },
411
- consecutiveFailures: { ...this.consecutiveFailures },
412
- lastError: this.lastError,
413
- };
414
- }
415
-
416
- private installShutdownHook(): void {
417
- if (this.shutdownHookInstalled) {
418
- return;
419
- }
420
- this.shutdownHookInstalled = true;
421
-
422
- const handler = (signal: NodeJS.Signals) => {
423
- persistLogger.info("shutdown signal received", { signal });
424
- void this.shutdown();
425
- };
426
- process.once("SIGTERM", handler);
427
- process.once("SIGINT", handler);
428
- }
429
-
430
- private startHeartbeat(): void {
431
- this.stopHeartbeat();
432
- this.heartbeatTimer = setInterval(() => {
433
- void this.touchRegistryHeartbeat(true);
434
- }, HEARTBEAT_INTERVAL_MS);
435
- this.heartbeatTimer.unref?.();
436
- }
437
-
438
- private stopHeartbeat(): void {
439
- if (this.heartbeatTimer != null) {
440
- clearInterval(this.heartbeatTimer);
441
- this.heartbeatTimer = null;
442
- }
443
- }
444
-
445
- private async touchRegistryHeartbeat(force = false): Promise<void> {
446
- if (this.registryFilePath == null || this.processName == null) {
447
- return;
448
- }
449
- const now = Date.now();
450
- if (!force && now - this.lastHeartbeatAt < HEARTBEAT_INTERVAL_MS) {
451
- return;
452
- }
453
- this.lastHeartbeatAt = now;
454
- await heartbeatLogPersistProcess({
455
- registryFilePath: this.registryFilePath,
456
- process: this.processName,
457
- });
458
- }
459
-
460
- private scheduleMediumFlush(): void {
461
- if (this.mediumRetryTimer != null) {
462
- return;
463
- }
464
- const { debounceMs, maxEntries, maxBytes } = this.config.medium;
465
- const queueBytes = estimateEntriesBytes(this.mediumQueue);
466
- if (this.mediumQueue.length >= maxEntries || queueBytes >= maxBytes) {
467
- persistLogger.warn("medium flush scheduled — threshold", {
468
- reason: this.mediumQueue.length >= maxEntries ? "maxEntries" : "maxBytes",
469
- queueLength: this.mediumQueue.length,
470
- maxEntries,
471
- queueBytes,
472
- maxBytes,
473
- });
474
- this.cancelMediumDebounce();
475
- void this.flushMedium();
476
- return;
477
- }
478
-
479
- if (this.mediumDebounceTimer == null && this.mediumQueue.length > 0) {
480
- persistLogger.debug("medium flush debounce scheduled", {
481
- debounceMs,
482
- queueLength: this.mediumQueue.length,
483
- queueBytes,
484
- });
485
- this.mediumDebounceTimer = setTimeout(() => {
486
- this.mediumDebounceTimer = null;
487
- persistLogger.debug("medium flush debounce fired");
488
- void this.flushMedium();
489
- }, debounceMs);
490
- this.mediumDebounceTimer.unref?.();
491
- }
492
- }
493
-
494
- private cancelMediumDebounce(): void {
495
- if (this.mediumDebounceTimer != null) {
496
- clearTimeout(this.mediumDebounceTimer);
497
- this.mediumDebounceTimer = null;
498
- persistLogger.debug("medium flush debounce cancelled");
499
- }
500
- }
501
-
502
- private scheduleSlowFlush(): void {
503
- if (this.slowRetryTimer != null) {
504
- return;
505
- }
506
- const { maxEntries, maxBytes, maxAgeMs } = this.config.slow;
507
- const queueBytes = estimateEntriesBytes(this.slowQueue);
508
- const ageMs = this.slowQueueStartedAt != null ? Date.now() - this.slowQueueStartedAt : 0;
509
- if (this.slowQueue.length >= maxEntries || queueBytes >= maxBytes || ageMs >= maxAgeMs) {
510
- persistLogger.debug("slow flush scheduled — threshold", {
511
- reason:
512
- this.slowQueue.length >= maxEntries
513
- ? "maxEntries"
514
- : queueBytes >= maxBytes
515
- ? "maxBytes"
516
- : "maxAgeMs",
517
- queueLength: this.slowQueue.length,
518
- maxEntries,
519
- queueBytes,
520
- maxBytes,
521
- ageMs,
522
- maxAgeMs,
523
- });
524
- void this.flushSlow();
525
- }
526
- }
527
-
528
- private evictFastOverflow(): void {
529
- const { maxBufferEntries } = this.config.fast;
530
- let evicted = 0;
531
- while (this.fastQueue.length + this.fastRetryQueue.length > maxBufferEntries) {
532
- const removed = this.fastQueue.length > 0 ? this.fastQueue.shift() : this.fastRetryQueue.shift();
533
- evicted += 1;
534
- persistLogger.warn("fast queue overflow — evicted entry", {
535
- evictedLevel: removed?.level,
536
- evictedTopic: removed?.topic,
537
- fromRetryQueue: this.fastQueue.length === 0,
538
- maxBufferEntries,
539
- });
540
- }
541
- if (evicted > 0) {
542
- persistLogger.warn("fast queue overflow summary", { evicted, maxBufferEntries });
543
- }
544
- }
545
-
546
- private evictMediumOverflow(): void {
547
- const { maxBufferEntries } = this.config.medium;
548
- let evicted = 0;
549
- while (this.mediumQueue.length > maxBufferEntries) {
550
- const successIndex = this.mediumQueue.findIndex((entry) => entry.level === LogLevel.SUCCESS);
551
- const removed = successIndex >= 0 ? this.mediumQueue.splice(successIndex, 1)[0] : this.mediumQueue.shift();
552
- evicted += 1;
553
- persistLogger.warn("medium queue overflow — evicted entry", {
554
- evictedLevel: removed?.level,
555
- evictedTopic: removed?.topic,
556
- queueLength: this.mediumQueue.length,
557
- maxBufferEntries,
558
- });
559
- }
560
- if (evicted > 0) {
561
- persistLogger.warn("medium queue overflow summary", {
562
- evicted,
563
- queueLength: this.mediumQueue.length,
564
- maxBufferEntries,
565
- });
566
- }
567
- }
568
-
569
- private evictSlowOverflow(): void {
570
- const { maxBufferEntries } = this.config.slow;
571
- let evicted = 0;
572
- while (this.slowQueue.length > maxBufferEntries) {
573
- const debugIndex = this.slowQueue.findIndex((entry) => entry.level === LogLevel.DEBUG);
574
- const removed = debugIndex >= 0 ? this.slowQueue.splice(debugIndex, 1)[0] : this.slowQueue.shift();
575
- evicted += 1;
576
- persistLogger.warn("slow queue overflow — evicted entry", {
577
- evictedLevel: removed?.level,
578
- evictedTopic: removed?.topic,
579
- queueLength: this.slowQueue.length,
580
- maxBufferEntries,
581
- });
582
- }
583
- if (evicted > 0) {
584
- persistLogger.warn("slow queue overflow summary", {
585
- evicted,
586
- queueLength: this.slowQueue.length,
587
- maxBufferEntries,
588
- });
589
- }
590
- if (this.slowQueue.length === 0) {
591
- this.slowQueueStartedAt = null;
592
- }
593
- }
594
-
595
- private async flushFast(): Promise<void> {
596
- if (this.laneFlushing.fast) {
597
- persistLogger.debug("fast flush skipped — already flushing");
598
- return;
599
- }
600
- if (this.service == null || this.processName == null) {
601
- return;
602
- }
603
-
604
- this.laneFlushing.fast = true;
605
- try {
606
- while (this.fastQueue.length > 0 || this.fastRetryQueue.length > 0) {
607
- const fromRetry = this.fastQueue.length === 0;
608
- const entry = this.fastQueue.shift() ?? this.fastRetryQueue.shift();
609
- if (entry == null) {
610
- break;
611
- }
612
-
613
- const idempotencyKey = computeLogBatchIdempotencyKey({
614
- service: this.service,
615
- process: this.processName,
616
- lane: "fast",
617
- entries: [entry],
618
- });
619
-
620
- try {
621
- const draft = buildLogListDraft({
622
- service: this.service,
623
- process: this.processName,
624
- lane: "fast",
625
- idempotencyKey,
626
- entries: [entry],
627
- });
628
- persistLogger.debug("fast flush posting batch", {
629
- idempotencyKey,
630
- fromRetry,
631
- level: entry.level,
632
- topic: entry.topic,
633
- });
634
- await postLogBatch(draft, this.config.postTimeoutMs);
635
- this.lastFlushAt.fast = new Date().toISOString();
636
- this.recordLaneSuccess("fast");
637
- persistLogger.info("fast lane flushed", {
638
- idempotencyKey,
639
- level: entry.level,
640
- topic: entry.topic,
641
- fastRetryRemaining: this.fastRetryQueue.length,
642
- });
643
- } catch (error) {
644
- this.fastRetryQueue.push(entry);
645
- this.handleLaneFlushFailure(
646
- "fast",
647
- error,
648
- () => this.scheduleLaneRetry("fast", () => void this.flushFast()),
649
- "fast flush failed — queued for retry",
650
- {
651
- idempotencyKey,
652
- fastRetryQueue: this.fastRetryQueue.length,
653
- },
654
- );
655
- break;
656
- }
657
- }
658
- } finally {
659
- this.laneFlushing.fast = false;
660
- }
661
- }
662
-
663
- private async flushMedium(): Promise<void> {
664
- if (this.laneFlushing.medium) {
665
- persistLogger.debug("medium flush skipped — already flushing");
666
- return;
667
- }
668
- if (this.mediumQueue.length === 0) {
669
- return;
670
- }
671
- if (this.service == null || this.processName == null) {
672
- return;
673
- }
674
-
675
- this.laneFlushing.medium = true;
676
- try {
677
- while (this.mediumQueue.length > 0) {
678
- const batch = takeEntriesWithinBytes(this.mediumQueue, this.config.medium.maxBytes);
679
- if (batch.length === 0) {
680
- break;
681
- }
682
-
683
- const idempotencyKey = computeLogBatchIdempotencyKey({
684
- service: this.service,
685
- process: this.processName,
686
- lane: "medium",
687
- entries: batch,
688
- });
689
- const batchBytes = estimateEntriesBytes(batch);
690
-
691
- try {
692
- persistLogger.debug("medium flush posting batch", {
693
- idempotencyKey,
694
- batchSize: batch.length,
695
- batchBytes,
696
- queueRemaining: this.mediumQueue.length,
697
- });
698
- const draft = buildLogListDraft({
699
- service: this.service,
700
- process: this.processName,
701
- lane: "medium",
702
- idempotencyKey,
703
- entries: batch,
704
- });
705
- await postLogBatch(draft, this.config.postTimeoutMs);
706
- this.mediumQueue.splice(0, batch.length);
707
- this.lastFlushAt.medium = new Date().toISOString();
708
- this.recordLaneSuccess("medium");
709
- persistLogger.info("medium lane flushed", {
710
- idempotencyKey,
711
- batchSize: batch.length,
712
- batchBytes,
713
- queueRemaining: this.mediumQueue.length,
714
- });
715
- } catch (error) {
716
- this.handleLaneFlushFailure(
717
- "medium",
718
- error,
719
- () => this.scheduleLaneRetry("medium", () => void this.flushMedium()),
720
- "medium flush failed — entries retained",
721
- {
722
- idempotencyKey,
723
- batchSize: batch.length,
724
- batchBytes,
725
- queueRemaining: this.mediumQueue.length,
726
- },
727
- );
728
- break;
729
- }
730
- }
731
- } finally {
732
- this.laneFlushing.medium = false;
733
- }
734
-
735
- if (this.mediumQueue.length > 0 && !this.laneFlushing.medium && this.mediumRetryTimer == null) {
736
- this.scheduleMediumFlush();
737
- }
738
- }
739
-
740
- private async flushSlow(): Promise<void> {
741
- if (this.laneFlushing.slow) {
742
- persistLogger.debug("slow flush skipped — already flushing");
743
- return;
744
- }
745
- if (this.slowQueue.length === 0) {
746
- return;
747
- }
748
- if (this.service == null || this.processName == null) {
749
- return;
750
- }
751
-
752
- this.laneFlushing.slow = true;
753
- try {
754
- while (this.slowQueue.length > 0) {
755
- const batch = takeEntriesWithinBytes(this.slowQueue, this.config.slow.maxBytes);
756
- if (batch.length === 0) {
757
- break;
758
- }
759
-
760
- const idempotencyKey = computeLogBatchIdempotencyKey({
761
- service: this.service,
762
- process: this.processName,
763
- lane: "slow",
764
- entries: batch,
765
- });
766
- const batchBytes = estimateEntriesBytes(batch);
767
-
768
- try {
769
- persistLogger.debug("slow flush posting batch", {
770
- idempotencyKey,
771
- batchSize: batch.length,
772
- batchBytes,
773
- queueRemaining: this.slowQueue.length,
774
- });
775
- const draft = buildLogListDraft({
776
- service: this.service,
777
- process: this.processName,
778
- lane: "slow",
779
- idempotencyKey,
780
- entries: batch,
781
- });
782
- await postLogBatch(draft, this.config.postTimeoutMs);
783
- this.slowQueue.splice(0, batch.length);
784
- this.lastFlushAt.slow = new Date().toISOString();
785
- this.recordLaneSuccess("slow");
786
- persistLogger.info("slow lane flushed", {
787
- idempotencyKey,
788
- batchSize: batch.length,
789
- batchBytes,
790
- queueRemaining: this.slowQueue.length,
791
- });
792
- } catch (error) {
793
- this.handleLaneFlushFailure(
794
- "slow",
795
- error,
796
- () => this.scheduleLaneRetry("slow", () => void this.flushSlow()),
797
- "slow flush failed — entries retained",
798
- {
799
- idempotencyKey,
800
- batchSize: batch.length,
801
- batchBytes,
802
- queueRemaining: this.slowQueue.length,
803
- },
804
- );
805
- break;
806
- }
807
- }
808
-
809
- if (this.slowQueue.length === 0) {
810
- this.slowQueueStartedAt = null;
811
- }
812
- } finally {
813
- this.laneFlushing.slow = false;
814
- }
815
-
816
- if (this.slowQueue.length > 0 && !this.laneFlushing.slow && this.slowRetryTimer == null) {
817
- this.scheduleSlowFlush();
818
- }
819
- }
820
- }
821
-
822
- export { LogPersist };