@condorinst/mobile-bridge 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,150 @@
1
+ # Troubleshooting
2
+
3
+ ## "My command did nothing"
4
+
5
+ This is the most common report, and it is usually correct: the command did
6
+ nothing, and nothing told you. The native side drops malformed or unauthorized
7
+ traffic with a log on the device and no reply to JavaScript.
8
+
9
+ There are six paths to silence. Work down the list.
10
+
11
+ ### 1. The payload was over 64 KB
12
+
13
+ The whole JSON message is measured, not just your arguments. Over the limit it
14
+ is discarded before dispatch.
15
+
16
+ **Check:** `JSON.stringify(args).length` at the call site.
17
+
18
+ ### 2. The command name is unknown to this device
19
+
20
+ A typo, or — more often — a command that exists in a newer native version than
21
+ the device runs. The native switch has no default branch that reports back.
22
+
23
+ **Check:** the command against `docs/bridge-contract.md`, then check
24
+ `ack.nativeVersion` from your handshake against the capability matrix. A device
25
+ in the field can be several versions behind your development device.
26
+
27
+ ### 3. The arguments had the wrong shape
28
+
29
+ `realTime`, `syncTime`, `deleteMemory` and `deviceConnection` validate their
30
+ payload natively and drop it on mismatch. `serialNo` is an **integer**, not a
31
+ string; `value` and `canGoBack` are **booleans**, not strings.
32
+
33
+ **Check:** `typeof` at the call site. `serialNo: "1234"` fails; `serialNo: 1234`
34
+ works.
35
+
36
+ ### 4. The permission name is not in the native enum
37
+
38
+ `requestPermission` takes a name the native side recognizes. Anything else is
39
+ dropped.
40
+
41
+ **Check:** the permission list in `docs/bridge-contract.md`. And note there is
42
+ no permission-result event at all — the outcome reaches you as a fresh
43
+ `systemStatus` emission. If you were waiting for a dedicated ack, you will wait
44
+ forever.
45
+
46
+ ### 5. The command was denied, or the person declined
47
+
48
+ `syncTime`, `deleteMemory` and `logout` open a Condor-owned confirmation modal.
49
+ Declining produces a `nack` with reason `user-rejected`, which `BridgeClient`
50
+ surfaces as a `console.warn` and nothing else — `send` is fire-and-forget.
51
+
52
+ `dfu` is denied outright. Subscribing to a key outside the allowed set produces
53
+ `nack` with reason `not-read-only`.
54
+
55
+ **Check:** the browser console for a warning starting with `BridgeClient:`.
56
+ Then re-read whether that command needs confirmation.
57
+
58
+ ### 6. You bypassed `BridgeClient`
59
+
60
+ A hand-rolled `window.parent.postMessage` almost certainly fails the Valibot
61
+ schema at the shell boundary and is dropped without a reply. The shell validates
62
+ every message before it touches the native bridge.
63
+
64
+ **Check:** that every message goes through `BridgeClient`.
65
+
66
+ There is a seventh path you cannot cause by accident but should know about: a
67
+ message that reaches the native channel without the shell's nonce is dropped
68
+ and logged as a security event. This is what happens if you create a nested
69
+ iframe and try to talk to the native side from inside it.
70
+
71
+ ## "My bundle was rejected and the app shows its default screen"
72
+
73
+ The app validates the bundle on download and keeps the previous version — or
74
+ falls back — on any failure.
75
+
76
+ ```bash
77
+ npx condor-mobile-bridge validate -i dist/bundle.zip --strict
78
+ ```
79
+
80
+ This applies the same gates. The failures it reports as **errors** are exactly
81
+ the ones that cause rejection:
82
+
83
+ | Finding | Cause |
84
+ | --- | --- |
85
+ | `too-large` | The `.zip` is over 2 MB. |
86
+ | `entry-missing` | No `.html` document in the archive. |
87
+ | `not-self-contained` | A `<script src>` or `<link rel="stylesheet">` pointing outside a `data:` URI. |
88
+
89
+ If `validate` passes and the app still falls back, the bundle never arrived:
90
+ check the manifest, the `bundleHash` (it must match the SHA-256 of the exact
91
+ `.zip` bytes you uploaded), and `minAppVersion` against the app build on the
92
+ device.
93
+
94
+ ## "It works in the browser but not on the device"
95
+
96
+ The browser mock is more permissive than the device in three ways that matter.
97
+
98
+ **`nativeVersion` is `null` in mock mode.** Code that reads it without a null
99
+ branch behaves differently in the two environments. Test both.
100
+
101
+ **Storage works in your browser and does not on the device.** Your dev server
102
+ serves from a real origin; the device gives you a `null` one. Anything that
103
+ touches `localStorage` works locally and throws in production. `validate` warns
104
+ about this — do not dismiss the warning.
105
+
106
+ **The CSP is not applied by your dev server.** External fonts, CDN scripts and
107
+ API calls all work locally and are blocked on the device.
108
+
109
+ Run `validate --strict` before concluding that something works.
110
+
111
+ ## "The screen is blank"
112
+
113
+ Almost always the bundle build, not the bridge.
114
+
115
+ **Code splitting.** If your bundler emitted more than one chunk, or ES module
116
+ imports between chunks, `srcdoc` cannot resolve them and nothing executes. Build
117
+ single-file: Vite with `vite-plugin-singlefile`, or an equivalent.
118
+
119
+ **A script that failed to parse.** Inlining a script that contains markup inside
120
+ JavaScript strings can break if done by a naive tool — use
121
+ `npx condor-mobile-bridge build`, which handles the escaping.
122
+
123
+ **First paint depends on the bridge.** If your render only runs inside an event
124
+ handler and no event has arrived yet, you get a blank screen. Render a skeleton
125
+ synchronously; fill it as events arrive.
126
+
127
+ ## "An ack never arrived"
128
+
129
+ Check, in order: was the command one of the three that need confirmation
130
+ (the person may have declined); did the command reach the native side at all
131
+ (the six paths above); and is your `requestId` correlation correct for
132
+ `sensorLog` — the reply carries back the id you sent, and a mismatch looks
133
+ exactly like silence.
134
+
135
+ Always have a timeout. `BridgeClient.fetchSensorLog` has one built in; anything
136
+ you correlate by hand needs your own.
137
+
138
+ ## Getting an error out of the device
139
+
140
+ You cannot reach an external logging service — `connect-src 'self'` blocks it.
141
+ Use the bridge:
142
+
143
+ ```ts
144
+ BridgeClient.send('jsError', { message: String(err), stack: err?.stack });
145
+ ```
146
+
147
+ That reaches Condor's logging and Crashlytics. It is rate limited to 20 per
148
+ 10 seconds and truncated at 4 KB per field, so do not use it as a general
149
+ telemetry channel — it will drop exactly when something is going wrong in a
150
+ loop.
@@ -0,0 +1,28 @@
1
+ # AGENTS.md — minimal Canvas home
2
+
3
+ This is a working Condor Canvas client. Copy it as the starting point for a new
4
+ one.
5
+
6
+ The full contract you must respect is in the SDK package:
7
+ `node_modules/@condorinst/mobile-bridge/AGENTS.md`. Read it before changing
8
+ anything here. The short version:
9
+
10
+ - No storage, no network, no `eval` — the iframe has a `null` origin and
11
+ inherits a CSP that blocks all three.
12
+ - Everything inline: one self-contained `index.html`, built single-chunk.
13
+ - Failures are silent. Drive the UI from events that arrive; never infer success
14
+ from the absence of an error.
15
+ - `serialNo` is a number, never a string.
16
+
17
+ ## Commands
18
+
19
+ ```bash
20
+ npm run build # esbuild → dist/index.html (single chunk)
21
+ npm run bundle # → dist/bundle.zip
22
+ npm run typecheck
23
+ npx condor-mobile-bridge validate -i dist/bundle.zip --strict # must exit 0
24
+ ```
25
+
26
+ Open `dist/index.html` in a browser to develop against the mock, including the
27
+ dev panel that emits native events by hand. Note that `nativeVersion` is `null`
28
+ there — the same value a real but older app reports, so handle it.
@@ -0,0 +1,42 @@
1
+ # Minimal White Label Home — example
2
+
3
+ A runnable, standalone example of a White Label Home built on
4
+ [`@condorinst/mobile-bridge`](../../README.md). It renders a device list,
5
+ handshakes with the native side, listens for connection/real-time events, and
6
+ sends commands back — all through `BridgeClient`.
7
+
8
+ Use it as a starting skeleton for your own bundle. See the
9
+ [Client Guide](../../docs/client-guide.md) for the full walkthrough.
10
+
11
+ ## Run it
12
+
13
+ ```bash
14
+ npm install
15
+ npm run build # bundles src/main.ts → dist/main.js + dist/index.html
16
+ ```
17
+
18
+ Open `dist/index.html` in any browser. Outside the app, the SDK activates
19
+ **mock mode** automatically: a dev panel appears (bottom-right) where you can
20
+ emit native events (e.g. `userDevice`, `deviceConnection`) by hand or enable
21
+ auto-respond to close the loop.
22
+
23
+ ## Produce the app bundle
24
+
25
+ ```bash
26
+ npm run bundle # condor-mobile-bridge build -i dist/index.html -o dist/bundle.zip
27
+ ```
28
+
29
+ This inlines everything into a single self-contained `index.html` and packs
30
+ `dist/bundle.zip` — the artifact the app consumes. The command prints the
31
+ `bundleSize` and `bundleHash` to register with the backend.
32
+
33
+ ## Layout
34
+
35
+ ```
36
+ minimal-home/
37
+ ├── index.html # markup + styles; references main.js
38
+ ├── src/main.ts # the client logic (BridgeClient)
39
+ ├── build.mjs # esbuild bundle + copy index.html into dist/
40
+ ├── package.json
41
+ └── tsconfig.json
42
+ ```
@@ -0,0 +1,31 @@
1
+ /**
2
+ * build.mjs — bundles the example into a `dist/` ready for the bundle CLI.
3
+ *
4
+ * 1. esbuild bundles `src/main.ts` (and the SDK) into `dist/main.js` (IIFE).
5
+ * 2. copies `index.html` into `dist/` (it references `main.js`).
6
+ *
7
+ * Then `npm run bundle` calls `condor-mobile-bridge build` to inline `main.js`
8
+ * into `index.html` and pack the self-contained `dist/bundle.zip` the app
9
+ * consumes.
10
+ *
11
+ * For browser dev: `npm run build` then open `dist/index.html` (mock mode).
12
+ */
13
+ import * as esbuild from 'esbuild';
14
+ import { copyFileSync, mkdirSync } from 'node:fs';
15
+
16
+ mkdirSync('dist', { recursive: true });
17
+
18
+ await esbuild.build({
19
+ entryPoints: ['src/main.ts'],
20
+ outfile: 'dist/main.js',
21
+ bundle: true,
22
+ format: 'iife',
23
+ target: 'es2018',
24
+ platform: 'browser',
25
+ minify: true,
26
+ sourcemap: false,
27
+ logLevel: 'info',
28
+ });
29
+
30
+ copyFileSync('index.html', 'dist/index.html');
31
+ console.log('Built dist/main.js + dist/index.html — now run `npm run bundle`.');
@@ -0,0 +1,77 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
6
+ <!-- Status bar color (Layer A): the native app reads this from the bundle.
7
+ Keep it in sync with the top of the .hero gradient below. -->
8
+ <meta name="theme-color" content="#0d9488">
9
+ <title>My White Label Home</title>
10
+ <style>
11
+ * { box-sizing: border-box; }
12
+ body {
13
+ margin: 0;
14
+ font-family: -apple-system, system-ui, "Segoe UI", sans-serif;
15
+ background: #0f172a;
16
+ color: #e2e8f0;
17
+ -webkit-font-smoothing: antialiased;
18
+ }
19
+ .hero {
20
+ background: linear-gradient(135deg, #0d9488 0%, #0f766e 100%);
21
+ padding: 24px 20px 28px;
22
+ text-align: center;
23
+ }
24
+ .hero h1 { margin: 4px 0; font-size: 22px; font-weight: 700; color: #fff; }
25
+ .hero .status { margin: 8px 0 0; font-size: 12px; color: #fcd34d; }
26
+ .hero .status.ok { color: #bbf7d0; }
27
+ .toolbar { display: flex; gap: 10px; padding: 16px 20px 4px; }
28
+ .toolbar button {
29
+ flex: 1;
30
+ background: #1e293b;
31
+ color: #e2e8f0;
32
+ border: 1px solid #334155;
33
+ border-radius: 10px;
34
+ padding: 11px;
35
+ font-size: 14px;
36
+ font-weight: 600;
37
+ }
38
+ .toolbar button:active { background: #334155; }
39
+ main { padding: 12px 16px 24px; }
40
+ .empty { text-align: center; color: #94a3b8; padding: 32px 16px; }
41
+ .card {
42
+ background: #1e293b;
43
+ border: 1px solid #334155;
44
+ border-radius: 14px;
45
+ padding: 16px;
46
+ margin-bottom: 12px;
47
+ }
48
+ .card-head {
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: space-between;
52
+ gap: 10px;
53
+ }
54
+ .card-head .name { font-size: 16px; font-weight: 700; color: #f8fafc; }
55
+ .card-head .name small { color: #64748b; font-weight: 500; }
56
+ .badge { font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 999px; }
57
+ .badge.on { background: #064e3b; color: #6ee7b7; }
58
+ .badge.off { background: #3f1d1d; color: #fca5a5; }
59
+ .actions { display: flex; gap: 8px; margin-top: 12px; }
60
+ .actions button {
61
+ flex: 1;
62
+ background: #0d9488;
63
+ color: #fff;
64
+ border: none;
65
+ border-radius: 9px;
66
+ padding: 10px;
67
+ font-size: 13px;
68
+ font-weight: 600;
69
+ }
70
+ .actions button:active { background: #0f766e; }
71
+ </style>
72
+ </head>
73
+ <body>
74
+ <div id="app"></div>
75
+ <script src="main.js"></script>
76
+ </body>
77
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "whitelabel-minimal-home",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "Minimal runnable White Label Home example for @condorinst/mobile-bridge.",
7
+ "scripts": {
8
+ "build": "node build.mjs",
9
+ "bundle": "condor-mobile-bridge build -i dist/index.html -o dist/bundle.zip",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "dependencies": {
16
+ "@condorinst/mobile-bridge": "^0.19.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^22.0.0",
20
+ "esbuild": "^0.25.0",
21
+ "typescript": "^5.6.0"
22
+ }
23
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Minimal White Label Home — runnable example for @condorinst/mobile-bridge.
3
+ *
4
+ * This is the kind of code a White Label client writes: it runs inside the
5
+ * app's sandboxed iframe (origin `null`, no `window.flutter_inappwebview`) and
6
+ * talks to the native side exclusively through `BridgeClient`
7
+ * (postMessage → shell → native bridge).
8
+ *
9
+ * Open `dist/index.html` in a browser and the SDK auto-activates **mock mode**,
10
+ * so you can develop the whole flow without building the app. Use the dev panel
11
+ * (bottom-right) to emit native events by hand or enable auto-respond.
12
+ */
13
+ import {
14
+ BRIDGE_KEYS,
15
+ BridgeClient,
16
+ type UserDevicePayload,
17
+ } from '@condorinst/mobile-bridge';
18
+
19
+ interface DeviceRow {
20
+ serialNo: number;
21
+ name: string;
22
+ connected: boolean;
23
+ realTimeOn: boolean;
24
+ }
25
+
26
+ const state = {
27
+ devices: new Map<number, DeviceRow>(),
28
+ shellVersion: '—',
29
+ connected: false,
30
+ };
31
+
32
+ declare global {
33
+ interface Window {
34
+ __home: {
35
+ refresh: () => void;
36
+ settings: () => void;
37
+ toggle: (serialNo: number) => void;
38
+ };
39
+ }
40
+ }
41
+
42
+ // ── Render ─────────────────────────────────────────────────────────────
43
+
44
+ function esc(value: string): string {
45
+ return value.replace(
46
+ /[&<>"]/g,
47
+ (c) =>
48
+ ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' })[c] as string,
49
+ );
50
+ }
51
+
52
+ function render(): void {
53
+ const el = document.getElementById('app');
54
+ if (!el) return;
55
+
56
+ const rows = [...state.devices.values()];
57
+ const cards = rows.length
58
+ ? rows.map(renderCard).join('')
59
+ : '<p class="empty">No devices for this user yet.</p>';
60
+
61
+ el.innerHTML = `
62
+ <header class="hero">
63
+ <h1>My White Label Home</h1>
64
+ <p class="status ${state.connected ? 'ok' : ''}">${
65
+ state.connected
66
+ ? `bridge connected &bull; shell ${esc(state.shellVersion)}`
67
+ : 'connecting to the native bridge…'
68
+ }</p>
69
+ </header>
70
+ <div class="toolbar">
71
+ <button onclick="__home.refresh()">Refresh devices</button>
72
+ <button onclick="__home.settings()">Settings</button>
73
+ </div>
74
+ <main>${cards}</main>
75
+ `;
76
+ }
77
+
78
+ function renderCard(d: DeviceRow): string {
79
+ return `
80
+ <section class="card">
81
+ <div class="card-head">
82
+ <span class="name">${esc(d.name)} <small>#${d.serialNo}</small></span>
83
+ <span class="badge ${d.connected ? 'on' : 'off'}">${
84
+ d.connected ? '● connected' : '○ disconnected'
85
+ }</span>
86
+ </div>
87
+ <div class="actions">
88
+ <button onclick="__home.toggle(${d.serialNo})">${
89
+ d.realTimeOn ? 'Stop real-time' : 'Start real-time'
90
+ }</button>
91
+ </div>
92
+ </section>
93
+ `;
94
+ }
95
+
96
+ // ── Subscribers (native → iframe) ───────────────────────────────────────
97
+
98
+ BridgeClient.on(BRIDGE_KEYS.userDevice, (payload: UserDevicePayload) => {
99
+ for (const cfg of payload.devicesConfig ?? []) {
100
+ const serialNo = Number((cfg as { serialNo?: number }).serialNo);
101
+ if (Number.isNaN(serialNo)) continue;
102
+ const existing = state.devices.get(serialNo);
103
+ state.devices.set(serialNo, {
104
+ serialNo,
105
+ name: String((cfg as { deviceName?: string }).deviceName ?? 'Device'),
106
+ connected: existing?.connected ?? false,
107
+ realTimeOn: existing?.realTimeOn ?? false,
108
+ });
109
+ }
110
+ render();
111
+ });
112
+
113
+ BridgeClient.on(BRIDGE_KEYS.deviceConnection, (payload) => {
114
+ const d = state.devices.get(payload.serialNo);
115
+ if (d) {
116
+ d.connected = payload.isConnect;
117
+ render();
118
+ }
119
+ });
120
+
121
+ BridgeClient.on(BRIDGE_KEYS.realTime, (payload) => {
122
+ const { serialNo, success, value } = payload;
123
+ const d = state.devices.get(serialNo);
124
+ if (d && success) {
125
+ d.realTimeOn = value;
126
+ render();
127
+ }
128
+ });
129
+
130
+ // ── Actions (iframe → native) ───────────────────────────────────────────
131
+
132
+ window.__home = {
133
+ refresh: () => BridgeClient.send('userDevice', { getFromServer: true }),
134
+ settings: () => BridgeClient.openSettings(),
135
+ toggle: (serialNo) => {
136
+ const d = state.devices.get(serialNo);
137
+ BridgeClient.send('realTime', { serialNo, value: !(d?.realTimeOn ?? false) });
138
+ },
139
+ };
140
+
141
+ // ── Boot ────────────────────────────────────────────────────────────────
142
+
143
+ function boot(): void {
144
+ render();
145
+ BridgeClient.handshake({ clientVersion: '1.0.0' })
146
+ .then((ack) => {
147
+ state.connected = ack.accepted;
148
+ state.shellVersion = ack.shellVersion;
149
+ render();
150
+ })
151
+ .catch((err: unknown) => console.error('[home] handshake failed:', err));
152
+
153
+ // Ask the native side for the current device list.
154
+ BridgeClient.send('userDevice', { getFromServer: false });
155
+ }
156
+
157
+ if (document.readyState === 'loading') {
158
+ document.addEventListener('DOMContentLoaded', boot);
159
+ } else {
160
+ boot();
161
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2018",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": ["ES2018", "DOM", "DOM.Iterable"],
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "types": ["node"]
12
+ },
13
+ "include": ["src", "build.mjs"]
14
+ }
package/llms.txt ADDED
@@ -0,0 +1,44 @@
1
+ # Condor Mobile Bridge
2
+
3
+ > TypeScript SDK for the JS↔Native bridge of the Condor Canvas white-label
4
+ > mobile app. A client builds a self-contained web bundle that renders as the
5
+ > app's Home inside a sandboxed iframe; this SDK is how that bundle talks to
6
+ > the native side — device roster, real-time light-sensor data, sensor log
7
+ > pagination, permissions, system status and language.
8
+
9
+ Every URL below is plain text over HTTPS, pinned to a released version, and
10
+ needs no authentication. Replace `0.19.0` with the version you depend on; the
11
+ contract changes between minors while the package is below 1.0.0.
12
+
13
+ The same files ship inside the npm tarball, so if you have run
14
+ `npm install @condorinst/mobile-bridge` you can read them from
15
+ `node_modules/@condorinst/mobile-bridge/` with no network access at all.
16
+
17
+ ## Start here
18
+
19
+ - [AGENTS.md](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/AGENTS.md): The contract in one screen — hard rules, what is unreachable, and the validation loop. Read before writing code.
20
+ - [Agent guide](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/docs/agent-guide.md): Step-by-step construction of a client Home, with the checks to run at each stage.
21
+
22
+ ## Reference
23
+
24
+ - [Bridge contract](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/docs/bridge-contract.md): Every observable key, every command, the three error shapes, and the capability matrix by native version.
25
+ - [Machine-readable contract](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/contract/bridge-contract.json): The same contract as JSON. Also importable as `@condorinst/mobile-bridge/contract`.
26
+ - [Limits](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/docs/limits.md): Every hard limit — payload size, rate limits, bundle size, timeouts — with what happens when you cross it.
27
+ - [Troubleshooting](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/docs/troubleshooting.md): Why a command did nothing. Silence is the platform's dominant failure mode.
28
+ - [TypeScript declarations](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/dist/index.d.ts): Authoritative payload shapes, with documentation on every member.
29
+
30
+ ## Guides
31
+
32
+ - [Client guide](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/docs/client-guide.md): Narrative walkthrough for a developer building the Home by hand.
33
+ - [Pilot onboarding](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/docs/pilot-onboarding.md): Checklist from zero to a bundle delivered to Condor.
34
+ - [README](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/README.md): Package overview, install, quickstart.
35
+ - [CHANGELOG](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/CHANGELOG.md): When each capability was introduced. The most precise record of what a given native version supports.
36
+
37
+ ## Examples
38
+
39
+ - [Minimal home](https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.19.0/examples/minimal-home/src/main.ts): A complete, working client in ~160 lines. Copy this as your starting point.
40
+
41
+ ## Optional
42
+
43
+ - [Public repository](https://bitbucket.org/condorinstprogdev/mobile-bridge): Full source, tests, and the larger `client_demo` example. Note that the repository landing page is a JavaScript application and returns no readable text to a fetcher — use the raw file URLs, or prefer the jsDelivr links above.
44
+ - [Package on npm](https://www.npmjs.com/package/@condorinst/mobile-bridge)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@condorinst/mobile-bridge",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "TypeScript SDK for the JS↔Native bridge of the Condor white-label mobile app.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "private": false,
@@ -16,13 +16,26 @@
16
16
  "types": "./dist/index.d.ts",
17
17
  "import": "./dist/index.js",
18
18
  "require": "./dist/index.cjs"
19
- }
19
+ },
20
+ "./contract": "./contract/bridge-contract.json",
21
+ "./package.json": "./package.json"
20
22
  },
21
23
  "bin": {
22
24
  "condor-mobile-bridge": "dist/cli.js"
23
25
  },
24
26
  "files": [
25
27
  "dist",
28
+ "docs",
29
+ "contract",
30
+ "examples/minimal-home/src",
31
+ "examples/minimal-home/index.html",
32
+ "examples/minimal-home/build.mjs",
33
+ "examples/minimal-home/package.json",
34
+ "examples/minimal-home/tsconfig.json",
35
+ "examples/minimal-home/README.md",
36
+ "examples/minimal-home/AGENTS.md",
37
+ "AGENTS.md",
38
+ "llms.txt",
26
39
  "README.md",
27
40
  "CHANGELOG.md",
28
41
  "LICENSE"