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.
Files changed (144) hide show
  1. package/dist/browser.d.ts +1 -1
  2. package/dist/browser.d.ts.map +1 -1
  3. package/dist/browser.js +1 -1
  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-BzbTJBsM.js.map +1 -1
  7. package/dist/chunks/{task-queue-H4mpSW8R.js → task-queue-6X3aglJh.js} +108 -3
  8. package/dist/chunks/task-queue-6X3aglJh.js.map +1 -0
  9. package/dist/node/preload/run-service-preload.d.ts.map +1 -1
  10. package/dist/node/preload/service-preload.interface.d.ts +3 -1
  11. package/dist/node/preload/service-preload.interface.d.ts.map +1 -1
  12. package/dist/node/process/managed-child-processes.d.ts.map +1 -1
  13. package/dist/node/service-guard/service-guard-node.d.ts.map +1 -1
  14. package/dist/node/service-host/index.d.ts +1 -0
  15. package/dist/node/service-host/index.d.ts.map +1 -1
  16. package/dist/node/service-host/service-host.d.ts +5 -1
  17. package/dist/node/service-host/service-host.d.ts.map +1 -1
  18. package/dist/node.d.ts +10 -5
  19. package/dist/node.d.ts.map +1 -1
  20. package/dist/node.js +492 -47
  21. package/dist/node.js.map +1 -1
  22. package/dist/preload.js +9 -6
  23. package/dist/preload.js.map +1 -1
  24. package/dist/service/index.d.ts +2 -2
  25. package/dist/service/index.d.ts.map +1 -1
  26. package/dist/service/registry.service.d.ts +19 -0
  27. package/dist/service/registry.service.d.ts.map +1 -1
  28. package/dist/shared/log/core/create-logger.d.ts.map +1 -0
  29. package/dist/shared/log/{log-batch.d.ts → core/log-batch.d.ts} +4 -4
  30. package/dist/shared/log/core/log-batch.d.ts.map +1 -0
  31. package/dist/shared/log/core/log-manager.d.ts.map +1 -0
  32. package/dist/shared/log/core/log-min-level.d.ts.map +1 -0
  33. package/dist/shared/log/core/log-scope.d.ts.map +1 -0
  34. package/dist/shared/log/index.d.ts +13 -13
  35. package/dist/shared/log/index.d.ts.map +1 -1
  36. package/dist/shared/log/spool/collector.d.ts +21 -0
  37. package/dist/shared/log/spool/collector.d.ts.map +1 -0
  38. package/dist/shared/log/spool/config.d.ts +7 -0
  39. package/dist/shared/log/spool/config.d.ts.map +1 -0
  40. package/dist/shared/log/spool/coordinator.d.ts +18 -0
  41. package/dist/shared/log/spool/coordinator.d.ts.map +1 -0
  42. package/dist/shared/log/spool/enable.d.ts +23 -0
  43. package/dist/shared/log/spool/enable.d.ts.map +1 -0
  44. package/dist/shared/log/spool/file.d.ts +21 -0
  45. package/dist/shared/log/spool/file.d.ts.map +1 -0
  46. package/dist/shared/log/spool/interface.d.ts +55 -0
  47. package/dist/shared/log/spool/interface.d.ts.map +1 -0
  48. package/dist/shared/log/spool/paths.d.ts +6 -0
  49. package/dist/shared/log/spool/paths.d.ts.map +1 -0
  50. package/dist/shared/log/spool/process-roles.d.ts +17 -0
  51. package/dist/shared/log/spool/process-roles.d.ts.map +1 -0
  52. package/dist/shared/log/spool/register-collect-log-runner.d.ts +15 -0
  53. package/dist/shared/log/spool/register-collect-log-runner.d.ts.map +1 -0
  54. package/dist/shared/log/spool/service-lifecycle.d.ts +13 -0
  55. package/dist/shared/log/spool/service-lifecycle.d.ts.map +1 -0
  56. package/dist/shared/log/spool/writer.d.ts +32 -0
  57. package/dist/shared/log/spool/writer.d.ts.map +1 -0
  58. package/dist/shared/log/{log-persist-batch-id.d.ts → upload/batch-id.d.ts} +3 -3
  59. package/dist/shared/log/upload/batch-id.d.ts.map +1 -0
  60. package/dist/shared/log/upload/env.d.ts +7 -0
  61. package/dist/shared/log/upload/env.d.ts.map +1 -0
  62. package/dist/shared/log/{log-persist-flush.d.ts → upload/flush.d.ts} +3 -3
  63. package/dist/shared/log/upload/flush.d.ts.map +1 -0
  64. package/dist/shared/log/{log-persist-hook.d.ts → upload/hook.d.ts} +2 -2
  65. package/dist/shared/log/upload/hook.d.ts.map +1 -0
  66. package/dist/shared/log/upload/interface.d.ts +33 -0
  67. package/dist/shared/log/upload/interface.d.ts.map +1 -0
  68. package/dist/shared/log/upload/logger.d.ts +22 -0
  69. package/dist/shared/log/upload/logger.d.ts.map +1 -0
  70. package/package.json +1 -1
  71. package/src/browser.ts +1 -0
  72. package/src/node/preload/run-service-preload.ts +11 -6
  73. package/src/node/preload/service-preload.interface.ts +3 -1
  74. package/src/node/process/create-managed-child-processes.ts +1 -1
  75. package/src/node/process/managed-child-processes.ts +8 -1
  76. package/src/node/service-guard/service-guard-node.ts +5 -0
  77. package/src/node/service-host/index.ts +4 -0
  78. package/src/node/service-host/service-host.ts +22 -3
  79. package/src/node.ts +26 -19
  80. package/src/service/index.ts +3 -0
  81. package/src/service/registry.service.ts +142 -0
  82. package/src/shared/log/README.md +206 -0
  83. package/src/shared/log/{log-batch.ts → core/log-batch.ts} +4 -4
  84. package/src/shared/log/{log-manager.ts → core/log-manager.ts} +3 -3
  85. package/src/shared/log/{log-min-level.ts → core/log-min-level.ts} +1 -1
  86. package/src/shared/log/{log-scope.ts → core/log-scope.ts} +1 -1
  87. package/src/shared/log/index.ts +13 -18
  88. package/src/shared/log/spool/collector.ts +330 -0
  89. package/src/shared/log/spool/config.ts +56 -0
  90. package/src/shared/log/spool/coordinator.ts +55 -0
  91. package/src/shared/log/spool/enable.ts +136 -0
  92. package/src/shared/log/spool/file.ts +75 -0
  93. package/src/shared/log/spool/interface.ts +62 -0
  94. package/src/shared/log/spool/paths.ts +30 -0
  95. package/src/shared/log/spool/process-roles.ts +75 -0
  96. package/src/shared/log/spool/register-collect-log-runner.ts +63 -0
  97. package/src/shared/log/spool/service-lifecycle.ts +35 -0
  98. package/src/shared/log/spool/writer.ts +273 -0
  99. package/src/shared/log/{log-persist-batch-id.ts → upload/batch-id.ts} +2 -2
  100. package/src/shared/log/upload/env.ts +26 -0
  101. package/src/shared/log/{log-persist-flush.ts → upload/flush.ts} +8 -7
  102. package/src/shared/log/{log-persist-hook.ts → upload/hook.ts} +1 -1
  103. package/src/shared/log/upload/interface.ts +38 -0
  104. package/src/shared/log/upload/logger.ts +37 -0
  105. package/dist/chunks/enable-log-persist-Dl55_mv2.js +0 -1674
  106. package/dist/chunks/enable-log-persist-Dl55_mv2.js.map +0 -1
  107. package/dist/chunks/task-queue-H4mpSW8R.js.map +0 -1
  108. package/dist/shared/log/create-logger.d.ts.map +0 -1
  109. package/dist/shared/log/enable-log-persist.d.ts +0 -28
  110. package/dist/shared/log/enable-log-persist.d.ts.map +0 -1
  111. package/dist/shared/log/log-batch.d.ts.map +0 -1
  112. package/dist/shared/log/log-manager.d.ts.map +0 -1
  113. package/dist/shared/log/log-min-level.d.ts.map +0 -1
  114. package/dist/shared/log/log-persist-batch-id.d.ts.map +0 -1
  115. package/dist/shared/log/log-persist-coordinator.d.ts +0 -37
  116. package/dist/shared/log/log-persist-coordinator.d.ts.map +0 -1
  117. package/dist/shared/log/log-persist-errors.d.ts +0 -4
  118. package/dist/shared/log/log-persist-errors.d.ts.map +0 -1
  119. package/dist/shared/log/log-persist-flush.d.ts.map +0 -1
  120. package/dist/shared/log/log-persist-hook.d.ts.map +0 -1
  121. package/dist/shared/log/log-persist-logger.d.ts +0 -16
  122. package/dist/shared/log/log-persist-logger.d.ts.map +0 -1
  123. package/dist/shared/log/log-persist-registry.d.ts +0 -18
  124. package/dist/shared/log/log-persist-registry.d.ts.map +0 -1
  125. package/dist/shared/log/log-persist.config.d.ts +0 -9
  126. package/dist/shared/log/log-persist.config.d.ts.map +0 -1
  127. package/dist/shared/log/log-persist.d.ts +0 -62
  128. package/dist/shared/log/log-persist.d.ts.map +0 -1
  129. package/dist/shared/log/log-persist.interface.d.ts +0 -132
  130. package/dist/shared/log/log-persist.interface.d.ts.map +0 -1
  131. package/dist/shared/log/log-scope.d.ts.map +0 -1
  132. package/src/shared/log/enable-log-persist.ts +0 -225
  133. package/src/shared/log/log-persist-coordinator.ts +0 -107
  134. package/src/shared/log/log-persist-errors.ts +0 -16
  135. package/src/shared/log/log-persist-logger.ts +0 -61
  136. package/src/shared/log/log-persist-registry.ts +0 -252
  137. package/src/shared/log/log-persist.config.ts +0 -115
  138. package/src/shared/log/log-persist.interface.ts +0 -150
  139. package/src/shared/log/log-persist.ts +0 -822
  140. /package/dist/shared/log/{create-logger.d.ts → core/create-logger.d.ts} +0 -0
  141. /package/dist/shared/log/{log-manager.d.ts → core/log-manager.d.ts} +0 -0
  142. /package/dist/shared/log/{log-min-level.d.ts → core/log-min-level.d.ts} +0 -0
  143. /package/dist/shared/log/{log-scope.d.ts → core/log-scope.d.ts} +0 -0
  144. /package/src/shared/log/{create-logger.ts → core/create-logger.ts} +0 -0
