@gjsify/v8 0.22.0 → 0.23.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 CHANGED
@@ -1,6 +1,8 @@
1
1
  # @gjsify/v8
2
2
 
3
- GJS stub implementation of the Node.js `v8` module. Provides getHeapStatistics and JSON-based serialize/deserialize.
3
+ GJS implementation of the Node.js `v8` module. Provides V8-wire-format
4
+ `serialize`/`deserialize` (`Serializer`, `Deserializer`, `DefaultSerializer`,
5
+ `DefaultDeserializer`) plus `getHeapStatistics()`.
4
6
 
5
7
  Part of the [gjsify](https://github.com/gjsify/gjsify) project — Node.js and Web APIs for GJS (GNOME JavaScript).
6
8
 
@@ -25,6 +27,57 @@ const buf = serialize({ hello: 'world' });
25
27
  const obj = deserialize(buf);
26
28
  ```
27
29
 
30
+ ## `getHeapStatistics()` — what the numbers mean
31
+
32
+ Node's `v8.getHeapStatistics()` reports V8's own accounting of its heap.
33
+ GJS runs on SpiderMonkey, which exposes no equivalent surface (there is nothing
34
+ like `HeapStatistics` on `imports.system`), so the memory-derived fields are
35
+ approximated from the **operating system's view of the whole process** instead
36
+ of the JS heap alone. Fields V8 computes from engine internals we cannot reach
37
+ are reported as `0`.
38
+
39
+ The result always has all 14 Node fields, always numeric — this function never
40
+ throws, on any platform.
41
+
42
+ | Field | Value on GJS |
43
+ |---|---|
44
+ | `total_heap_size` | process virtual size |
45
+ | `total_physical_size` | process resident set size |
46
+ | `used_heap_size` | process resident set size |
47
+ | `malloced_memory` | process data-segment size (Linux only) |
48
+ | `peak_malloced_memory` | peak process virtual size (Linux only) |
49
+ | `total_heap_size_executable`, `total_available_size`, `heap_size_limit`, `does_zap_garbage`, `number_of_native_contexts`, `number_of_detached_contexts`, `total_global_handles_size`, `used_global_handles_size`, `external_memory` | `0` — no SpiderMonkey equivalent |
50
+
51
+ ### Platform support
52
+
53
+ The reader lives in `src/heap/` (`linux.ts` / `darwin.ts` / `win32.ts` behind an
54
+ `index.ts`, mirroring `@gjsify/os`). Detection is capability-based — the
55
+ filesystem is probed for what is actually there, not a `uname` string parsed.
56
+
57
+ | Platform | Source | Coverage |
58
+ |---|---|---|
59
+ | Linux (incl. Android, Flatpak, containers) | `/proc/self/status` — one `g_file_get_contents()`, no subprocess | `VmSize`, `VmRSS`, `VmData`, `VmPeak` |
60
+ | macOS | `ps -o rss=,vsz=` — no procfs; Mach's `task_info()` (what libuv uses) needs a native bridge | `rss`, `vsz` only; `malloced_memory` and `peak_malloced_memory` stay `0` |
61
+ | Windows | none reachable | all memory fields `0` — see below |
62
+ | any other POSIX | none reachable | all memory fields `0` |
63
+
64
+ **Degraded contract on Windows.** There is no `/proc` and no `ps(1)`. The native
65
+ source is `GetProcessMemoryInfo()` (psapi), which GLib does not wrap and GJS
66
+ cannot call without a native bridge; shelling out to `wmic` / `tasklist` /
67
+ PowerShell would cost hundreds of milliseconds per call and `wmic` is deprecated
68
+ and removed from recent Windows builds. `getHeapStatistics()` therefore returns
69
+ the **all-zero** shape: every field present and numeric so destructuring callers
70
+ keep working, none of them meaningful. This is byte-for-byte the same value the
71
+ Linux reader already produced when `/proc` was unreadable, so no consumer sees a
72
+ new shape. Callers that need a real number should check for `0`.
73
+
74
+ ### Not implemented
75
+
76
+ `getHeapSpaceStatistics()` returns `[]`, `getHeapCodeStatistics()` returns
77
+ zeros, and `writeHeapSnapshot()` / `setFlagsFromString()` / the profiler and
78
+ `promiseHooks` surfaces are stubs — all of them are V8-internal APIs with no
79
+ SpiderMonkey counterpart.
80
+
28
81
  ## License
29
82
 
30
83
  MIT
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import e from"@girs/glib-2.0";function readProcessMemory(){try{let[t,n]=e.spawn_command_line_sync(`sh -c 'ps -o rss=,vsz= -p $PPID'`);if(!t||!n)return null;let r=new TextDecoder().decode(n).trim(),i=/^(\d+)\s+(\d+)$/.exec(r);return i?{virtual:parseInt(i[2],10)*1024,resident:parseInt(i[1],10)*1024,data:0,peak:0}:null}catch{return null}}export{readProcessMemory};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{readProcessMemory as e}from"./darwin.js";import{readProcessMemory as t}from"./linux.js";import{readProcessMemory as n}from"./win32.js";import r from"@girs/glib-2.0";let i=null;function detectHeapPlatform(){return i===null&&(i=r.get_current_dir().startsWith(`/`)?r.file_test(`/proc/self/status`,r.FileTest.EXISTS)?`linux`:r.file_test(`/System/Library/CoreServices/SystemVersion.plist`,r.FileTest.EXISTS)?`darwin`:`unknown`:`win32`),i}const a={virtual:0,resident:0,data:0,peak:0};function readProcessMemory(){switch(detectHeapPlatform()){case`linux`:return t();case`darwin`:return e();case`win32`:return n();default:return null}}function getHeapStatistics(){let e=readProcessMemory()??a;return{total_heap_size:e.virtual,total_heap_size_executable:0,total_physical_size:e.resident,total_available_size:0,used_heap_size:e.resident,heap_size_limit:0,malloced_memory:e.data,peak_malloced_memory:e.peak,does_zap_garbage:0,number_of_native_contexts:0,number_of_detached_contexts:0,total_global_handles_size:0,used_global_handles_size:0,external_memory:0}}export{detectHeapPlatform,getHeapStatistics,readProcessMemory};
@@ -0,0 +1,2 @@
1
+ import"../_virtual/_rolldown/runtime.js";import e from"@girs/glib-2.0";function readProcessMemory(){let t;try{let[n,r]=e.file_get_contents(`/proc/self/status`);if(!n||!r)return null;t=new TextDecoder().decode(r)}catch{return null}let n=new Map;for(let e of t.split(`
2
+ `)){let t=/^(\w+):\s+(\d+)(\s+kB)?/.exec(e);t&&n.set(t[1],parseInt(t[2])*(t[3]?1024:1))}return n.size===0?null:{virtual:n.get(`VmSize`)??0,resident:n.get(`VmRSS`)??0,data:n.get(`VmData`)??0,peak:n.get(`VmPeak`)??0}}export{readProcessMemory};
File without changes
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";function readProcessMemory(){return null}export{readProcessMemory};
package/lib/esm/index.js CHANGED
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{getHeapStatistics as e}from"./heap.js";import{DefaultDeserializer as t,DefaultSerializer as n,Deserializer as r,Serializer as i}from"./serdes.js";function serialize(e){let t=new n;return t.writeHeader(),t.writeValue(e),t.releaseBuffer()}function deserialize(e){let n=new t(e);return n.readHeader(),n.readValue()}function getHeapSpaceStatistics(){return[]}function getHeapCodeStatistics(){return{code_and_metadata_size:0,bytecode_and_metadata_size:0,external_script_source_size:0,cpu_profiler_metadata_size:0}}function setFlagsFromString(e){}function getHeapSnapshot(e){return null}function writeHeapSnapshot(e){return``}function isStringOneByteRepresentation(e){for(let t=0;t<e.length;t++)if(e.charCodeAt(t)>255)return!1;return!0}var GCProfiler=class{#e=!1;#t=0;start(){this.#e||(this.#e=!0,this.#t=Date.now())}stop(){if(this.#e){this.#e=!1;try{let e=globalThis.imports?.system;typeof e?.gc==`function`&&e.gc()}catch{}return{version:1,startTime:this.#t,endTime:Date.now(),stats:[]}}}[Symbol.dispose](){this.stop()}},SyncCPUProfileHandle=class{stop(){}[Symbol.dispose](){this.stop()}};function startCpuProfile(){return new SyncCPUProfileHandle}var a={getHeapStatistics:e,getHeapSpaceStatistics,getHeapCodeStatistics,setFlagsFromString,getHeapSnapshot,writeHeapSnapshot,serialize,deserialize,isStringOneByteRepresentation,Serializer:i,Deserializer:r,DefaultSerializer:n,DefaultDeserializer:t,GCProfiler,startCpuProfile};export{t as DefaultDeserializer,n as DefaultSerializer,r as Deserializer,GCProfiler,i as Serializer,SyncCPUProfileHandle,a as default,deserialize,getHeapCodeStatistics,getHeapSnapshot,getHeapSpaceStatistics,e as getHeapStatistics,isStringOneByteRepresentation,serialize,setFlagsFromString,startCpuProfile,writeHeapSnapshot};
1
+ import"./_virtual/_rolldown/runtime.js";import{getHeapStatistics as e}from"./heap/index.js";import{DefaultDeserializer as t,DefaultSerializer as n,Deserializer as r,Serializer as i}from"./serdes.js";function serialize(e){let t=new n;return t.writeHeader(),t.writeValue(e),t.releaseBuffer()}function deserialize(e){let n=new t(e);return n.readHeader(),n.readValue()}function getHeapSpaceStatistics(){return[]}function getHeapCodeStatistics(){return{code_and_metadata_size:0,bytecode_and_metadata_size:0,external_script_source_size:0,cpu_profiler_metadata_size:0}}function setFlagsFromString(e){}function getHeapSnapshot(e){return null}function writeHeapSnapshot(e){return``}function isStringOneByteRepresentation(e){for(let t=0;t<e.length;t++)if(e.charCodeAt(t)>255)return!1;return!0}var GCProfiler=class{#e=!1;#t=0;start(){this.#e||(this.#e=!0,this.#t=Date.now())}stop(){if(this.#e){this.#e=!1;try{let e=globalThis.imports?.system;typeof e?.gc==`function`&&e.gc()}catch{}return{version:1,startTime:this.#t,endTime:Date.now(),stats:[]}}}[Symbol.dispose](){this.stop()}},SyncCPUProfileHandle=class{stop(){}[Symbol.dispose](){this.stop()}};function startCpuProfile(){return new SyncCPUProfileHandle}var a={getHeapStatistics:e,getHeapSpaceStatistics,getHeapCodeStatistics,setFlagsFromString,getHeapSnapshot,writeHeapSnapshot,serialize,deserialize,isStringOneByteRepresentation,Serializer:i,Deserializer:r,DefaultSerializer:n,DefaultDeserializer:t,GCProfiler,startCpuProfile};export{t as DefaultDeserializer,n as DefaultSerializer,r as Deserializer,GCProfiler,i as Serializer,SyncCPUProfileHandle,a as default,deserialize,getHeapCodeStatistics,getHeapSnapshot,getHeapSpaceStatistics,e as getHeapStatistics,isStringOneByteRepresentation,serialize,setFlagsFromString,startCpuProfile,writeHeapSnapshot};
@@ -0,0 +1,14 @@
1
+ import type { ProcessMemory } from './types.js';
2
+ /**
3
+ * Read this process's `rss` / `vsz` via `ps`.
4
+ *
5
+ * The pid is obtained without any extra binding by asking a short-lived shell
6
+ * for its OWN parent — `g_spawn_command_line_sync()` forks us, so the shell's
7
+ * `$PPID` is this process. Both `ps` columns are in KiB on macOS; `-o rss=`
8
+ * suppresses the header so the output is a bare `<rss> <vsz>` line.
9
+ *
10
+ * Spawning for a memory reading is heavier than Linux's single file read, but
11
+ * `getHeapStatistics()` is a diagnostics call, not a hot path. Returns `null`
12
+ * on any failure so the caller falls back to the all-zero contract.
13
+ */
14
+ export declare function readProcessMemory(): ProcessMemory | null;
@@ -0,0 +1,43 @@
1
+ import type { ProcessMemory } from './types.js';
2
+ export type { ProcessMemory };
3
+ export type HeapPlatform = 'linux' | 'darwin' | 'win32' | 'unknown';
4
+ /**
5
+ * Detect which reader applies, once per process:
6
+ *
7
+ * - Windows — GLib returns native paths, so `g_get_current_dir()` is
8
+ * drive-rooted (`C:\…`) instead of `/`-rooted.
9
+ * - Linux — `/proc/self/status` exists (procfs is Linux-only).
10
+ * - macOS — no procfs, but the system version plist is always present.
11
+ * - anything else — no reader; the all-zero contract applies.
12
+ */
13
+ export declare function detectHeapPlatform(): HeapPlatform;
14
+ /** Read the current process's memory figures, or `null` if unavailable here. */
15
+ export declare function readProcessMemory(): ProcessMemory | null;
16
+ export interface HeapStatistics {
17
+ total_heap_size: number;
18
+ total_heap_size_executable: number;
19
+ total_physical_size: number;
20
+ total_available_size: number;
21
+ used_heap_size: number;
22
+ heap_size_limit: number;
23
+ malloced_memory: number;
24
+ peak_malloced_memory: number;
25
+ does_zap_garbage: number;
26
+ number_of_native_contexts: number;
27
+ number_of_detached_contexts: number;
28
+ total_global_handles_size: number;
29
+ used_global_handles_size: number;
30
+ external_memory: number;
31
+ }
32
+ /**
33
+ * Node-shaped `v8.getHeapStatistics()`.
34
+ *
35
+ * Every field is always present and numeric — Node never throws here and
36
+ * neither do we. Fields V8 computes from engine internals we have no access to
37
+ * (`heap_size_limit`, `does_zap_garbage`, the context/handle counters, …) are
38
+ * reported as `0`; the memory-derived fields come from the platform reader
39
+ * above and are `0` too when no reader is available (Windows, or a Linux host
40
+ * with `/proc` masked). Callers that destructure the result keep working
41
+ * everywhere; callers that need a meaningful number should check for `0`.
42
+ */
43
+ export declare function getHeapStatistics(): HeapStatistics;
@@ -0,0 +1,10 @@
1
+ import type { ProcessMemory } from './types.js';
2
+ /**
3
+ * Read `VmSize` / `VmRSS` / `VmData` / `VmPeak` out of `/proc/self/status`.
4
+ *
5
+ * procfs is Linux-only but is present on every mainstream distribution and
6
+ * inside Flatpak/OCI containers, so this is the fast path (a single
7
+ * `g_file_get_contents()`, no subprocess). Returns `null` when procfs is not
8
+ * readable so the caller can fall back to the documented all-zero contract.
9
+ */
10
+ export declare function readProcessMemory(): ProcessMemory | null;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Process memory figures, in BYTES. A reader sets `0` for any figure the
3
+ * platform does not expose (rather than guessing), and returns `null` from
4
+ * `readProcessMemory()` when it can read nothing at all.
5
+ */
6
+ export interface ProcessMemory {
7
+ /** Virtual memory size (Linux `VmSize`, BSD `vsz`). */
8
+ virtual: number;
9
+ /** Resident set size (Linux `VmRSS`, BSD `rss`). */
10
+ resident: number;
11
+ /** Data-segment size (Linux `VmData`). */
12
+ data: number;
13
+ /** Peak virtual memory size (Linux `VmPeak`). */
14
+ peak: number;
15
+ }
@@ -0,0 +1,2 @@
1
+ import type { ProcessMemory } from './types.js';
2
+ export declare function readProcessMemory(): ProcessMemory | null;
@@ -1,4 +1,4 @@
1
- import { getHeapStatistics } from './heap.js';
1
+ import { getHeapStatistics } from './heap/index.js';
2
2
  import { Serializer, Deserializer, DefaultSerializer, DefaultDeserializer } from './serdes.js';
3
3
  export { getHeapStatistics };
4
4
  export { Serializer, Deserializer, DefaultSerializer, DefaultDeserializer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/v8",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Node.js v8 module for Gjs",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -33,11 +33,14 @@
33
33
  "v8"
34
34
  ],
35
35
  "devDependencies": {
36
- "@gjsify/cli": "^0.22.0",
37
- "@gjsify/unit": "^0.22.0",
36
+ "@gjsify/cli": "^0.23.0",
37
+ "@gjsify/unit": "^0.23.0",
38
38
  "@types/node": "^25.9.2",
39
39
  "typescript": "^6.0.3"
40
40
  },
41
+ "dependencies": {
42
+ "@girs/glib-2.0": "^4.1.0"
43
+ },
41
44
  "gjsify": {
42
45
  "runtimes": {
43
46
  "gjs": "polyfill",
package/lib/esm/heap.js DELETED
@@ -1,2 +0,0 @@
1
- import"./_virtual/_rolldown/runtime.js";import e from"@girs/glib-2.0";function readProcStatus(){let t=new Map;try{let[n,r]=e.file_get_contents(`/proc/self/status`);if(!n||!r)return t;let i=new TextDecoder().decode(r);for(let e of i.split(`
2
- `)){let n=/^(\w+):\s+(\d+)(\s+kB)?/.exec(e);n&&t.set(n[1],parseInt(n[2])*(n[3]?1024:1))}}catch{}return t}function getHeapStatistics(){let e=readProcStatus();return{total_heap_size:e.get(`VmSize`)??0,total_heap_size_executable:0,total_physical_size:e.get(`VmRSS`)??0,total_available_size:0,used_heap_size:e.get(`VmRSS`)??0,heap_size_limit:0,malloced_memory:e.get(`VmData`)??0,peak_malloced_memory:e.get(`VmPeak`)??0,does_zap_garbage:0,number_of_native_contexts:0,number_of_detached_contexts:0,total_global_handles_size:0,used_global_handles_size:0,external_memory:0}}export{getHeapStatistics};
@@ -1,16 +0,0 @@
1
- export declare function getHeapStatistics(): {
2
- total_heap_size: number;
3
- total_heap_size_executable: number;
4
- total_physical_size: number;
5
- total_available_size: number;
6
- used_heap_size: number;
7
- heap_size_limit: number;
8
- malloced_memory: number;
9
- peak_malloced_memory: number;
10
- does_zap_garbage: number;
11
- number_of_native_contexts: number;
12
- number_of_detached_contexts: number;
13
- total_global_handles_size: number;
14
- used_global_handles_size: number;
15
- external_memory: number;
16
- };