@oh-my-pi/pi-natives 15.12.4 → 15.13.1

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/native/index.d.ts CHANGED
@@ -118,6 +118,25 @@ export declare class Shell {
118
118
  abort(): Promise<void>
119
119
  }
120
120
 
121
+ /**
122
+ * Install the bounded Tokio runtime napi-rs adopts for async exports.
123
+ *
124
+ * The JS loader calls this exactly once, synchronously, right *after* `dlopen`
125
+ * returns and *before* any async native runs — never from `#[module_init]`.
126
+ * Building a multi-thread runtime eagerly spawns worker threads, and doing
127
+ * that during module init (while the dynamic-loader lock is held) deadlocks on
128
+ * some hosts: a fresh worker blocks acquiring the loader lock that the init
129
+ * thread still owns. napi-rs only materializes its runtime on the first async
130
+ * call (`RT` is a `LazyLock`) and `create_custom_tokio_runtime` merely records
131
+ * the runtime in a `OnceLock`, so installing it post-load is still honored.
132
+ * Without it napi builds its own default (one worker per CPU, spawned eagerly)
133
+ * which aborts the process (`os error 1455`) on a memory-constrained Windows
134
+ * host before any JS error can surface; [`create_windows_napi_tokio_runtime`]
135
+ * pre-flights the spawn instead. If no runtime can be built we leave napi-rs
136
+ * to its default. Idempotent.
137
+ */
138
+ export declare function __ompInstallTokioRuntime(): void
139
+
121
140
  /**
122
141
  * Version sentinel — exists solely so the JS loader can prove at load time
123
142
  * that the `.node` file on disk is from the same package release as the
@@ -136,7 +155,7 @@ export declare class Shell {
136
155
  * `packages/natives/native/index.js` (which derives the name from
137
156
  * `package.json#version`).
138
157
  */
139
- export declare function __piNativesV15_12_4(): void
158
+ export declare function __piNativesV15_13_1(): void
140
159
 
141
160
  /**
142
161
  * Apply conservative pre-execution rewrites to a bash command.
package/native/index.js CHANGED
@@ -23,7 +23,8 @@ export const PtySession = nativeBindings.PtySession;
23
23
  export const Shell = nativeBindings.Shell;
24
24
 
25
25
  // functions
26
- export const __piNativesV15_12_4 = nativeBindings.__piNativesV15_12_4;
26
+ export const __ompInstallTokioRuntime = nativeBindings.__ompInstallTokioRuntime;
27
+ export const __piNativesV15_13_1 = nativeBindings.__piNativesV15_13_1;
27
28
  export const applyBashFixups = nativeBindings.applyBashFixups;
28
29
  export const astEdit = nativeBindings.astEdit;
29
30
  export const astGrep = nativeBindings.astGrep;
@@ -55,6 +55,13 @@ export interface ResolveLoaderCandidatesInput {
55
55
 
56
56
  export function resolveLoaderCandidates(input: ResolveLoaderCandidatesInput): string[];
57
57
 
58
+ export interface CleanupStaleNativeVersionsInput {
59
+ nativesDir: string;
60
+ currentVersion: string;
61
+ }
62
+
63
+ export function cleanupStaleNativeVersions(input: CleanupStaleNativeVersionsInput): string[];
64
+
58
65
  export interface ExtractEmbeddedAddonArchiveInput {
59
66
  archivePath: string;
60
67
  files: EmbeddedAddonFile[];
@@ -177,6 +177,37 @@ export function resolveLoaderCandidates({
177
177
  }
178
178
 
179
179
  // =========================================================================
180
+
181
+ /**
182
+ * Remove version-pinned native cache directories older than the loaded package.
183
+ * Best-effort by design: permission errors and concurrent processes must not
184
+ * abort startup after the native addon has already loaded successfully.
185
+ *
186
+ * @param {{ nativesDir: string; currentVersion: string }} input
187
+ * @returns {string[]}
188
+ */
189
+ export function cleanupStaleNativeVersions({ nativesDir, currentVersion }) {
190
+ const removed = [];
191
+ let entries;
192
+ try {
193
+ entries = fs.readdirSync(nativesDir, { withFileTypes: true });
194
+ } catch {
195
+ return removed;
196
+ }
197
+
198
+ for (const entry of entries) {
199
+ if (!entry.isDirectory() || entry.name === currentVersion) continue;
200
+ const targetPath = path.join(nativesDir, entry.name);
201
+ try {
202
+ fs.rmSync(targetPath, { recursive: true, force: true });
203
+ removed.push(targetPath);
204
+ } catch {
205
+ // Stale caches are opportunistic cleanup only.
206
+ }
207
+ }
208
+ return removed;
209
+ }
210
+
180
211
  // Side-effectful loader. Everything below runs only when `loadNative()` is
