# Vendored modification of `@picovoice/pvrecorder-node` (Windows ARM64 prebuild)

This directory contains a single locally-built Windows ARM64 prebuild of the
`pv_recorder.node` native module that ships with `@picovoice/pvrecorder-node`.
It is used to override the upstream prebuild for SEA builds — see
`script/cli-package-json.js` for the wire-up.

## Upstream package

- **Name**: `@picovoice/pvrecorder-node`
- **Version pinned against**: `1.2.8`
- **Source**: <https://github.com/Picovoice/pvrecorder>
- **License**: Apache License, Version 2.0

> Copyright 2021-2024 Picovoice Inc.
>
> Licensed under the Apache License, Version 2.0 (the "License"); you may not
> use this file except in compliance with the License. You may obtain a copy of
> the License at <http://www.apache.org/licenses/LICENSE-2.0>.

The full Apache 2.0 license text is at the URL above. The upstream npm package
does not ship a `LICENSE` file; the canonical text lives in the source
repository linked above.

## What was modified

In compliance with Section 4(b) of the Apache 2.0 license, this notice states
the change made to the upstream source before this binary was built:

In `project/node/CMakeLists.txt`, after the existing block that adds
`delayhook.c` to the `windows-amd64` and `windows-arm64` builds, the following
block was added so that `node.exe` is delay-loaded on Windows ARM64:

```cmake
# Delay-load node.exe so that Node API calls resolve to the host process,
# even when it isn't named node.exe (e.g., SEA builds, Electron, embedders).
if (${PV_RECORDER_PLATFORM} STREQUAL "windows-arm64")
    target_link_options(pv_recorder_napi PRIVATE "LINKER:--delayload=node.exe")
endif()
```

No other files in the upstream source were modified.

## Why

The upstream Windows ARM64 prebuild of `pv_recorder.node` _statically_ imports
`node.exe`. When the binary is loaded inside a Single Executable Application
(SEA) where the host process is `copilot.exe`, the Windows loader resolves the
static `node.exe` import by searching `PATH` and maps a _second_ `node.exe`
image into the process. The pvrecorder NAPI calls then go through the second
Node's exports while operating on `napi_env` values issued by the SEA's
embedded Node, producing memory corruption and a hard crash on the first NAPI
call after init.

The upstream binding already includes `project/node/delayhook.c`, which
implements `__pfnDliNotifyHook2` to redirect delay-load resolutions of
`node.exe` to `GetModuleHandle(NULL)` — i.e. to whatever EXE is hosting the
process (which inside our SEA is `copilot.exe`, and `copilot.exe`'s export
table is a superset of `node.exe`'s because the SEA was minted from
`node.exe`). However, the linker is never told to delay-load `node.exe`, so
the import remains static and the hook is dead code. The one-line change above
enables the existing fix.

The upstream amd64 prebuild does not have this bug — it imports its C runtime
from `msvcrt.dll` and does not import from `node.exe` at all — so the change
is intentionally scoped to `windows-arm64` only.

## How to reproduce this build

The change above was applied on top of upstream commit
[`a69cfd644896ec69bc1b06e0fd6a6394be77a6bc`](https://github.com/Picovoice/pvrecorder/commit/a69cfd644896ec69bc1b06e0fd6a6394be77a6bc)
(the latest `main` at the time the binary was built).

1. Clone <https://github.com/Picovoice/pvrecorder> and check out the commit
   above (or a later commit, if you have verified the relevant CMake file has
   not diverged).
2. Apply the CMake diff shown in [What was modified](#what-was-modified).
3. On a Windows ARM64 machine, install MSYS2 and the CLANGARM64 toolchain
   (`mingw-w64-clang-aarch64-toolchain`, `-cmake`, `-make`, `-binutils`).
4. From the MSYS2 CLANGARM64 shell:

    ```bash
    cd <pvrecorder>/project/node
    mkdir build && cd build
    cmake .. -G "MinGW Makefiles" \
        -DPV_RECORDER_PLATFORM=windows-arm64 \
        -DPV_WINDOWS_NODE_ARCH=win-arm64 \
        -DOUTPUT_DIR=windows/arm64
    cmake --build .
    ```

5. The output is `<pvrecorder>/lib/node/windows/arm64/pv_recorder.node`.

To verify the fix is present in the resulting binary, parse its PE imports and
confirm `node.exe` appears under the **delay** import directory and not under
the regular import directory.

## Lifecycle

This vendored binary exists only to bridge the gap until the equivalent
upstream change is accepted and released by Picovoice. Once a published
`@picovoice/pvrecorder-node` release ships with the delay-load fix, this
override should be deleted and the wire-up in `script/cli-package-json.js`
removed.

The CI script `npm run check:pvrecorder-vendored` fails the build whenever the
installed `@picovoice/pvrecorder-node` version diverges from the pinned
version above, prompting human re-evaluation of whether the override is still
needed.
