From 6dde7706bfe8647c626921be0697e98a64ffccbb Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 12 Sep 2026 18:07:40 -0700 Subject: [PATCH] refactor(windows): vendor the registry addon as @orca/windows-registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit windows-native-registry@3.2.2 was last published in 2023 by a single maintainer. Orca called two of its exports, both read-only, so the whole dependency is replaced by a local N-API addon under native/. The vendored addon is read-only by construction: setValue, createKey and deleteKey are gone, so RegDeleteTreeW no longer ships in the app. Two upstream defects are also fixed rather than carried over — the name/data scratch buffers were file-scope statics that concurrent reads would scribble over, and createKey/deleteKey called .c_str() on a temporary. Build wiring keeps the existing shape: still an optionalDependency gated to win32, still excluded from pnpm's allowBuilds so only Orca's own Windows rebuild runs node-gyp for it, still copied into the packaged resources. The CI native caches now key on the vendored sources so an addon.cc edit cannot restore a stale .node. --- .../install-node-dependencies/action.yml | 8 +- .github/workflows/pr.yml | 8 +- config/packaged-runtime-node-modules.cjs | 2 +- config/scripts/ensure-native-runtime.mjs | 4 +- config/scripts/ensure-native-runtime.test.mjs | 6 +- ...package-electron-runtime-contract.test.mjs | 22 ++-- .../scripts/pr-workflow-parallelism.test.mjs | 2 +- .../rebuild-native-deps-node-pty.test.mjs | 4 +- .../rebuild-native-deps-test-fixtures.mjs | 2 +- config/scripts/rebuild-native-deps.mjs | 6 +- docs/reference/windows-process-enumeration.md | 2 +- native/windows-registry/binding.gyp | 17 +++ native/windows-registry/index.d.ts | 22 ++++ native/windows-registry/index.js | 35 ++++++ native/windows-registry/package.json | 21 ++++ native/windows-registry/src/addon.cc | 115 ++++++++++++++++++ package.json | 4 +- pnpm-lock.yaml | 54 +++----- pnpm-workspace.yaml | 2 +- src/main/windows-native-registry.ts | 2 +- 20 files changed, 264 insertions(+), 74 deletions(-) create mode 100644 native/windows-registry/binding.gyp create mode 100644 native/windows-registry/index.d.ts create mode 100644 native/windows-registry/index.js create mode 100644 native/windows-registry/package.json create mode 100644 native/windows-registry/src/addon.cc diff --git a/.github/actions/install-node-dependencies/action.yml b/.github/actions/install-node-dependencies/action.yml index 95ef7e96cc9..a31ed45eefe 100644 --- a/.github/actions/install-node-dependencies/action.yml +++ b/.github/actions/install-node-dependencies/action.yml @@ -150,9 +150,9 @@ runs: with: path: | node_modules/.pnpm/node-pty@*/node_modules/node-pty/build - node_modules/.pnpm/windows-native-registry@*/node_modules/windows-native-registry/build + node_modules/.pnpm/@orca+windows-registry@*/node_modules/@orca/windows-registry/build node_modules/.pnpm/@vscode+windows-process-tree@*/node_modules/@vscode/windows-process-tree/build - key: native-modules-${{ runner.os }}-${{ steps.native-cache-scope.outputs.scope }}-${{ runner.arch }}-${{ inputs.native-runtime }}-node${{ steps.requested-node.outputs.node-version || steps.default-node.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch') }} + key: native-modules-${{ runner.os }}-${{ steps.native-cache-scope.outputs.scope }}-${{ runner.arch }}-${{ inputs.native-runtime }}-node${{ steps.requested-node.outputs.node-version || steps.default-node.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch', 'native/windows-registry/src/addon.cc', 'native/windows-registry/binding.gyp', 'native/windows-registry/package.json') }} - name: Restore compiled native modules without saving id: native-cache-restore-only @@ -161,9 +161,9 @@ runs: with: path: | node_modules/.pnpm/node-pty@*/node_modules/node-pty/build - node_modules/.pnpm/windows-native-registry@*/node_modules/windows-native-registry/build + node_modules/.pnpm/@orca+windows-registry@*/node_modules/@orca/windows-registry/build node_modules/.pnpm/@vscode+windows-process-tree@*/node_modules/@vscode/windows-process-tree/build - key: native-modules-${{ runner.os }}-${{ steps.native-cache-scope.outputs.scope }}-${{ runner.arch }}-${{ inputs.native-runtime }}-node${{ steps.requested-node.outputs.node-version || steps.default-node.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch') }} + key: native-modules-${{ runner.os }}-${{ steps.native-cache-scope.outputs.scope }}-${{ runner.arch }}-${{ inputs.native-runtime }}-node${{ steps.requested-node.outputs.node-version || steps.default-node.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch', 'native/windows-registry/src/addon.cc', 'native/windows-registry/binding.gyp', 'native/windows-registry/package.json') }} # pnpm's bundled gyp_main.py is not executable on fresh Linux runners. - name: Use external node-gyp diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 45a3d12c2d8..fe58295a67b 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -829,9 +829,9 @@ jobs: with: path: | node_modules/.pnpm/node-pty@*/node_modules/node-pty/build - node_modules/.pnpm/windows-native-registry@*/node_modules/windows-native-registry/build + node_modules/.pnpm/@orca+windows-registry@*/node_modules/@orca/windows-registry/build node_modules/.pnpm/@vscode+windows-process-tree@*/node_modules/@vscode/windows-process-tree/build - key: native-modules-${{ runner.os }}-${{ steps.deps.outputs.native-cache-scope }}-${{ runner.arch }}-node-node${{ steps.deps.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch') }} + key: native-modules-${{ runner.os }}-${{ steps.deps.outputs.native-cache-scope }}-${{ runner.arch }}-node-node${{ steps.deps.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch', 'native/windows-registry/src/addon.cc', 'native/windows-registry/binding.gyp', 'native/windows-registry/package.json') }} # vitest runs here directly rather than through `pnpm test`, so the addon # assertions only hold once install-node-dependencies has rebuilt natives. @@ -897,9 +897,9 @@ jobs: with: path: | node_modules/.pnpm/node-pty@*/node_modules/node-pty/build - node_modules/.pnpm/windows-native-registry@*/node_modules/windows-native-registry/build + node_modules/.pnpm/@orca+windows-registry@*/node_modules/@orca/windows-registry/build node_modules/.pnpm/@vscode+windows-process-tree@*/node_modules/@vscode/windows-process-tree/build - key: native-modules-${{ runner.os }}-${{ steps.deps.outputs.native-cache-scope }}-${{ runner.arch }}-electron-node${{ steps.deps.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch') }} + key: native-modules-${{ runner.os }}-${{ steps.deps.outputs.native-cache-scope }}-${{ runner.arch }}-electron-node${{ steps.deps.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch', 'native/windows-registry/src/addon.cc', 'native/windows-registry/binding.gyp', 'native/windows-registry/package.json') }} - name: Prepare Electron native runtime run: node config/scripts/ensure-native-runtime.mjs --runtime=electron diff --git a/config/packaged-runtime-node-modules.cjs b/config/packaged-runtime-node-modules.cjs index 1ee443f8288..a491ae23ac8 100644 --- a/config/packaged-runtime-node-modules.cjs +++ b/config/packaged-runtime-node-modules.cjs @@ -35,7 +35,7 @@ const PACKAGED_RUNTIME_PACKAGE_ROOTS = [ ] const WINDOWS_PACKAGED_RUNTIME_PACKAGE_ROOTS = [ '@vscode/windows-process-tree', - 'windows-native-registry' + '@orca/windows-registry' ] const NODE_PTY_PREBUILD_PREFIX_BY_PLATFORM = { diff --git a/config/scripts/ensure-native-runtime.mjs b/config/scripts/ensure-native-runtime.mjs index 10e8426c2a5..6278a1e0b41 100644 --- a/config/scripts/ensure-native-runtime.mjs +++ b/config/scripts/ensure-native-runtime.mjs @@ -22,7 +22,7 @@ const runtime = readRuntimeArg() const NATIVE_MODULES = [ 'node-pty', ...(process.platform === 'win32' - ? ['windows-native-registry', '@vscode/windows-process-tree'] + ? ['@orca/windows-registry', '@vscode/windows-process-tree'] : []) ] const NODE_PTY_CONPTY_RUNTIME_FILES = ['conpty.dll', 'OpenConsole.exe'] @@ -275,7 +275,7 @@ function loadNativeModule(moduleName) { } return } - if (moduleName === 'windows-native-registry') { + if (moduleName === '@orca/windows-registry') { const registry = require(moduleName) // Why: the package defers loading its .node addon until the first registry call. registry.getRegistryKey(registry.HK.CU, 'Environment') diff --git a/config/scripts/ensure-native-runtime.test.mjs b/config/scripts/ensure-native-runtime.test.mjs index 1e7d888d2e2..47fe435edcc 100644 --- a/config/scripts/ensure-native-runtime.test.mjs +++ b/config/scripts/ensure-native-runtime.test.mjs @@ -87,7 +87,7 @@ describe('ensure-native-runtime', () => { const log = readFileSync(logPath, 'utf8') expect(log.match(/pnpm exec node-gyp rebuild\n/g)).toHaveLength(2) expect(log).toContain(join('node_modules', 'node-pty')) - expect(log).toContain(join('node_modules', 'windows-native-registry')) + expect(log).toContain(join('node_modules', '@orca', 'windows-registry')) } finally { rmSync(projectDir, { recursive: true, force: true }) } @@ -299,11 +299,11 @@ function writeFakeWindowsRegistry(projectDir, { requiresMarker = false } = {}) { if (process.platform !== 'win32') { return } - const registryDir = join(projectDir, 'node_modules', 'windows-native-registry') + const registryDir = join(projectDir, 'node_modules', '@orca', 'windows-registry') mkdirSync(registryDir, { recursive: true }) writeFileSync( join(registryDir, 'package.json'), - '{"name":"windows-native-registry","version":"3.2.2","main":"index.js"}\n' + '{"name":"@orca/windows-registry","version":"1.0.0","main":"index.js"}\n' ) const markerGate = requiresMarker ? `if (!require('node:fs').existsSync(process.env.ORCA_NATIVE_TEST_MARKER)) { throw new Error('registry ABI mismatch sentinel') }` diff --git a/config/scripts/package-electron-runtime-contract.test.mjs b/config/scripts/package-electron-runtime-contract.test.mjs index aa34e043268..27e5b02482e 100644 --- a/config/scripts/package-electron-runtime-contract.test.mjs +++ b/config/scripts/package-electron-runtime-contract.test.mjs @@ -19,24 +19,20 @@ describe('Electron runtime package contract', () => { }) it('keeps the native Windows registry addon optional and platform-gated', () => { - const rebuildScript = readFileSync( - join(projectDir, 'config/scripts/rebuild-native-deps.mjs'), - 'utf8' + const rebuildScript = readProject('config/scripts/rebuild-native-deps.mjs') + const ensureScript = readProject('config/scripts/ensure-native-runtime.mjs') + expect(packageJson.optionalDependencies['@orca/windows-registry']).toBe( + 'file:./native/windows-registry' ) - const ensureScript = readFileSync( - join(projectDir, 'config/scripts/ensure-native-runtime.mjs'), - 'utf8' - ) - expect(packageJson.optionalDependencies['windows-native-registry']).toBe('3.2.2') // Why: pnpm installs optional target architectures on every host; the root // Windows-only rebuild owns this addon so macOS/Linux never run node-gyp for it. - expect(pnpmWorkspace.allowBuilds['windows-native-registry']).toBe(false) + expect(pnpmWorkspace.allowBuilds['@orca/windows-registry']).toBe(false) // Why assert the guard and the member separately: the list now carries more // than one addon, so pinning the whole literal only tested its formatting. expect(rebuildScript).toContain("rebuildPlatform === 'win32'") - expect(rebuildScript).toContain("'windows-native-registry'") + expect(rebuildScript).toContain("'@orca/windows-registry'") expect(ensureScript).toContain("process.platform === 'win32'") - expect(ensureScript).toContain("'windows-native-registry'") + expect(ensureScript).toContain("'@orca/windows-registry'") const packageTargets = { win32: createPackagedRuntimeNodeModuleResources('win32'), darwin: createPackagedRuntimeNodeModuleResources('darwin'), @@ -44,14 +40,14 @@ describe('Electron runtime package contract', () => { } expect(packageTargets.win32).toEqual( expect.arrayContaining([ - expect.objectContaining({ to: join('node_modules', 'windows-native-registry') }), + expect.objectContaining({ to: join('node_modules', '@orca', 'windows-registry') }), expect.objectContaining({ to: join('node_modules', 'node-addon-api') }) ]) ) for (const platform of ['darwin', 'linux']) { expect(packageTargets[platform]).not.toEqual( expect.arrayContaining([ - expect.objectContaining({ to: join('node_modules', 'windows-native-registry') }) + expect.objectContaining({ to: join('node_modules', '@orca', 'windows-registry') }) ]) ) } diff --git a/config/scripts/pr-workflow-parallelism.test.mjs b/config/scripts/pr-workflow-parallelism.test.mjs index 92d4fe4c26b..2074d8c631d 100644 --- a/config/scripts/pr-workflow-parallelism.test.mjs +++ b/config/scripts/pr-workflow-parallelism.test.mjs @@ -389,7 +389,7 @@ describe('PR workflow parallelism', () => { expect(cacheStep.with.key).toContain('config/scripts/ensure-native-runtime.mjs') expect(cacheStep.with.key).toContain('config/scripts/rebuild-native-deps.mjs') expect(cacheStep.with.path).toContain('node-pty@*/node_modules/node-pty/build') - expect(cacheStep.with.path).toContain('windows-native-registry@') + expect(cacheStep.with.path).toContain('@orca+windows-registry@') expect(cacheStep.with.path).toContain('@vscode+windows-process-tree@') expect(cacheStep.with['restore-keys']).toBeUndefined() } diff --git a/config/scripts/rebuild-native-deps-node-pty.test.mjs b/config/scripts/rebuild-native-deps-node-pty.test.mjs index c3a8f9bbd83..27fb7e3651e 100644 --- a/config/scripts/rebuild-native-deps-node-pty.test.mjs +++ b/config/scripts/rebuild-native-deps-node-pty.test.mjs @@ -239,9 +239,9 @@ describe('rebuild-native-deps patched node-pty rebuild', () => { }) expect(result.status, result.stderr).toBe(0) - expect(result.stdout).toContain('Rebuilding failed native modules: windows-native-registry') + expect(result.stdout).toContain('Rebuilding failed native modules: @orca/windows-registry') const rebuildCall = JSON.parse(readFileSync(rebuildLogPath, 'utf8').trim()) - expect(rebuildCall.onlyModules).toEqual(['windows-native-registry']) + expect(rebuildCall.onlyModules).toEqual(['@orca/windows-registry']) } finally { removeTreeSync(projectDir) } diff --git a/config/scripts/rebuild-native-deps-test-fixtures.mjs b/config/scripts/rebuild-native-deps-test-fixtures.mjs index db5af45a454..ad9bb4c1ce2 100644 --- a/config/scripts/rebuild-native-deps-test-fixtures.mjs +++ b/config/scripts/rebuild-native-deps-test-fixtures.mjs @@ -358,7 +358,7 @@ exports.loadNativeModule = function loadNativeModule(nativeName) { } export function writeFakeWindowsRegistry(projectDir) { - const registryDir = join(projectDir, 'node_modules', 'windows-native-registry') + const registryDir = join(projectDir, 'node_modules', '@orca', 'windows-registry') mkdirSync(registryDir, { recursive: true }) writeFileSync( join(registryDir, 'index.js'), diff --git a/config/scripts/rebuild-native-deps.mjs b/config/scripts/rebuild-native-deps.mjs index d7426d8cf1d..deec8186f89 100644 --- a/config/scripts/rebuild-native-deps.mjs +++ b/config/scripts/rebuild-native-deps.mjs @@ -76,9 +76,7 @@ if (ignoreModules.length > 0) { const NATIVE_MODULES = [ 'node-pty', 'cpu-features', - ...(rebuildPlatform === 'win32' - ? ['windows-native-registry', '@vscode/windows-process-tree'] - : []) + ...(rebuildPlatform === 'win32' ? ['@orca/windows-registry', '@vscode/windows-process-tree'] : []) ] const onlyModules = NATIVE_MODULES.filter((m) => !ignoreModules.includes(m)) const forceRebuild = @@ -542,7 +540,7 @@ if (failures.length > 0) { } function loadNativeModule(moduleName) { - if (moduleName === 'windows-native-registry') { + if (moduleName === '@orca/windows-registry') { const registry = projectRequire(moduleName) // Why: the package defers loading its .node addon until the first registry call. registry.getRegistryKey(registry.HK.CU, 'Environment') diff --git a/docs/reference/windows-process-enumeration.md b/docs/reference/windows-process-enumeration.md index 685103ba9ba..e56bec2cd7a 100644 --- a/docs/reference/windows-process-enumeration.md +++ b/docs/reference/windows-process-enumeration.md @@ -504,7 +504,7 @@ miss exactly the detached, reparented descendants the trackers exist to find ## Packaging The addon is Windows-only, so it follows the same contract as -`windows-native-registry` (asserted by +`@orca/windows-registry` (asserted by `config/scripts/package-electron-runtime-contract.test.mjs`): - an `optionalDependency`, so a macOS/Linux install tolerates its absence; diff --git a/native/windows-registry/binding.gyp b/native/windows-registry/binding.gyp new file mode 100644 index 00000000000..cc1fb2990ac --- /dev/null +++ b/native/windows-registry/binding.gyp @@ -0,0 +1,17 @@ +{ + "targets": [ + { + "target_name": "orca_windows_registry", + "sources": ["src/addon.cc"], + "libraries": ["advapi32.lib"], + "include_dirs": [" | null diff --git a/native/windows-registry/index.js b/native/windows-registry/index.js new file mode 100644 index 00000000000..ed8cc7a6ad1 --- /dev/null +++ b/native/windows-registry/index.js @@ -0,0 +1,35 @@ +'use strict' + +// Why lazy: non-Windows installs never build the addon, so requiring it at module load would +// break any import of this package on macOS/Linux — including test collection. +let addon = null +function getAddon() { + if (!addon) { + addon = require('./build/Release/orca_windows_registry.node') + } + return addon +} + +const HK = { + CR: 0x80000000, + CU: 0x80000001, + LM: 0x80000002, + U: 0x80000003, + PD: 0x80000004, + CC: 0x80000005, + DD: 0x80000006 +} + +function getRegistryKey(root, path) { + const values = getAddon().getKey(root, path) + if (!values) { + return null + } + const byName = {} + for (const value of values) { + byName[value.name] = value + } + return byName +} + +module.exports = { HK, getRegistryKey } diff --git a/native/windows-registry/package.json b/native/windows-registry/package.json new file mode 100644 index 00000000000..eef53104aaf --- /dev/null +++ b/native/windows-registry/package.json @@ -0,0 +1,21 @@ +{ + "name": "@orca/windows-registry", + "version": "1.0.0", + "private": true, + "description": "Read-only Windows registry value enumeration for Orca", + "license": "MIT", + "files": [ + "index.js", + "index.d.ts", + "binding.gyp", + "src" + ], + "os": [ + "win32" + ], + "main": "index.js", + "types": "index.d.ts", + "dependencies": { + "node-addon-api": "^8.5.0" + } +} diff --git a/native/windows-registry/src/addon.cc b/native/windows-registry/src/addon.cc new file mode 100644 index 00000000000..3b00071a501 --- /dev/null +++ b/native/windows-registry/src/addon.cc @@ -0,0 +1,115 @@ +// Read-only Windows registry value enumeration for Orca. +// +// Vendored from windows-native-registry@3.2.2 (MIT, Eugene Pankov), reduced to the one +// entry point Orca calls. The upstream write surface (setValue/createKey/deleteKey) is +// deliberately absent: Orca only ever reads, and shipping RegDeleteTreeW in the app is +// capability we have no use for. +// +// Two upstream defects are fixed here rather than carried over: +// - the name/data scratch buffers were file-scope statics, so two concurrent reads +// scribbled over each other; they are per-call locals now (heap for the 1 MB data +// buffer, which is too large for a thread's stack). +// - createKey/deleteKey called .c_str() on a temporary Utf16Value(); that use-after-free +// left with the write surface. + +#include +#include + +#include +#include + +namespace { + +// Registry value names cap at 32767 wide chars; data at 1 MB, matching upstream's ceiling. +constexpr DWORD kNameMax = 32767; +constexpr DWORD kDataMax = 1024 * 1024; + +Napi::Value GetKey(const Napi::CallbackInfo& info) { + auto env = info.Env(); + if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsString()) { + Napi::TypeError::New(env, "getKey(root: number, path: string)").ThrowAsJavaScriptException(); + return env.Null(); + } + + auto root = reinterpret_cast(static_cast(info[0].As().Int64Value())); + auto path = info[1].As().Utf16Value(); + + HKEY key = nullptr; + if (RegOpenKeyExW(root, reinterpret_cast(path.c_str()), 0, KEY_READ, &key) != + ERROR_SUCCESS) { + return env.Null(); + } + + // +2 on data so the two-wide-char terminator below always lands inside the buffer. + std::vector name(kNameMax); + auto data = std::make_unique(kDataMax + 2); + + auto values = Napi::Array::New(env); + DWORD index = 0; + + while (true) { + DWORD nameLength = kNameMax - 1; + DWORD dataLength = kDataMax - 1; + DWORD valueType = 0; + + LSTATUS error = RegEnumValueW(key, index, name.data(), &nameLength, nullptr, &valueType, + data.get(), &dataLength); + if (error != ERROR_SUCCESS) { + if (error == ERROR_NO_MORE_ITEMS) { + break; + } + RegCloseKey(key); + return env.Null(); + } + + auto entry = Napi::Object::New(env); + entry.Set("name", Napi::String::New(env, reinterpret_cast(name.data()))); + entry.Set("type", Napi::Number::New(env, static_cast(valueType))); + + if (valueType == REG_SZ || valueType == REG_EXPAND_SZ) { + // RegEnumValueW does not guarantee a terminator when the stored value lacks one. + data[dataLength] = 0; + data[dataLength + 1] = 0; + entry.Set("value", Napi::String::New(env, reinterpret_cast(data.get()))); + } else if (valueType == REG_DWORD) { + DWORD dword = 0; + if (dataLength >= sizeof(DWORD)) { + memcpy(&dword, data.get(), sizeof(DWORD)); + } + entry.Set("value", Napi::Number::New(env, static_cast(dword))); + } else if (valueType == REG_BINARY) { + auto bytes = Napi::Array::New(env, dataLength); + for (DWORD i = 0; i < dataLength; i++) { + bytes.Set(i, Napi::Number::New(env, static_cast(data[i]))); + } + entry.Set("value", bytes); + } else if (valueType == REG_MULTI_SZ) { + data[dataLength] = 0; + data[dataLength + 1] = 0; + auto parts = Napi::Array::New(env); + DWORD pos = 0; + uint32_t partIndex = 0; + // dataLength includes the trailing empty string's terminator; stop before it. + while (dataLength >= 2 && pos < dataLength - 2) { + auto part = Napi::String::New(env, reinterpret_cast(data.get() + pos)); + parts.Set(partIndex++, part); + pos += static_cast((part.Utf16Value().length() + 1) * 2); + } + entry.Set("value", parts); + } + + values.Set(index++, entry); + } + + RegCloseKey(key); + return values; +} + +} // namespace + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + exports.Set("getKey", Napi::Function::New(env, GetKey)); + return exports; +} + +NODE_API_MODULE(orca_windows_registry, Init) diff --git a/package.json b/package.json index 9cbae455f2d..60028845fd5 100644 --- a/package.json +++ b/package.json @@ -295,13 +295,13 @@ "zustand": "^5.0.14" }, "optionalDependencies": { + "@orca/windows-registry": "file:./native/windows-registry", "@vscode/windows-process-tree": "0.8.0", "sherpa-onnx-darwin-arm64": "1.12.37", "sherpa-onnx-darwin-x64": "1.12.37", "sherpa-onnx-linux-arm64": "1.12.37", "sherpa-onnx-linux-x64": "1.12.37", - "sherpa-onnx-win-x64": "1.12.37", - "windows-native-registry": "3.2.2" + "sherpa-onnx-win-x64": "1.12.37" }, "lint-staged": { "{*.{ts,tsx,js,jsx,mjs,mts,cts},!(cloud)/**/*.{ts,tsx,js,jsx,mjs,mts,cts}}": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58b5d255c5b..bead5fb8ef3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -508,6 +508,9 @@ importers: specifier: ^5.0.14 version: 5.0.14(@types/react@19.2.17)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) optionalDependencies: + '@orca/windows-registry': + specifier: file:./native/windows-registry + version: file:native/windows-registry '@vscode/windows-process-tree': specifier: 0.8.0 version: 0.8.0(patch_hash=e66202cc623996d02040c93449eb9ae353fddadf426cb53202a59ee710ee6fe7) @@ -526,9 +529,6 @@ importers: sherpa-onnx-win-x64: specifier: 1.12.37 version: 1.12.37 - windows-native-registry: - specifier: 3.2.2 - version: 3.2.2 packages: @@ -1323,6 +1323,10 @@ packages: resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} + '@orca/windows-registry@file:native/windows-registry': + resolution: {directory: native/windows-registry, type: directory} + os: [win32] + '@oxc-parser/binding-android-arm-eabi@0.141.0': resolution: {integrity: sha512-jk7086MFvR/T4DG9IY7MKBVt1PMxvSZoz/TvnifodvS0pjghVwJHRttnAExhlwdMOgHv1TmLdENnbNpYk2zjvA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5571,9 +5575,6 @@ packages: resolution: {integrity: sha512-vLBWCKb+7LWsX+TbfzWOkw0W81m377tyx3hOweBTjO43CXZnRGS1/JPWs20fr0PgZyDXk6ROYrylsEycK8raDA==} engines: {node: '>=22.12.0'} - node-addon-api@4.3.0: - resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} - node-addon-api@7.1.0: resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} engines: {node: ^16 || ^18 || >= 20} @@ -5581,6 +5582,10 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-addon-api@8.9.2: + resolution: {integrity: sha512-VijLXbi3UACN69I0JVXJsX4tjACjNoQDgv2gTF6sx2wWEi8tkSg2eX8p5gSIFi8z2+DL3oHmY6OyKce38SDolg==} + engines: {node: ^18 || ^20 || >= 21} + node-api-version@0.2.1: resolution: {integrity: sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==} @@ -6595,16 +6600,9 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.4.10: - resolution: {integrity: sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==} - tldts-core@7.4.12: resolution: {integrity: sha512-nYNzS2WRf4QJmjzFFgAxLOBjyBxAGRbCy9PVBPaglcYyYajh40VBn+v5Ngr96ZMc7oM0+aCJdtQnNejvdBnXMQ==} - tldts@7.4.10: - resolution: {integrity: sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==} - hasBin: true - tldts@7.4.12: resolution: {integrity: sha512-WylhSDKVeYnWXL3a+vKTaOxjnOeEGw938hImY8zoRWJjRRK/Jp1K+IihBzIONpUmW4e3WmXT6q5FW6vlESVZCA==} hasBin: true @@ -6897,10 +6895,6 @@ packages: engines: {node: '>=8'} hasBin: true - windows-native-registry@3.2.2: - resolution: {integrity: sha512-yg1ZnuzUExn+Uq3cODB23Ju15URw0F/sRrSpjavVhHMi46s6Pcukp4+PtumoELmefoeHarCgci0XV34COpuYkw==} - os: [win32] - wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -7878,6 +7872,11 @@ snapshots: '@opentelemetry/api@1.9.1': optional: true + '@orca/windows-registry@file:native/windows-registry': + dependencies: + node-addon-api: 8.9.2 + optional: true + '@oxc-parser/binding-android-arm-eabi@0.141.0': optional: true @@ -12244,14 +12243,14 @@ snapshots: dependencies: semver: 7.8.1 - node-addon-api@4.3.0: - optional: true - node-addon-api@7.1.0: optional: true node-addon-api@7.1.1: {} + node-addon-api@8.9.2: + optional: true + node-api-version@0.2.1: dependencies: semver: 7.8.1 @@ -13481,16 +13480,8 @@ snapshots: tinyrainbow@3.1.0: {} - tldts-core@7.4.10: - optional: true - tldts-core@7.4.12: {} - tldts@7.4.10: - dependencies: - tldts-core: 7.4.10 - optional: true - tldts@7.4.12: dependencies: tldts-core: 7.4.12 @@ -13509,7 +13500,7 @@ snapshots: tough-cookie@6.0.2: dependencies: - tldts: 7.4.10 + tldts: 7.4.12 optional: true trim-lines@3.0.1: {} @@ -13776,11 +13767,6 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - windows-native-registry@3.2.2: - dependencies: - node-addon-api: 4.3.0 - optional: true - wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4111f3708a0..cc5b51dda4c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -50,7 +50,7 @@ allowBuilds: node-pty: true sherpa-onnx: true ssh2: false - windows-native-registry: false + "@orca/windows-registry": false overrides: monaco-editor>dompurify: 3.4.14 diff --git a/src/main/windows-native-registry.ts b/src/main/windows-native-registry.ts index e3a2d77889d..cc22c6b36a8 100644 --- a/src/main/windows-native-registry.ts +++ b/src/main/windows-native-registry.ts @@ -21,5 +21,5 @@ const requireFromMain = createRequire(__filename) export function loadWindowsNativeRegistry(): WindowsNativeRegistryModule { // Why: non-Windows installs omit this optional dependency, so never resolve it at module load. - return requireFromMain('windows-native-registry') as WindowsNativeRegistryModule + return requireFromMain('@orca/windows-registry') as WindowsNativeRegistryModule }