{"_id":"@amritk/lynx-deep-linking","_rev":"4-81caa1be326705d7b2f9442a44d1079b","name":"@amritk/lynx-deep-linking","dist-tags":{"latest":"0.2.2"},"versions":{"0.1.0":{"name":"@amritk/lynx-deep-linking","version":"0.1.0","keywords":["lynx","deep-linking","deeplink","universal-links","app-links","url-scheme","native-module","typescript","mjst"],"author":{"name":"amritk"},"license":"MIT","_id":"@amritk/lynx-deep-linking@0.1.0","maintainers":[{"name":"amritk","email":"amrit+spam@hockey-community.com"}],"homepage":"https://github.com/amritk/mini/tree/main/packages/lynx-deep-linking#readme","bugs":{"url":"https://github.com/amritk/mini/issues"},"dist":{"shasum":"0f02769d7415046736ccd96c994342483c91aa6c","tarball":"https://registry.npmjs.org/@amritk/lynx-deep-linking/-/lynx-deep-linking-0.1.0.tgz","fileCount":30,"integrity":"sha512-nCbrTKitx6M4gJ+BJT2F8/37RvU3ZKkHg+uOXZDzFmglzC1TVfOrkJj0tBkxCi+0gZK27VaMz/JZQ6AvR18vWg==","signatures":[{"sig":"MEYCIQCOSCridYP7123lyakxOghILRJQpw2jzDAasCCX/ilGyAIhAN1MYO2GQ5AoHLUWDrtvumLXmPPhUJR/OZxA2+MOrxIC","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":92476},"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"},"./package.json":"./package.json","./lynx.lib.json":"./lynx.lib.json"},"gitHead":"aea9072d4a679858c41a7d4eaf1f90f0d153798f","scripts":{"test":"NODE_ENV=production vitest run --root ../.. packages/lynx-deep-linking/","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/lynx-deep-linking"},"_npmVersion":"11.16.0","description":"Deep links for Lynx: an Android and iOS native module, and a promise-shaped facade that reaches it from the main thread.","directories":{},"sideEffects":false,"_nodeVersion":"26.3.0","dependencies":{"@amritk/mini-helpers":"workspace:*","@amritk/mini-lynx-native":"workspace:*"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5"},"peerDependencies":{"typescript":"^5"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/lynx-deep-linking_0.1.0_1785895675089_0.1828791111824819","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@amritk/lynx-deep-linking","version":"0.2.0","keywords":["lynx","deep-linking","deeplink","universal-links","app-links","url-scheme","native-module","typescript","mjst"],"author":{"name":"amritk"},"license":"MIT","_id":"@amritk/lynx-deep-linking@0.2.0","maintainers":[{"name":"amritk","email":"amrit+spam@hockey-community.com"}],"homepage":"https://github.com/amritk/mini/tree/main/packages/lynx-deep-linking#readme","bugs":{"url":"https://github.com/amritk/mini/issues"},"dist":{"shasum":"c325d6e7fbb091cc35f66c9d117ec133e04a8d35","tarball":"https://registry.npmjs.org/@amritk/lynx-deep-linking/-/lynx-deep-linking-0.2.0.tgz","fileCount":57,"integrity":"sha512-pFrywc/X74SuaBrpUnAEchMPB0m53yfacht/33rlI268mXfxZOxKG7I4dtuXEMxDjT1N16IEEE6f4XTpTVSZVg==","signatures":[{"sig":"MEQCIGTWw+Pe0aE7XlarbM4HaH8RIaXNdN1dU0xcQHJhU9q0AiBTZVf+duzBS5M3qrHlxB8vsdE/M77bSocNTXBsUKBHiQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amritk%2flynx-deep-linking@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":125252},"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"},"./package.json":"./package.json","./lynx.lib.json":"./lynx.lib.json"},"gitHead":"9c6b6bc96beba6a5f106f5e1bf5d7d0ab3b762c1","scripts":{"test":"NODE_ENV=production vitest run --root ../.. packages/lynx-deep-linking/","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":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9883b101-d584-442a-bc77-e099b9a9fe7b"}},"repository":{"url":"git+https://github.com/amritk/mini.git","type":"git","directory":"packages/lynx-deep-linking"},"_npmVersion":"12.0.2","description":"Deep links for Lynx: an Android and iOS native module, and a promise-shaped facade that reaches it from the main thread.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{"@amritk/mini-helpers":"0.2.0","@amritk/mini-lynx-native":"0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5"},"peerDependencies":{"typescript":"^5"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/lynx-deep-linking_0.2.0_1785905545509_0.2836285716260052","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@amritk/lynx-deep-linking","version":"0.2.1","keywords":["lynx","deep-linking","deeplink","universal-links","app-links","url-scheme","native-module","typescript","mjst"],"author":{"name":"amritk"},"license":"MIT","_id":"@amritk/lynx-deep-linking@0.2.1","maintainers":[{"name":"amritk","email":"amrit+spam@hockey-community.com"}],"homepage":"https://github.com/amritk/mini/tree/main/packages/lynx-deep-linking#readme","bugs":{"url":"https://github.com/amritk/mini/issues"},"dist":{"shasum":"a8fd5e6eca92d44336a0bc365f6e3f94e96f6511","tarball":"https://registry.npmjs.org/@amritk/lynx-deep-linking/-/lynx-deep-linking-0.2.1.tgz","fileCount":57,"integrity":"sha512-GSciT3wKJ5+2/EPB2qeWC5KonEf9ygyvUd1azKKem2S1P5FYbNx6O7QSUbNebaS9+1D5+PVLgbg2DbrxcA/exg==","signatures":[{"sig":"MEYCIQCOLFD3jw9OsBqiIEyGm8vjEjlpv3KzDIN7l2fANcerLAIhAICn/LajIWJosSfUSJLuJs5bgn+8I+TotXKBQ28x7+XY","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amritk%2flynx-deep-linking@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":125318},"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"},"./package.json":"./package.json","./lynx.lib.json":"./lynx.lib.json"},"gitHead":"9c78706160a9e76fc5a6e31ef33a1b7c5fdfeb03","scripts":{"test":"NODE_ENV=production vitest run --root ../.. packages/lynx-deep-linking/","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:9883b101-d584-442a-bc77-e099b9a9fe7b"}},"repository":{"url":"git+https://github.com/amritk/mini.git","type":"git","directory":"packages/lynx-deep-linking"},"_npmVersion":"12.0.2","description":"Deep links for Lynx: an Android and iOS native module, and a promise-shaped facade that reaches it from the main thread.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{"@amritk/mini-helpers":"0.2.0","@amritk/mini-lynx-native":"0.2.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5"},"peerDependencies":{"typescript":"^5"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/lynx-deep-linking_0.2.1_1785949011839_0.15717486136932401","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@amritk/lynx-deep-linking","version":"0.2.2","description":"Deep links for Lynx: an Android and iOS native module, and a promise-shaped facade that reaches it from the main thread.","type":"module","license":"MIT","author":{"name":"amritk"},"sideEffects":false,"keywords":["lynx","deep-linking","deeplink","universal-links","app-links","url-scheme","native-module","typescript","mjst"],"repository":{"type":"git","url":"git+https://github.com/amritk/mini.git","directory":"packages/lynx-deep-linking"},"homepage":"https://github.com/amritk/mini/tree/main/packages/lynx-deep-linking#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/lynx-deep-linking/"},"exports":{"./package.json":"./package.json","./lynx.lib.json":"./lynx.lib.json",".":{"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"}},"dependencies":{"@amritk/mini-helpers":"0.2.0","@amritk/mini-lynx-native":"0.2.2"},"peerDependencies":{"typescript":"^5"},"peerDependenciesMeta":{"typescript":{"optional":true}},"devDependencies":{"typescript":"^5"},"gitHead":"c2516a9f0a935e9e8e1eda316d651ac48e0ccc71","_id":"@amritk/lynx-deep-linking@0.2.2","_nodeVersion":"24.19.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-gwMFmpCcqtigRhCk+/SKrCdgbr/0pBGzDwL2QIy/PyatpxPQX/iJ9owwRANerXElkNm097dc47YYTrc4X8KatA==","shasum":"1a6ae2652dfb79c77d33c0da029df7e3f1836f04","tarball":"https://registry.npmjs.org/@amritk/lynx-deep-linking/-/lynx-deep-linking-0.2.2.tgz","fileCount":57,"unpackedSize":126226,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amritk%2flynx-deep-linking@0.2.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCH0hjLJ3VL3Qx0xtKFg7K6k7LmbPX0+IdttAYuMnpfGwIgHpoh3MdHLjoAGAkovBTsBU7Akyis8MY9R09ameKWD+o="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9883b101-d584-442a-bc77-e099b9a9fe7b"}},"directories":{},"maintainers":[{"name":"amritk","email":"amrit+spam@hockey-community.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/lynx-deep-linking_0.2.2_1787351375761_0.7345871056148685"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-05T02:07:54.980Z","modified":"2026-08-21T22:29:36.317Z","0.1.0":"2026-08-05T02:07:55.214Z","0.2.0":"2026-08-05T04:52:25.654Z","0.2.1":"2026-08-05T16:56:52.076Z","0.2.2":"2026-08-21T22:29:35.919Z"},"bugs":{"url":"https://github.com/amritk/mini/issues"},"author":{"name":"amritk"},"license":"MIT","homepage":"https://github.com/amritk/mini/tree/main/packages/lynx-deep-linking#readme","keywords":["lynx","deep-linking","deeplink","universal-links","app-links","url-scheme","native-module","typescript","mjst"],"repository":{"type":"git","url":"git+https://github.com/amritk/mini.git","directory":"packages/lynx-deep-linking"},"description":"Deep links for Lynx: an Android and iOS native module, and a promise-shaped facade that reaches it from the main thread.","maintainers":[{"name":"amritk","email":"amrit+spam@hockey-community.com"}],"readme":"# @amritk/lynx-deep-linking\n\n**Deep links for Lynx.** An Android native module, an iOS native module, and a\npromise-shaped facade that reaches them from a main-thread\n[`@amritk/mini-lynx`](../mini-lynx) tree.\n\n```ts\nimport { getInitialURL, onDeepLink, openURL, parseURL } from '@amritk/lynx-deep-linking'\n\nconst open = (url: string) => router.navigate(parseURL(url).path)\n\nconst initial = await getInitialURL() // the link that launched the app\nif (initial) open(initial)\n\nonDeepLink(({ url }) => open(url)) // the ones that arrive while it runs\n\nawait openURL('https://example.com') // and the outbound direction\n```\n\n## Why this exists\n\nLynx ships no linking module — `NativeModules` is an access point, not a\ncatalogue, and the official answer to anything platform-shaped is \"write native\ncode and send it into your Lynx code\". Two community packages do exist, and\nboth are parts of a framework rather than a library you can add:\n[`@sigx/lynx-linking`](https://www.npmjs.com/package/@sigx/lynx-linking) depends\non `@sigx/lynx-core` and wants a `sigx prebuild` step, and\n[`@tamer4lynx/tamer-linking`](https://www.npmjs.com/package/@tamer4lynx/tamer-linking)\npeers on ReactLynx and links through the `t4l` CLI. Neither reaches the main\nthread a `mini-lynx` tree renders on.\n\n## Install\n\n```sh\nbun add @amritk/lynx-deep-linking\n```\n\n`@amritk/mini-lynx-native` comes with it — it is the wire every call travels.\n\n## JavaScript setup\n\nOne line, in your **background** chunk:\n\n```ts\nimport { installNativeBridge } from '@amritk/mini-lynx-native/background'\n\ninstallNativeBridge()\n```\n\nWithout it every call queues forever and nothing says why. `NativeModules` is a\nbackground-thread global and `@amritk/mini-lynx` renders on the main thread, so\nthis package cannot reach the platform without a chunk it does not own\ninstalling the other half. See\n[`@amritk/mini-lynx-native`](../mini-lynx-native) for the reasoning.\n\nThen, at your app root on the main thread:\n\n```tsx\nimport { onCleanup } from '@amritk/mini-lynx'\nimport { getInitialURL, onDeepLink, parseURL } from '@amritk/lynx-deep-linking'\n\nconst App = () => {\n  const open = (url: string) => {\n    const { host, path, query } = parseURL(url)\n    if (host === 'order') router.navigate(`/order/${path.slice(1)}`, query)\n  }\n\n  // Two mechanisms because they are two different moments: the launch URL\n  // happened before this process could listen, and is never re-delivered.\n  getInitialURL().then((url) => url && open(url))\n  onCleanup(onDeepLink(({ url }) => open(url)))\n\n  return <Router />\n}\n```\n\nSubscribe at the **root**, on first render — not on the screen the link points\nat, which does not exist yet.\n\n## Host-app setup\n\n`lynx.lib.json` declares both native sources, so Lynx's autolinking picks them\nup. What autolinking cannot supply is the scheme your app answers to, or the\ndelegate callbacks only your app receives.\n\n### Android\n\nDeclare your scheme on the activity that hosts your LynxView:\n\n```xml\n<activity android:name=\".MainActivity\" android:launchMode=\"singleTask\" android:exported=\"true\">\n  <intent-filter android:autoVerify=\"true\">\n    <action android:name=\"android.intent.action.VIEW\" />\n    <category android:name=\"android.intent.category.DEFAULT\" />\n    <category android:name=\"android.intent.category.BROWSABLE\" />\n    <data android:scheme=\"myapp\" />\n  </intent-filter>\n</activity>\n```\n\nThen forward one callback. This is the only line this library needs, and there\nis no way around it — `onNewIntent` is an activity method no library can\nobserve:\n\n```kotlin\noverride fun onNewIntent(intent: Intent) {\n  super.onNewIntent(intent)\n  setIntent(intent)\n  DeepLinks.handleIntent(intent)\n}\n```\n\n**The cold start needs nothing.** A `ContentProvider` in this library's manifest\nregisters an activity lifecycle callback during process startup and captures the\nlaunching intent, which is what makes `getInitialURL()` work with no wiring at\nall.\n\nTo open a **custom scheme** — another app of yours, WhatsApp, Spotify — add it\nto your manifest, or `openURL` will report `noHandler` on a device where the app\nis plainly installed. Android 11 hides everything you have not declared:\n\n```xml\n<queries>\n  <intent>\n    <action android:name=\"android.intent.action.VIEW\" />\n    <data android:scheme=\"whatsapp\" />\n  </intent>\n</queries>\n```\n\n`https`, `mailto`, `tel` and `sms` are already declared by this library and\nreach your app through manifest merging.\n\nIf your build does not run Lynx's annotation processor, register the module by\nhand:\n\n```kotlin\nLynxEnv.inst().registerModule(\"MiniLynxDeepLinkingModule\", MiniLynxDeepLinkingModule::class.java)\n```\n\n### iOS\n\nDeclare your scheme in `Info.plist`:\n\n```xml\n<key>CFBundleURLTypes</key>\n<array>\n  <dict>\n    <key>CFBundleURLSchemes</key>\n    <array><string>myapp</string></array>\n  </dict>\n</array>\n```\n\nRegister the module where you build your Lynx config:\n\n```objc\n#import <MiniLynxDeepLinking/MiniLynxDeepLinkingModule.h>\n\n[config registerModule:MiniLynxDeepLinkingModule.class];\n```\n\nThen forward what iOS delivers only to your delegate. There is no supported way\nfor a library to intercept these without swizzling — the same bargain\n[`@amritk/lynx-notifications`](../lynx-notifications) makes for APNs:\n\n```objc\n// UIApplicationDelegate — a link into a running app\n- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {\n  [MiniLynxDeepLinkingCenter.shared handleURL:url];\n  return YES;\n}\n\n// Universal Links (https://) arrive as a user activity instead\n- (BOOL)application:(UIApplication *)application\n    continueUserActivity:(NSUserActivity *)userActivity\n      restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *))restorationHandler {\n  [MiniLynxDeepLinkingCenter.shared handleUserActivity:userActivity];\n  return YES;\n}\n```\n\n**A scene-based app needs one more**, because iOS puts a launch URL in the\nscene's connection options rather than in the app's launch options — where this\nlibrary reads it automatically:\n\n```objc\n- (void)scene:(UIScene *)scene\n    willConnectToSession:(UISceneSession *)session\n                 options:(UISceneConnectionOptions *)options {\n  [MiniLynxDeepLinkingCenter.shared handleLaunchURL:options.URLContexts.anyObject.URL];\n}\n\n- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {\n  [MiniLynxDeepLinkingCenter.shared handleURL:URLContexts.anyObject.URL];\n}\n```\n\nTo open a custom scheme, list it in `LSApplicationQueriesSchemes` — `canOpenURL`\nanswers false for anything missing, and the list is capped at 50 entries:\n\n```xml\n<key>LSApplicationQueriesSchemes</key>\n<array><string>whatsapp</string></array>\n```\n\n## API\n\n```ts\ngetInitialURL(): Promise<string | null>            // the URL that launched the process\nonDeepLink(listener: (link: DeepLink) => void): () => void  // the ones after that\n\nopenURL(url: string): Promise<OpenResult>          // hand a URL to the system\ncanOpenURL(url: string): Promise<boolean>          // …would it go anywhere?\nopenSettings(): Promise<boolean>                   // this app's page in Settings\n\nisDeepLinkingAvailable(): Promise<boolean>         // is the native module linked at all\n\nparseURL(url: string): ParsedURL                   // pure, no bridge\ncreateURL(scheme: string, path?: string, options?: CreateURLOptions): string\n```\n\n`OpenResult` is a discriminated union — `{ ok: true }` or\n`{ ok: false, error, message }` — rather than a rejection. Lynx has no error\nconvention for bridge callbacks, so a failure has to travel as a value anyway,\nand \"no app on this device opens that\" is an ordinary branch in a UI.\n\n`openSettings` is the missing half of every permission API in this repository:\n[`@amritk/lynx-location`](../lynx-location) and\n[`@amritk/lynx-notifications`](../lynx-notifications) both report a `denied` that\nonly the settings app can undo.\n\n## The things that actually bite\n\n- **The first segment is the host.** `myapp://profile/42` parses as host\n  `profile`, path `/42` — not path `/profile/42`. A `//` opens an authority and\n  the grammar does not care that a custom scheme has no host. Every parser on\n  every platform agrees; route on `host` too, or put a dummy authority in your\n  links (`myapp://app/profile/42`).\n- **`noHandler` usually means undeclared, not uninstalled.** Android 11 package\n  visibility and iOS `LSApplicationQueriesSchemes` both hide apps you never said\n  you wanted to reach. See the two snippets above.\n- **The launch URL is not an event.** It is `getInitialURL()`, once, at the\n  root. `onDeepLink` never re-delivers it — an app wired to both would otherwise\n  navigate twice for one tap.\n- **`getInitialURL()` does not change.** It is the launch of *this process*, not\n  the latest link. Reading it on every screen mount re-runs the launch\n  navigation on every mount.\n- **A link can arrive with no view up.** The native side holds one and replays\n  it when `onDeepLink` subscribes — which is why subscribing at the root during\n  the first render matters, and why the held link is delivered only once.\n- **`launchMode=\"singleTask\"` (or `singleTop`) on Android**, or every link\n  starts a second copy of your activity and `onNewIntent` never fires.\n- **Recents is not a new link.** Android re-delivers the original intent when a\n  user returns through the recents screen; this library ignores those, which is\n  why swiping back to your app does not re-open last week's deep link.\n- **`openSettings()` does not come back.** The app is backgrounded, and on iOS a\n  permission changed in Settings *terminates* it. Do not put unsaved state\n  behind it.\n\n## Testing\n\n`@amritk/lynx-deep-linking/testing` ships the native module's contract as an\nin-memory fake, which is what this package's own suite runs against:\n\n```ts\nimport { installNativeBridge } from '@amritk/mini-lynx-native/background'\nimport { createFakeContexts, createFakeEmitter } from '@amritk/mini-lynx-native/testing'\nimport { MODULE } from '@amritk/lynx-deep-linking'\nimport { createFakeDeepLinking } from '@amritk/lynx-deep-linking/testing'\n\nconst contexts = createFakeContexts()\nconst emitter = createFakeEmitter()\nconst linking = createFakeDeepLinking(emitter)\n\nsetPeerContext(contexts.mainThread)\ninstallNativeBridge({ peer: contexts.background, emitter, modules: { [MODULE]: linking.module } })\n\nlinking.setInitialURL('myapp://order/42') // a cold start\nlinking.deliver('myapp://order/99')       // a link into a running app\nlinking.hold('myapp://order/7')           // one that arrived with no view up\n```\n\nIt reproduces the platforms rather than smoothing them over: a scheme the host\napp never declared fails as `noHandler` even though the \"device\" could obviously\nopen it, and the held link is delivered exactly once. A fake friendlier than a\ndevice would hide the bugs worth catching.\n\n## Status, and what is actually verified\n\n**Pre-alpha, and nothing here has run on a device.** Four checks of decreasing\nreach stand behind it, and it is worth knowing which one a green run came from:\n\n| Check | Command | Strength |\n| --- | --- | --- |\n| Facade behaviour | `bun run test` | real code, fake platform |\n| Cross-language signatures | `src/native-contract.test.ts` | parses Kotlin + Objective-C, compares to TypeScript |\n| Kotlin compiles + packages | `bun run check:android` | real Lynx AAR, real Android SDK |\n| Objective-C compiles | `pod lib lint` (manual, macOS — not in CI) | real Lynx pod, real iOS SDK |\n\nNone of that is a device. Whether the `ContentProvider` initialiser wins the\nrace against the first activity, whether `+load` fires before the launch\nnotification in a host app that links statically, what a scene-based app\nactually delivers, and whether Lynx's annotation processor registers the module\nwithout the generated Spec its own template extends are all unverified. See\n[`AGENTS.md`](./AGENTS.md).\n\n## Licence\n\nMIT\n","readmeFilename":"README.md"}