@@ -0,0 +1,874 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { resolve, dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { g as createLogger, c as LOG_PERSIST_LOADER_KEY, a as CategoryList, D as postLogListCreate, q as isCreateTargetAlreadyExistsError, W as registerLogPersistOffer } from './list.api-BzbTJBsM.js';
5
+ import { mkdir, writeFile, rename } from 'node:fs/promises';
6
+ import { createHash } from 'node:crypto';
7
+
8
+ function readEnv(name, env = process.env) {
9
+ const value = env[name]?.trim();
10
+ if (value == null || value === "") {
11
+ return undefined;
12
+ }
13
+ return value;
14
+ }
15
+ function requireEnv(name, env = process.env) {
16
+ const value = readEnv(name, env);
17
+ if (value == null) {
18
+ throw new Error(`Missing required environment variable: ${name}`);
19
+ }
20
+ return value;
21
+ }
22
+
23
+ function envInt(name, defaultValue, options) {
24
+ const env = options?.env ?? process.env;
25
+ const raw = readEnv(name, env);
26
+ if (raw == null) {
27
+ return defaultValue;
28
+ }
29
+ const value = Number(raw);
30
+ if (!Number.isInteger(value)) {
31
+ throw new Error(`Invalid ${name}: ${raw}`);
32
+ }
33
+ const min = options?.min;
34
+ if (min != null && value < min) {
35
+ throw new Error(`Invalid ${name}: ${raw}`);
36
+ }
37
+ const max = options?.max;
38
+ if (max != null && value > max) {
39
+ throw new Error(`Invalid ${name}: ${raw}`);
40
+ }
41
+ return value;
42
+ }
43
+ function envMs(name, defaultMs, options) {
44
+ return envInt(name, defaultMs, { ...options, min: options?.min ?? 1 });
45
+ }
46
+ function envPort(defaultPort = 3100, options) {
47
+ const envKey = options?.envKey ?? "PORT";
48
+ return envInt(envKey, defaultPort, { ...options, min: 1, max: 65535 });
49
+ }
50
+ function envNumber(name, defaultValue, options) {
51
+ const env = options?.env ?? process.env;
52
+ const raw = readEnv(name, env);
53
+ if (raw == null) {
54
+ return defaultValue;
55
+ }
56
+ const value = Number(raw);
57
+ if (!Number.isFinite(value)) {
58
+ throw new Error(`Invalid ${name}: ${raw}`);
59
+ }
60
+ const min = options?.min;
61
+ const resolved = options?.floor === true ? Math.floor(value) : value;
62
+ if (min != null && resolved < min) {
63
+ throw new Error(`Invalid ${name}: ${raw}`);
64
+ }
65
+ return resolved;
66
+ }
67
+ function envBool(name, defaultValue = false, env = process.env) {
68
+ const raw = readEnv(name, env)?.toLowerCase();
69
+ if (raw == null) {
70
+ return defaultValue;
71
+ }
72
+ return raw === "1" || raw === "true" || raw === "yes";
73
+ }
74
+
75
+ const DEFAULT_ENV_FILES = [".env.local", ".env"];
76
+ const PROD_ENV_FILES = [".env.prod"];
77
+ const SERVICE_ENV_PROFILE_ENV_KEY = "SERVICE_ENV_PROFILE";
78
+ const SERVICE_ENV_PROFILE_PROD = "prod";
79
+ const CLI_PROD_FLAG = "--prod";
80
+ /** Resolve active env profile from `SERVICE_ENV_PROFILE` or `--prod` on argv. */
81
+ function envProfileFromProcess(options) {
82
+ const env = options?.env ?? process.env;
83
+ const argv = options?.argv ?? process.argv;
84
+ const fromEnv = env[SERVICE_ENV_PROFILE_ENV_KEY]?.trim();
85
+ if (fromEnv != null && fromEnv !== "") {
86
+ return fromEnv;
87
+ }
88
+ if (argv.includes(CLI_PROD_FLAG)) {
89
+ return SERVICE_ENV_PROFILE_PROD;
90
+ }
91
+ return undefined;
92
+ }
93
+ /** Default env file list for a profile (`prod` → `.env.prod`; otherwise `.env.local` → `.env`). */
94
+ function resolveDefaultEnvFiles(profile) {
95
+ if (profile === SERVICE_ENV_PROFILE_PROD) {
96
+ return PROD_ENV_FILES;
97
+ }
98
+ return DEFAULT_ENV_FILES;
99
+ }
100
+ /** Pin `SERVICE_ENV_PROFILE=prod` when `--prod` is on argv (for child process inheritance). */
101
+ function pinEnvProfileFromArgv(env = process.env, argv = process.argv) {
102
+ if (env[SERVICE_ENV_PROFILE_ENV_KEY]?.trim()) {
103
+ return;
104
+ }
105
+ if (argv.includes(CLI_PROD_FLAG)) {
106
+ env[SERVICE_ENV_PROFILE_ENV_KEY] = SERVICE_ENV_PROFILE_PROD;
107
+ }
108
+ }
109
+ function stripQuotes(value) {
110
+ return value.replace(/^["']|["']$/g, "");
111
+ }
112
+ /** Parse one KEY=VALUE line; returns null if line should be skipped. */
113
+ function parseEnvLine(line) {
114
+ const trimmed = line.trim();
115
+ if (trimmed === "" || trimmed.startsWith("#")) {
116
+ return null;
117
+ }
118
+ const eq = trimmed.indexOf("=");
119
+ if (eq === -1) {
120
+ return null;
121
+ }
122
+ const key = trimmed.slice(0, eq).trim();
123
+ const raw = trimmed.slice(eq + 1).trim();
124
+ return { key, value: stripQuotes(raw) };
125
+ }
126
+ function parseEnvFileContent(content, env) {
127
+ for (const line of content.split("\n")) {
128
+ const parsed = parseEnvLine(line);
129
+ if (parsed == null) {
130
+ continue;
131
+ }
132
+ if (env[parsed.key] == null || env[parsed.key] === "") {
133
+ env[parsed.key] = parsed.value;
134
+ }
135
+ }
136
+ }
137
+ function parseEnvFile(filePath, env) {
138
+ if (!existsSync(filePath)) {
139
+ return;
140
+ }
141
+ parseEnvFileContent(readFileSync(filePath, "utf8"), env);
142
+ }
143
+ /**
144
+ * Load env files into `process.env` (or `options.env`).
145
+ * Default files: `.env.local` → `.env`, or `.env.prod` when profile is `prod` / `--prod`.
146
+ * Existing non-empty env values are not overwritten.
147
+ */
148
+ function loadEnvFiles(root, options) {
149
+ const env = options?.env ?? process.env;
150
+ const files = options?.files ?? resolveDefaultEnvFiles(envProfileFromProcess({ env }));
151
+ for (const name of files) {
152
+ parseEnvFile(resolve(root, name), env);
153
+ }
154
+ options?.afterLoad?.();
155
+ }
156
+
157
+ /**
158
+ * Resolve project root from a module URL (typically `import.meta.url`).
159
+ * @param relativePath Path relative to the module file (default `".."` = parent directory).
160
+ */
161
+ function resolveProjectRootFromModule(importMetaUrl, relativePath = "..") {
162
+ return resolve(fileURLToPath(new URL(relativePath, importMetaUrl)));
163
+ }
164
+ /**
165
+ * Walk upward from `importMetaUrl` until a `package.json` with matching `name` is found.
166
+ * Use for esbuild bundles under `dist/` where a single `..` hop lands in `dist/`, not repo root.
167
+ */
168
+ function resolveProjectRootByPackageName(importMetaUrl, packageName) {
169
+ let dir = dirname(fileURLToPath(importMetaUrl));
170
+ for (;;) {
171
+ const packageJsonPath = resolve(dir, "package.json");
172
+ if (existsSync(packageJsonPath)) {
173
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
174
+ if (pkg.name === packageName) {
175
+ return dir;
176
+ }
177
+ }
178
+ const parent = dirname(dir);
179
+ if (parent === dir) {
180
+ break;
181
+ }
182
+ dir = parent;
183
+ }
184
+ throw new Error(`Could not find package root for "${packageName}" from ${importMetaUrl}`);
185
+ }
186
+
187
+ /** Master switch — env name kept for compatibility (`LOG_PERSIST_ENABLED`). */
188
+ function logSpoolEnabledFromEnv(env = process.env) {
189
+ return envBool("LOG_PERSIST_ENABLED", false, env);
190
+ }
191
+ function logPersistServiceFromEnv(env = process.env) {
192
+ const raw = env.LOG_PERSIST_SERVICE?.trim();
193
+ return raw != null && raw !== "" ? raw : undefined;
194
+ }
195
+ function logPersistProcessFromEnv(env = process.env) {
196
+ const raw = env.LOG_PERSIST_PROCESS?.trim();
197
+ return raw != null && raw !== "" ? raw : undefined;
198
+ }
199
+ /** `LOG_PERSIST_REGISTRY_DIR` or `RUNTIME_DATA_DIR`. */
200
+ function runtimeDataDirFromEnv(env = process.env) {
201
+ const configured = env.LOG_PERSIST_REGISTRY_DIR?.trim();
202
+ if (configured != null && configured !== "") {
203
+ return configured;
204
+ }
205
+ const runtime = env.RUNTIME_DATA_DIR?.trim();
206
+ return runtime != null && runtime !== "" ? runtime : undefined;
207
+ }
208
+
209
+ /** User-facing / lifecycle logs — may be persisted like normal app logs. */
210
+ const LOG_PERSIST_TOPIC = "log-persist";
211
+ /** Operational logs from the persist pipeline — never re-offered to avoid feedback loops. */
212
+ const LOG_PERSIST_INTERNAL_TOPIC = "log-persist-internal";
213
+ function createPipelineLogger(module) {
214
+ const logger = createLogger({ module });
215
+ function write(level, message, data) {
216
+ logger[level](message, {
217
+ topic: LOG_PERSIST_INTERNAL_TOPIC,
218
+ ...(data != null ? { data } : {}),
219
+ });
220
+ }
221
+ return {
222
+ debug: (message, data) => write("debug", message, data),
223
+ info: (message, data) => write("info", message, data),
224
+ warn: (message, data) => write("warn", message, data),
225
+ error: (message, data) => write("error", message, data),
226
+ };
227
+ }
228
+ /** Supabase upload path (`postLogBatch`). */
229
+ const persistLogger = createPipelineLogger("log-upload");
230
+ /** File spool writer + collector. */
231
+ const spoolLogger = createPipelineLogger("log-spool");
232
+ function isLogPersistInternalTopic(topic) {
233
+ return topic === LOG_PERSIST_INTERNAL_TOPIC;
234
+ }
235
+
236
+ const LOG_SPOOL_SERVICE_ID_ENV = "LOG_SPOOL_SERVICE_ID";
237
+ const DEFAULT_LOG_SPOOL_CONFIG = {
238
+ writer: {
239
+ maxEntries: 2000,
240
+ maxBytes: 2 * 1024 * 1024,
241
+ flushDebounceMs: 3_000,
242
+ maxAgeMs: 60_000,
243
+ postTimeoutMs: 30_000,
244
+ },
245
+ gc: {
246
+ intervalMs: 24 * 60 * 60 * 1000,
247
+ syncedRetentionMs: 7 * 24 * 60 * 60 * 1000,
248
+ tmpRetentionMs: 7 * 24 * 60 * 60 * 1000,
249
+ },
250
+ collectIntervalMs: 60_000,
251
+ };
252
+ function resolveLogSpoolConfig(partial) {
253
+ const defaults = DEFAULT_LOG_SPOOL_CONFIG;
254
+ return {
255
+ writer: { ...defaults.writer, ...partial?.writer },
256
+ gc: { ...defaults.gc, ...partial?.gc },
257
+ collectIntervalMs: partial?.collectIntervalMs ?? defaults.collectIntervalMs,
258
+ };
259
+ }
260
+ function logSpoolConfigFromEnv(env = process.env) {
261
+ return resolveLogSpoolConfig({
262
+ writer: {
263
+ maxEntries: envInt("LOG_SPOOL_MAX_ENTRIES", DEFAULT_LOG_SPOOL_CONFIG.writer.maxEntries, { env, min: 1 }),
264
+ maxBytes: envInt("LOG_SPOOL_MAX_BYTES", DEFAULT_LOG_SPOOL_CONFIG.writer.maxBytes, { env, min: 1 }),
265
+ flushDebounceMs: envMs("LOG_SPOOL_FLUSH_DEBOUNCE_MS", DEFAULT_LOG_SPOOL_CONFIG.writer.flushDebounceMs, {
266
+ env,
267
+ }),
268
+ maxAgeMs: envMs("LOG_SPOOL_MAX_AGE_MS", DEFAULT_LOG_SPOOL_CONFIG.writer.maxAgeMs, { env }),
269
+ postTimeoutMs: envMs("LOG_PERSIST_POST_TIMEOUT_MS", DEFAULT_LOG_SPOOL_CONFIG.writer.postTimeoutMs, { env }),
270
+ },
271
+ gc: {
272
+ intervalMs: envMs("LOG_SPOOL_GC_INTERVAL_MS", DEFAULT_LOG_SPOOL_CONFIG.gc.intervalMs, { env }),
273
+ syncedRetentionMs: envMs("LOG_SPOOL_SYNCED_RETENTION_MS", DEFAULT_LOG_SPOOL_CONFIG.gc.syncedRetentionMs, {
274
+ env,
275
+ }),
276
+ tmpRetentionMs: envMs("LOG_SPOOL_TMP_RETENTION_MS", DEFAULT_LOG_SPOOL_CONFIG.gc.tmpRetentionMs, { env }),
277
+ },
278
+ collectIntervalMs: envMs("LOG_SPOOL_COLLECT_INTERVAL_MS", DEFAULT_LOG_SPOOL_CONFIG.collectIntervalMs, { env }),
279
+ });
280
+ }
281
+ function logSpoolServiceIdFromEnv(env = process.env) {
282
+ const raw = env[LOG_SPOOL_SERVICE_ID_ENV]?.trim();
283
+ return raw != null && raw !== "" ? raw : undefined;
284
+ }
285
+
286
+ const LOG_SPOOL_DIR_NAME = "log-spool";
287
+ function resolveLogSpoolRoot(explicit) {
288
+ if (explicit != null && explicit.trim() !== "") {
289
+ return explicit;
290
+ }
291
+ const configured = process.env.LOG_SPOOL_DIR?.trim();
292
+ if (configured != null && configured !== "") {
293
+ return configured;
294
+ }
295
+ const runtimeDir = runtimeDataDirFromEnv();
296
+ if (runtimeDir == null) {
297
+ throw new Error("[LogSpool] spool root required: set LOG_SPOOL_DIR or LOG_PERSIST_REGISTRY_DIR / RUNTIME_DATA_DIR");
298
+ }
299
+ return join(runtimeDir, LOG_SPOOL_DIR_NAME);
300
+ }
301
+ function logSpoolProcessDir(spoolRoot, serviceId, processRole) {
302
+ return join(spoolRoot, serviceId, processRole);
303
+ }
304
+
305
+ /** Four-process L3 blueprint roles — always scanned by collect-log. */
306
+ const LOG_SPOOL_CORE_PROCESS_ROLES = ["main", "guard", "scheduler", "worker"];
307
+ const LOG_SPOOL_EXTRA_PROCESS_ROLES_ENV = "LOG_SPOOL_EXTRA_PROCESS_ROLES";
308
+ const EXTRA_ROLE_PATTERN = /^[a-z][a-z0-9-]*$/;
309
+ function parseRoleList(raw) {
310
+ if (raw == null || raw.trim() === "") {
311
+ return [];
312
+ }
313
+ return raw
314
+ .split(",")
315
+ .map((part) => part.trim())
316
+ .filter((part) => part !== "");
317
+ }
318
+ /** Parse comma-separated extra roles from env (e.g. `chrome-sidecar`). */
319
+ function logSpoolExtraProcessRolesFromEnv(env = process.env) {
320
+ return parseRoleList(env[LOG_SPOOL_EXTRA_PROCESS_ROLES_ENV]);
321
+ }
322
+ /** Core + extras, deduped, stable order (core first). */
323
+ function resolveAllLogSpoolProcessRoles(env = process.env) {
324
+ const extras = logSpoolExtraProcessRolesFromEnv(env);
325
+ const seen = new Set(LOG_SPOOL_CORE_PROCESS_ROLES);
326
+ const all = [...LOG_SPOOL_CORE_PROCESS_ROLES];
327
+ for (const role of extras) {
328
+ if (!seen.has(role)) {
329
+ seen.add(role);
330
+ all.push(role);
331
+ }
332
+ }
333
+ return all;
334
+ }
335
+ function isLogSpoolCoreProcessRole(value) {
336
+ return LOG_SPOOL_CORE_PROCESS_ROLES.includes(value);
337
+ }
338
+ /** Whether `value` is a known spool role (core or configured extra). */
339
+ function isKnownLogSpoolProcessRole(value, env = process.env) {
340
+ if (isLogSpoolCoreProcessRole(value)) {
341
+ return true;
342
+ }
343
+ return logSpoolExtraProcessRolesFromEnv(env).includes(value);
344
+ }
345
+ /** Validate extra role token — lowercase alphanumeric + hyphen, not a duplicate core name. */
346
+ function assertValidLogSpoolExtraProcessRole(role) {
347
+ if (role === "" || !EXTRA_ROLE_PATTERN.test(role)) {
348
+ throw new Error(`[LogSpool] invalid extra process role "${role}" — use lowercase letters, digits, hyphen (e.g. chrome-sidecar)`);
349
+ }
350
+ if (isLogSpoolCoreProcessRole(role)) {
351
+ throw new Error(`[LogSpool] extra process role "${role}" duplicates a core role`);
352
+ }
353
+ }
354
+ function mergeLogSpoolExtraProcessRolesEnv(env, extraRoles) {
355
+ if (extraRoles.length === 0) {
356
+ return;
357
+ }
358
+ for (const role of extraRoles) {
359
+ assertValidLogSpoolExtraProcessRole(role);
360
+ }
361
+ const merged = new Set([...logSpoolExtraProcessRolesFromEnv(env), ...extraRoles]);
362
+ env[LOG_SPOOL_EXTRA_PROCESS_ROLES_ENV] = [...merged].join(",");
363
+ }
364
+
365
+ function estimateEntriesBytes(entries) {
366
+ if (entries.length === 0) {
367
+ return 0;
368
+ }
369
+ try {
370
+ return Buffer.byteLength(JSON.stringify(entries), "utf8");
371
+ }
372
+ catch {
373
+ return entries.length * 512;
374
+ }
375
+ }
376
+ function buildLogListDraft(input) {
377
+ const { service, process, lane, idempotencyKey, entries } = input;
378
+ // TODO: `lane` is legacy transport metadata — scheduled for removal; callers still pass a fixed value for decode compatibility.
379
+ const first = entries[0];
380
+ const last = entries[entries.length - 1];
381
+ return {
382
+ name: `log-batch:${service}:${process}:${lane}`,
383
+ value: idempotencyKey,
384
+ category: CategoryList.LIST,
385
+ tagList: ["log", lane, service],
386
+ details: {
387
+ manifestVersion: 0,
388
+ loaderKey: LOG_PERSIST_LOADER_KEY,
389
+ meta: {
390
+ service,
391
+ process,
392
+ pid: globalThis.process.pid,
393
+ lane,
394
+ count: entries.length,
395
+ from: first?.timestamp ?? null,
396
+ to: last?.timestamp ?? null,
397
+ flushedAt: Date.now(),
398
+ idempotencyKey,
399
+ },
400
+ preview: first?.message ?? "",
401
+ items: entries,
402
+ },
403
+ };
404
+ }
405
+ async function withTimeout(promise, timeoutMs, label) {
406
+ let timer = null;
407
+ try {
408
+ return await Promise.race([
409
+ promise,
410
+ new Promise((_resolve, reject) => {
411
+ timer = setTimeout(() => {
412
+ reject(new Error(`${label} timed out after ${timeoutMs}ms`));
413
+ }, timeoutMs);
414
+ }),
415
+ ]);
416
+ }
417
+ finally {
418
+ if (timer != null) {
419
+ clearTimeout(timer);
420
+ }
421
+ }
422
+ }
423
+ async function postLogBatch(draft, postTimeoutMs) {
424
+ try {
425
+ const result = await withTimeout(postLogListCreate({
426
+ name: draft.name,
427
+ value: draft.value,
428
+ category: draft.category,
429
+ details: draft.details,
430
+ }), postTimeoutMs, "postLogListCreate");
431
+ if (!result.success) {
432
+ const message = result.error?.message ?? "postLogListCreate failed";
433
+ persistLogger.error("post log batch failed", {
434
+ idempotencyKey: draft.value,
435
+ name: draft.name,
436
+ error: message,
437
+ });
438
+ throw new Error(message);
439
+ }
440
+ }
441
+ catch (error) {
442
+ if (isCreateTargetAlreadyExistsError(error)) {
443
+ persistLogger.error("post log batch idempotent duplicate — treating as success", {
444
+ idempotencyKey: draft.value,
445
+ name: draft.name,
446
+ });
447
+ return;
448
+ }
449
+ throw error;
450
+ }
451
+ }
452
+ /** Take a prefix of entries that fits within maxBytes. */
453
+ function takeEntriesWithinBytes(entries, maxBytes) {
454
+ if (entries.length === 0) {
455
+ return [];
456
+ }
457
+ const taken = [];
458
+ for (const entry of entries) {
459
+ const candidate = [...taken, entry];
460
+ if (taken.length > 0 && estimateEntriesBytes(candidate) > maxBytes) {
461
+ break;
462
+ }
463
+ taken.push(entry);
464
+ }
465
+ const result = taken.length > 0 ? taken : [entries[0]];
466
+ if (result.length < entries.length) {
467
+ persistLogger.warn("trimmed batch to maxBytes", {
468
+ requested: entries.length,
469
+ taken: result.length,
470
+ maxBytes,
471
+ takenBytes: estimateEntriesBytes(result),
472
+ });
473
+ }
474
+ return result;
475
+ }
476
+
477
+ const BATCH_ID_VERSION = 1;
478
+ /** Stable subset of LogEntry for idempotency hashing (same batch → same key). */
479
+ function canonicalLogEntry(entry) {
480
+ return {
481
+ timestamp: entry.timestamp,
482
+ level: entry.level,
483
+ message: entry.message,
484
+ topic: entry.topic,
485
+ module: entry.module,
486
+ traceId: entry.traceId,
487
+ traceParentId: entry.traceParentId ?? null,
488
+ extra: entry.extra ?? null,
489
+ data: entry.data ?? null,
490
+ labels: entry.labels ?? null,
491
+ };
492
+ }
493
+ /**
494
+ * Deterministic List.value for a log batch — retries after timeout map to the same row key.
495
+ * SHA-256 over canonical JSON (service + process + lane + entries in order).
496
+ */
497
+ function computeLogBatchIdempotencyKey(input) {
498
+ const payload = {
499
+ v: BATCH_ID_VERSION,
500
+ service: input.service,
501
+ process: input.process,
502
+ lane: input.lane,
503
+ entries: input.entries.map(canonicalLogEntry),
504
+ };
505
+ const hash = createHash("sha256").update(JSON.stringify(payload), "utf8").digest("hex");
506
+ return `${hash.slice(0, 32)}`;
507
+ }
508
+
509
+ /** `role.timestamp.hash.tmp|json` — role is lowercase alphanumeric + hyphen. */
510
+ const BATCH_FILE_PATTERN = /^([a-z][a-z0-9-]*)\.(\d{14})\.([a-f0-9]{8})\.(tmp|json)$/;
511
+ function formatLogSpoolBatchTimestamp(ms) {
512
+ const d = new Date(ms);
513
+ const pad = (n) => String(n).padStart(2, "0");
514
+ return (`${d.getFullYear()}` +
515
+ `${pad(d.getMonth() + 1)}` +
516
+ `${pad(d.getDate())}` +
517
+ `${pad(d.getHours())}` +
518
+ `${pad(d.getMinutes())}` +
519
+ `${pad(d.getSeconds())}`);
520
+ }
521
+ function computeLogSpoolShortHash(input) {
522
+ const processKey = `${input.serviceId}:${input.processRole}`;
523
+ const idempotencyKey = computeLogBatchIdempotencyKey({
524
+ service: input.serviceValue,
525
+ process: processKey,
526
+ lane: "slow",
527
+ entries: input.entries,
528
+ });
529
+ return idempotencyKey.slice(0, 8);
530
+ }
531
+ function buildLogSpoolBatchBaseName(input) {
532
+ const ts = formatLogSpoolBatchTimestamp(input.firstEntryTimestamp);
533
+ return `${input.processRole}.${ts}.${input.shortHash}`;
534
+ }
535
+ function parseLogSpoolBatchFilename(filename) {
536
+ const match = BATCH_FILE_PATTERN.exec(filename);
537
+ if (match == null) {
538
+ return null;
539
+ }
540
+ const processRole = match[1];
541
+ const tsRaw = match[2];
542
+ const shortHash = match[3];
543
+ const extension = match[4];
544
+ const year = Number(tsRaw.slice(0, 4));
545
+ const month = Number(tsRaw.slice(4, 6));
546
+ const day = Number(tsRaw.slice(6, 8));
547
+ const hour = Number(tsRaw.slice(8, 10));
548
+ const minute = Number(tsRaw.slice(10, 12));
549
+ const second = Number(tsRaw.slice(12, 14));
550
+ const timestampMs = Date.UTC(year, month - 1, day, hour, minute, second);
551
+ if (!Number.isFinite(timestampMs)) {
552
+ return null;
553
+ }
554
+ return { processRole, timestampMs, shortHash, extension };
555
+ }
556
+
557
+ class LogSpoolWriter {
558
+ static instance = null;
559
+ enabled = false;
560
+ buffer = [];
561
+ flushInProgress = false;
562
+ debounceTimer = null;
563
+ ageTimer = null;
564
+ serviceId = null;
565
+ processRole = null;
566
+ serviceValue = null;
567
+ spoolRoot = null;
568
+ config = DEFAULT_LOG_SPOOL_CONFIG.writer;
569
+ static getInstance() {
570
+ LogSpoolWriter.instance ??= new LogSpoolWriter();
571
+ return LogSpoolWriter.instance;
572
+ }
573
+ async enable(options) {
574
+ if (this.enabled) {
575
+ spoolLogger.warn("log spool writer already enabled", {
576
+ serviceId: this.serviceId,
577
+ processRole: this.processRole,
578
+ });
579
+ return;
580
+ }
581
+ this.serviceId = options.serviceId;
582
+ this.processRole = options.processRole;
583
+ this.serviceValue = options.serviceValue;
584
+ this.spoolRoot = options.spoolRoot ?? resolveLogSpoolRoot();
585
+ this.config = resolveLogSpoolConfig(options.config).writer;
586
+ this.enabled = true;
587
+ registerLogPersistOffer((entry) => this.offer(entry));
588
+ spoolLogger.info("log spool writer enabled", {
589
+ serviceId: options.serviceId,
590
+ processRole: options.processRole,
591
+ serviceValue: options.serviceValue,
592
+ spoolRoot: this.spoolRoot,
593
+ config: this.config,
594
+ });
595
+ }
596
+ async shutdown() {
597
+ this.cancelDebounce();
598
+ this.cancelAgeFlush();
599
+ registerLogPersistOffer(null);
600
+ this.enabled = false;
601
+ while (this.buffer.length > 0) {
602
+ await this.flushChunk({ reason: "shutdown" });
603
+ }
604
+ spoolLogger.info("log spool writer shutdown complete", {
605
+ serviceId: this.serviceId,
606
+ processRole: this.processRole,
607
+ });
608
+ }
609
+ getStats() {
610
+ return {
611
+ enabled: this.enabled,
612
+ bufferedEntries: this.buffer.length,
613
+ bufferedBytes: estimateEntriesBytes(this.buffer),
614
+ flushInProgress: this.flushInProgress,
615
+ debouncePending: this.debounceTimer != null,
616
+ ageFlushPending: this.ageTimer != null,
617
+ serviceId: this.serviceId,
618
+ processRole: this.processRole,
619
+ };
620
+ }
621
+ offer(entry) {
622
+ if (isLogPersistInternalTopic(entry.topic)) {
623
+ return;
624
+ }
625
+ if (!this.enabled) {
626
+ return;
627
+ }
628
+ if (this.buffer.length === 0) {
629
+ this.scheduleAgeFlush();
630
+ }
631
+ this.buffer.push(entry);
632
+ if (this.shouldScheduleDebouncedFlush()) {
633
+ this.scheduleDebouncedFlush();
634
+ }
635
+ }
636
+ shouldScheduleDebouncedFlush() {
637
+ return (this.buffer.length >= this.config.maxEntries || estimateEntriesBytes(this.buffer) >= this.config.maxBytes);
638
+ }
639
+ scheduleDebouncedFlush() {
640
+ if (this.debounceTimer != null) {
641
+ return;
642
+ }
643
+ spoolLogger.debug("log spool flush debounce scheduled", {
644
+ debounceMs: this.config.flushDebounceMs,
645
+ bufferedEntries: this.buffer.length,
646
+ });
647
+ this.debounceTimer = setTimeout(() => {
648
+ this.debounceTimer = null;
649
+ void this.flushChunk({ reason: "debounce" });
650
+ }, this.config.flushDebounceMs);
651
+ this.debounceTimer.unref?.();
652
+ }
653
+ scheduleAgeFlush() {
654
+ if (this.ageTimer != null) {
655
+ return;
656
+ }
657
+ spoolLogger.debug("log spool age flush scheduled", {
658
+ maxAgeMs: this.config.maxAgeMs,
659
+ });
660
+ this.ageTimer = setTimeout(() => {
661
+ this.ageTimer = null;
662
+ void this.flushChunk({ reason: "maxAge" });
663
+ }, this.config.maxAgeMs);
664
+ this.ageTimer.unref?.();
665
+ }
666
+ cancelDebounce() {
667
+ if (this.debounceTimer != null) {
668
+ clearTimeout(this.debounceTimer);
669
+ this.debounceTimer = null;
670
+ }
671
+ }
672
+ cancelAgeFlush() {
673
+ if (this.ageTimer != null) {
674
+ clearTimeout(this.ageTimer);
675
+ this.ageTimer = null;
676
+ }
677
+ }
678
+ async flushChunk(input) {
679
+ if (this.flushInProgress || this.buffer.length === 0) {
680
+ return;
681
+ }
682
+ if (this.serviceId == null || this.processRole == null || this.serviceValue == null || this.spoolRoot == null) {
683
+ return;
684
+ }
685
+ this.cancelDebounce();
686
+ this.cancelAgeFlush();
687
+ this.flushInProgress = true;
688
+ try {
689
+ const byCount = this.buffer.slice(0, this.config.maxEntries);
690
+ const entries = takeEntriesWithinBytes(byCount, this.config.maxBytes);
691
+ this.buffer = this.buffer.slice(entries.length);
692
+ if (entries.length === 0) {
693
+ if (this.buffer.length > 0) {
694
+ this.scheduleAgeFlush();
695
+ }
696
+ else {
697
+ this.cancelAgeFlush();
698
+ }
699
+ return;
700
+ }
701
+ const batchFile = await this.writeBatchTmp({
702
+ entries,
703
+ serviceId: this.serviceId,
704
+ processRole: this.processRole,
705
+ serviceValue: this.serviceValue,
706
+ spoolRoot: this.spoolRoot,
707
+ });
708
+ spoolLogger.info("log spool flushed to tmp", {
709
+ reason: input.reason,
710
+ fileName: batchFile.fileName,
711
+ entryCount: entries.length,
712
+ remainingBuffered: this.buffer.length,
713
+ });
714
+ if (this.buffer.length > 0) {
715
+ this.scheduleAgeFlush();
716
+ if (this.shouldScheduleDebouncedFlush()) {
717
+ this.scheduleDebouncedFlush();
718
+ }
719
+ }
720
+ else {
721
+ this.cancelAgeFlush();
722
+ }
723
+ }
724
+ catch (error) {
725
+ const message = error instanceof Error ? error.message : String(error);
726
+ spoolLogger.error("log spool flush failed", { reason: input.reason, error: message });
727
+ if (this.buffer.length > 0) {
728
+ this.scheduleAgeFlush();
729
+ }
730
+ }
731
+ finally {
732
+ this.flushInProgress = false;
733
+ }
734
+ }
735
+ async writeBatchTmp(input) {
736
+ const first = input.entries[0];
737
+ const shortHash = computeLogSpoolShortHash({
738
+ serviceValue: input.serviceValue,
739
+ serviceId: input.serviceId,
740
+ processRole: input.processRole,
741
+ entries: input.entries,
742
+ });
743
+ const baseName = buildLogSpoolBatchBaseName({
744
+ processRole: input.processRole,
745
+ firstEntryTimestamp: first.timestamp,
746
+ shortHash,
747
+ });
748
+ const fileName = `${baseName}.tmp`;
749
+ const processDir = logSpoolProcessDir(input.spoolRoot, input.serviceId, input.processRole);
750
+ await mkdir(processDir, { recursive: true });
751
+ const tmpPath = join(processDir, fileName);
752
+ const partPath = `${tmpPath}.part`;
753
+ const payload = {
754
+ entries: input.entries,
755
+ meta: {
756
+ serviceId: input.serviceId,
757
+ processRole: input.processRole,
758
+ serviceValue: input.serviceValue,
759
+ },
760
+ };
761
+ const content = `${JSON.stringify(payload)}\n`;
762
+ await writeFile(partPath, content, "utf8");
763
+ await rename(partPath, tmpPath);
764
+ return { fileName, tmpPath };
765
+ }
766
+ }
767
+ async function enableLogSpoolWriter(options) {
768
+ await LogSpoolWriter.getInstance().enable(options);
769
+ }
770
+ async function shutdownLogSpoolWriter() {
771
+ await LogSpoolWriter.getInstance().shutdown();
772
+ }
773
+ function getLogSpoolWriterStats() {
774
+ return LogSpoolWriter.getInstance().getStats();
775
+ }
776
+
777
+ function resolveLogSpoolProcessRole(explicit) {
778
+ const raw = explicit ?? logPersistProcessFromEnv();
779
+ if (raw == null || raw === "") {
780
+ throw new Error("[LogSpool] LOG_PERSIST_PROCESS is required when LOG_PERSIST_ENABLED=true");
781
+ }
782
+ if (!isKnownLogSpoolProcessRole(raw)) {
783
+ throw new Error(`[LogSpool] invalid LOG_PERSIST_PROCESS: ${raw} (not a core role and not listed in ${LOG_SPOOL_EXTRA_PROCESS_ROLES_ENV})`);
784
+ }
785
+ return raw;
786
+ }
787
+ /**
788
+ * Env vars for child spawn after main claims registry slot.
789
+ * Optional when using {@link ManagedChildProcesses.spawn} — auto-injected from parent env.
790
+ */
791
+ function buildLogSpoolSpawnEnv(input) {
792
+ return {
793
+ [LOG_SPOOL_SERVICE_ID_ENV]: input.serviceId,
794
+ LOG_PERSIST_PROCESS: input.processRole,
795
+ };
796
+ }
797
+ /**
798
+ * When persistence is enabled and parent has `LOG_SPOOL_SERVICE_ID`, map spawn `label`
799
+ * to spool env when `label` is a known core or extra process role.
800
+ */
801
+ function resolveLogSpoolSpawnEnvForLabel(label, env = process.env) {
802
+ if (!logSpoolEnabledFromEnv(env)) {
803
+ return {};
804
+ }
805
+ const serviceId = env[LOG_SPOOL_SERVICE_ID_ENV]?.trim();
806
+ if (serviceId == null || serviceId === "") {
807
+ return {};
808
+ }
809
+ if (!isKnownLogSpoolProcessRole(label, env)) {
810
+ return {};
811
+ }
812
+ return buildLogSpoolSpawnEnv({ serviceId, processRole: label });
813
+ }
814
+ async function ensureLogSpoolFromEnv(options = {}) {
815
+ if (!logSpoolEnabledFromEnv()) {
816
+ registerLogPersistOffer(null);
817
+ return false;
818
+ }
819
+ const serviceId = options.serviceId ?? logSpoolServiceIdFromEnv();
820
+ const serviceValue = options.serviceValue ?? logPersistServiceFromEnv();
821
+ const processRole = resolveLogSpoolProcessRole(options.processRole);
822
+ if (serviceId == null || serviceId === "") {
823
+ throw new Error("[LogSpool] LOG_SPOOL_SERVICE_ID is required when LOG_PERSIST_ENABLED=true (set after registry claim or pass serviceId)");
824
+ }
825
+ if (serviceValue == null || serviceValue === "") {
826
+ throw new Error("[LogSpool] LOG_PERSIST_SERVICE is required when LOG_PERSIST_ENABLED=true");
827
+ }
828
+ const spoolRoot = options.spoolRoot ?? resolveLogSpoolRoot();
829
+ await enableLogSpoolWriter({
830
+ serviceId,
831
+ processRole,
832
+ serviceValue,
833
+ spoolRoot,
834
+ config: options.config ?? logSpoolConfigFromEnv(),
835
+ });
836
+ const lifecycleLogger = createLogger({ module: "log-spool" });
837
+ lifecycleLogger.info("log spool enabled from env", {
838
+ topic: LOG_PERSIST_TOPIC,
839
+ data: { serviceId, processRole, serviceValue, spoolRoot },
840
+ });
841
+ return true;
842
+ }
843
+ async function shutdownLogSpoolFromEnv() {
844
+ await shutdownLogSpoolWriter();
845
+ }
846
+ function getLogSpoolStats() {
847
+ return getLogSpoolWriterStats();
848
+ }
849
+ function validateLogSpoolPreloadEnv(env = process.env) {
850
+ if (!logSpoolEnabledFromEnv(env)) {
851
+ return;
852
+ }
853
+ const missing = [];
854
+ if (env.LOG_PERSIST_SERVICE?.trim() == null || env.LOG_PERSIST_SERVICE.trim() === "") {
855
+ missing.push("LOG_PERSIST_SERVICE");
856
+ }
857
+ const runtimeDir = env.LOG_PERSIST_REGISTRY_DIR?.trim() || env.RUNTIME_DATA_DIR?.trim();
858
+ if (runtimeDir == null || runtimeDir === "") {
859
+ missing.push("LOG_PERSIST_REGISTRY_DIR or RUNTIME_DATA_DIR");
860
+ }
861
+ if (missing.length > 0) {
862
+ throw new Error(`[LogSpool] preload env incomplete: ${missing.join(", ")}`);
863
+ }
864
+ for (const role of env[LOG_SPOOL_EXTRA_PROCESS_ROLES_ENV]?.split(",") ?? []) {
865
+ const trimmed = role.trim();
866
+ if (trimmed === "") {
867
+ continue;
868
+ }
869
+ assertValidLogSpoolExtraProcessRole(trimmed);
870
+ }
871
+ }
872
+
873
+ export { envMs as A, envNumber as B, CLI_PROD_FLAG as C, envPort as D, envProfileFromProcess as E, parseEnvFile as F, parseEnvLine as G, resolveDefaultEnvFiles as H, resolveProjectRootFromModule as I, LOG_SPOOL_SERVICE_ID_ENV as L, SERVICE_ENV_PROFILE_ENV_KEY as S, logSpoolEnabledFromEnv as a, readEnv as b, requireEnv as c, resolveLogSpoolSpawnEnvForLabel as d, resolveLogSpoolConfig as e, resolveLogSpoolRoot as f, resolveAllLogSpoolProcessRoles as g, logSpoolProcessDir as h, parseLogSpoolBatchFilename as i, computeLogBatchIdempotencyKey as j, buildLogListDraft as k, loadEnvFiles as l, mergeLogSpoolExtraProcessRolesEnv as m, postLogBatch as n, logSpoolConfigFromEnv as o, pinEnvProfileFromArgv as p, ensureLogSpoolFromEnv as q, resolveProjectRootByPackageName as r, spoolLogger as s, shutdownLogSpoolFromEnv as t, getLogSpoolStats as u, validateLogSpoolPreloadEnv as v, SERVICE_ENV_PROFILE_PROD as w, buildLogSpoolSpawnEnv as x, envBool as y, envInt as z };
874
+ //# sourceMappingURL=enable-DqnfLPsQ.js.map