181
212
  // called from `native/index.js` — tests that only import the pure helpers
182
213
  // above pay nothing for variant detection, subprocess spawns, or fs probes.
@@ -485,6 +516,26 @@ function validateLoadedBindings(ctx, bindings, candidate) {
485
516
  );
486
517
  }
487
518
 
519
+ /**
520
+ * Install the addon's bounded Tokio runtime now that `dlopen` has returned and
521
+ * the dynamic-loader lock is released. The Rust `#[module_init]` deliberately
522
+ * does NOT build the runtime — spawning worker threads under the loader lock
523
+ * deadlocks on some hosts — so it exposes `__ompInstallTokioRuntime` for the
524
+ * loader to call once, before any async native runs. Best-effort: older addons
525
+ * predating this export simply fall back to napi-rs's default runtime.
526
+ */
527
+ function installNativeTokioRuntime(bindings) {
528
+ const install = bindings.__ompInstallTokioRuntime;
529
+ if (typeof install !== "function") return;
530
+ try {
531
+ install();
532
+ startupMarker("native:tokioRuntime:installed");
533
+ } catch (err) {
534
+ startupMarker(`native:tokioRuntime:failed:${err instanceof Error ? err.message : String(err)}`);
535
+ }
536
+ }
537
+
538
+
488
539
  function buildHelpMessage(ctx) {
489
540
  if (ctx.isCompiledBinary) {
490
541
  const expectedPaths = ctx.addonFilenames.map(filename => ` ${path.join(ctx.versionedDir, filename)}`).join("\n");
@@ -518,7 +569,8 @@ function initLoaderContext() {
518
569
  const packageVersion = packageJson.version;
519
570
  const nativeDir = path.join(import.meta.dir, "..", "native");
520
571
  const execDir = path.dirname(process.execPath);
521
- const versionedDir = path.join(getNativesDir(), packageVersion);
572
+ const nativesDir = getNativesDir();
573
+ const versionedDir = path.join(nativesDir, packageVersion);
522
574
  const userDataDir =
523
575
  process.platform === "win32"
524
576
  ? path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"), "omp")
@@ -576,6 +628,7 @@ function initLoaderContext() {
576
628
  candidates,
577
629
  versionSentinelExport,
578
630
  isWorkspaceLoad,
631
+ nativesDir,
579
632
  };
580
633
  }
581
634
 
@@ -595,6 +648,8 @@ export function loadNative() {
595
648
  startupMarker(`native:require:${path.basename(candidate)}`);
596
649
  const bindings = require_(candidate);
597
650
  validateLoadedBindings(ctx, bindings, candidate);
651
+ installNativeTokioRuntime(bindings);
652
+ cleanupStaleNativeVersions({ nativesDir: ctx.nativesDir, currentVersion: ctx.packageVersion });
598
653
  startupMarker("native:loadNative:done");
599
654
  return bindings;
600
655
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-natives",
3
- "version": "15.12.4",
3
+ "version": "15.13.1",
4
4
  "description": "Native Rust bindings for grep, clipboard, image processing, syntax highlighting, PTY, and shell operations via N-API",
5
5
  "type": "module",
6
6
  "homepage": "https://omp.sh",
@@ -66,10 +66,10 @@
66
66
  }
67
67
  },
68
68
  "optionalDependencies": {
69
- "@oh-my-pi/pi-natives-linux-x64": "15.12.4",
70
- "@oh-my-pi/pi-natives-linux-arm64": "15.12.4",
71
- "@oh-my-pi/pi-natives-darwin-x64": "15.12.4",
72
- "@oh-my-pi/pi-natives-darwin-arm64": "15.12.4",
73
- "@oh-my-pi/pi-natives-win32-x64": "15.12.4"
69
+ "@oh-my-pi/pi-natives-linux-x64": "15.13.1",
70
+ "@oh-my-pi/pi-natives-linux-arm64": "15.13.1",
71
+ "@oh-my-pi/pi-natives-darwin-x64": "15.13.1",
72
+ "@oh-my-pi/pi-natives-darwin-arm64": "15.13.1",
73
+ "@oh-my-pi/pi-natives-win32-x64": "15.13.1"
74
74
  }
75
75
  }