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
@@ -58,6 +58,13 @@ export interface ReleaseSystemRegistrySlotsInput {
58
58
  maxAttempts?: number;
59
59
  }
60
60
 
61
+ export interface ReleaseSystemRegistrySlotsByServiceIdInput {
62
+ /** Runtime Service row ids bound to ACTIVE slots (one slot per id). */
63
+ serviceIds: string[];
64
+ traceId?: string;
65
+ maxAttempts?: number;
66
+ }
67
+
61
68
  export interface ReleasedRegistrySlot {
62
69
  serviceValue: string;
63
70
  serviceId: string;
@@ -71,6 +78,13 @@ export interface ReleaseSystemRegistrySlotsOutcome {
71
78
  unchangedServiceValues: string[];
72
79
  }
73
80
 
81
+ export interface ReleaseSystemRegistrySlotsByServiceIdOutcome {
82
+ config: Config;
83
+ released: ReleasedRegistrySlot[];
84
+ /** Requested `serviceIds` with no ACTIVE slot released (unknown id, already EMPTY, or never bound). */
85
+ unchangedServiceIds: string[];
86
+ }
87
+
74
88
  export interface AppendSystemRegistrySlotsInput {
75
89
  /** Logical service keys to declare with one new EMPTY slot each when missing. */
76
90
  serviceValues: string[];
@@ -365,6 +379,34 @@ function releaseActiveSlotsForServiceValues(
365
379
  return { nextSlots, released };
366
380
  }
367
381
 
382
+ function releaseActiveSlotsForServiceIds(
383
+ slots: ServiceSlot[],
384
+ serviceIdSet: ReadonlySet<string>,
385
+ ): { nextSlots: ServiceSlot[]; released: ReleasedRegistrySlot[] } {
386
+ const nextSlots = [...slots];
387
+ const released: ReleasedRegistrySlot[] = [];
388
+
389
+ for (let slotIndex = 0; slotIndex < nextSlots.length; slotIndex += 1) {
390
+ const slot = nextSlots[slotIndex];
391
+ if (slot.serviceId == null || !serviceIdSet.has(slot.serviceId) || slot.status !== ServiceSlotStatus.ACTIVE) {
392
+ continue;
393
+ }
394
+
395
+ released.push({
396
+ serviceValue: slot.serviceValue,
397
+ serviceId: slot.serviceId,
398
+ slotIndex,
399
+ });
400
+ nextSlots[slotIndex] = {
401
+ ...slot,
402
+ serviceId: null,
403
+ status: ServiceSlotStatus.EMPTY,
404
+ };
405
+ }
406
+
407
+ return { nextSlots, released };
408
+ }
409
+
368
410
  /**
369
411
  * Force-release ACTIVE registry slots for the given logical service keys (`ACTIVE → EMPTY`).
370
412
  * Ops/dev recovery when a process crashed without {@link unregisterService}.
@@ -475,6 +517,106 @@ export async function releaseSystemRegistrySlots(
475
517
  );
476
518
  }
477
519
 
520
+ /**
521
+ * Force-release ACTIVE registry slots for the given runtime Service row ids (`ACTIVE → EMPTY`).
522
+ * Targets one slot per `serviceId` — use when multiple slots share the same `serviceValue`.
523
+ * Ops/dev recovery when a process crashed without {@link unregisterService}.
524
+ * Does not delete Service rows or mutate `details.runtime`.
525
+ */
526
+ export async function releaseSystemRegistrySlotsByServiceId(
527
+ input: ReleaseSystemRegistrySlotsByServiceIdInput,
528
+ ): Promise<ReleaseSystemRegistrySlotsByServiceIdOutcome> {
529
+ const serviceIds = [...new Set(input.serviceIds.map((value) => value.trim()).filter((value) => value !== ""))];
530
+ if (serviceIds.length === 0) {
531
+ throw new Error("[releaseSystemRegistrySlotsByServiceId] serviceIds must not be empty");
532
+ }
533
+
534
+ const serviceIdSet = new Set(serviceIds);
535
+ const maxAttempts = input.maxAttempts ?? DEFAULT_REGISTER_RETRY_ATTEMPTS;
536
+ const logger = createLogger({
537
+ module: "releaseSystemRegistrySlotsByServiceId",
538
+ traceId: input.traceId,
539
+ labels: { serviceIds: serviceIds.join(",") },
540
+ });
541
+
542
+ logger.info("开始按 serviceId 强制释放 registry 槽位", {
543
+ topic: LOG_TOPIC_REGISTRY,
544
+ data: { serviceIds },
545
+ });
546
+
547
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
548
+ try {
549
+ const config = await loadSystemRegistryConfig();
550
+ const revision = parseRegistryRevision(config.details.meta);
551
+ const slots = parseServiceSlots(config);
552
+
553
+ const { nextSlots, released } = releaseActiveSlotsForServiceIds(slots, serviceIdSet);
554
+ const releasedIdSet = new Set(released.map((entry) => entry.serviceId));
555
+ const unchangedServiceIds = serviceIds.filter((serviceId) => !releasedIdSet.has(serviceId));
556
+
557
+ if (released.length === 0) {
558
+ logger.info("无 ACTIVE 槽位需要释放", {
559
+ topic: LOG_TOPIC_REGISTRY,
560
+ data: { serviceIds, attempt },
561
+ });
562
+ return { config, released, unchangedServiceIds: serviceIds };
563
+ }
564
+
565
+ await persistSlotClaim({
566
+ configId: config.id,
567
+ revision,
568
+ slots: nextSlots,
569
+ });
570
+
571
+ const updated = await loadSystemRegistryConfig();
572
+ logger.info("槽位强制释放成功", {
573
+ topic: LOG_TOPIC_REGISTRY,
574
+ data: {
575
+ released,
576
+ unchangedServiceIds,
577
+ attempt,
578
+ revision: revision + 1,
579
+ },
580
+ });
581
+
582
+ return {
583
+ config: updated,
584
+ released,
585
+ unchangedServiceIds,
586
+ };
587
+ } catch (error) {
588
+ if (error instanceof ServiceRegistryError) {
589
+ logger.error("槽位强制释放被拒绝", {
590
+ topic: LOG_TOPIC_REGISTRY,
591
+ data: { serviceIds, code: error.code, message: error.message },
592
+ });
593
+ throw error;
594
+ }
595
+
596
+ if (isOptimisticLockError(error) && attempt < maxAttempts) {
597
+ logger.warn("registry 乐观锁冲突,重试", {
598
+ topic: LOG_TOPIC_REGISTRY,
599
+ data: { serviceIds, attempt, maxAttempts },
600
+ });
601
+ await sleep(50 * attempt);
602
+ continue;
603
+ }
604
+
605
+ const message = error instanceof Error ? error.message : String(error);
606
+ logger.error("槽位强制释放失败", {
607
+ topic: LOG_TOPIC_REGISTRY,
608
+ data: { serviceIds, attempt, message },
609
+ });
610
+ throw new ServiceRegistryError(message, "REGISTRY_UPDATE_FAILED");
611
+ }
612
+ }
613
+
614
+ throw new ServiceRegistryError(
615
+ "[releaseSystemRegistrySlotsByServiceId] Optimistic lock retries exhausted",
616
+ "REGISTRY_UPDATE_FAILED",
617
+ );
618
+ }
619
+
478
620
  /**
479
621
  * Append EMPTY {@link ServiceSlot} rows for logical service keys not yet declared.
480
622
  * Idempotent — skips keys that already appear in `details.objects`.
@@ -0,0 +1,206 @@
1
+ # Log module (`src/shared/log`)
2
+
3
+ Structured logging, optional file-backed persistence, and Supabase `LogBatch` upload for L3 services.
4
+
5
+ ## Layout
6
+
7
+ ```text
8
+ src/shared/log/
9
+ README.md
10
+ index.ts # browser-safe public API (re-exports core + upload types)
11
+
12
+ core/ # in-process logging (browser + Node)
13
+ log-manager.ts # LogManager singleton, LogEntry, levels
14
+ create-logger.ts # createLogger({ module, scope, … })
15
+ log-scope.ts # traceId / labels / patchScope
16
+ log-min-level.ts # LOG_MIN_LEVEL env
17
+ log-batch.ts # decode persisted LogBatch Lists (read side)
18
+
19
+ upload/ # shared Supabase upload pipeline (Node)
20
+ env.ts # LOG_PERSIST_ENABLED, SERVICE, PROCESS, runtime dir
21
+ interface.ts # LogBatch meta, loader keys, lane type
22
+ batch-id.ts # computeLogBatchIdempotencyKey
23
+ flush.ts # buildLogListDraft, postLogBatch
24
+ hook.ts # LogManager → spool writer offer bridge
25
+ logger.ts # internal pipeline loggers (no feedback loop)
26
+
27
+ spool/ # file-backed persistence (Node only)
28
+ enable.ts # ensureLogSpoolFromEnv, spawn env helpers
29
+ service-lifecycle.ts # isLogSpoolEnabled, enableLogSpoolFromEnvInChild, shutdownLogSpool
30
+ writer.ts # per-process memory buffer → .tmp files
31
+ collector.ts # guard: upload .tmp, rename .json, periodic GC
32
+ paths.ts # {RUNTIME_DATA_DIR}/log-spool/{serviceId}/…
33
+ file.ts # batch filename parse/build
34
+ process-roles.ts # main|guard|scheduler|worker + extras
35
+ config.ts # writer / GC / collect interval env
36
+ coordinator.ts # createLogSpoolCoordinator facade
37
+ register-collect-log-runner.ts
38
+ ```
39
+
40
+ **Package exports**
41
+
42
+ | Entry | Scope |
43
+ |-------|--------|
44
+ | `target-supabase-sdk` / `index.ts` | `core/` + decode types from `upload/interface` |
45
+ | `target-supabase-sdk/node` | spool + upload symbols used by L3 services |
46
+
47
+ Node-only code must not be imported from the browser bundle. Spool files use `node:fs`.
48
+
49
+ ---
50
+
51
+ ## Data flow
52
+
53
+ ```text
54
+ App code
55
+ → createLogger / logManager
56
+ → offerToLogPersist (when spool enabled)
57
+ → LogSpoolWriter (memory buffer)
58
+ → {spoolRoot}/{serviceId}/{role}/*.tmp
59
+
60
+ Guard collect-log runner (1 min tick)
61
+ → read *.tmp → postLogBatch → rename *.json
62
+ → GC old tmp/json (once per day; 7 d retention)
63
+
64
+ log-service (downstream)
65
+ → decodeLogBatch / LogTrace merge by traceId
66
+ ```
67
+
68
+ **One-line rule:** producers flush `LogEntry[]` to `.tmp`; only the guard uploads `LogBatch` and renames to `.json`.
69
+
70
+ ---
71
+
72
+ ## Environment
73
+
74
+ | Key | Role |
75
+ |-----|------|
76
+ | `LOG_PERSIST_ENABLED` | Master switch (`true` → file spool) |
77
+ | `LOG_PERSIST_SERVICE` | Logical service value in List meta (e.g. `watch-service`) |
78
+ | `LOG_PERSIST_PROCESS` | This process role: `main` \| `guard` \| `scheduler` \| `worker` or extra |
79
+ | `LOG_SPOOL_SERVICE_ID` | Instance id — set by main after registry claim |
80
+ | `LOG_SPOOL_EXTRA_PROCESS_ROLES` | Comma-separated extras (e.g. `chrome-sidecar`) |
81
+ | `RUNTIME_DATA_DIR` | Runtime root; spool lives at `{dir}/log-spool/` |
82
+ | `LOG_SPOOL_DIR` | Optional override for spool root parent |
83
+
84
+ ### Writer tuning
85
+
86
+ | Key | Default |
87
+ |-----|---------|
88
+ | `LOG_SPOOL_MAX_ENTRIES` | 2000 |
89
+ | `LOG_SPOOL_MAX_BYTES` | 2 MB |
90
+ | `LOG_SPOOL_FLUSH_DEBOUNCE_MS` | 3 s (after count/byte threshold) |
91
+ | `LOG_SPOOL_MAX_AGE_MS` | 60 s (flush buffered logs even when below threshold) |
92
+ | `LOG_PERSIST_POST_TIMEOUT_MS` | 30 s |
93
+
94
+ ### Collector / GC
95
+
96
+ | Key | Default |
97
+ |-----|---------|
98
+ | `LOG_SPOOL_COLLECT_INTERVAL_MS` | 60 s |
99
+ | `LOG_SPOOL_GC_INTERVAL_MS` | 24 h (how often guard runs tmp/json GC) |
100
+ | `LOG_SPOOL_SYNCED_RETENTION_MS` | 7 d (delete `.json` when mtime older than this) |
101
+ | `LOG_SPOOL_TMP_RETENTION_MS` | 7 d (delete stale `.tmp` by filename timestamp) |
102
+
103
+ ---
104
+
105
+ ## On-disk layout
106
+
107
+ ```text
108
+ {RUNTIME_DATA_DIR}/log-spool/{serviceId}/
109
+ main/main.{YYYYMMDDHHmmss}.{hash8}.tmp → .json after upload
110
+ guard/…
111
+ scheduler/…
112
+ worker/…
113
+ {extra}/…
114
+ ```
115
+
116
+ Upload state: `.tmp` = pending, `.json` = uploaded (no central index file).
117
+
118
+ Batch file JSON: `{ entries: LogEntry[], meta?: { serviceId, processRole, serviceValue } }`.
119
+
120
+ Uploaded List meta:
121
+
122
+ - `meta.process` = `{serviceId}:{processRole}` (e.g. `abc123:worker`)
123
+ - `meta.service` = logical value (`watch-service`)
124
+ - `meta.lane` = `"slow"` (legacy transport field; required for decode today)
125
+
126
+ ---
127
+
128
+ ## L3 integration
129
+
130
+ ### Preload (`runServicePreload`)
131
+
132
+ Sync phases set `LOG_PERSIST_SERVICE`, `RUNTIME_DATA_DIR`, validate env, merge `logSpoolExtraProcessRoles`.
133
+ **Do not** call `ensureLogSpoolFromEnv` in preload — it is async and needs `LOG_SPOOL_SERVICE_ID`.
134
+
135
+ ### Main (`createServiceHost`)
136
+
137
+ After registry claim, main sets `LOG_SPOOL_SERVICE_ID` and calls `ensureLogSpoolFromEnv({ processRole: "main", … })`.
138
+
139
+ ### Child processes
140
+
141
+ Guard / scheduler / worker `main()`:
142
+
143
+ ```typescript
144
+ import { enableLogSpoolFromEnvInChild } from "target-supabase-sdk/node";
145
+
146
+ await enableLogSpoolFromEnvInChild();
147
+ ```
148
+
149
+ ### Shutdown
150
+
151
+ ```typescript
152
+ import { shutdownLogSpool } from "target-supabase-sdk/node";
153
+
154
+ await shutdownLogSpool();
155
+ ```
156
+
157
+ Flushes remaining memory buffer to `.tmp`.
158
+
159
+ ### Extra process roles (e.g. download-service `chrome-sidecar`)
160
+
161
+ ```javascript
162
+ // scripts/preload.mjs
163
+ runServicePreload({
164
+ // …
165
+ logSpoolExtraProcessRoles: ["chrome-sidecar"],
166
+ });
167
+ ```
168
+
169
+ ---
170
+
171
+ ## Public Node API (curated)
172
+
173
+ | Symbol | Role |
174
+ |--------|------|
175
+ | `ensureLogSpoolFromEnv` | Enable writer in current process |
176
+ | `enableLogSpoolFromEnvInChild` | Child entry: no-op when disabled, else `ensureLogSpoolFromEnv` |
177
+ | `isLogSpoolEnabled` | Read master switch (`LOG_PERSIST_ENABLED`) |
178
+ | `getMainLogSpoolWriterStats` | Writer stats or `null` when disabled (main observability) |
179
+ | `shutdownLogSpool` | No-op when disabled, else flush + disable |
180
+ | `shutdownLogSpoolFromEnv` | Flush buffer + disable (unconditional) |
181
+ | `getLogSpoolStats` | Writer buffer stats |
182
+ | `buildLogSpoolSpawnEnv` | Manual child env (usually unnecessary) |
183
+ | `validateLogSpoolPreloadEnv` | Preload validation |
184
+ | `logSpoolEnabledFromEnv` | Read master switch |
185
+ | `createLogSpoolCoordinator` | Service-bound lifecycle facade |
186
+ | `registerCollectLogRunner` | Guard TriggerNode runner (via `ServiceGuardNode`) |
187
+ | `runCollectLogTick` | Manual / tests |
188
+ | `resolveLogSpoolRoot` | Spool directory path |
189
+
190
+ Internal helpers (`resolveLogSpoolSpawnEnvForLabel`, `resolveAllLogSpoolProcessRoles`, etc.) are not exported from `node` — use preload `logSpoolExtraProcessRoles` or env instead.
191
+
192
+ ---
193
+
194
+ ## Concurrency (Windows)
195
+
196
+ - One writer per `.tmp` file — producers only create new files.
197
+ - Guard renames after successful upload; does not RMW batch contents.
198
+ - GC scans directories; no shared JSON index file.
199
+
200
+ ---
201
+
202
+ ## Related skills
203
+
204
+ - [log-spool](../../../.cursor/skills/log-spool/SKILL.md) — agent quick reference
205
+ - [service-preload](../../../.cursor/skills/service-preload/SKILL.md) — preload protocol
206
+ - [l3-service-host](../../../.cursor/skills/l3-service-host/SKILL.md) — `createServiceHost` integration
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Read side of the log-persist pipeline: decode persisted `LogBatch` Lists into
3
3
  * structured batches and flat entries. Pure and defensive — malformed rows are
4
- * skipped rather than thrown. Mirrors the write side in `log-persist-flush.ts`.
4
+ * skipped rather than thrown. Mirrors the write side in `upload/flush.ts`.
5
5
  */
6
6
 
7
- import type { List } from "../../list/list.interface";
8
- import type { LogEntry } from "./log-manager";
9
- import { LOG_PERSIST_LOADER_KEY, type LogBatchMeta, type LogPersistLane } from "./log-persist.interface";
7
+ import type { List } from "../../../list/list.interface";
8
+ import type { LogEntry } from "../core/log-manager";
9
+ import { LOG_PERSIST_LOADER_KEY, type LogBatchMeta, type LogPersistLane } from "../upload/interface";
10
10
 
11
11
  const LANES: ReadonlySet<string> = new Set<LogPersistLane>(["fast", "medium", "slow"]);
12
12
 
@@ -1,6 +1,6 @@
1
- import { isDevEnvironment } from "../../core.utils";
2
- import { generateUniqueId } from "../utils/id.utils";
3
- import { offerToLogPersist } from "./log-persist-hook";
1
+ import { isDevEnvironment } from "../../../core.utils";
2
+ import { generateUniqueId } from "../../utils/id.utils";
3
+ import { offerToLogPersist } from "../upload/hook";
4
4
  import { formatScopeLabels, type LoggerResetScopePatch, type LogScope, patchScope, resolveLogData } from "./log-scope";
5
5
 
6
6
  /**
@@ -0,0 +1,46 @@
1
+ import { isDevEnvironment } from "../../../core.utils";
2
+ import { LogLevel } from "./log-manager";
3
+
4
+ /** Env key for global {@link LogManager} min level (console + log-persist when enabled). */
5
+ export const LOG_MIN_LEVEL_ENV_KEY = "LOG_MIN_LEVEL";
6
+
7
+ const LOG_LEVEL_BY_NAME: Record<string, LogLevel> = {
8
+ DEBUG: LogLevel.DEBUG,
9
+ INFO: LogLevel.INFO,
10
+ SUCCESS: LogLevel.SUCCESS,
11
+ WARN: LogLevel.WARN,
12
+ ERROR: LogLevel.ERROR,
13
+ CRITICAL: LogLevel.CRITICAL,
14
+ };
15
+
16
+ export interface ResolveLogMinLevelOptions {
17
+ env?: NodeJS.ProcessEnv;
18
+ /** When {@link LOG_MIN_LEVEL_ENV_KEY} unset; defaults to LogManager convention (DEBUG in dev, INFO in prod). */
19
+ defaultLevel?: LogLevel;
20
+ }
21
+
22
+ function sdkDefaultLogMinLevel(): LogLevel {
23
+ return isDevEnvironment() ? LogLevel.DEBUG : LogLevel.INFO;
24
+ }
25
+
26
+ /**
27
+ * Parse `LOG_MIN_LEVEL` after `.env` load. Invalid values throw (same style as `envInt`).
28
+ */
29
+ export function resolveLogMinLevel(options?: ResolveLogMinLevelOptions): LogLevel {
30
+ const env = options?.env ?? process.env;
31
+ const raw = env[LOG_MIN_LEVEL_ENV_KEY]?.trim().toUpperCase();
32
+ if (raw == null || raw === "") {
33
+ return options?.defaultLevel ?? sdkDefaultLogMinLevel();
34
+ }
35
+
36
+ const level = LOG_LEVEL_BY_NAME[raw];
37
+ if (level == null) {
38
+ throw new Error(`Invalid ${LOG_MIN_LEVEL_ENV_KEY}: ${raw}`);
39
+ }
40
+ return level;
41
+ }
42
+
43
+ /** Alias for {@link resolveLogMinLevel}. */
44
+ export function logMinLevelFromEnv(options?: ResolveLogMinLevelOptions): LogLevel {
45
+ return resolveLogMinLevel(options);
46
+ }
@@ -1,4 +1,4 @@
1
- import { generateUniqueId } from "../utils/id.utils";
1
+ import { generateUniqueId } from "../../utils/id.utils";
2
2
 
3
3
  /** Logger-bound scope: module + trace chain + optional labels. */
4
4
  export interface LogScope {
@@ -1,16 +1,14 @@
1
1
  /**
2
- * Log domain public API — curated re-exports only.
2
+ * Log domain public API — curated re-exports only (browser-safe).
3
3
  */
4
4
 
5
5
  export type {
6
6
  CreateLoggerFromModuleInput,
7
7
  CreateLoggerFromScopeInput,
8
8
  CreateLoggerInput,
9
- } from "./create-logger";
10
- // create-logger
11
- export { createLogger } from "./create-logger";
12
- export type { DecodedLogBatch, FlatLogEntry } from "./log-batch";
13
- // log-batch (persisted LogBatch decode — read side of log-persist)
9
+ } from "./core/create-logger";
10
+ export { createLogger } from "./core/create-logger";
11
+ export type { DecodedLogBatch, FlatLogEntry } from "./core/log-batch";
14
12
  export {
15
13
  decodeLogBatch,
16
14
  decodeLogBatchList,
@@ -19,12 +17,15 @@ export {
19
17
  isLogBatchList,
20
18
  isLogEntry,
21
19
  parseLogBatchMeta,
22
- } from "./log-batch";
23
- export type { LogEntry, LoggerWithScope, LogOptions, LogRestParams, WithScopeOptions } from "./log-manager";
24
- // log-manager
25
- export { LogLevel, LogManager, logManager } from "./log-manager";
26
- export type { LogBatchMeta, LogPersistLane } from "./log-persist.interface";
27
- export { LOG_BATCH_LOADER_KEY, LOG_PERSIST_LOADER_KEY } from "./log-persist.interface";
20
+ } from "./core/log-batch";
21
+ export type { LogEntry, LoggerWithScope, LogOptions, LogRestParams, WithScopeOptions } from "./core/log-manager";
22
+ export { LogLevel, LogManager, logManager } from "./core/log-manager";
23
+ export type { ResolveLogMinLevelOptions } from "./core/log-min-level";
24
+ export {
25
+ LOG_MIN_LEVEL_ENV_KEY,
26
+ logMinLevelFromEnv,
27
+ resolveLogMinLevel,
28
+ } from "./core/log-min-level";
28
29
  export type {
29
30
  CreateScopeInput,
30
31
  LoggerResetScopePatch,
@@ -32,8 +33,7 @@ export type {
32
33
  LogScopeInput,
33
34
  LogScopePatch,
34
35
  PatchScopeInput,
35
- } from "./log-scope";
36
- // log-scope
36
+ } from "./core/log-scope";
37
37
  export {
38
38
  createScope,
39
39
  formatScopeLabels,
@@ -41,4 +41,6 @@ export {
41
41
  patchScope,
42
42
  resolveLogData,
43
43
  withModule,
44
- } from "./log-scope";
44
+ } from "./core/log-scope";
45
+ export type { LogBatchMeta, LogPersistLane } from "./upload/interface";
46
+ export { LOG_BATCH_LOADER_KEY, LOG_PERSIST_LOADER_KEY } from "./upload/interface";