@nimbus-sh/core 0.4.0 → 0.6.0
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/README.md +35 -7
- package/dist/_shared/cache-stats.d.ts +23 -1
- package/dist/_shared/cache-stats.d.ts.map +1 -1
- package/dist/_shared/cache-stats.js +13 -1
- package/dist/_shared/error-text.d.ts +15 -0
- package/dist/_shared/error-text.d.ts.map +1 -0
- package/dist/_shared/error-text.js +21 -0
- package/dist/_shared/exports-resolver.d.ts +19 -7
- package/dist/_shared/exports-resolver.d.ts.map +1 -1
- package/dist/_shared/exports-resolver.js +1 -1
- package/dist/_shared/retry.d.ts.map +1 -1
- package/dist/_shared/retry.js +4 -2
- package/dist/constants.d.ts +5 -11
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +23 -49
- package/dist/runtime/assets-loader.js +1 -1
- package/dist/runtime/esbuild-service.d.ts +4 -3
- package/dist/runtime/esbuild-service.d.ts.map +1 -1
- package/dist/runtime/esbuild-service.js +9 -4
- package/dist/runtime/installed-runtimes.d.ts +4 -3
- package/dist/runtime/installed-runtimes.d.ts.map +1 -1
- package/dist/runtime/long-running-handle.d.ts +3 -3
- package/dist/runtime/long-running-handle.d.ts.map +1 -1
- package/dist/runtime/os-contracts.d.ts +7 -5
- package/dist/runtime/os-contracts.d.ts.map +1 -1
- package/dist/runtime/port-registry.d.ts +28 -0
- package/dist/runtime/port-registry.d.ts.map +1 -1
- package/dist/runtime/port-registry.js +79 -9
- package/dist/runtime/process-logs.d.ts +9 -1
- package/dist/runtime/process-logs.d.ts.map +1 -1
- package/dist/runtime/project-detect.d.ts +6 -1
- package/dist/runtime/project-detect.d.ts.map +1 -1
- package/dist/runtime/require-resolver.d.ts.map +1 -1
- package/dist/runtime/require-resolver.js +3 -4
- package/dist/runtime/runtime-registry.d.ts +4 -3
- package/dist/runtime/runtime-registry.d.ts.map +1 -1
- package/dist/runtime/runtime-registry.js +2 -1
- package/dist/runtime/sqlite-runtime-fs-bridge.js +1 -5
- package/dist/runtime/virtual-socket-kernel.d.ts.map +1 -1
- package/dist/runtime/virtual-socket-kernel.generated.d.ts +2 -2
- package/dist/runtime/virtual-socket-kernel.generated.d.ts.map +1 -1
- package/dist/runtime/virtual-socket-kernel.generated.js +3 -3
- package/dist/runtime/virtual-socket-kernel.js +10 -5
- package/dist/runtime/wasm-runner.d.ts.map +1 -1
- package/dist/runtime/wasm-runner.js +22 -14
- package/dist/shell/features.d.ts.map +1 -1
- package/dist/shell/features.js +5 -4
- package/dist/shell/unix-commands.d.ts +14 -1
- package/dist/shell/unix-commands.d.ts.map +1 -1
- package/dist/shell/unix-commands.js +115 -69
- package/dist/substrate/lifo/commands/net/ip.d.ts.map +1 -1
- package/dist/substrate/lifo/commands/system/ps.d.ts +2 -1
- package/dist/substrate/lifo/commands/system/ps.d.ts.map +1 -1
- package/dist/substrate/lifo/kernel/ServiceManager.d.ts.map +1 -1
- package/dist/substrate/lifo/kernel/ServiceManager.js +0 -2
- package/dist/substrate/lifo/kernel/network/NetworkStack.d.ts +22 -11
- package/dist/substrate/lifo/kernel/network/NetworkStack.d.ts.map +1 -1
- package/dist/substrate/lifo/kernel/network/NetworkStack.js +0 -2
- package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.d.ts +2 -1
- package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.d.ts.map +1 -1
- package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.js +3 -4
- package/dist/substrate/lifo/kernel/vfs/VFS.d.ts +17 -0
- package/dist/substrate/lifo/kernel/vfs/VFS.d.ts.map +1 -1
- package/dist/substrate/lifo/kernel/vfs/VFS.js +33 -0
- package/dist/substrate/lifo/node-compat/esbuild.d.ts.map +1 -1
- package/dist/substrate/lifo/node-compat/esbuild.js +2 -4
- package/dist/substrate/lifo/node-compat/index.d.ts.map +1 -1
- package/dist/substrate/lifo/node-compat/index.js +3 -2
- package/dist/substrate/lifo/node-compat/loose-equality.d.ts +19 -0
- package/dist/substrate/lifo/node-compat/loose-equality.d.ts.map +1 -0
- package/dist/substrate/lifo/node-compat/loose-equality.js +91 -0
- package/dist/substrate/lifo/node-compat/process.d.ts.map +1 -1
- package/dist/substrate/lifo/node-compat/process.js +6 -3
- package/dist/substrate/lifo/shell/Shell.d.ts +19 -0
- package/dist/substrate/lifo/shell/Shell.d.ts.map +1 -1
- package/dist/substrate/lifo/shell/Shell.js +86 -75
- package/dist/substrate/lifo/shell/lexer.d.ts +9 -0
- package/dist/substrate/lifo/shell/lexer.d.ts.map +1 -1
- package/dist/substrate/lifo/shell/lexer.js +40 -0
- package/dist/vfs/seed-project.d.ts.map +1 -1
- package/dist/vfs/seed-project.js +9 -3
- package/dist/vfs/sqlite-vfs.d.ts +63 -31
- package/dist/vfs/sqlite-vfs.d.ts.map +1 -1
- package/dist/vfs/sqlite-vfs.js +206 -41
- package/package.json +3 -2
- package/src/_shared/cache-stats.ts +27 -1
- package/src/_shared/error-text.ts +21 -0
- package/src/_shared/exports-resolver.ts +24 -4
- package/src/_shared/retry.ts +5 -3
- package/src/constants.ts +37 -53
- package/src/runtime/assets-loader.ts +1 -1
- package/src/runtime/esbuild-service.ts +16 -11
- package/src/runtime/installed-runtimes.ts +4 -3
- package/src/runtime/long-running-handle.ts +3 -3
- package/src/runtime/os-contracts.ts +7 -5
- package/src/runtime/port-registry.ts +97 -9
- package/src/runtime/process-logs.ts +12 -3
- package/src/runtime/project-detect.ts +7 -1
- package/src/runtime/require-resolver.ts +7 -7
- package/src/runtime/runtime-registry.ts +9 -7
- package/src/runtime/sqlite-runtime-fs-bridge.ts +9 -6
- package/src/runtime/virtual-socket-kernel.generated.ts +3 -3
- package/src/runtime/virtual-socket-kernel.ts +10 -5
- package/src/runtime/wasm-runner.ts +55 -29
- package/src/shell/features.ts +5 -4
- package/src/shell/unix-commands.ts +273 -128
- package/src/substrate/lifo/commands/net/ip.ts +15 -15
- package/src/substrate/lifo/commands/system/ps.ts +2 -1
- package/src/substrate/lifo/kernel/ServiceManager.ts +0 -2
- package/src/substrate/lifo/kernel/network/NetworkStack.ts +24 -17
- package/src/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.ts +37 -12
- package/src/substrate/lifo/kernel/vfs/VFS.ts +34 -0
- package/src/substrate/lifo/node-compat/esbuild.ts +16 -8
- package/src/substrate/lifo/node-compat/index.ts +3 -2
- package/src/substrate/lifo/node-compat/loose-equality.ts +81 -0
- package/src/substrate/lifo/node-compat/process.ts +5 -2
- package/src/substrate/lifo/shell/Shell.ts +92 -70
- package/src/substrate/lifo/shell/lexer.ts +38 -0
- package/src/vfs/seed-project.ts +13 -6
- package/src/vfs/sqlite-vfs.ts +233 -77
- package/dist/_shared/install-phase.d.ts +0 -47
- package/dist/_shared/install-phase.d.ts.map +0 -1
- package/dist/_shared/install-phase.js +0 -1
- package/dist/_shared/rpc-dispose.d.ts +0 -4
- package/dist/_shared/rpc-dispose.d.ts.map +0 -1
- package/dist/_shared/rpc-dispose.js +0 -30
- package/dist/_shared/w7-frame.d.ts +0 -79
- package/dist/_shared/w7-frame.d.ts.map +0 -1
- package/dist/_shared/w7-frame.js +0 -839
- package/dist/_shared/weighted-credit-pool.d.ts +0 -61
- package/dist/_shared/weighted-credit-pool.d.ts.map +0 -1
- package/dist/_shared/weighted-credit-pool.js +0 -196
- package/dist/observability/diag-counters.d.ts +0 -236
- package/dist/observability/diag-counters.d.ts.map +0 -1
- package/dist/observability/diag-counters.js +0 -195
- package/dist/observability/heap-estimate.d.ts +0 -225
- package/dist/observability/heap-estimate.d.ts.map +0 -1
- package/dist/observability/heap-estimate.js +0 -185
- package/dist/observability/heavy-alloc-coord.d.ts +0 -93
- package/dist/observability/heavy-alloc-coord.d.ts.map +0 -1
- package/dist/observability/heavy-alloc-coord.js +0 -181
- package/dist/observability/oom-classify.d.ts +0 -138
- package/dist/observability/oom-classify.d.ts.map +0 -1
- package/dist/observability/oom-classify.js +0 -261
- package/dist/observability/oom-discriminator.d.ts +0 -176
- package/dist/observability/oom-discriminator.d.ts.map +0 -1
- package/dist/observability/oom-discriminator.js +0 -272
- package/src/_shared/install-phase.ts +0 -56
- package/src/_shared/rpc-dispose.ts +0 -31
- package/src/_shared/w7-frame.ts +0 -1012
- package/src/_shared/weighted-credit-pool.ts +0 -244
- package/src/observability/diag-counters.ts +0 -343
- package/src/observability/heap-estimate.ts +0 -331
- package/src/observability/heavy-alloc-coord.ts +0 -241
- package/src/observability/oom-classify.ts +0 -253
- package/src/observability/oom-discriminator.ts +0 -405
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* oom-discriminator.ts — singleton ring buffer for OOM / failure
|
|
3
|
-
* forensics + last-known-RPC-frame + last-known-facet-id +
|
|
4
|
-
* session-recovery-event ring, plus snapshot/rehydrate hooks for DO
|
|
5
|
-
* storage persistence.
|
|
6
|
-
*
|
|
7
|
-
* W5 (Lever 5 / J.1.1) — original module. Pairs with oom-classify.ts.
|
|
8
|
-
* C'.2 — added the recovery_event ring.
|
|
9
|
-
*
|
|
10
|
-
* Why singleton-per-isolate
|
|
11
|
-
* ─────────────────────────
|
|
12
|
-
* Same pattern as src/observability/diag-counters.ts. The supervisor bundle
|
|
13
|
-
* is the consumer; all writers (sqlite-vfs, facet-pool, facet-manager,
|
|
14
|
-
* supervisor-rpc, npm-installer, nimbus-session) live in the same
|
|
15
|
-
* isolate. Module scope provides one process-local diagnostic without
|
|
16
|
-
* threading a handle through each writer.
|
|
17
|
-
*
|
|
18
|
-
* Two distinct rings
|
|
19
|
-
* ──────────────────
|
|
20
|
-
* - failures: things that failed (the original W5 ring).
|
|
21
|
-
* - recoveryEvents: lifecycle transitions of the session. Cold isolate
|
|
22
|
-
* boot → 'cold' → 'hydrated' → 'active' → 'drained'.
|
|
23
|
-
*
|
|
24
|
-
* Bounded-size guarantees
|
|
25
|
-
* ───────────────────────
|
|
26
|
-
* - failures ring: 50 entries (RING_SIZE).
|
|
27
|
-
* - recoveryEvents ring: 50 entries (RECOVERY_RING_SIZE).
|
|
28
|
-
* - per-message cap: 200 chars (truncated at insert).
|
|
29
|
-
* - per-RPC-frame: single slot, one object.
|
|
30
|
-
* - per-facet-id: single slot, one object.
|
|
31
|
-
*
|
|
32
|
-
* Snapshot size stays bounded even with both rings full.
|
|
33
|
-
*/
|
|
34
|
-
import { type OomCause } from './oom-classify.js';
|
|
35
|
-
export interface RpcFrame {
|
|
36
|
-
method: string;
|
|
37
|
-
payloadBytes: number;
|
|
38
|
-
atMs: number;
|
|
39
|
-
}
|
|
40
|
-
export interface FacetId {
|
|
41
|
-
codeId: string;
|
|
42
|
-
slotIndex: number;
|
|
43
|
-
atMs: number;
|
|
44
|
-
}
|
|
45
|
-
export interface DiagFailure {
|
|
46
|
-
/** ms epoch */
|
|
47
|
-
at: number;
|
|
48
|
-
/** Lifecycle stage (e.g. 'install', 'resolve', 'rpc', 'facet', 'ws'). */
|
|
49
|
-
phase: string;
|
|
50
|
-
/** Discriminated cause. See oom-classify.ts. */
|
|
51
|
-
cause: OomCause;
|
|
52
|
-
/** Best-effort RSS estimate in bytes (peak observed). */
|
|
53
|
-
rssEstimateBytes: number;
|
|
54
|
-
/** process.memoryUsage().heapUsed if available; 0 in DO contexts. */
|
|
55
|
-
heapUsedBytes: number;
|
|
56
|
-
/** SqliteVFS LRU hot bytes at time of failure. */
|
|
57
|
-
lruBytes: number;
|
|
58
|
-
/** Sum of in-flight RPC payload bytes (best-effort). */
|
|
59
|
-
inFlightBytes: number;
|
|
60
|
-
/** Snapshot of the most recent RPC frame, if any. */
|
|
61
|
-
lastRpcFrame: RpcFrame | null;
|
|
62
|
-
/** Snapshot of the most recent facet dispatch, if any. */
|
|
63
|
-
lastFacetId: FacetId | null;
|
|
64
|
-
/** Optional facet exit code (when failure represents a facet termination). */
|
|
65
|
-
exitCode?: number;
|
|
66
|
-
/** Optional facet pid. */
|
|
67
|
-
pid?: number;
|
|
68
|
-
/** Free-form message; truncated to MESSAGE_CAP characters. */
|
|
69
|
-
message?: string;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Session lifecycle states for the C'.2 recovery_event ring.
|
|
73
|
-
*
|
|
74
|
-
* This module records state-machine transitions; it does not enforce them.
|
|
75
|
-
*
|
|
76
|
-
* State semantics:
|
|
77
|
-
* - 'cold' : fresh DO instance, no in-memory session state yet.
|
|
78
|
-
* - 'hydrated' : Phase R completed — SQL state read into in-memory cache.
|
|
79
|
-
* - 'active' : Phase B+W+O completed — kernel/shell/terminal wired.
|
|
80
|
-
* - 'drained' : webSocketError/Close fired; in-memory caches nulled,
|
|
81
|
-
* SQL state freshly persisted; awaiting next reconnect.
|
|
82
|
-
*
|
|
83
|
-
* Trigger labels: human-readable event that caused the transition.
|
|
84
|
-
* Examples: 'first-fetch', 'ws-upgrade', 'ws-close', 'ws-error',
|
|
85
|
-
* 'isolate-evicted', 'manual-test'.
|
|
86
|
-
*/
|
|
87
|
-
/** Session state union.
|
|
88
|
-
*
|
|
89
|
-
* The original four are the high-level lifecycle markers:
|
|
90
|
-
* cold — never inited
|
|
91
|
-
* hydrated — re-init complete (warm path; Phase O skipped)
|
|
92
|
-
* active — live shell processing input
|
|
93
|
-
* drained — webSocketError/Close fired; SQL freshly persisted
|
|
94
|
-
*
|
|
95
|
-
* The four [B'.4] additions are the fine-grained phases of
|
|
96
|
-
* initSession itself, recorded as the function progresses:
|
|
97
|
-
* rehydrate — Phase R: loading state from DO SQLite
|
|
98
|
-
* build — Phase B: constructing Kernel/Shell/registry
|
|
99
|
-
* wire — Phase W: attaching terminal, replaying scrollback
|
|
100
|
-
* online — Phase O: cold-start MOTD + starter hint (cold only;
|
|
101
|
-
* on warm re-init this phase is skipped and the
|
|
102
|
-
* initSession ends with 'hydrated' instead)
|
|
103
|
-
*
|
|
104
|
-
* The phase transitions are SUPPLEMENTAL to the high-level markers,
|
|
105
|
-
* not a replacement. Probes that look for the legacy `'hydrated'`
|
|
106
|
-
* marker continue to work; probes that want fine-grained debugging
|
|
107
|
-
* can grep for the phase states.
|
|
108
|
-
*/
|
|
109
|
-
export type SessionState = 'cold' | 'hydrated' | 'active' | 'drained' | 'rehydrate' | 'build' | 'wire' | 'online';
|
|
110
|
-
export interface DiagRecoveryEvent {
|
|
111
|
-
/** ms epoch */
|
|
112
|
-
at: number;
|
|
113
|
-
/** Source state. */
|
|
114
|
-
fromState: SessionState;
|
|
115
|
-
/** Destination state. */
|
|
116
|
-
toState: SessionState;
|
|
117
|
-
/** Human-readable trigger ('ws-close', 'isolate-evicted', etc.). */
|
|
118
|
-
trigger: string;
|
|
119
|
-
/** _w9IsolateGen at the time of the event. Lets a probe detect the
|
|
120
|
-
* difference between an in-isolate transition and a cross-isolate one
|
|
121
|
-
* (the latter implies workerd recycled the DO). */
|
|
122
|
-
isolateGen: number;
|
|
123
|
-
/** True when the transition could not preserve state that should
|
|
124
|
-
* have survived (e.g. SQL persist threw or a snapshot was missing). */
|
|
125
|
-
dataLoss: boolean;
|
|
126
|
-
/** Number of SQL keys/rows rehydrated on a hydrated transition.
|
|
127
|
-
* Zero when the transition is one that doesn't read SQL. */
|
|
128
|
-
snapshotKeysRehydrated: number;
|
|
129
|
-
/** Optional free-form notes (200 char cap). */
|
|
130
|
-
notes?: string;
|
|
131
|
-
}
|
|
132
|
-
/** Append a failure to the ring. Newest first. Capped at RING_SIZE. */
|
|
133
|
-
export declare function recordFailure(f: DiagFailure): void;
|
|
134
|
-
/** Read a snapshot of the ring. Newest first. Caller-side mutations
|
|
135
|
-
* do not affect the singleton. */
|
|
136
|
-
export declare function getFailures(): DiagFailure[];
|
|
137
|
-
/** Record the current RPC frame (called at every RPC entry). Bounded
|
|
138
|
-
* to a single slot — the LATEST frame wins. */
|
|
139
|
-
export declare function setLastRpcFrame(method: string, payloadBytes: number): void;
|
|
140
|
-
export declare function getLastRpcFrame(): RpcFrame | null;
|
|
141
|
-
/** Record the most recent facet dispatch. */
|
|
142
|
-
export declare function setLastFacetId(codeId: string, slotIndex: number): void;
|
|
143
|
-
export declare function getLastFacetId(): FacetId | null;
|
|
144
|
-
/** Append a recovery event to the ring. Newest first. Capped at
|
|
145
|
-
* RECOVERY_RING_SIZE. */
|
|
146
|
-
export declare function recordRecoveryEvent(e: DiagRecoveryEvent): void;
|
|
147
|
-
/** Read a snapshot of the recovery ring. Newest first. */
|
|
148
|
-
export declare function getRecoveryEvents(): DiagRecoveryEvent[];
|
|
149
|
-
/** Reset the recovery ring. Tests + manual operator use only. */
|
|
150
|
-
export declare function resetRecoveryEvents(): void;
|
|
151
|
-
/**
|
|
152
|
-
* Build a JSON-serializable snapshot suitable for ctx.storage.put.
|
|
153
|
-
* Bounded ≤ 40 KB even with both rings full of MESSAGE_CAP-truncated
|
|
154
|
-
* messages. Schema version embedded so a future shape change can
|
|
155
|
-
* cleanly reject old snapshots.
|
|
156
|
-
*
|
|
157
|
-
* Schema version 2 includes both failure and recovery-event rings.
|
|
158
|
-
*/
|
|
159
|
-
export interface DiagSnapshot {
|
|
160
|
-
/** Schema version. Bump when shape changes. */
|
|
161
|
-
v: 2;
|
|
162
|
-
failures: DiagFailure[];
|
|
163
|
-
recoveryEvents: DiagRecoveryEvent[];
|
|
164
|
-
lastRpcFrame: RpcFrame | null;
|
|
165
|
-
lastFacetId: FacetId | null;
|
|
166
|
-
}
|
|
167
|
-
export declare function snapshotForStorage(): DiagSnapshot;
|
|
168
|
-
/**
|
|
169
|
-
* Restore from a snapshot. Garbage / null / wrong-shape input is
|
|
170
|
-
* silently ignored. Does NOT throw — constructor-time rehydration must
|
|
171
|
-
* never block DO startup.
|
|
172
|
-
*
|
|
173
|
-
* Only the current v2 schema is accepted.
|
|
174
|
-
*/
|
|
175
|
-
export declare function rehydrateFromStorage(blob: unknown): void;
|
|
176
|
-
//# sourceMappingURL=oom-discriminator.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"oom-discriminator.d.ts","sourceRoot":"","sources":["../../src/observability/oom-discriminator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* oom-discriminator.ts — singleton ring buffer for OOM / failure
|
|
3
|
-
* forensics + last-known-RPC-frame + last-known-facet-id +
|
|
4
|
-
* session-recovery-event ring, plus snapshot/rehydrate hooks for DO
|
|
5
|
-
* storage persistence.
|
|
6
|
-
*
|
|
7
|
-
* W5 (Lever 5 / J.1.1) — original module. Pairs with oom-classify.ts.
|
|
8
|
-
* C'.2 — added the recovery_event ring.
|
|
9
|
-
*
|
|
10
|
-
* Why singleton-per-isolate
|
|
11
|
-
* ─────────────────────────
|
|
12
|
-
* Same pattern as src/observability/diag-counters.ts. The supervisor bundle
|
|
13
|
-
* is the consumer; all writers (sqlite-vfs, facet-pool, facet-manager,
|
|
14
|
-
* supervisor-rpc, npm-installer, nimbus-session) live in the same
|
|
15
|
-
* isolate. Module scope provides one process-local diagnostic without
|
|
16
|
-
* threading a handle through each writer.
|
|
17
|
-
*
|
|
18
|
-
* Two distinct rings
|
|
19
|
-
* ──────────────────
|
|
20
|
-
* - failures: things that failed (the original W5 ring).
|
|
21
|
-
* - recoveryEvents: lifecycle transitions of the session. Cold isolate
|
|
22
|
-
* boot → 'cold' → 'hydrated' → 'active' → 'drained'.
|
|
23
|
-
*
|
|
24
|
-
* Bounded-size guarantees
|
|
25
|
-
* ───────────────────────
|
|
26
|
-
* - failures ring: 50 entries (RING_SIZE).
|
|
27
|
-
* - recoveryEvents ring: 50 entries (RECOVERY_RING_SIZE).
|
|
28
|
-
* - per-message cap: 200 chars (truncated at insert).
|
|
29
|
-
* - per-RPC-frame: single slot, one object.
|
|
30
|
-
* - per-facet-id: single slot, one object.
|
|
31
|
-
*
|
|
32
|
-
* Snapshot size stays bounded even with both rings full.
|
|
33
|
-
*/
|
|
34
|
-
import { isOomCause } from './oom-classify.js';
|
|
35
|
-
const RING_SIZE = 50;
|
|
36
|
-
const RECOVERY_RING_SIZE = 50;
|
|
37
|
-
const MESSAGE_CAP = 200;
|
|
38
|
-
const state = {
|
|
39
|
-
failures: [],
|
|
40
|
-
recoveryEvents: [],
|
|
41
|
-
lastRpcFrame: null,
|
|
42
|
-
lastFacetId: null,
|
|
43
|
-
};
|
|
44
|
-
function getState() {
|
|
45
|
-
return state;
|
|
46
|
-
}
|
|
47
|
-
/** Append a failure to the ring. Newest first. Capped at RING_SIZE. */
|
|
48
|
-
export function recordFailure(f) {
|
|
49
|
-
const s = getState();
|
|
50
|
-
// Defensive copy + message cap.
|
|
51
|
-
const entry = {
|
|
52
|
-
at: Number(f.at) || Date.now(),
|
|
53
|
-
phase: String(f.phase ?? 'unknown'),
|
|
54
|
-
cause: f.cause,
|
|
55
|
-
rssEstimateBytes: Number(f.rssEstimateBytes) || 0,
|
|
56
|
-
heapUsedBytes: Number(f.heapUsedBytes) || 0,
|
|
57
|
-
lruBytes: Number(f.lruBytes) || 0,
|
|
58
|
-
inFlightBytes: Number(f.inFlightBytes) || 0,
|
|
59
|
-
lastRpcFrame: f.lastRpcFrame ?? s.lastRpcFrame,
|
|
60
|
-
lastFacetId: f.lastFacetId ?? s.lastFacetId,
|
|
61
|
-
};
|
|
62
|
-
if (typeof f.exitCode === 'number')
|
|
63
|
-
entry.exitCode = f.exitCode;
|
|
64
|
-
if (typeof f.pid === 'number')
|
|
65
|
-
entry.pid = f.pid;
|
|
66
|
-
if (f.message) {
|
|
67
|
-
const m = String(f.message);
|
|
68
|
-
entry.message = m.length > MESSAGE_CAP ? m.slice(0, MESSAGE_CAP) : m;
|
|
69
|
-
}
|
|
70
|
-
s.failures.unshift(entry);
|
|
71
|
-
if (s.failures.length > RING_SIZE)
|
|
72
|
-
s.failures.length = RING_SIZE;
|
|
73
|
-
}
|
|
74
|
-
/** Read a snapshot of the ring. Newest first. Caller-side mutations
|
|
75
|
-
* do not affect the singleton. */
|
|
76
|
-
export function getFailures() {
|
|
77
|
-
return getState().failures.slice(0);
|
|
78
|
-
}
|
|
79
|
-
/** Record the current RPC frame (called at every RPC entry). Bounded
|
|
80
|
-
* to a single slot — the LATEST frame wins. */
|
|
81
|
-
export function setLastRpcFrame(method, payloadBytes) {
|
|
82
|
-
const s = getState();
|
|
83
|
-
s.lastRpcFrame = {
|
|
84
|
-
method: String(method),
|
|
85
|
-
payloadBytes: Number(payloadBytes) || 0,
|
|
86
|
-
atMs: Date.now(),
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
export function getLastRpcFrame() {
|
|
90
|
-
return getState().lastRpcFrame;
|
|
91
|
-
}
|
|
92
|
-
/** Record the most recent facet dispatch. */
|
|
93
|
-
export function setLastFacetId(codeId, slotIndex) {
|
|
94
|
-
const s = getState();
|
|
95
|
-
s.lastFacetId = {
|
|
96
|
-
codeId: String(codeId),
|
|
97
|
-
slotIndex: Number(slotIndex) || 0,
|
|
98
|
-
atMs: Date.now(),
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
export function getLastFacetId() {
|
|
102
|
-
return getState().lastFacetId;
|
|
103
|
-
}
|
|
104
|
-
// ── C'.2 recovery_event ring ────────────────────────────────────────────
|
|
105
|
-
//
|
|
106
|
-
// The ring is bounded at RECOVERY_RING_SIZE; the diag endpoint reads via
|
|
107
|
-
// getRecoveryEvents() (newest first).
|
|
108
|
-
/** Append a recovery event to the ring. Newest first. Capped at
|
|
109
|
-
* RECOVERY_RING_SIZE. */
|
|
110
|
-
export function recordRecoveryEvent(e) {
|
|
111
|
-
const s = getState();
|
|
112
|
-
// Defensive copy + notes cap. We intentionally do NOT validate the
|
|
113
|
-
// state-machine direction here (e.g. that 'drained' only follows
|
|
114
|
-
// 'active') — the state machine lives elsewhere; this ring is a
|
|
115
|
-
// recorder, not an enforcer. If a probe sees an impossible
|
|
116
|
-
// transition, that's a real bug in the state machine and the
|
|
117
|
-
// probe must fail.
|
|
118
|
-
const entry = {
|
|
119
|
-
at: Number(e.at) || Date.now(),
|
|
120
|
-
fromState: e.fromState,
|
|
121
|
-
toState: e.toState,
|
|
122
|
-
trigger: String(e.trigger ?? 'unknown'),
|
|
123
|
-
isolateGen: Number(e.isolateGen) || 0,
|
|
124
|
-
dataLoss: !!e.dataLoss,
|
|
125
|
-
snapshotKeysRehydrated: Number(e.snapshotKeysRehydrated) || 0,
|
|
126
|
-
};
|
|
127
|
-
if (e.notes) {
|
|
128
|
-
const n = String(e.notes);
|
|
129
|
-
entry.notes = n.length > MESSAGE_CAP ? n.slice(0, MESSAGE_CAP) : n;
|
|
130
|
-
}
|
|
131
|
-
s.recoveryEvents.unshift(entry);
|
|
132
|
-
if (s.recoveryEvents.length > RECOVERY_RING_SIZE) {
|
|
133
|
-
s.recoveryEvents.length = RECOVERY_RING_SIZE;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/** Read a snapshot of the recovery ring. Newest first. */
|
|
137
|
-
export function getRecoveryEvents() {
|
|
138
|
-
return getState().recoveryEvents.slice(0);
|
|
139
|
-
}
|
|
140
|
-
/** Reset the recovery ring. Tests + manual operator use only. */
|
|
141
|
-
export function resetRecoveryEvents() {
|
|
142
|
-
getState().recoveryEvents.length = 0;
|
|
143
|
-
}
|
|
144
|
-
export function snapshotForStorage() {
|
|
145
|
-
const s = getState();
|
|
146
|
-
return {
|
|
147
|
-
v: 2,
|
|
148
|
-
failures: s.failures.slice(0),
|
|
149
|
-
recoveryEvents: s.recoveryEvents.slice(0),
|
|
150
|
-
lastRpcFrame: s.lastRpcFrame,
|
|
151
|
-
lastFacetId: s.lastFacetId,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Restore from a snapshot. Garbage / null / wrong-shape input is
|
|
156
|
-
* silently ignored. Does NOT throw — constructor-time rehydration must
|
|
157
|
-
* never block DO startup.
|
|
158
|
-
*
|
|
159
|
-
* Only the current v2 schema is accepted.
|
|
160
|
-
*/
|
|
161
|
-
export function rehydrateFromStorage(blob) {
|
|
162
|
-
if (!isRecord(blob) || blob.v !== 2)
|
|
163
|
-
return;
|
|
164
|
-
const b = blob;
|
|
165
|
-
if (!Array.isArray(b.failures))
|
|
166
|
-
return;
|
|
167
|
-
const s = getState();
|
|
168
|
-
s.failures.length = 0;
|
|
169
|
-
for (const f of b.failures) {
|
|
170
|
-
const entry = parseFailure(f);
|
|
171
|
-
if (!entry)
|
|
172
|
-
continue;
|
|
173
|
-
s.failures.push(entry);
|
|
174
|
-
if (s.failures.length >= RING_SIZE)
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
s.recoveryEvents.length = 0;
|
|
178
|
-
if (Array.isArray(b.recoveryEvents)) {
|
|
179
|
-
for (const e of b.recoveryEvents) {
|
|
180
|
-
const entry = parseRecoveryEvent(e);
|
|
181
|
-
if (!entry)
|
|
182
|
-
continue;
|
|
183
|
-
s.recoveryEvents.push(entry);
|
|
184
|
-
if (s.recoveryEvents.length >= RECOVERY_RING_SIZE)
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
const lastRpcFrame = parseRpcFrame(b.lastRpcFrame);
|
|
189
|
-
if (lastRpcFrame)
|
|
190
|
-
s.lastRpcFrame = lastRpcFrame;
|
|
191
|
-
const lastFacetId = parseFacetId(b.lastFacetId);
|
|
192
|
-
if (lastFacetId)
|
|
193
|
-
s.lastFacetId = lastFacetId;
|
|
194
|
-
}
|
|
195
|
-
function isRecord(value) {
|
|
196
|
-
return typeof value === 'object' && value !== null;
|
|
197
|
-
}
|
|
198
|
-
function parseRpcFrame(value) {
|
|
199
|
-
if (!isRecord(value)
|
|
200
|
-
|| typeof value.method !== 'string'
|
|
201
|
-
|| typeof value.payloadBytes !== 'number'
|
|
202
|
-
|| typeof value.atMs !== 'number')
|
|
203
|
-
return null;
|
|
204
|
-
return { method: value.method, payloadBytes: value.payloadBytes, atMs: value.atMs };
|
|
205
|
-
}
|
|
206
|
-
function parseFacetId(value) {
|
|
207
|
-
if (!isRecord(value)
|
|
208
|
-
|| typeof value.codeId !== 'string'
|
|
209
|
-
|| typeof value.slotIndex !== 'number'
|
|
210
|
-
|| typeof value.atMs !== 'number')
|
|
211
|
-
return null;
|
|
212
|
-
return { codeId: value.codeId, slotIndex: value.slotIndex, atMs: value.atMs };
|
|
213
|
-
}
|
|
214
|
-
function parseFailure(value) {
|
|
215
|
-
if (!isRecord(value)
|
|
216
|
-
|| typeof value.at !== 'number'
|
|
217
|
-
|| typeof value.phase !== 'string'
|
|
218
|
-
|| !isOomCause(value.cause)
|
|
219
|
-
|| typeof value.rssEstimateBytes !== 'number'
|
|
220
|
-
|| typeof value.heapUsedBytes !== 'number'
|
|
221
|
-
|| typeof value.lruBytes !== 'number'
|
|
222
|
-
|| typeof value.inFlightBytes !== 'number')
|
|
223
|
-
return null;
|
|
224
|
-
const entry = {
|
|
225
|
-
at: value.at,
|
|
226
|
-
phase: value.phase,
|
|
227
|
-
cause: value.cause,
|
|
228
|
-
rssEstimateBytes: value.rssEstimateBytes,
|
|
229
|
-
heapUsedBytes: value.heapUsedBytes,
|
|
230
|
-
lruBytes: value.lruBytes,
|
|
231
|
-
inFlightBytes: value.inFlightBytes,
|
|
232
|
-
lastRpcFrame: parseRpcFrame(value.lastRpcFrame),
|
|
233
|
-
lastFacetId: parseFacetId(value.lastFacetId),
|
|
234
|
-
};
|
|
235
|
-
if (typeof value.exitCode === 'number')
|
|
236
|
-
entry.exitCode = value.exitCode;
|
|
237
|
-
if (typeof value.pid === 'number')
|
|
238
|
-
entry.pid = value.pid;
|
|
239
|
-
if (typeof value.message === 'string')
|
|
240
|
-
entry.message = value.message.slice(0, MESSAGE_CAP);
|
|
241
|
-
return entry;
|
|
242
|
-
}
|
|
243
|
-
const SESSION_STATES = [
|
|
244
|
-
'cold', 'hydrated', 'active', 'drained',
|
|
245
|
-
'rehydrate', 'build', 'wire', 'online',
|
|
246
|
-
];
|
|
247
|
-
function isSessionState(value) {
|
|
248
|
-
return SESSION_STATES.some((state) => state === value);
|
|
249
|
-
}
|
|
250
|
-
function parseRecoveryEvent(value) {
|
|
251
|
-
if (!isRecord(value)
|
|
252
|
-
|| typeof value.at !== 'number'
|
|
253
|
-
|| !isSessionState(value.fromState)
|
|
254
|
-
|| !isSessionState(value.toState)
|
|
255
|
-
|| typeof value.trigger !== 'string'
|
|
256
|
-
|| typeof value.isolateGen !== 'number'
|
|
257
|
-
|| typeof value.dataLoss !== 'boolean'
|
|
258
|
-
|| typeof value.snapshotKeysRehydrated !== 'number')
|
|
259
|
-
return null;
|
|
260
|
-
const entry = {
|
|
261
|
-
at: value.at,
|
|
262
|
-
fromState: value.fromState,
|
|
263
|
-
toState: value.toState,
|
|
264
|
-
trigger: value.trigger,
|
|
265
|
-
isolateGen: value.isolateGen,
|
|
266
|
-
dataLoss: value.dataLoss,
|
|
267
|
-
snapshotKeysRehydrated: value.snapshotKeysRehydrated,
|
|
268
|
-
};
|
|
269
|
-
if (typeof value.notes === 'string')
|
|
270
|
-
entry.notes = value.notes.slice(0, MESSAGE_CAP);
|
|
271
|
-
return entry;
|
|
272
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* _shared/install-phase.ts — single source of truth for the npm-install
|
|
3
|
-
* state-machine phase enum.
|
|
4
|
-
*
|
|
5
|
-
* History: CLN-1 (2026-05-11) — consolidated from two diverged copies.
|
|
6
|
-
* - `npm/installer.ts:93-95` was the progress-event variant. Included
|
|
7
|
-
* 'lock-check' (a real progress event emitted while parsing
|
|
8
|
-
* package-lock.json) but lacked 'idle'.
|
|
9
|
-
* - `observability/diag-counters.ts:33-35` was the counter-state variant.
|
|
10
|
-
* Included 'idle' (the resting state when no install is in flight)
|
|
11
|
-
* but lacked 'lock-check'. The diag-counter's `setInstallPhase()`
|
|
12
|
-
* was actually called with 'idle' (installer.ts:184,201) — passing
|
|
13
|
-
* a value not declared in installer.ts's local type. The type check
|
|
14
|
-
* fell through to diag-counters' type because installer.ts imports
|
|
15
|
-
* setInstallPhase from there; the local `InstallPhase` definition
|
|
16
|
-
* in installer.ts was only used by `InstallProgress.phase`.
|
|
17
|
-
*
|
|
18
|
-
* Going forward both consumers import from this module; any new phase
|
|
19
|
-
* value added here is automatically available everywhere.
|
|
20
|
-
*
|
|
21
|
-
* Phase semantics (chronological order for a single install run):
|
|
22
|
-
*
|
|
23
|
-
* idle resting state, no install in flight
|
|
24
|
-
* ↓
|
|
25
|
-
* lock-check parsing existing package-lock.json (if present)
|
|
26
|
-
* ↓
|
|
27
|
-
* resolve walking the dep tree, fetching packuments
|
|
28
|
-
* ↓
|
|
29
|
-
* hoist hoisting peer-deps + flattening node_modules
|
|
30
|
-
* ↓
|
|
31
|
-
* diff comparing target tree against current install state
|
|
32
|
-
* ↓
|
|
33
|
-
* fetch downloading missing tarballs
|
|
34
|
-
* ↓
|
|
35
|
-
* write extracting tarballs into the VFS
|
|
36
|
-
* ↓
|
|
37
|
-
* link-bins creating bin/ symlinks
|
|
38
|
-
* ↓
|
|
39
|
-
* bundle (post-install) pre-bundling for esbuild fast path
|
|
40
|
-
* ↓
|
|
41
|
-
* done install complete; about to return to 'idle'
|
|
42
|
-
*
|
|
43
|
-
* The phase strings are surfaced via /api/_diag/install-pipeline and
|
|
44
|
-
* displayed in the install-progress UI; treat them as a public API.
|
|
45
|
-
*/
|
|
46
|
-
export type InstallPhase =
|
|
47
|
-
| 'idle'
|
|
48
|
-
| 'lock-check'
|
|
49
|
-
| 'resolve'
|
|
50
|
-
| 'hoist'
|
|
51
|
-
| 'diff'
|
|
52
|
-
| 'fetch'
|
|
53
|
-
| 'write'
|
|
54
|
-
| 'link-bins'
|
|
55
|
-
| 'bundle'
|
|
56
|
-
| 'done';
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
type DisposableSymbolConstructor = SymbolConstructor & { readonly dispose?: symbol };
|
|
2
|
-
|
|
3
|
-
export function disposeRpcResource(value: unknown): boolean {
|
|
4
|
-
if ((typeof value !== 'object' && typeof value !== 'function') || value === null) return false;
|
|
5
|
-
const disposerKey = (Symbol as DisposableSymbolConstructor).dispose;
|
|
6
|
-
if (!disposerKey) return false;
|
|
7
|
-
const disposer = Reflect.get(value, disposerKey);
|
|
8
|
-
if (typeof disposer !== 'function') return false;
|
|
9
|
-
try {
|
|
10
|
-
Reflect.apply(disposer, value, []);
|
|
11
|
-
return true;
|
|
12
|
-
} catch {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function disposeRpcResources(values: Iterable<unknown>): void {
|
|
18
|
-
for (const value of values) disposeRpcResource(value);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function useRpcResource<T, R>(
|
|
22
|
-
promise: Promise<T>,
|
|
23
|
-
use: (value: T) => R | Promise<R>,
|
|
24
|
-
): Promise<R> {
|
|
25
|
-
const value = await promise;
|
|
26
|
-
try {
|
|
27
|
-
return await use(value);
|
|
28
|
-
} finally {
|
|
29
|
-
disposeRpcResource(value);
|
|
30
|
-
}
|
|
31
|
-
}
|