{"_id":"@amritk/mini-lynx-native","_rev":"3-4568f882a8a452a0d6db835d0582c0a3","name":"@amritk/mini-lynx-native","dist-tags":{"latest":"0.2.2"},"versions":{"0.2.0":{"name":"@amritk/mini-lynx-native","version":"0.2.0","keywords":["lynx","native-modules","bridge","background-thread","typescript","mjst"],"author":{"name":"amritk"},"license":"MIT","_id":"@amritk/mini-lynx-native@0.2.0","maintainers":[{"name":"amritk","email":"amrit+spam@hockey-community.com"}],"homepage":"https://github.com/amritk/mini/tree/main/packages/mini-lynx-native#readme","bugs":{"url":"https://github.com/amritk/mini/issues"},"dist":{"shasum":"a008a521889f22ef4784f29f856e78913b99c8f2","tarball":"https://registry.npmjs.org/@amritk/mini-lynx-native/-/mini-lynx-native-0.2.0.tgz","fileCount":19,"integrity":"sha512-NvYfmwV0OV4Cpe+5dss/IILLmkc4H2BIz+FxjUioaQx36wC77viyEs5CkzUUnnuacX6t5S0F6p9cC8N2imerow==","signatures":[{"sig":"MEUCIQDIG5nJZKh0X5YhQqc5npGwuJ4qgBwLUBAllNhkCEAI3QIgbLcWo4qM95gpckKRZGqRxdOb3l/87hyHAcqqgY4VV+s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":56024},"type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","development":"./src/index.ts"},"./testing":{"types":"./dist/testing/index.d.ts","import":"./dist/testing/index.js","default":"./dist/testing/index.js","development":"./src/testing/index.ts"},"./background":{"types":"./dist/background/index.d.ts","import":"./dist/background/index.js","default":"./dist/background/index.js","development":"./src/background/index.ts"},"./package.json":"./package.json"},"gitHead":"9c6b6bc96beba6a5f106f5e1bf5d7d0ab3b762c1","scripts":{"test":"NODE_ENV=production vitest run --root ../.. packages/mini-lynx-native/","build":"tsgo -p tsconfig.build.json && tsc-alias -p tsconfig.build.json -f && node ../../scripts/strip-comments.mjs","types:check":"tsgo -p . --noEmit"},"_npmUser":{"name":"amritk","email":"amrit+spam@hockey-community.com"},"repository":{"url":"git+https://github.com/amritk/mini.git","type":"git","directory":"packages/mini-lynx-native"},"_npmVersion":"11.16.0","description":"The thread hop between a main-thread mini-lynx tree and Lynx's background-thread NativeModules, as a typed request/reply channel.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5","@lynx-js/types":"^4.0.0"},"peerDependencies":{"typescript":"^5","@lynx-js/types":">=4.0.0"},"peerDependenciesMeta":{"typescript":{"optional":true},"@lynx-js/types":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/mini-lynx-native_0.2.0_1785906484028_0.39494022841930776","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@amritk/mini-lynx-native","version":"0.2.1","keywords":["lynx","native-modules","bridge","background-thread","typescript","mjst"],"author":{"name":"amritk"},"license":"MIT","_id":"@amritk/mini-lynx-native@0.2.1","maintainers":[{"name":"amritk","email":"amrit+spam@hockey-community.com"}],"homepage":"https://github.com/amritk/mini/tree/main/packages/mini-lynx-native#readme","bugs":{"url":"https://github.com/amritk/mini/issues"},"dist":{"shasum":"127bd6c06dab9b0da88637675870c6c18a2cf28a","tarball":"https://registry.npmjs.org/@amritk/mini-lynx-native/-/mini-lynx-native-0.2.1.tgz","fileCount":52,"integrity":"sha512-h/p2kk+s/ogfdbu3LdGhdQz8YEh9i93KeRzjnBcmbeIyB10V5SVMlfke91ONpupXPBDf3Dj/vbHJckJ2kQWSGQ==","signatures":[{"sig":"MEYCIQDWSQFFIuT9jakMlp0aB02wR3tVNwHEwBTmoMAK39zRAAIhANmnOotd1NEFC+tsEvEIPBXLaO6OwEMlbYaoMwURakGa","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amritk%2fmini-lynx-native@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":97061},"type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./testing":{"types":"./dist/testing/index.d.ts","import":"./dist/testing/index.js","default":"./dist/testing/index.js"},"./background":{"types":"./dist/background/index.d.ts","import":"./dist/background/index.js","default":"./dist/background/index.js"},"./package.json":"./package.json"},"gitHead":"9c78706160a9e76fc5a6e31ef33a1b7c5fdfeb03","scripts":{"test":"NODE_ENV=production vitest run --root ../.. packages/mini-lynx-native/","build":"tsgo -p tsconfig.build.json && tsc-alias -p tsconfig.build.json -f && node ../../scripts/strip-comments.mjs","types:check":"tsgo -p . --noEmit","prepublishOnly":"node ../../scripts/check-publishable.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d28d86e9-ff08-4084-832b-af70cea38e75"}},"repository":{"url":"git+https://github.com/amritk/mini.git","type":"git","directory":"packages/mini-lynx-native"},"_npmVersion":"12.0.2","description":"The thread hop between a main-thread mini-lynx tree and Lynx's background-thread NativeModules, as a typed request/reply channel.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5","@lynx-js/types":"^4.0.0"},"peerDependencies":{"typescript":"^5","@lynx-js/types":">=4.0.0"},"peerDependenciesMeta":{"typescript":{"optional":true},"@lynx-js/types":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/mini-lynx-native_0.2.1_1785949011371_0.8254196045866802","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@amritk/mini-lynx-native","version":"0.2.2","description":"The thread hop between a main-thread mini-lynx tree and Lynx's background-thread NativeModules, as a typed request/reply channel.","type":"module","license":"MIT","author":{"name":"amritk"},"sideEffects":false,"keywords":["lynx","native-modules","bridge","background-thread","typescript","mjst"],"repository":{"type":"git","url":"git+https://github.com/amritk/mini.git","directory":"packages/mini-lynx-native"},"homepage":"https://github.com/amritk/mini/tree/main/packages/mini-lynx-native#readme","bugs":{"url":"https://github.com/amritk/mini/issues"},"publishConfig":{"access":"public"},"scripts":{"build":"tsgo -p tsconfig.build.json && tsc-alias -p tsconfig.build.json -f && node ../../scripts/strip-comments.mjs","prepublishOnly":"node ../../scripts/check-publishable.mjs","types:check":"tsgo -p . --noEmit","test":"NODE_ENV=production vitest run --root ../.. packages/mini-lynx-native/"},"exports":{"./package.json":"./package.json",".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./background":{"types":"./dist/background/index.d.ts","import":"./dist/background/index.js","default":"./dist/background/index.js"},"./testing":{"types":"./dist/testing/index.d.ts","import":"./dist/testing/index.js","default":"./dist/testing/index.js"}},"peerDependencies":{"@lynx-js/types":">=4.0.0","typescript":"^5"},"peerDependenciesMeta":{"@lynx-js/types":{"optional":true},"typescript":{"optional":true}},"devDependencies":{"@lynx-js/types":"^4.0.0","typescript":"^5"},"gitHead":"c2516a9f0a935e9e8e1eda316d651ac48e0ccc71","_id":"@amritk/mini-lynx-native@0.2.2","_nodeVersion":"24.19.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-20smQERhQbeqvFgfUm8hKsXJrw99czRm8cHfpjNbaYRArPmbhmOxR0bDQ136Xu90qHYerTHX/QIq0LAssfiCWQ==","shasum":"3f98a300d844b03173931a442c58b613a1e8b204","tarball":"https://registry.npmjs.org/@amritk/mini-lynx-native/-/mini-lynx-native-0.2.2.tgz","fileCount":52,"unpackedSize":99486,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amritk%2fmini-lynx-native@0.2.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDjaI4YHFdhS/ge2P4/9Jgc206D6nHwKkoMkpM1Vh51EQIhAN4ifPYU9RySUMvLCcgi2aJioPxdJrdONjts5UJiFrCD"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d28d86e9-ff08-4084-832b-af70cea38e75"}},"directories":{},"maintainers":[{"name":"amritk","email":"amrit+spam@hockey-community.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mini-lynx-native_0.2.2_1787351375640_0.6089133889138503"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-05T05:08:03.853Z","modified":"2026-08-21T22:29:36.128Z","0.2.0":"2026-08-05T05:08:04.176Z","0.2.1":"2026-08-05T16:56:51.519Z","0.2.2":"2026-08-21T22:29:35.777Z"},"bugs":{"url":"https://github.com/amritk/mini/issues"},"author":{"name":"amritk"},"license":"MIT","homepage":"https://github.com/amritk/mini/tree/main/packages/mini-lynx-native#readme","keywords":["lynx","native-modules","bridge","background-thread","typescript","mjst"],"repository":{"type":"git","url":"git+https://github.com/amritk/mini.git","directory":"packages/mini-lynx-native"},"description":"The thread hop between a main-thread mini-lynx tree and Lynx's background-thread NativeModules, as a typed request/reply channel.","maintainers":[{"name":"amritk","email":"amrit+spam@hockey-community.com"}],"readme":"# @amritk/mini-lynx-native\n\nThe thread hop between a main-thread `@amritk/mini-lynx` tree and Lynx's\nbackground-thread `NativeModules`, as a typed request/reply channel.\n\n## The problem\n\nLynx is explicit about it: *\"native modules can only be used in Background\nThread Scripting.\"* `NativeModules` and `GlobalEventEmitter` are background\nglobals.\n\n`@amritk/mini-lynx` renders on the **main** thread, because the Element PAPI it\ndrives is a main-thread API.\n\nThose two facts do not overlap. A component reaching for `NativeModules` finds\n`undefined` — no error, no warning, just a property that is not there.\n\nThis package is the wire between them.\n\n## Install\n\n```sh\nbun add @amritk/mini-lynx-native\n```\n\n## Setup\n\nTwo lines, in two chunks.\n\n```ts\n// background chunk — once, at startup\nimport { installNativeBridge } from '@amritk/mini-lynx-native/background'\n\ninstallNativeBridge()\n```\n\n```tsx\n// main-thread chunk — anywhere in a component\nimport { callNative } from '@amritk/mini-lynx-native'\n\nconst level = await callNative<number>('BatteryModule', 'level')\n```\n\nCalls made before the background half is installed are **queued**, not lost —\nthe main-thread chunk usually runs first, because the engine calls `renderPage`\nto get a first screen up.\n\n## API\n\n| Function | What it does |\n| --- | --- |\n| `callNative(module, method, ...args)` | Invokes a native method that **returns** its result. |\n| `callNativeAsync(module, method, ...args)` | Invokes a native method that takes a **trailing callback**. |\n| `isNativeModuleAvailable(module, method?)` | Feature-detects without invoking anything. |\n| `onNativeEvent(name, listener)` | Subscribes to `sendGlobalEvent`. Returns the unsubscribe. |\n| `resetNativeChannel()` | Detaches and rejects everything in flight. For teardown and tests. |\n| `setPeerContext(proxy)` | Substitutes the context. For tests. |\n\n`callNative` and `callNativeAsync` differ in the shape of the **native** method,\nnot the JavaScript one — both return promises, because both cross a thread.\n\n### Everything is async, including the synchronous things\n\nA native method that returns immediately still reaches you a thread later. The\npractical consequence: **a native value cannot be read during a component's\nbuild.** Bind a signal, start the call, write the signal when it settles.\n\n```tsx\nconst level = signal(0)\ncallNative<number>('BatteryModule', 'level').then(level)\n\nconst View = () => <text>{() => `${level()}%`}</text>\n```\n\n### What crosses\n\nArguments and results are serialised by the engine, so they must be plain JSON.\nNo functions, no class instances, no `Date`. A failure crosses as a message and\nis rebuilt into an `Error` on the main thread — an `Error` itself would arrive\nas `{}`.\n\n## Testing\n\n`@amritk/mini-lynx-native/testing` ships the two fakes both halves run against,\nso a bridge-backed module can be tested with no device:\n\n```ts\nimport { resetNativeChannel, setPeerContext } from '@amritk/mini-lynx-native'\nimport { installNativeBridge } from '@amritk/mini-lynx-native/background'\nimport { createFakeContexts, createFakeEmitter } from '@amritk/mini-lynx-native/testing'\n\nconst contexts = createFakeContexts()\nconst emitter = createFakeEmitter()\nsetPeerContext(contexts.mainThread)\nconst uninstall = installNativeBridge({\n  peer: contexts.background,\n  emitter,\n  modules: { BatteryModule: { level: () => 87 } },\n})\n```\n\n## Caveats worth knowing before you build on it\n\n- **The app owns the background half.** This package cannot inject itself into a\n  chunk it cannot see; which module runs in the background context is a bundler\n  question. One line, once.\n- **A native method that never calls back leaves a promise pending forever.**\n  The wire cannot detect it. `resetNativeChannel()` settles the stragglers.\n- **Calls are not retried across a background reload.** Subscriptions are\n  replayed because they are idempotent; a native method that charged a card is\n  not something the channel should retry on its own initiative.\n\n## Licence\n\nMIT\n","readmeFilename":"README.md"}