@condorinst/mobile-bridge 0.20.0 → 0.22.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.
- package/AGENTS.md +109 -42
- package/CHANGELOG.md +99 -0
- package/README.md +80 -14
- package/contract/bridge-contract.json +59 -14
- package/dist/cli.cjs +933 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +932 -3
- package/dist/cli.js.map +1 -1
- package/dist/dev-harness.global.js +2110 -0
- package/dist/dev.cjs +1662 -0
- package/dist/dev.cjs.map +1 -0
- package/dist/dev.d.cts +683 -0
- package/dist/dev.d.ts +683 -0
- package/dist/dev.js +1611 -0
- package/dist/dev.js.map +1 -0
- package/dist/index.cjs +96 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +236 -14
- package/dist/index.d.ts +236 -14
- package/dist/index.js +94 -3
- package/dist/index.js.map +1 -1
- package/docs/agent-guide.md +75 -24
- package/docs/bridge-contract.md +121 -20
- package/docs/client-guide.md +42 -29
- package/docs/device-testing.md +114 -0
- package/docs/pilot-onboarding.md +31 -13
- package/docs/troubleshooting.md +79 -18
- package/examples/minimal-home/AGENTS.md +9 -6
- package/examples/minimal-home/README.md +10 -5
- package/examples/minimal-home/build.mjs +2 -1
- package/examples/minimal-home/index.html +2 -0
- package/examples/minimal-home/package.json +3 -2
- package/examples/minimal-home/src/main.ts +151 -73
- package/llms-full.txt +2086 -0
- package/llms.txt +15 -13
- package/package.json +9 -3
package/AGENTS.md
CHANGED
|
@@ -6,19 +6,18 @@ owns login, settings and the Bluetooth connection to the devices; you own what
|
|
|
6
6
|
the person sees on the Home.
|
|
7
7
|
|
|
8
8
|
This file is the contract. Read it before writing code. Everything here is
|
|
9
|
-
enforced
|
|
9
|
+
enforced, either by a gate in the app or by the browser runtime on a real
|
|
10
10
|
device, where mistakes are expensive to diagnose.
|
|
11
11
|
|
|
12
12
|
## The one thing that makes this different
|
|
13
13
|
|
|
14
|
-
**Failures are silent.**
|
|
15
|
-
|
|
16
|
-
exception, no rejected promise, no console error.
|
|
17
|
-
moves on.
|
|
14
|
+
**Failures are silent.** A malformed command, an oversized payload, a command
|
|
15
|
+
sent before the app is ready, a request for an account with no device: nothing
|
|
16
|
+
happens. No exception, no rejected promise, no console error.
|
|
18
17
|
|
|
19
|
-
So
|
|
20
|
-
Drive
|
|
21
|
-
|
|
18
|
+
So never write code whose correctness you infer from the absence of an error.
|
|
19
|
+
Drive the UI from the events that arrive, give every wait a deadline, and
|
|
20
|
+
always have a screen for "it did not come".
|
|
22
21
|
|
|
23
22
|
## Hard rules
|
|
24
23
|
|
|
@@ -35,12 +34,14 @@ wait for.
|
|
|
35
34
|
whole bundle**.
|
|
36
35
|
5. **Build single-chunk.** No code splitting, no ES module imports between
|
|
37
36
|
chunks — `srcdoc` cannot resolve them. Vite: `vite-plugin-singlefile`.
|
|
38
|
-
6. **The `.zip` must be at most 2 MB.**
|
|
39
|
-
|
|
37
|
+
6. **The `.zip` must be at most 2 MB.** `data:` URIs inflate assets by about a
|
|
38
|
+
third.
|
|
40
39
|
7. **Use `BridgeClient`, never raw `postMessage`.** Hand-rolled messages fail
|
|
41
40
|
schema validation and are dropped without a trace.
|
|
42
41
|
8. **Never assume a capability.** Read `nativeVersion` from the handshake and
|
|
43
42
|
branch on it. See the capability matrix in `docs/bridge-contract.md`.
|
|
43
|
+
9. **Never ask once and wait.** Use `BridgeClient.requestUserDevices()` for the
|
|
44
|
+
device list; a single `send('userDevice')` can be lost (see below).
|
|
44
45
|
|
|
45
46
|
## What you cannot reach
|
|
46
47
|
|
|
@@ -49,38 +50,101 @@ wait for.
|
|
|
49
50
|
camera, microphone and geolocation (denied unconditionally) · the `dfu` key and
|
|
50
51
|
command (denied by policy).
|
|
51
52
|
|
|
52
|
-
## The
|
|
53
|
+
## The golden path
|
|
53
54
|
|
|
54
55
|
```bash
|
|
55
|
-
npm install @condorinst/mobile-bridge
|
|
56
|
+
npm install @condorinst/mobile-bridge@^0.22.0
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
```ts
|
|
59
|
-
import {
|
|
60
|
+
import {
|
|
61
|
+
BridgeClient, BRIDGE_KEYS, versionAtLeast, type UserDevicePayload,
|
|
62
|
+
} from '@condorinst/mobile-bridge';
|
|
60
63
|
|
|
61
|
-
// 1.
|
|
64
|
+
// 1. Subscribe before you ask for anything. Roster changes arrive here too.
|
|
65
|
+
BridgeClient.on(BRIDGE_KEYS.userDevice, (devices: UserDevicePayload) => renderDevices(devices));
|
|
66
|
+
BridgeClient.on(BRIDGE_KEYS.systemStatus, (s) => renderEnvironment(s)); // bluetooth, internet, permissions
|
|
67
|
+
|
|
68
|
+
// 2. Handshake. nativeVersion is the ONLY capability signal; ack.accepted is
|
|
69
|
+
// always true and carries no information — never branch on it.
|
|
62
70
|
const ack = await BridgeClient.handshake({ clientVersion: '1.0.0' });
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
const canAddressById = versionAtLeast(ack.nativeVersion, '0.17.0'); // null → false
|
|
72
|
+
|
|
73
|
+
// 3. Ask for the devices with the helper. It resends until they arrive.
|
|
74
|
+
const result = await BridgeClient.requestUserDevices();
|
|
75
|
+
if (result.status === 'timeout') {
|
|
76
|
+
// No reply in 30 s. hasDevices === false suggests an account with no device;
|
|
77
|
+
// otherwise the app could not load the list. Either way: show an empty state
|
|
78
|
+
// with a "Try again" button that calls requestUserDevices({ getFromServer: true }).
|
|
79
|
+
renderEmpty(result.hasDevices === false);
|
|
80
|
+
} else {
|
|
81
|
+
// 4. Subscribe to the connection state of each device (`add` pairs nothing).
|
|
82
|
+
for (const d of result.devices.devicesConfig) {
|
|
83
|
+
BridgeClient.send('deviceConnection', {
|
|
84
|
+
type: 'add', serialNo: d.serialNo, ...(canAddressById ? { deviceId: d.deviceId } : {}),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 5. History: one page per call; it resolves { success: false, error } on
|
|
90
|
+
// failure and only rejects on timeout.
|
|
91
|
+
const page = await BridgeClient.fetchSensorLog({ requestId: crypto.randomUUID(), serialNo });
|
|
92
|
+
```
|
|
65
93
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
94
|
+
**Why the helper.** Device data comes from a background service that the app
|
|
95
|
+
starts *after* your bundle is already running. Anything you send to it before it
|
|
96
|
+
is up is lost without a reply — while the handshake, `systemStatus` and
|
|
97
|
+
`language` already work, so the bridge *looks* ready. And an account with no
|
|
98
|
+
linked device gets no `userDevice` reply at all.
|
|
99
|
+
|
|
100
|
+
**Identify devices by `deviceId`.** `serialNo` can repeat: an ActLumus and an
|
|
101
|
+
ActLumus Plus may share one. Join `devicesConfig`, `devicesTelemetry` and
|
|
102
|
+
`devicesUserConfig` on `deviceId`; send `serialNo` in every command, plus
|
|
103
|
+
`deviceId` when `nativeVersion` ≥ 0.17.0 (`versionAtLeast` does the comparison).
|
|
104
|
+
The typed shapes (`DeviceConfigItem`, `DeviceTelemetryItem`, …) show every field
|
|
105
|
+
and its unit — `devicesConfig` has no name or battery; those are in the
|
|
106
|
+
telemetry.
|
|
107
|
+
|
|
108
|
+
**Timestamps are device-local.** `log_time` is `'YYYY-MM-DD HH:MM:SS'` in the
|
|
109
|
+
device's wall-clock time. Parse and build it with local `Date` getters, never
|
|
110
|
+
with `toISOString()` or a `'Z'` suffix.
|
|
111
|
+
|
|
112
|
+
Three commands open a confirmation dialog owned by Condor: `syncTime`,
|
|
113
|
+
`deleteMemory` and `logout`. If the person declines, you get nothing — the ack
|
|
114
|
+
never arrives. Do not block your UI on them.
|
|
69
115
|
|
|
70
|
-
|
|
71
|
-
BridgeClient.send('userDevice', { getFromServer: false });
|
|
116
|
+
## Develop in the harness, not in a bare browser tab
|
|
72
117
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// Resolves with { success: false, error } on failure — it only REJECTS on timeout.
|
|
118
|
+
```bash
|
|
119
|
+
npx condor-mobile-bridge dev -i dist/index.html --scenario cold-start
|
|
76
120
|
```
|
|
77
121
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
122
|
+
It serves your bundle at `http://localhost:5173` inside the **same sandbox,
|
|
123
|
+
shell and policy as the app**, with a simulated native side. Opening
|
|
124
|
+
`dist/index.html` directly does not work: `BridgeClient.handshake()` rejects at
|
|
125
|
+
once outside the shell. Rebuild and reload; the harness re-reads the file.
|
|
81
126
|
|
|
82
|
-
|
|
83
|
-
|
|
127
|
+
| Scenario | What it reproduces |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `default` | two devices, realistic latency |
|
|
130
|
+
| `cold-start` | the background service takes 4 s; earlier `ping`/`userDevice` are lost |
|
|
131
|
+
| `no-devices` | `userDevice` never answers; `hasDevices: false` |
|
|
132
|
+
| `shared-serial` | an ActLumus and a Plus share serial 7047 |
|
|
133
|
+
| `offline` | no internet; `sensorLog` fails |
|
|
134
|
+
|
|
135
|
+
The harness console (`[condor dev]`) says what was sent (with its arguments),
|
|
136
|
+
answered and dropped. The **◀ OS back** button at the top presses the OS back
|
|
137
|
+
button. `--native-version null` (or `0.15.0`, …) makes the handshake report that
|
|
138
|
+
version, to test your capability branches.
|
|
139
|
+
|
|
140
|
+
## Publishing — ask the human, never run it yourself
|
|
141
|
+
|
|
142
|
+
`npx condor-mobile-bridge canvas publish` puts the bundle live at once for every
|
|
143
|
+
user of a company, and asks for the person's password. **Do not run it.** When
|
|
144
|
+
the bundle is ready, tell the human the exact command — for example
|
|
145
|
+
`npx condor-mobile-bridge canvas publish dist/index.html --version 1.0.1` — and
|
|
146
|
+
recommend publishing to their **test company** first. See
|
|
147
|
+
`docs/device-testing.md`.
|
|
84
148
|
|
|
85
149
|
## The validation loop — run this before you call the work done
|
|
86
150
|
|
|
@@ -91,22 +155,23 @@ npx condor-mobile-bridge validate -i dist/bundle.zip --strict
|
|
|
91
155
|
|
|
92
156
|
`validate` applies the same gates the app applies on download, plus a lint for
|
|
93
157
|
the APIs the sandbox blocks at runtime. Under `--strict` it exits non-zero on
|
|
94
|
-
warnings too. **Treat a non-zero exit as a build failure.**
|
|
95
|
-
|
|
96
|
-
It is a textual check, so it cannot see through minification: a clean run is
|
|
97
|
-
necessary, not sufficient. It will not catch a capability you assumed without
|
|
98
|
-
checking `nativeVersion`.
|
|
158
|
+
warnings too. **Treat a non-zero exit as a build failure.** It is a textual
|
|
159
|
+
check, so a clean run is necessary, not sufficient.
|
|
99
160
|
|
|
100
161
|
## Definition of done
|
|
101
162
|
|
|
102
163
|
- [ ] `validate --strict` exits 0.
|
|
103
|
-
- [ ] The
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
- [ ]
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
164
|
+
- [ ] The bundle works in **every** harness scenario: it leaves the loading
|
|
165
|
+
screen in `cold-start`, shows an empty state in `no-devices`, keeps two
|
|
166
|
+
cards apart in `shared-serial`, and explains the failure in `offline`.
|
|
167
|
+
- [ ] The UI renders before any event arrives — first paint must not depend on
|
|
168
|
+
the bridge.
|
|
169
|
+
- [ ] Every capability beyond the baseline is guarded by a `nativeVersion` check
|
|
170
|
+
(`versionAtLeast`), and the page still renders with
|
|
171
|
+
`--native-version null`.
|
|
172
|
+
- [ ] Nothing waits forever on a reply that may never come.
|
|
173
|
+
|
|
174
|
+
To put the bundle on a phone, read `docs/device-testing.md`.
|
|
110
175
|
|
|
111
176
|
## Where to read more
|
|
112
177
|
|
|
@@ -115,7 +180,9 @@ checking `nativeVersion`.
|
|
|
115
180
|
| Every key, command, error shape and capability | `docs/bridge-contract.md` |
|
|
116
181
|
| The same, as JSON you can parse | `contract/bridge-contract.json` |
|
|
117
182
|
| Step-by-step build of a Home | `docs/agent-guide.md` |
|
|
183
|
+
| "My command did nothing" / "the device list never came" | `docs/troubleshooting.md` |
|
|
184
|
+
| Getting the bundle onto a device, and seeing its errors | `docs/device-testing.md` |
|
|
118
185
|
| Every hard limit, with the code that enforces it | `docs/limits.md` |
|
|
119
|
-
| "My command did nothing" | `docs/troubleshooting.md` |
|
|
120
186
|
| Payload shapes, authoritative | `dist/index.d.ts` (your editor shows them) |
|
|
121
187
|
| A working client you can copy | `examples/minimal-home/` |
|
|
188
|
+
| All of the above in one file | `llms-full.txt` |
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,105 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.22.0] — 2026-10-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`condor-mobile-bridge canvas publish | list | revoke`** — publish the bundle
|
|
15
|
+
of your company in the Condor Canvas app from the command line, through the
|
|
16
|
+
same endpoints and login as the Condor web admin. The bundle is validated
|
|
17
|
+
locally first; the command shows company, version, size and hash and asks
|
|
18
|
+
before sending; the password is read only from a prompt without echo (there
|
|
19
|
+
is no flag or variable for it) and never stored; the two-factor code is asked
|
|
20
|
+
when the company uses it. Production API by default; `--env dev` for Condor
|
|
21
|
+
staff, `--base-url` to override. Company and user ids may come from
|
|
22
|
+
`CONDOR_CANVAS_COMPANY` / `CONDOR_CANVAS_USER`.
|
|
23
|
+
- `docs/device-testing.md`: publishing from the command line, and the test
|
|
24
|
+
company workflow.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- `docs/device-testing.md` no longer sends clients to a development
|
|
29
|
+
environment: clients work in production, on a test company.
|
|
30
|
+
- `AGENTS.md`: an agent must never run `canvas publish`; it hands the command to
|
|
31
|
+
the human.
|
|
32
|
+
|
|
33
|
+
## [0.21.0] — 2026-10-20
|
|
34
|
+
|
|
35
|
+
A client built from the 0.20.0 docs worked in the browser and stalled on the
|
|
36
|
+
first real device: the device list never arrived. This release makes the
|
|
37
|
+
browser behave like the device, gives the boot sequence a helper, and types the
|
|
38
|
+
device payloads.
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- **`condor-mobile-bridge dev`** — serves a bundle (`index.html`, `dist/` or
|
|
43
|
+
`.zip`) in a browser harness that reproduces the app: the bundle runs in
|
|
44
|
+
`<iframe sandbox="allow-scripts">` behind the real `BridgeShell`, with the
|
|
45
|
+
app's policy and CSP and a simulated native side. Scenarios: `default`,
|
|
46
|
+
`cold-start` (requests lost while the app's background service starts),
|
|
47
|
+
`no-devices` (`userDevice` never answers), `shared-serial` and `offline`.
|
|
48
|
+
The page logs every command, reply and dropped request.
|
|
49
|
+
- **`@condorinst/mobile-bridge/dev`** — the same harness as a library
|
|
50
|
+
(`createDevHarness`, `createSimulatedNative`, scenarios and fixtures) for a
|
|
51
|
+
host page on your own dev server.
|
|
52
|
+
- **`BridgeClient.requestUserDevices({ getFromServer })`** — requests the device
|
|
53
|
+
list and resends it with backoff (1.5 s, 3 s, 6 s, then every 10 s, and on
|
|
54
|
+
every `systemStatus`) until it arrives. Never rejects: resolves
|
|
55
|
+
`{ status: 'timeout', hasDevices }` after 30 s, with `hasDevices` from the
|
|
56
|
+
latest `systemStatus` received — also one that arrived before the call.
|
|
57
|
+
- **`BridgeClient.waitForService()`** — resends `ping` until the app's
|
|
58
|
+
background service answers.
|
|
59
|
+
- **Typed device payloads**: `DeviceConfigItem`, `DeviceTelemetryItem`,
|
|
60
|
+
`DeviceUserConfigItem`, `DeviceSyncErrorItem` and `SensorLogItem`, with units
|
|
61
|
+
(battery and memory in %, `bleConnectionPeriod` in minutes, epoch ms) and the
|
|
62
|
+
join rule across the three arrays of `userDevice` (`deviceId`).
|
|
63
|
+
- **`docs/device-testing.md`** — how a bundle reaches a phone, development vs
|
|
64
|
+
production, inspecting the WebView, and where errors go.
|
|
65
|
+
- **`llms-full.txt`** — `AGENTS.md`, the docs and the reference example in one
|
|
66
|
+
file, generated on build.
|
|
67
|
+
- Contract: `payloadItems`, `devTools`, `sensorLog` paging rules, and two
|
|
68
|
+
silent-drop paths for `userDevice` (request before the background service is
|
|
69
|
+
up; a list that failed to load, including an account with no device).
|
|
70
|
+
- `validate` fails a bundle that includes the dev harness
|
|
71
|
+
(`dev-harness-bundled`).
|
|
72
|
+
- `dev --native-version <v|null>` sets the `nativeVersion` the harness
|
|
73
|
+
reports; the harness page has an **◀ OS back** button (emits
|
|
74
|
+
`navigationBack` only after the bundle reported `canGoBack: true`), and its
|
|
75
|
+
log shows the arguments of every command.
|
|
76
|
+
- `versionAtLeast(version, min)` — numeric capability check; `null` → `false`.
|
|
77
|
+
- `SensorLogItem` declares the ActLumus Plus channels `light_sensor_x/_y/_z/_fd`
|
|
78
|
+
(`null` on gen1).
|
|
79
|
+
|
|
80
|
+
### Changed
|
|
81
|
+
|
|
82
|
+
- **`BridgeClient.handshake()` rejects at once outside the shell** (a page
|
|
83
|
+
opened directly in a browser), with a message pointing to the harness. It
|
|
84
|
+
used to wait 5 s and time out.
|
|
85
|
+
- `UserDevicePayload` arrays and `SensorLogPayload.logs` are typed instead of
|
|
86
|
+
`Record<string, unknown>`. Code that cast items to `Record<string, unknown>`
|
|
87
|
+
still compiles.
|
|
88
|
+
- `AGENTS.md` is rewritten around the boot sequence that survives a cold start
|
|
89
|
+
and an account with no device; the docs no longer describe a browser mock for
|
|
90
|
+
`BridgeClient`, which never existed.
|
|
91
|
+
- `examples/minimal-home` uses the helpers and the harness, and reads the device
|
|
92
|
+
name from the telemetry (it read a field `devicesConfig` does not have).
|
|
93
|
+
`examples/client_demo` drops its private mock and reads `log_time` as
|
|
94
|
+
device-local time.
|
|
95
|
+
|
|
96
|
+
### Documented
|
|
97
|
+
|
|
98
|
+
- `userDevice` is not guaranteed to answer; `systemStatus.hasDevices` is a hint
|
|
99
|
+
(`false` also when the lookup failed); what `getFromServer` does.
|
|
100
|
+
- `deviceConnection` `type: 'add'` subscribes to a device's connection state and
|
|
101
|
+
pairs nothing.
|
|
102
|
+
- `sensorLog` error codes are matched by prefix; transport failures carry free
|
|
103
|
+
text. A full 7-day sweep recipe (keep the range on every page).
|
|
104
|
+
- The back button example keeps navigation in memory instead of `history`.
|
|
105
|
+
- `sensorLog`: without a range the first page is the most recent; with
|
|
106
|
+
`startTime` the oldest of the range. The log period is programmable per
|
|
107
|
+
device (default 60 s).
|
|
108
|
+
|
|
10
109
|
## [0.20.0] — 2026-10-13
|
|
11
110
|
|
|
12
111
|
### Added
|
package/README.md
CHANGED
|
@@ -20,7 +20,8 @@ Over the network, every document is plain text at a pinned, unauthenticated
|
|
|
20
20
|
URL. `llms.txt` is the index:
|
|
21
21
|
|
|
22
22
|
```
|
|
23
|
-
https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.
|
|
23
|
+
https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.22.0/llms.txt
|
|
24
|
+
https://cdn.jsdelivr.net/npm/@condorinst/mobile-bridge@0.22.0/llms-full.txt # every doc in one file
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
The machine-readable contract — keys, commands, limits, capability matrix — is
|
|
@@ -43,7 +44,15 @@ const ack = await BridgeClient.handshake({ clientVersion: '1.0.0' });
|
|
|
43
44
|
// ack.nativeVersion is your capability signal. ack.accepted is always true.
|
|
44
45
|
|
|
45
46
|
BridgeClient.on(BRIDGE_KEYS.userDevice, (devices) => render(devices));
|
|
46
|
-
BridgeClient.
|
|
47
|
+
const result = await BridgeClient.requestUserDevices(); // resends until the list arrives
|
|
48
|
+
if (result.status === 'timeout') renderEmptyState();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Develop it in the browser with the harness, which runs the bundle under the
|
|
52
|
+
same sandbox and shell as the app:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx condor-mobile-bridge dev -i dist/index.html --scenario cold-start
|
|
47
56
|
```
|
|
48
57
|
|
|
49
58
|
Then read the [Client Guide](docs/client-guide.md) — _"Build your White Label
|
|
@@ -63,11 +72,15 @@ The published package ships a prebuilt `dist/` (ESM + CJS + type
|
|
|
63
72
|
declarations) — **no build toolchain is required on your side**.
|
|
64
73
|
|
|
65
74
|
> **Versioning during the pilot.** The package is on `0.x` (beta). Pin
|
|
66
|
-
> `"@condorinst/mobile-bridge": "^0.
|
|
67
|
-
> caret range auto-updates **patches only** (`0.
|
|
75
|
+
> `"@condorinst/mobile-bridge": "^0.22.0"`. Under semver, while on `0.x` a
|
|
76
|
+
> caret range auto-updates **patches only** (`0.22.x`); moving to `0.23.0` is
|
|
68
77
|
> an intentional opt-in. `1.0.0` is reserved for production.
|
|
69
78
|
|
|
70
|
-
## Quickstart
|
|
79
|
+
## Quickstart (legacy, shell-side `Bridge`)
|
|
80
|
+
|
|
81
|
+
> **Not for client bundles.** This is the API the Condor shell uses to talk to
|
|
82
|
+
> the native layer. A client bundle cannot reach it from the sandbox — use
|
|
83
|
+
> `BridgeClient` (above, and in the next section).
|
|
71
84
|
|
|
72
85
|
```typescript
|
|
73
86
|
import { Bridge, BRIDGE_KEYS } from '@condorinst/mobile-bridge';
|
|
@@ -82,8 +95,8 @@ const unsubscribe = Bridge.on(BRIDGE_KEYS.deviceConnection, (payload) => {
|
|
|
82
95
|
console.log('Device:', payload);
|
|
83
96
|
});
|
|
84
97
|
|
|
85
|
-
// Command: enable real-time
|
|
86
|
-
Bridge.send('realTime', {
|
|
98
|
+
// Command: enable real-time for one device
|
|
99
|
+
Bridge.send('realTime', { serialNo: 1234, value: true });
|
|
87
100
|
|
|
88
101
|
// Native hook: open the app's native Settings screen
|
|
89
102
|
Bridge.openSettings();
|
|
@@ -134,8 +147,8 @@ skip it and the app just stays on Home when back is pressed. See
|
|
|
134
147
|
dev/mock APIs live only on the shell.
|
|
135
148
|
- `handshake` resolves with `{ shellVersion, nativeVersion, accepted }`. Since
|
|
136
149
|
0.17.0 the shell relays `nativeVersion` into the iframe — it is the only
|
|
137
|
-
capability signal you get, and it is `null`
|
|
138
|
-
older than 0.6.0. `accepted` is always `true`; never branch on it.
|
|
150
|
+
capability signal you get, and it is `null` when the shell could not reach
|
|
151
|
+
the native side and against shells older than 0.6.0. `accepted` is always `true`; never branch on it.
|
|
139
152
|
- Commands not allowed by the `ShellPolicy` are silently dropped (with a
|
|
140
153
|
`console.warn`). The external client has no visibility into which commands
|
|
141
154
|
do or do not exist.
|
|
@@ -198,6 +211,50 @@ if (!verify.ok) {
|
|
|
198
211
|
| **3** | (Native side) | Shell nonce, stamped on every outgoing message via `Bridge.setOutgoingStamp`, validated natively. **This is the guarantee.** |
|
|
199
212
|
| **4** | (Fallback if 1+2 fail) | Dual `InAppWebView` — shell and client in separate WebViews. |
|
|
200
213
|
|
|
214
|
+
## Dev harness — `condor-mobile-bridge dev`
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
npx condor-mobile-bridge dev -i dist/index.html [--scenario cold-start] [--port 5173]
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Serves the bundle (an `index.html`, a `dist/` folder or a `.zip`) at
|
|
221
|
+
`http://localhost:5173` inside a sandboxed iframe, behind the real
|
|
222
|
+
`BridgeShell` with the app's policy and CSP, and a simulated native side. The
|
|
223
|
+
input is re-read on every reload.
|
|
224
|
+
|
|
225
|
+
Scenarios reproduce device behaviour a naive mock hides: `default`,
|
|
226
|
+
`cold-start` (requests lost while the app's background service starts),
|
|
227
|
+
`no-devices` (`userDevice` never answers), `shared-serial` (two devices, one
|
|
228
|
+
`serialNo`) and `offline`. The page's console (`[condor dev]`) logs what was
|
|
229
|
+
sent, answered and dropped.
|
|
230
|
+
|
|
231
|
+
With your own dev server, mount the same harness from the **host** page:
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
import { createDevHarness } from '@condorinst/mobile-bridge/dev';
|
|
235
|
+
const html = await (await fetch('/bundle/index.html')).text();
|
|
236
|
+
await createDevHarness({ html, scenario: 'no-devices', mount: document.getElementById('device')! });
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Never import `@condorinst/mobile-bridge/dev` into the bundle itself: `validate`
|
|
240
|
+
fails it with `dev-harness-bundled`.
|
|
241
|
+
|
|
242
|
+
## Publish CLI — `condor-mobile-bridge canvas`
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
npx condor-mobile-bridge canvas publish dist/index.html --version 1.4.0 # validate, confirm, publish
|
|
246
|
+
npx condor-mobile-bridge canvas list
|
|
247
|
+
npx condor-mobile-bridge canvas revoke <bundleId> --fallback-version 1.3.0
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Publishes the bundle of your company in the Condor Canvas app, through the same
|
|
251
|
+
endpoints and login as the Condor web admin: company id, user id, password
|
|
252
|
+
(prompted without echo, never stored) and the two-factor code when your company
|
|
253
|
+
uses one. It needs a **manager** user and an interactive terminal. A publish goes
|
|
254
|
+
**live at once** for every user of the company — use a test company first. The
|
|
255
|
+
API is production by default (`--env dev` is for Condor staff; `--base-url`
|
|
256
|
+
overrides). Details: `docs/device-testing.md`.
|
|
257
|
+
|
|
201
258
|
## Build CLI — `condor-mobile-bridge build`
|
|
202
259
|
|
|
203
260
|
Generates the **self-contained `.zip` bundle** that the white-label app
|
|
@@ -280,10 +337,10 @@ The client passes `Record<string, unknown>`.
|
|
|
280
337
|
|
|
281
338
|
## Versioning policy (semver)
|
|
282
339
|
|
|
283
|
-
- **PATCH** (`0.
|
|
340
|
+
- **PATCH** (`0.22.x`): bugfix with no API change.
|
|
284
341
|
- **MINOR** (`0.x.0`): new observable key, new command, backward-compatible API.
|
|
285
342
|
> While on `0.x`, a minor bump may carry breaking changes (semver treats
|
|
286
|
-
> pre-1.0 minors as breaking). Pin `^0.
|
|
343
|
+
> pre-1.0 minors as breaking). Pin `^0.22.0` to stay on safe patches.
|
|
287
344
|
- **MAJOR** (`x.0.0`): incompatible schema change (key rename, payload shape
|
|
288
345
|
change, public API change). `1.0.0` marks the production-stable contract.
|
|
289
346
|
|
|
@@ -301,9 +358,15 @@ The package detects automatically:
|
|
|
301
358
|
JSON string. Takes precedence when present.
|
|
302
359
|
- **Android / iOS (legacy)** — `window.flutter_inappwebview` (via the `flutter_inappwebview`
|
|
303
360
|
package). Still supported for the transition; will be removed in a future minor.
|
|
304
|
-
- **Browser outside the app** (dev/test): **mock mode** activates automatically
|
|
361
|
+
- **Browser outside the app** (dev/test): **mock mode** activates automatically
|
|
362
|
+
for `Bridge`. A client bundle (`BridgeClient`) runs in the browser only
|
|
363
|
+
through the `dev` harness.
|
|
305
364
|
|
|
306
|
-
## Mock mode
|
|
365
|
+
## Mock mode (shell-side `Bridge`)
|
|
366
|
+
|
|
367
|
+
> For code that uses the legacy `Bridge` directly — the shell, tests,
|
|
368
|
+
> Storybook. A client bundle uses `BridgeClient`, which has no mock of its own:
|
|
369
|
+
> develop it with `condor-mobile-bridge dev` (above).
|
|
307
370
|
|
|
308
371
|
When the package runs outside the Flutter WebView (regular browser, Vite dev
|
|
309
372
|
server, Storybook), the adapter activates mock mode automatically. You can
|
|
@@ -361,7 +424,10 @@ console.log(state.subscriberKeys);
|
|
|
361
424
|
// → ['deviceConnection']
|
|
362
425
|
```
|
|
363
426
|
|
|
364
|
-
## Dev panel
|
|
427
|
+
## Dev panel (shell-side `Bridge`)
|
|
428
|
+
|
|
429
|
+
> The `dev` harness installs this panel for you. Call `installDevPanel` yourself
|
|
430
|
+
> only when hosting `Bridge` directly.
|
|
365
431
|
|
|
366
432
|
An opt-in HTML panel to inspect the bridge in the browser during development.
|
|
367
433
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"contractVersion": "0.
|
|
3
|
+
"contractVersion": "0.22.0",
|
|
4
4
|
"description": "Machine-readable contract of the Condor Canvas JS<->Native bridge. It states what the TypeScript declarations in dist/index.d.ts cannot express: which keys and commands a sandboxed client can actually reach, which ones need human confirmation, where messages are dropped in silence, the hard limits, and which capability each native version carries. The TypeScript types remain canonical for payload shapes.",
|
|
5
5
|
"audience": "Developers and AI agents building the client bundle that renders inside the Canvas Home.",
|
|
6
6
|
|
|
7
7
|
"versions": {
|
|
8
8
|
"note": "Four independent numbers coexist. Only nativeVersion tells you what the device can do.",
|
|
9
|
-
"package": "0.
|
|
9
|
+
"package": "0.22.0",
|
|
10
10
|
"shellVersion": "0.6.0",
|
|
11
11
|
"clientProtocolVersion": "0.5.0",
|
|
12
12
|
"nativeVersion": "0.17.0",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"how": "await BridgeClient.handshake({ clientVersion }) resolves to { shellVersion, nativeVersion, accepted }. Compare ack.nativeVersion against the minNativeVersion in capabilities[].",
|
|
15
15
|
"acceptedIsAlwaysTrue": true,
|
|
16
16
|
"acceptedWarning": "The native side never rejects a handshake, so `accepted` carries no information. Never branch on it.",
|
|
17
|
-
"nativeVersionMayBeNull": "null
|
|
17
|
+
"nativeVersionMayBeNull": "null when the shell could not reach the native side, and in bundles compiled against SDK <= 0.16.x, where the shell did not relay it. Treat null as 'assume nothing beyond the 0.13.0 baseline'. The dev harness reports 0.17.0 unless a scenario sets it to null."
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
"keys": {
|
|
81
81
|
"note": "13 keys exist; 12 are reachable by the sandboxed client. Subscribe with BridgeClient.on(key, cb). Payload shapes are canonical in dist/index.d.ts under the listed type.",
|
|
82
82
|
"entries": {
|
|
83
|
-
"pingPong": { "reachableByClient": true, "payloadType": "PingPongPayload", "emittedWhen": "reply to the `ping` command" },
|
|
84
|
-
"userDevice": { "reachableByClient": true, "payloadType": "UserDevicePayload", "emittedWhen": "reply to the `userDevice` command, and on device roster changes" },
|
|
83
|
+
"pingPong": { "reachableByClient": true, "payloadType": "PingPongPayload", "emittedWhen": "reply to the `ping` command, once the app's background service is up" },
|
|
84
|
+
"userDevice": { "reachableByClient": true, "payloadType": "UserDevicePayload", "emittedWhen": "reply to the `userDevice` command when the device list loads, and on device roster changes. Not guaranteed: see silentDropPaths and prefer BridgeClient.requestUserDevices()." },
|
|
85
85
|
"deviceLightSensors": { "reachableByClient": true, "payloadType": "DeviceLightSensorsPayload", "emittedWhen": "real-time stream, only while realTime is on for that device", "discriminator": "type", "variants": ["actlumus", "actlumus2"] },
|
|
86
86
|
"syncTime": { "reachableByClient": true, "payloadType": "SyncTimeAckPayload", "emittedWhen": "ack of the `syncTime` command" },
|
|
87
87
|
"realTime": { "reachableByClient": true, "payloadType": "RealTimeAckPayload", "emittedWhen": "ack of the `realTime` command" },
|
|
@@ -99,11 +99,11 @@
|
|
|
99
99
|
"commands": {
|
|
100
100
|
"note": "Send with BridgeClient.send(command, args). It is fire-and-forget: there is no return value and no throw. Replies arrive on the key named in repliesOn.",
|
|
101
101
|
"entries": {
|
|
102
|
-
"ping": { "args": {}, "requiresConfirmation": false, "repliesOn": "pingPong" },
|
|
103
|
-
"userDevice": { "args": { "getFromServer": "boolean, default false" }, "requiresConfirmation": false, "repliesOn": "userDevice" },
|
|
102
|
+
"ping": { "args": {}, "requiresConfirmation": false, "repliesOn": "pingPong", "note": "Handled by the app's background service: lost without a reply if sent before the service is up. BridgeClient.waitForService() resends it until a pingPong arrives." },
|
|
103
|
+
"userDevice": { "args": { "getFromServer": "boolean, default false. false: memory or cache while fresh, else the server (use at boot). true: the server first, cache on failure (use for a refresh button)." }, "requiresConfirmation": false, "repliesOn": "userDevice", "prefer": "BridgeClient.requestUserDevices({ getFromServer }), which resends with backoff and resolves { status: 'timeout', hasDevices } instead of waiting forever.", "note": "Handled by the app's background service. A request sent before it is up is lost, and no reply is sent when the list fails to load — including an account with no linked device. An empty list is never sent." },
|
|
104
104
|
"realTime": { "args": { "serialNo": "int", "deviceId": "int, optional, minNativeVersion 0.17.0", "value": "boolean" }, "requiresConfirmation": false, "repliesOn": "realTime", "note": "Off by default. The deviceLightSensors stream only flows while this is on. Always send serialNo; add deviceId to address one device when two share a serialNo." },
|
|
105
|
-
"deviceConnection": { "args": { "type": "'add' | 'remove'", "serialNo": "int", "deviceId": "int, optional, minNativeVersion 0.17.0" }, "requiresConfirmation": false, "repliesOn": "deviceConnection" },
|
|
106
|
-
"sensorLog": { "args": { "requestId": "string", "serialNo": "int", "bodyPart": "'wrist' | 'necklace', optional", "limit": "1..1000, optional", "startingAfter": "opaque cursor, optional", "endingBefore": "opaque cursor, optional", "startTime": "timestamp, optional, minNativeVersion 0.15.0", "endTime": "timestamp, optional, minNativeVersion 0.15.0" }, "requiresConfirmation": false, "repliesOn": "sensorLog", "prefer": "BridgeClient.fetchSensorLog(query), which correlates requestId and applies a timeout for you." },
|
|
105
|
+
"deviceConnection": { "args": { "type": "'add' | 'remove'", "serialNo": "int", "deviceId": "int, optional, minNativeVersion 0.17.0" }, "requiresConfirmation": false, "repliesOn": "deviceConnection", "note": "Pairs nothing. 'add' subscribes to the BLE connection state of a device from the userDevice list; the state arrives on the deviceConnection key on every change. 'remove' stops it." },
|
|
106
|
+
"sensorLog": { "args": { "requestId": "string", "serialNo": "int", "bodyPart": "'wrist' | 'necklace', optional", "limit": "1..1000, optional", "startingAfter": "opaque cursor, optional", "endingBefore": "opaque cursor, optional", "startTime": "timestamp, optional, minNativeVersion 0.15.0", "endTime": "timestamp, optional, minNativeVersion 0.15.0" }, "requiresConfirmation": false, "repliesOn": "sensorLog", "prefer": "BridgeClient.fetchSensorLog(query), which correlates requestId and applies a timeout for you.", "paging": { "orderWithinPage": "chronological ascending", "noCursorNoRange": "the most recent `limit` logs; older pages with endingBefore while hasPrevious", "withStartTime": "the oldest logs of the range first; newer pages with startingAfter while hasMore", "onlyEndTime": "the most recent logs before endTime", "defaultLimit": 10, "maxLimit": 1000, "logPeriod": "one log per period, programmable per device, default 60 s; derive it from consecutive log_time values" } },
|
|
107
107
|
"openSettings": { "args": {}, "requiresConfirmation": false, "repliesOn": null },
|
|
108
108
|
"requestPermission": { "args": { "permission": "string, must match a native AppPermission name" }, "requiresConfirmation": false, "repliesOn": null, "note": "There is no permission-result key. The OS shows its own prompt and the outcome reaches you as a fresh systemStatus emission." },
|
|
109
109
|
"setStatusBarColor": { "args": { "color": "'#RRGGBB'" }, "requiresConfirmation": false, "repliesOn": null },
|
|
@@ -148,7 +148,9 @@
|
|
|
148
148
|
{ "cause": "unknown command type", "trigger": "typo, or a command added in a newer nativeVersion than the device runs", "observable": "nothing" },
|
|
149
149
|
{ "cause": "permission name outside the native enum", "trigger": "requestPermission with an unrecognized string", "observable": "nothing" },
|
|
150
150
|
{ "cause": "malformed args", "trigger": "realTime, syncTime, deleteMemory or deviceConnection with wrong types", "observable": "nothing" },
|
|
151
|
-
{ "cause": "postMessage failing schema validation", "trigger": "hand-rolled postMessage instead of BridgeClient", "observable": "nothing" }
|
|
151
|
+
{ "cause": "postMessage failing schema validation", "trigger": "hand-rolled postMessage instead of BridgeClient", "observable": "nothing" },
|
|
152
|
+
{ "cause": "background service not running yet", "trigger": "userDevice or ping sent before the app's background service is up, typically right after a cold start; the app does not re-send the list when the service comes up", "observable": "nothing (the handshake, systemStatus and language already work)", "mitigation": "BridgeClient.requestUserDevices() / waitForService()" },
|
|
153
|
+
{ "cause": "device list did not load", "trigger": "userDevice for an account with no linked device, or when the server request fails (no internet, session problem); the app replies only on success", "observable": "nothing; systemStatus.hasDevices stays false", "mitigation": "BridgeClient.requestUserDevices() resolves { status: 'timeout', hasDevices } after 30 s: render an empty state with a retry" }
|
|
152
154
|
],
|
|
153
155
|
|
|
154
156
|
"capabilities": [
|
|
@@ -172,16 +174,49 @@
|
|
|
172
174
|
"integrity": { "algorithm": "sha256", "format": "sha256-<lowercase hex>", "computedOver": "the exact bytes of the .zip" },
|
|
173
175
|
"tooling": {
|
|
174
176
|
"build": "npx condor-mobile-bridge build -i dist/index.html -o dist/bundle.zip",
|
|
175
|
-
"validate": "npx condor-mobile-bridge validate -i dist/bundle.zip --strict"
|
|
177
|
+
"validate": "npx condor-mobile-bridge validate -i dist/bundle.zip --strict",
|
|
178
|
+
"publish": "npx condor-mobile-bridge canvas publish dist/index.html --version <v> — interactive (company id, user id, password, 2FA code); live at once for every user of the company; needs a manager user. Never run it from an agent."
|
|
176
179
|
},
|
|
177
180
|
"statusBarHook": "<meta name=\"theme-color\" content=\"#RRGGBB\"> in the bundle sets the native status bar colour. The setStatusBarColor command overrides it at runtime."
|
|
178
181
|
},
|
|
179
182
|
|
|
183
|
+
"payloadItems": {
|
|
184
|
+
"note": "Item shapes of userDevice and sensorLog. Canonical types in dist/index.d.ts. Fields not listed here are not part of the public contract — do not read them.",
|
|
185
|
+
"joinKey": "deviceId. An item of devicesTelemetry or devicesUserConfig without deviceId is legacy data: fall back to serialNo only when that serialNo appears once in devicesConfig.",
|
|
186
|
+
"hasDevices": "systemStatus.hasDevices is true only after the app loaded a non-empty list from the server; false means no device OR the lookup failed. A hint, never a verdict.",
|
|
187
|
+
"devicesConfig": {
|
|
188
|
+
"type": "DeviceConfigItem",
|
|
189
|
+
"role": "the list of linked devices to render",
|
|
190
|
+
"fields": { "deviceId": "int, unique", "serialNo": "int, may repeat across an ActLumus and a Plus", "deviceBodyPart": "'wrist' | 'necklace' | null", "modelId": "'actlumus' | 'actlumus_plus', absent = ActLumus (gen1)", "bleConnectionPeriod": "int, minutes", "autosync": "bool", "eraseDeviceMemory": "bool", "autoEraseDeviceMemory": "bool", "autoEraseDeviceMemoryThreshold": "int", "readSensorsMemory": "bool", "startDevice": "bool", "readFullMemory": "bool", "allowFirmwareUpdate": "bool" }
|
|
191
|
+
},
|
|
192
|
+
"devicesTelemetry": {
|
|
193
|
+
"type": "DeviceTelemetryItem",
|
|
194
|
+
"role": "last state read from each device over BLE; readings are null until the first sync",
|
|
195
|
+
"fields": { "serialNo": "int", "deviceId": "int, omitted on legacy data", "updataDateTime": "epoch ms | null — last sync; the misspelling is the real field name", "deviceName": "string | null", "deviceVersion": "string | null (firmware)", "hardwareVersion": "string | null", "deviceDateTime": "epoch ms | null", "memoryDataUsage": "number 0-100 (%) | null", "memorySystemUsage": "number 0-100 (%) | null", "batteryLevel": "number 0-100 (%) | null", "batteryVoltage": "number, volts | null", "isCharging": "bool | null", "isOnDock": "bool | null", "syncErrors": "Array<{ serialNo, attemptDate: epoch ms, message: localized string, messageCode: string | null }>" }
|
|
196
|
+
},
|
|
197
|
+
"devicesUserConfig": {
|
|
198
|
+
"type": "DeviceUserConfigItem",
|
|
199
|
+
"role": "per-device toggles",
|
|
200
|
+
"fields": { "serialNo": "int", "deviceId": "int, omitted on legacy data", "syncTime": "bool", "realTime": "bool", "deleteMemory": "bool", "readAllMemory": "bool", "readAllSystemLog": "bool" }
|
|
201
|
+
},
|
|
202
|
+
"sensorLogItem": {
|
|
203
|
+
"type": "SensorLogItem",
|
|
204
|
+
"fields": { "id": "int", "log_time": "'YYYY-MM-DD HH:MM:SS', device-LOCAL time, not UTC", "log_type": "int", "body_part": "'wrist' | 'necklace' | null", "temperature": "°C | null", "lux_photopic": "lux | null", "lux_melanopic": "lux | null", "light_sensor_f1..f8, light_sensor_clear, light_sensor_nir": "raw channels | null (null = the hardware has no such channel)", "act_data_pim, act_data_tat, act_data_zcm": "int | null", "device_log_time, app_log_time": "upload batch markers, not per-log timestamps" }
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
"devTools": {
|
|
209
|
+
"harness": "npx condor-mobile-bridge dev -i <index.html|dist|bundle.zip> [--scenario <name>] [--port 5173]",
|
|
210
|
+
"subpath": "@condorinst/mobile-bridge/dev exports createDevHarness() for a host page; never import it into the bundle (validate fails it with dev-harness-bundled)",
|
|
211
|
+
"scenarios": ["default", "cold-start", "no-devices", "shared-serial", "offline"],
|
|
212
|
+
"outsideShell": "BridgeClient.handshake() rejects at once when the page is not inside the shell (opened directly in a browser)."
|
|
213
|
+
},
|
|
214
|
+
|
|
180
215
|
"verifiedAgainst": {
|
|
181
216
|
"monorepo": "condorinstprogdev/mobile_applications",
|
|
182
217
|
"branch": "dev_canvas",
|
|
183
|
-
"commit": "
|
|
184
|
-
"verifiedOn": "2026-10-
|
|
218
|
+
"commit": "a1a524fd8491e4c266c7ea7bbcd9b1aa334620db",
|
|
219
|
+
"verifiedOn": "2026-10-20",
|
|
185
220
|
"sources": {
|
|
186
221
|
"nativeVersion": "apps/canvas/lib/ui/pages/home/bridge/condor_bridge.dart:38",
|
|
187
222
|
"jsToNativePayloadBytes": "apps/canvas/lib/ui/pages/home/bridge/condor_bridge.dart:94",
|
|
@@ -196,7 +231,17 @@
|
|
|
196
231
|
"csp": "apps/canvas/web-mock-src/shell.template.html:28",
|
|
197
232
|
"shellPolicy": "apps/canvas/web-mock-src/src/shell.ts:48-98",
|
|
198
233
|
"webViewPermissionDeny": "apps/canvas/lib/ui/pages/home/home_page.dart:263-271",
|
|
199
|
-
"opaqueOrigin": "apps/canvas/lib/ui/pages/home/home_page.dart:299-301"
|
|
234
|
+
"opaqueOrigin": "apps/canvas/lib/ui/pages/home/home_page.dart:299-301",
|
|
235
|
+
"userDeviceToBackgroundService": "apps/canvas/lib/ui/pages/home/bridge/condor_bridge.dart:671-683",
|
|
236
|
+
"userDeviceRepliesOnlyOnSuccess": "packages/condor_core/lib/src/data/services/user_devices_service.dart:28-39",
|
|
237
|
+
"getFromServerSemantics": "packages/condor_core/lib/src/data/repositories/user_devices_repository.dart:49-85",
|
|
238
|
+
"hasDevices": "apps/canvas/lib/data/services/sys_notification_service.dart:224-237",
|
|
239
|
+
"deviceConfigItem": "packages/condor_core/lib/src/domain/entities/device_config_entity.dart:61-77",
|
|
240
|
+
"deviceTelemetryItem": "packages/condor_core/lib/src/domain/entities/device_telemetry_entity.dart:100-117",
|
|
241
|
+
"deviceUserConfigItem": "packages/condor_core/lib/src/domain/entities/device_user_config_entity.dart:78-88",
|
|
242
|
+
"deviceSyncErrorItem": "packages/condor_core/lib/src/domain/entities/device_sync_error_entity.dart:16-23",
|
|
243
|
+
"userDevicesEnvelope": "packages/condor_core/lib/src/domain/entities/user_devices_entity.dart:282-289",
|
|
244
|
+
"bleConnectionPeriodMinutes": "packages/condor_core/lib/src/domain/entities/device_session_record_entity.dart:25"
|
|
200
245
|
},
|
|
201
246
|
"maintenance": "Re-verify every entry in sources before cutting a release. A published contract that drifts from the app is worse than no contract."
|
|
202
247
|
}
|