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.
- package/dist/browser.d.ts +2 -3
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +2 -2
- package/dist/chunks/enable-DqnfLPsQ.js +874 -0
- package/dist/chunks/enable-DqnfLPsQ.js.map +1 -0
- package/dist/chunks/{list.api-DSTOHzOC.js → list.api-BzbTJBsM.js} +35 -2
- package/dist/chunks/list.api-BzbTJBsM.js.map +1 -0
- package/dist/chunks/{task-queue-BHvNgb-X.js → task-queue-6X3aglJh.js} +110 -17
- package/dist/chunks/task-queue-6X3aglJh.js.map +1 -0
- package/dist/node/preload/run-service-preload.d.ts +1 -0
- package/dist/node/preload/run-service-preload.d.ts.map +1 -1
- package/dist/node/preload/service-preload.interface.d.ts +7 -0
- package/dist/node/preload/service-preload.interface.d.ts.map +1 -1
- package/dist/node/process/managed-child-processes.d.ts.map +1 -1
- package/dist/node/service-guard/service-guard-node.d.ts.map +1 -1
- package/dist/node/service-host/index.d.ts +1 -0
- package/dist/node/service-host/index.d.ts.map +1 -1
- package/dist/node/service-host/service-host.d.ts +5 -1
- package/dist/node/service-host/service-host.d.ts.map +1 -1
- package/dist/node.d.ts +10 -5
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +494 -49
- package/dist/node.js.map +1 -1
- package/dist/preload.js +20 -7
- package/dist/preload.js.map +1 -1
- package/dist/resource/resource.interface.d.ts +20 -0
- package/dist/resource/resource.interface.d.ts.map +1 -0
- package/dist/service/config.api.d.ts.map +1 -1
- package/dist/service/index.d.ts +2 -2
- package/dist/service/index.d.ts.map +1 -1
- package/dist/service/registry.service.d.ts +19 -0
- package/dist/service/registry.service.d.ts.map +1 -1
- package/dist/shared/log/core/create-logger.d.ts.map +1 -0
- package/dist/shared/log/{log-batch.d.ts → core/log-batch.d.ts} +4 -4
- package/dist/shared/log/core/log-batch.d.ts.map +1 -0
- package/dist/shared/log/core/log-manager.d.ts.map +1 -0
- package/dist/shared/log/core/log-min-level.d.ts +15 -0
- package/dist/shared/log/core/log-min-level.d.ts.map +1 -0
- package/dist/shared/log/core/log-scope.d.ts.map +1 -0
- package/dist/shared/log/index.d.ts +13 -11
- package/dist/shared/log/index.d.ts.map +1 -1
- package/dist/shared/log/spool/collector.d.ts +21 -0
- package/dist/shared/log/spool/collector.d.ts.map +1 -0
- package/dist/shared/log/spool/config.d.ts +7 -0
- package/dist/shared/log/spool/config.d.ts.map +1 -0
- package/dist/shared/log/spool/coordinator.d.ts +18 -0
- package/dist/shared/log/spool/coordinator.d.ts.map +1 -0
- package/dist/shared/log/spool/enable.d.ts +23 -0
- package/dist/shared/log/spool/enable.d.ts.map +1 -0
- package/dist/shared/log/spool/file.d.ts +21 -0
- package/dist/shared/log/spool/file.d.ts.map +1 -0
- package/dist/shared/log/spool/interface.d.ts +55 -0
- package/dist/shared/log/spool/interface.d.ts.map +1 -0
- package/dist/shared/log/spool/paths.d.ts +6 -0
- package/dist/shared/log/spool/paths.d.ts.map +1 -0
- package/dist/shared/log/spool/process-roles.d.ts +17 -0
- package/dist/shared/log/spool/process-roles.d.ts.map +1 -0
- package/dist/shared/log/spool/register-collect-log-runner.d.ts +15 -0
- package/dist/shared/log/spool/register-collect-log-runner.d.ts.map +1 -0
- package/dist/shared/log/spool/service-lifecycle.d.ts +13 -0
- package/dist/shared/log/spool/service-lifecycle.d.ts.map +1 -0
- package/dist/shared/log/spool/writer.d.ts +32 -0
- package/dist/shared/log/spool/writer.d.ts.map +1 -0
- package/dist/shared/log/{log-persist-batch-id.d.ts → upload/batch-id.d.ts} +3 -3
- package/dist/shared/log/upload/batch-id.d.ts.map +1 -0
- package/dist/shared/log/upload/env.d.ts +7 -0
- package/dist/shared/log/upload/env.d.ts.map +1 -0
- package/dist/shared/log/{log-persist-flush.d.ts → upload/flush.d.ts} +3 -3
- package/dist/shared/log/upload/flush.d.ts.map +1 -0
- package/dist/shared/log/{log-persist-hook.d.ts → upload/hook.d.ts} +2 -2
- package/dist/shared/log/upload/hook.d.ts.map +1 -0
- package/dist/shared/log/upload/interface.d.ts +33 -0
- package/dist/shared/log/upload/interface.d.ts.map +1 -0
- package/dist/shared/log/upload/logger.d.ts +22 -0
- package/dist/shared/log/upload/logger.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/browser.ts +4 -1
- package/src/node/preload/run-service-preload.ts +23 -4
- package/src/node/preload/service-preload.interface.ts +8 -0
- package/src/node/process/create-managed-child-processes.ts +1 -1
- package/src/node/process/managed-child-processes.ts +8 -1
- package/src/node/service-guard/service-guard-node.ts +5 -0
- package/src/node/service-host/index.ts +4 -0
- package/src/node/service-host/service-host.ts +22 -3
- package/src/node.ts +26 -19
- package/src/resource/resource.interface.ts +37 -0
- package/src/service/config.api.ts +1 -0
- package/src/service/index.ts +3 -0
- package/src/service/registry.service.ts +142 -0
- package/src/shared/log/README.md +206 -0
- package/src/shared/log/{log-batch.ts → core/log-batch.ts} +4 -4
- package/src/shared/log/{log-manager.ts → core/log-manager.ts} +3 -3
- package/src/shared/log/core/log-min-level.ts +46 -0
- package/src/shared/log/{log-scope.ts → core/log-scope.ts} +1 -1
- package/src/shared/log/index.ts +17 -15
- package/src/shared/log/spool/collector.ts +330 -0
- package/src/shared/log/spool/config.ts +56 -0
- package/src/shared/log/spool/coordinator.ts +55 -0
- package/src/shared/log/spool/enable.ts +136 -0
- package/src/shared/log/spool/file.ts +75 -0
- package/src/shared/log/spool/interface.ts +62 -0
- package/src/shared/log/spool/paths.ts +30 -0
- package/src/shared/log/spool/process-roles.ts +75 -0
- package/src/shared/log/spool/register-collect-log-runner.ts +63 -0
- package/src/shared/log/spool/service-lifecycle.ts +35 -0
- package/src/shared/log/spool/writer.ts +273 -0
- package/src/shared/log/{log-persist-batch-id.ts → upload/batch-id.ts} +2 -2
- package/src/shared/log/upload/env.ts +26 -0
- package/src/shared/log/{log-persist-flush.ts → upload/flush.ts} +8 -7
- package/src/shared/log/{log-persist-hook.ts → upload/hook.ts} +1 -1
- package/src/shared/log/upload/interface.ts +38 -0
- package/src/shared/log/upload/logger.ts +37 -0
- package/dist/chunks/enable-log-persist-Cu3SGxK7.js +0 -1674
- package/dist/chunks/enable-log-persist-Cu3SGxK7.js.map +0 -1
- package/dist/chunks/list.api-DSTOHzOC.js.map +0 -1
- package/dist/chunks/task-queue-BHvNgb-X.js.map +0 -1
- package/dist/idea/idea.interface.d.ts +0 -25
- package/dist/idea/idea.interface.d.ts.map +0 -1
- package/dist/shared/log/create-logger.d.ts.map +0 -1
- package/dist/shared/log/enable-log-persist.d.ts +0 -28
- package/dist/shared/log/enable-log-persist.d.ts.map +0 -1
- package/dist/shared/log/log-batch.d.ts.map +0 -1
- package/dist/shared/log/log-manager.d.ts.map +0 -1
- package/dist/shared/log/log-persist-batch-id.d.ts.map +0 -1
- package/dist/shared/log/log-persist-coordinator.d.ts +0 -37
- package/dist/shared/log/log-persist-coordinator.d.ts.map +0 -1
- package/dist/shared/log/log-persist-errors.d.ts +0 -4
- package/dist/shared/log/log-persist-errors.d.ts.map +0 -1
- package/dist/shared/log/log-persist-flush.d.ts.map +0 -1
- package/dist/shared/log/log-persist-hook.d.ts.map +0 -1
- package/dist/shared/log/log-persist-logger.d.ts +0 -16
- package/dist/shared/log/log-persist-logger.d.ts.map +0 -1
- package/dist/shared/log/log-persist-registry.d.ts +0 -18
- package/dist/shared/log/log-persist-registry.d.ts.map +0 -1
- package/dist/shared/log/log-persist.config.d.ts +0 -9
- package/dist/shared/log/log-persist.config.d.ts.map +0 -1
- package/dist/shared/log/log-persist.d.ts +0 -62
- package/dist/shared/log/log-persist.d.ts.map +0 -1
- package/dist/shared/log/log-persist.interface.d.ts +0 -132
- package/dist/shared/log/log-persist.interface.d.ts.map +0 -1
- package/dist/shared/log/log-scope.d.ts.map +0 -1
- package/src/idea/idea.interface.ts +0 -28
- package/src/shared/log/enable-log-persist.ts +0 -225
- package/src/shared/log/log-persist-coordinator.ts +0 -107
- package/src/shared/log/log-persist-errors.ts +0 -16
- package/src/shared/log/log-persist-logger.ts +0 -61
- package/src/shared/log/log-persist-registry.ts +0 -252
- package/src/shared/log/log-persist.config.ts +0 -115
- package/src/shared/log/log-persist.interface.ts +0 -150
- package/src/shared/log/log-persist.ts +0 -822
- /package/dist/shared/log/{create-logger.d.ts → core/create-logger.d.ts} +0 -0
- /package/dist/shared/log/{log-manager.d.ts → core/log-manager.d.ts} +0 -0
- /package/dist/shared/log/{log-scope.d.ts → core/log-scope.d.ts} +0 -0
- /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 `
|
|
4
|
+
* skipped rather than thrown. Mirrors the write side in `upload/flush.ts`.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { List } from "
|
|
8
|
-
import type { LogEntry } from "
|
|
9
|
-
import { LOG_PERSIST_LOADER_KEY, type LogBatchMeta, type LogPersistLane } from "
|
|
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 "
|
|
2
|
-
import { generateUniqueId } from "
|
|
3
|
-
import { offerToLogPersist } from "
|
|
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
|
+
}
|
package/src/shared/log/index.ts
CHANGED
|
@@ -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
|
-
|
|
11
|
-
export {
|
|
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
|
-
|
|
25
|
-
export {
|
|
26
|
-
export
|
|
27
|
-
|
|
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";
|