@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.
Files changed (156) hide show
  1. package/README.md +35 -7
  2. package/dist/_shared/cache-stats.d.ts +23 -1
  3. package/dist/_shared/cache-stats.d.ts.map +1 -1
  4. package/dist/_shared/cache-stats.js +13 -1
  5. package/dist/_shared/error-text.d.ts +15 -0
  6. package/dist/_shared/error-text.d.ts.map +1 -0
  7. package/dist/_shared/error-text.js +21 -0
  8. package/dist/_shared/exports-resolver.d.ts +19 -7
  9. package/dist/_shared/exports-resolver.d.ts.map +1 -1
  10. package/dist/_shared/exports-resolver.js +1 -1
  11. package/dist/_shared/retry.d.ts.map +1 -1
  12. package/dist/_shared/retry.js +4 -2
  13. package/dist/constants.d.ts +5 -11
  14. package/dist/constants.d.ts.map +1 -1
  15. package/dist/constants.js +23 -49
  16. package/dist/runtime/assets-loader.js +1 -1
  17. package/dist/runtime/esbuild-service.d.ts +4 -3
  18. package/dist/runtime/esbuild-service.d.ts.map +1 -1
  19. package/dist/runtime/esbuild-service.js +9 -4
  20. package/dist/runtime/installed-runtimes.d.ts +4 -3
  21. package/dist/runtime/installed-runtimes.d.ts.map +1 -1
  22. package/dist/runtime/long-running-handle.d.ts +3 -3
  23. package/dist/runtime/long-running-handle.d.ts.map +1 -1
  24. package/dist/runtime/os-contracts.d.ts +7 -5
  25. package/dist/runtime/os-contracts.d.ts.map +1 -1
  26. package/dist/runtime/port-registry.d.ts +28 -0
  27. package/dist/runtime/port-registry.d.ts.map +1 -1
  28. package/dist/runtime/port-registry.js +79 -9
  29. package/dist/runtime/process-logs.d.ts +9 -1
  30. package/dist/runtime/process-logs.d.ts.map +1 -1
  31. package/dist/runtime/project-detect.d.ts +6 -1
  32. package/dist/runtime/project-detect.d.ts.map +1 -1
  33. package/dist/runtime/require-resolver.d.ts.map +1 -1
  34. package/dist/runtime/require-resolver.js +3 -4
  35. package/dist/runtime/runtime-registry.d.ts +4 -3
  36. package/dist/runtime/runtime-registry.d.ts.map +1 -1
  37. package/dist/runtime/runtime-registry.js +2 -1
  38. package/dist/runtime/sqlite-runtime-fs-bridge.js +1 -5
  39. package/dist/runtime/virtual-socket-kernel.d.ts.map +1 -1
  40. package/dist/runtime/virtual-socket-kernel.generated.d.ts +2 -2
  41. package/dist/runtime/virtual-socket-kernel.generated.d.ts.map +1 -1
  42. package/dist/runtime/virtual-socket-kernel.generated.js +3 -3
  43. package/dist/runtime/virtual-socket-kernel.js +10 -5
  44. package/dist/runtime/wasm-runner.d.ts.map +1 -1
  45. package/dist/runtime/wasm-runner.js +22 -14
  46. package/dist/shell/features.d.ts.map +1 -1
  47. package/dist/shell/features.js +5 -4
  48. package/dist/shell/unix-commands.d.ts +14 -1
  49. package/dist/shell/unix-commands.d.ts.map +1 -1
  50. package/dist/shell/unix-commands.js +115 -69
  51. package/dist/substrate/lifo/commands/net/ip.d.ts.map +1 -1
  52. package/dist/substrate/lifo/commands/system/ps.d.ts +2 -1
  53. package/dist/substrate/lifo/commands/system/ps.d.ts.map +1 -1
  54. package/dist/substrate/lifo/kernel/ServiceManager.d.ts.map +1 -1
  55. package/dist/substrate/lifo/kernel/ServiceManager.js +0 -2
  56. package/dist/substrate/lifo/kernel/network/NetworkStack.d.ts +22 -11
  57. package/dist/substrate/lifo/kernel/network/NetworkStack.d.ts.map +1 -1
  58. package/dist/substrate/lifo/kernel/network/NetworkStack.js +0 -2
  59. package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.d.ts +2 -1
  60. package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.d.ts.map +1 -1
  61. package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.js +3 -4
  62. package/dist/substrate/lifo/kernel/vfs/VFS.d.ts +17 -0
  63. package/dist/substrate/lifo/kernel/vfs/VFS.d.ts.map +1 -1
  64. package/dist/substrate/lifo/kernel/vfs/VFS.js +33 -0
  65. package/dist/substrate/lifo/node-compat/esbuild.d.ts.map +1 -1
  66. package/dist/substrate/lifo/node-compat/esbuild.js +2 -4
  67. package/dist/substrate/lifo/node-compat/index.d.ts.map +1 -1
  68. package/dist/substrate/lifo/node-compat/index.js +3 -2
  69. package/dist/substrate/lifo/node-compat/loose-equality.d.ts +19 -0
  70. package/dist/substrate/lifo/node-compat/loose-equality.d.ts.map +1 -0
  71. package/dist/substrate/lifo/node-compat/loose-equality.js +91 -0
  72. package/dist/substrate/lifo/node-compat/process.d.ts.map +1 -1
  73. package/dist/substrate/lifo/node-compat/process.js +6 -3
  74. package/dist/substrate/lifo/shell/Shell.d.ts +19 -0
  75. package/dist/substrate/lifo/shell/Shell.d.ts.map +1 -1
  76. package/dist/substrate/lifo/shell/Shell.js +86 -75
  77. package/dist/substrate/lifo/shell/lexer.d.ts +9 -0
  78. package/dist/substrate/lifo/shell/lexer.d.ts.map +1 -1
  79. package/dist/substrate/lifo/shell/lexer.js +40 -0
  80. package/dist/vfs/seed-project.d.ts.map +1 -1
  81. package/dist/vfs/seed-project.js +9 -3
  82. package/dist/vfs/sqlite-vfs.d.ts +63 -31
  83. package/dist/vfs/sqlite-vfs.d.ts.map +1 -1
  84. package/dist/vfs/sqlite-vfs.js +206 -41
  85. package/package.json +3 -2
  86. package/src/_shared/cache-stats.ts +27 -1
  87. package/src/_shared/error-text.ts +21 -0
  88. package/src/_shared/exports-resolver.ts +24 -4
  89. package/src/_shared/retry.ts +5 -3
  90. package/src/constants.ts +37 -53
  91. package/src/runtime/assets-loader.ts +1 -1
  92. package/src/runtime/esbuild-service.ts +16 -11
  93. package/src/runtime/installed-runtimes.ts +4 -3
  94. package/src/runtime/long-running-handle.ts +3 -3
  95. package/src/runtime/os-contracts.ts +7 -5
  96. package/src/runtime/port-registry.ts +97 -9
  97. package/src/runtime/process-logs.ts +12 -3
  98. package/src/runtime/project-detect.ts +7 -1
  99. package/src/runtime/require-resolver.ts +7 -7
  100. package/src/runtime/runtime-registry.ts +9 -7
  101. package/src/runtime/sqlite-runtime-fs-bridge.ts +9 -6
  102. package/src/runtime/virtual-socket-kernel.generated.ts +3 -3
  103. package/src/runtime/virtual-socket-kernel.ts +10 -5
  104. package/src/runtime/wasm-runner.ts +55 -29
  105. package/src/shell/features.ts +5 -4
  106. package/src/shell/unix-commands.ts +273 -128
  107. package/src/substrate/lifo/commands/net/ip.ts +15 -15
  108. package/src/substrate/lifo/commands/system/ps.ts +2 -1
  109. package/src/substrate/lifo/kernel/ServiceManager.ts +0 -2
  110. package/src/substrate/lifo/kernel/network/NetworkStack.ts +24 -17
  111. package/src/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.ts +37 -12
  112. package/src/substrate/lifo/kernel/vfs/VFS.ts +34 -0
  113. package/src/substrate/lifo/node-compat/esbuild.ts +16 -8
  114. package/src/substrate/lifo/node-compat/index.ts +3 -2
  115. package/src/substrate/lifo/node-compat/loose-equality.ts +81 -0
  116. package/src/substrate/lifo/node-compat/process.ts +5 -2
  117. package/src/substrate/lifo/shell/Shell.ts +92 -70
  118. package/src/substrate/lifo/shell/lexer.ts +38 -0
  119. package/src/vfs/seed-project.ts +13 -6
  120. package/src/vfs/sqlite-vfs.ts +233 -77
  121. package/dist/_shared/install-phase.d.ts +0 -47
  122. package/dist/_shared/install-phase.d.ts.map +0 -1
  123. package/dist/_shared/install-phase.js +0 -1
  124. package/dist/_shared/rpc-dispose.d.ts +0 -4
  125. package/dist/_shared/rpc-dispose.d.ts.map +0 -1
  126. package/dist/_shared/rpc-dispose.js +0 -30
  127. package/dist/_shared/w7-frame.d.ts +0 -79
  128. package/dist/_shared/w7-frame.d.ts.map +0 -1
  129. package/dist/_shared/w7-frame.js +0 -839
  130. package/dist/_shared/weighted-credit-pool.d.ts +0 -61
  131. package/dist/_shared/weighted-credit-pool.d.ts.map +0 -1
  132. package/dist/_shared/weighted-credit-pool.js +0 -196
  133. package/dist/observability/diag-counters.d.ts +0 -236
  134. package/dist/observability/diag-counters.d.ts.map +0 -1
  135. package/dist/observability/diag-counters.js +0 -195
  136. package/dist/observability/heap-estimate.d.ts +0 -225
  137. package/dist/observability/heap-estimate.d.ts.map +0 -1
  138. package/dist/observability/heap-estimate.js +0 -185
  139. package/dist/observability/heavy-alloc-coord.d.ts +0 -93
  140. package/dist/observability/heavy-alloc-coord.d.ts.map +0 -1
  141. package/dist/observability/heavy-alloc-coord.js +0 -181
  142. package/dist/observability/oom-classify.d.ts +0 -138
  143. package/dist/observability/oom-classify.d.ts.map +0 -1
  144. package/dist/observability/oom-classify.js +0 -261
  145. package/dist/observability/oom-discriminator.d.ts +0 -176
  146. package/dist/observability/oom-discriminator.d.ts.map +0 -1
  147. package/dist/observability/oom-discriminator.js +0 -272
  148. package/src/_shared/install-phase.ts +0 -56
  149. package/src/_shared/rpc-dispose.ts +0 -31
  150. package/src/_shared/w7-frame.ts +0 -1012
  151. package/src/_shared/weighted-credit-pool.ts +0 -244
  152. package/src/observability/diag-counters.ts +0 -343
  153. package/src/observability/heap-estimate.ts +0 -331
  154. package/src/observability/heavy-alloc-coord.ts +0 -241
  155. package/src/observability/oom-classify.ts +0 -253
  156. 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
- }