{"_id":"@3alijny/miniapp-sdk","_rev":"2-40d8cfdc34cfb7d3977073da98bf15f2","name":"@3alijny/miniapp-sdk","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@3alijny/miniapp-sdk","version":"0.1.0","keywords":["3alijny","miniapp","webview","bridge","pwa"],"author":{"name":"3alijny"},"license":"UNLICENSED","_id":"@3alijny/miniapp-sdk@0.1.0","maintainers":[{"name":"unk.moha","email":"akshnd28r@gmail.com"}],"homepage":"https://github.com/xxM0ha/miniapp-sdk#readme","bugs":{"url":"https://github.com/xxM0ha/miniapp-sdk/issues"},"dist":{"shasum":"ae919723dd91169a5228ff38d765b745f387c6c6","tarball":"https://registry.npmjs.org/@3alijny/miniapp-sdk/-/miniapp-sdk-0.1.0.tgz","fileCount":9,"integrity":"sha512-TsLvyFMKF/yI0v0jf88Qh5vDYOWuBgyRyy8mb5INkgZST1jQOzQ0bkNsALwba81pG5f9/h4QWjqbXVQehSysBQ==","signatures":[{"sig":"MEQCICGaMgyzoggKRmWBDXN+PRfvP9TLNQ36lr5Vm7j6cRaWAiAzVUm/kO2sB73PBqVRxQIw69/OdXOEHWrkEzD3BgppCw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":63116},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"gitHead":"720355ad26da163141f7d88b6d8d22db812d6c39","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build"},"_npmUser":{"name":"unk.moha","email":"akshnd28r@gmail.com"},"repository":{"url":"git+https://github.com/xxM0ha/miniapp-sdk.git","type":"git"},"_npmVersion":"10.9.2","description":"Bridge SDK for 3alijny mini apps — PWAs running inside the 3alijny native app","directories":{},"sideEffects":false,"_nodeVersion":"23.11.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","react":"^19.0.0","typescript":"^5.7.2","@types/react":"^19.0.0"},"peerDependencies":{"react":">=18"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/miniapp-sdk_0.1.0_1786480058967_0.954534927320958","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@3alijny/miniapp-sdk","version":"0.2.0","description":"Bridge SDK for 3alijny mini apps — PWAs running inside the 3alijny native app","license":"UNLICENSED","author":{"name":"3alijny"},"homepage":"https://github.com/xxM0ha/miniapp-sdk#readme","repository":{"type":"git","url":"git+https://github.com/xxM0ha/miniapp-sdk.git"},"bugs":{"url":"https://github.com/xxM0ha/miniapp-sdk/issues"},"keywords":["3alijny","miniapp","webview","bridge","pwa"],"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"sideEffects":false,"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build"},"peerDependencies":{"react":">=18"},"peerDependenciesMeta":{"react":{"optional":true}},"devDependencies":{"@types/react":"^19.0.0","react":"^19.0.0","tsup":"^8.3.5","typescript":"^5.7.2"},"publishConfig":{"access":"public"},"engines":{"node":">=18"},"_id":"@3alijny/miniapp-sdk@0.2.0","gitHead":"052a04afa971b626c5ec9f7f0c8f5205db5e3595","_nodeVersion":"23.11.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-kCXnL5Kr2rHEXw30eI4u26cOQg/aTPvOdVohJxexg0MFJ0lqy9U1b/XB6zFHAhxqcsMO5tPxP/MuZ3GIrI8/eA==","shasum":"75ffc0b52bafe631a2bf44beaa203d9c63d24f32","tarball":"https://registry.npmjs.org/@3alijny/miniapp-sdk/-/miniapp-sdk-0.2.0.tgz","fileCount":9,"unpackedSize":78096,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGg7szOHk+M8QrDQPu11BaFK5Xcc4hJLT1GBq4oyodd9AiEAvXa+mOp+Gqsbdj9P70XG8tkKJGAuWSpuGq2THUmYj+Y="}]},"_npmUser":{"name":"unk.moha","email":"akshnd28r@gmail.com"},"directories":{},"maintainers":[{"name":"unk.moha","email":"akshnd28r@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/miniapp-sdk_0.2.0_1786503443951_0.9151158854943664"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-11T20:27:38.726Z","modified":"2026-08-12T02:57:24.305Z","0.1.0":"2026-08-11T20:27:39.098Z","0.2.0":"2026-08-12T02:57:24.116Z"},"bugs":{"url":"https://github.com/xxM0ha/miniapp-sdk/issues"},"author":{"name":"3alijny"},"license":"UNLICENSED","homepage":"https://github.com/xxM0ha/miniapp-sdk#readme","keywords":["3alijny","miniapp","webview","bridge","pwa"],"repository":{"type":"git","url":"git+https://github.com/xxM0ha/miniapp-sdk.git"},"description":"Bridge SDK for 3alijny mini apps — PWAs running inside the 3alijny native app","maintainers":[{"name":"unk.moha","email":"akshnd28r@gmail.com"}],"readme":"# @3alijny/miniapp-sdk\n\nBridge SDK for **3alijny mini apps** — PWAs that run inside the 3alijny native\napp's WebView container.\n\nThe native app injects a `window.AlijnyHost` object before any page script runs.\nThis package is the typed wrapper around it: promises, React hooks, and one\nerror class whose `code` you can switch on.\n\n```bash\nnpm i @3alijny/miniapp-sdk\n```\n\n> Published publicly so mini apps can install it without registry credentials.\n> Public ≠ open source — see LICENSE. It contains no secrets: the security model\n> is the host's origin allowlist and a session that carries no auth token,\n> neither of which depends on this code being private.\n\n## Quick start\n\n```ts\nimport { applyEnvToDocument, ready, getEnv } from '@3alijny/miniapp-sdk';\n\napplyEnvToDocument();        // sets <html dir/lang> + --alijny-inset-* CSS vars\nawait ready();               // hides the native loading overlay\nconsole.log(getEnv());       // { platform, appVersion, insets, direction, ... }\n```\n\n```tsx\nimport { useSession, haptics, toast, close } from '@3alijny/miniapp-sdk';\n\nfunction Header() {\n  const { session, loading, error } = useSession();\n\n  if (loading) return <Spinner />;\n  if (error?.code === 'unavailable') return <BrowserNotice />;\n\n  return <h1>أهلاً {session?.user?.name}</h1>;\n}\n```\n\n## Calling your own backend\n\nEvery mini app runs its own backend. To authenticate a request, attach an\nidentity token — a short-lived JWT signed by 3alijny and scoped to your app:\n\n```ts\nimport { authHeader, idempotencyKey } from '@3alijny/miniapp-sdk';\n\nconst key = idempotencyKey();          // once per order, reused across retries\n\nawait fetch('https://lab-api.3alijny.com/orders/', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'Idempotency-Key': key,\n    ...(await authHeader()),           // cached + auto-refreshed\n  },\n  body: JSON.stringify(order),\n});\n```\n\n**Your backend must verify the token.** Fetch 3alijny's JWKS, check the\nsignature, and reject any token whose `aud` isn't your app id:\n\n```python\nclaims = jwt.decode(\n    bearer, jwks.get_signing_key_from_jwt(bearer).key,\n    algorithms=[\"RS256\"], audience=\"lab\", issuer=\"https://3alijny.com\",\n)\nstudent_id = int(claims[\"sub\"])   # plus name / university / phone claims\n```\n\nA frontend claiming \"I am student 40\" proves nothing — anyone can `curl` that.\nThe signature is what makes it safe to act on.\n\n## What crosses the bridge\n\n**Profile data, and an identity token for *your* backend — never a 3alijny\ncredential.** `getSession()` returns id, name, university, class year, status,\nsubscription and case counts, for rendering. `authHeader()` returns a token\nscoped to your mini app (`aud`), signed by 3alijny, valid for minutes.\n\nA mini app therefore cannot call the 3alijny API as the student, and a leaked\nidentity token cannot read patients or change a subscription — it only proves\nidentity to the one backend it was minted for.\n\n## API\n\n| Function | Returns | Notes |\n|---|---|---|\n| `ready()` | `Promise<void>` | Call once when you first paint. The container hides its loading overlay; it gives up after ~2.5s anyway. |\n| `getSession()` | `Promise<MiniAppSession>` | Profile only. `authenticated: false` when signed out. |\n| `close()` | `Promise<void>` | Dismiss the mini app. |\n| `navigate(path, { replace })` | `Promise<void>` | Route the **native** app, e.g. `/case-tracker`. Must start with `/`. |\n| `haptics(style)` | `Promise<void>` | `light` \\| `medium` \\| `heavy` \\| `success` \\| `warning` \\| `error` \\| `selection`. |\n| `toast(message, { title })` | `Promise<void>` | Native toast (Android) / alert (iOS). |\n| `authHeader()` | `Promise<{Authorization}>` | Bearer header for **your** backend. Caches, refreshes, and shares one in-flight mint between concurrent callers. |\n| `getIdentityToken()` | `Promise<IdentityToken>` | The raw token, if you need `expiresAt` or want to cache it yourself. |\n| `clearIdentityCache()` | `void` | Forget the cached token — call if your backend rejects it as expired. |\n| `idempotencyKey(prefix?)` | `string` | Generate once per write, reuse across retries. |\n| `pay(params)` | `Promise<PayResult>` | **Rejects with `unavailable` today** — no payment backend for mini-app orders yet. |\n| `on(event, cb)` | `() => void` | `resume` \\| `pause` \\| `back`. Returns unsubscribe. |\n| `getEnv()` | `MiniAppEnv \\| null` | Synchronous. `null` outside the app. |\n| `isInsideApp()` | `boolean` | |\n| `waitForHost(ms?)` | `Promise<AlijnyHost>` | Rarely needed — the host is injected before your scripts run. |\n| `applyEnvToDocument(env?)` | `void` | Sets `<html dir/lang>` and `--alijny-inset-*` CSS variables. |\n\n### React\n\n`useSession()`, `useMiniAppEnv()`, `useIsInsideApp()`, `useHostEvent(name, cb)`.\nReact is an optional peer dependency — a non-React mini app can ignore it.\n\n## Running outside the app\n\nThere is **no mock**. In a desktop browser every call rejects with\n`MiniAppError` / `code: 'unavailable'`. Branch on it:\n\n```ts\ntry {\n  await haptics('success');\n} catch (err) {\n  if (err.code !== 'unavailable') throw err;   // ignore outside the app\n}\n```\n\nDesign your UI so it degrades: `useSession()` surfacing `unavailable` means\n\"render the signed-out state\", not \"show an error\".\n\n## Safe areas\n\nThe container is full-bleed — the page draws under the status bar and home\nindicator. After `applyEnvToDocument()`:\n\n```css\nbody {\n  padding-top: var(--alijny-inset-top, 0px);\n  padding-bottom: var(--alijny-inset-bottom, 0px);\n}\n```\n\n## Error codes\n\n`unavailable` (not inside the app, or method not implemented by this host build)\n· `unknown_method` (host is older than this SDK) · `invalid_params` ·\n`unauthenticated` · `cancelled` · `timeout` (host didn't answer in 15s) ·\n`internal`.\n\n## Versioning\n\n`BRIDGE_VERSION` is the wire protocol version. The host advertises the version\nit implements as `getEnv()?.bridgeVersion`; the manifest can lock a mini app to\na minimum host build so an old app never loads a PWA it can't serve.\n\n`src/protocol.ts` mirrors `lib/miniapp/protocol.ts` in the **3alijnyApp** repo.\nThe host owns the contract — change it there first, then mirror it here and\npublish a new version.\n\n## Development\n\n```bash\nnpm install\nnpm run build       # dist/: ESM + CJS + .d.ts\nnpm run dev         # watch\nnpm run typecheck\n```\n\nTo test against a mini app before publishing:\n\n```bash\nnpm pack                                    # -> 3alijny-miniapp-sdk-0.1.0.tgz\nnpm i ../miniapp-sdk/3alijny-miniapp-sdk-0.1.0.tgz   # from the mini app repo\n```\n\n`npm link` also works, but a packed tarball catches `files`/`exports` mistakes\nthat linking hides.\n","readmeFilename":"README.md"}