@kubohiroya/turbowarp-webrtc-qrcode-pairing 0.1.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/LICENSE +373 -0
- package/README.ja.md +122 -0
- package/README.md +414 -0
- package/dist/extension-manifest.json +318 -0
- package/dist/webrtc-qrcode-pairing.js +4064 -0
- package/docs/architecture.ja.md +57 -0
- package/docs/architecture.md +65 -0
- package/docs/design/qr-pairing/README.md +16 -0
- package/docs/design/qr-pairing/architecture.md +191 -0
- package/docs/design/qr-pairing/block-api.md +123 -0
- package/docs/design/qr-pairing/dataflow.md +202 -0
- package/docs/design/qr-pairing/extraction-plan.md +134 -0
- package/docs/design/qr-pairing/interfaces.ts +473 -0
- package/docs/design/qr-pairing/requirements.md +152 -0
- package/docs/design/qr-pairing/test-plan.md +126 -0
- package/docs/index.html +34 -0
- package/docs/initialization-issue.ja.md +34 -0
- package/docs/integration-guide.ja.md +245 -0
- package/docs/integration-guide.md +258 -0
- package/docs/migration.ja.md +110 -0
- package/docs/migration.md +115 -0
- package/package.json +90 -0
- package/schemas/extension-manifest.schema.json +49 -0
- package/src/block-definitions.json +264 -0
- package/src/clock.ts +18 -0
- package/src/config/feature-flags.ts +18 -0
- package/src/config/qr-config.ts +22 -0
- package/src/config.ts +12 -0
- package/src/errors.ts +51 -0
- package/src/extension-manifest.ts +149 -0
- package/src/extension.ts +281 -0
- package/src/globals.d.ts +54 -0
- package/src/index.ts +8 -0
- package/src/pairing/controller.ts +836 -0
- package/src/pairing/limits.ts +12 -0
- package/src/pairing/types.ts +96 -0
- package/src/ports/display.ts +136 -0
- package/src/ports/qr-scan.ts +151 -0
- package/src/ports/webrtc.ts +61 -0
- package/src/qr/courier.ts +267 -0
- package/src/qr/envelope.ts +0 -0
- package/src/qr/hash.ts +13 -0
- package/src/qr/limits.ts +44 -0
- package/src/qr/svg.ts +34 -0
- package/templates/README.ja.md +183 -0
- package/templates/README.md +198 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# Integration guide
|
|
2
|
+
|
|
3
|
+
[日本語](integration-guide.ja.md)
|
|
4
|
+
|
|
5
|
+
How to pair two machines with this extension from a TurboWarp project. For the block-by-block
|
|
6
|
+
reference, see [Block reference](../README.md#block-reference).
|
|
7
|
+
|
|
8
|
+
## 1. Before you start
|
|
9
|
+
|
|
10
|
+
Load these extensions **before** this one, and run all of them unsandboxed:
|
|
11
|
+
|
|
12
|
+
| Extension | Why |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `turbowarp-webrtc` | Creates and accepts the offer and the answer, and owns the connection |
|
|
15
|
+
| `turbowarp-jsqr` | Decodes QR codes from camera frames |
|
|
16
|
+
| `turbowarp-camera-source` | Acquires the camera and exposes frames |
|
|
17
|
+
|
|
18
|
+
`turbowarp-webrtc` must publish runtime capability **v3**, which version 0.4.0 and later do. Version
|
|
19
|
+
0.3.0 and earlier publish v2, which has `createOffer` and `getOffer` but not `acceptOffer`,
|
|
20
|
+
`getAnswer`, `acceptAnswer` or `connectionState`, so a round trip cannot complete. A version that is
|
|
21
|
+
too old reports `webrtc-capability-missing`.
|
|
22
|
+
|
|
23
|
+
Turn the pairing blocks on at startup, before the extension loads:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
globalThis.__TWQP_FEATURE_FLAGS__ = {qrCodePairing: true};
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
While the flag is off, no pairing blocks appear and `pairing phase of [SESSION]` reports `disabled`.
|
|
30
|
+
|
|
31
|
+
The flag is read once while the bundle evaluates, so setting it afterwards has no effect. To get a
|
|
32
|
+
single file that already has it set, run the repository's helper:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm run build:flagged # optionally: pnpm run build:flagged Q
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
It writes `local/webrtc-qrcode-pairing.flagged.js` and prints a `file://` URL to load as a custom
|
|
39
|
+
extension. The optional argument fixes the error correction level. The file is local to the working
|
|
40
|
+
copy and is not published; loading `dist/webrtc-qrcode-pairing.js` instead is how you check the
|
|
41
|
+
blocks really are off by default.
|
|
42
|
+
|
|
43
|
+
## 2. Roles and names
|
|
44
|
+
|
|
45
|
+
One machine is the **hub**: it creates the offer, projects it, and reads the answer back. The other
|
|
46
|
+
is the **camera machine**: it reads the offer and shows the answer.
|
|
47
|
+
|
|
48
|
+
Each machine picks its own names. They do **not** have to match:
|
|
49
|
+
|
|
50
|
+
- The hub calls itself `studio` and the camera machine `cam-A`.
|
|
51
|
+
- The camera machine learns both names from the offer. It registers the connection under `studio`
|
|
52
|
+
and answers as `cam-A`.
|
|
53
|
+
|
|
54
|
+
So the hub's `remote peer` is the camera machine's `local peer`, and vice versa. The names only need
|
|
55
|
+
to be meaningful to the person reading the screen.
|
|
56
|
+
|
|
57
|
+
Pass an empty `LOCAL_PEER` on the camera machine to accept whatever name the offer assigns. Pass a
|
|
58
|
+
name to require it: an offer addressed to a different machine is then rejected with `peer-mismatch`,
|
|
59
|
+
which catches an operator scanning the wrong projection.
|
|
60
|
+
|
|
61
|
+
## 3. The hub script
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
when green flag clicked
|
|
65
|
+
start offer pairing [pairing-1] as [studio] to [cam-A]
|
|
66
|
+
set pairing timeout of [pairing-1] to (600) seconds
|
|
67
|
+
show pairing QR part (1) of [pairing-1] on this sprite
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The timeout is set after the session exists: every block but the two `start`
|
|
71
|
+
blocks needs an open session and reports `no-session` otherwise. The deadline is
|
|
72
|
+
measured from the moment the session started, so setting it right afterwards
|
|
73
|
+
loses nothing.
|
|
74
|
+
|
|
75
|
+
`start offer pairing` returns once the offer exists and its QR parts are prepared; the phase becomes
|
|
76
|
+
`offer-ready`. Creating the offer waits for ICE gathering to finish, so it can take a moment.
|
|
77
|
+
|
|
78
|
+
Then project the parts and let the camera machine read them. When the operator brings the answer
|
|
79
|
+
back:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
scan pairing QR for [pairing-1] from camera [default]
|
|
83
|
+
wait until pairing [pairing-1] is connected
|
|
84
|
+
end pairing QR display of [pairing-1]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`scan pairing QR` returns when every part of the answer has arrived and passed its checks. At that
|
|
88
|
+
point the phase is `answer-received`, then `connecting`. `wait until ... is connected` returns when
|
|
89
|
+
WebRTC reports the connection, and fails if the session is cancelled, times out, or fails.
|
|
90
|
+
|
|
91
|
+
## 4. The camera machine script
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
when green flag clicked
|
|
95
|
+
start answer pairing [pairing-1] as []
|
|
96
|
+
set pairing timeout of [pairing-1] to (600) seconds
|
|
97
|
+
scan pairing QR for [pairing-1] from camera [default]
|
|
98
|
+
show pairing QR part (1) of [pairing-1] on this sprite
|
|
99
|
+
wait until pairing [pairing-1] is connected
|
|
100
|
+
end pairing QR display of [pairing-1]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
After the offer is complete the extension accepts it and prepares the answer on its own, so the
|
|
104
|
+
phase moves `receiving` → `offer-received` → `creating-answer` → `answer-ready` without another
|
|
105
|
+
block.
|
|
106
|
+
|
|
107
|
+
## 5. Showing parts so they can be photographed
|
|
108
|
+
|
|
109
|
+
A long connection code needs several QR codes. Show them one at a time and let the operator advance
|
|
110
|
+
them, rather than cycling automatically: a person photographing a projection needs to see each part
|
|
111
|
+
stay still.
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
show pairing QR part (1) of [pairing-1] on this sprite
|
|
115
|
+
say (join (join (current pairing QR part of [pairing-1]) " / ") (pairing QR part count of [pairing-1]))
|
|
116
|
+
|
|
117
|
+
when [space v] key pressed
|
|
118
|
+
show next pairing QR part of [pairing-1] on this sprite
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Practical points:
|
|
122
|
+
|
|
123
|
+
- Show the part number and the total ("2 / 3") so the operator knows what is left to photograph.
|
|
124
|
+
- The sprite keeps its original costume: `end pairing QR display` puts it back, and so do cancelling,
|
|
125
|
+
a timeout, the stop button, and removing the sprite.
|
|
126
|
+
- Keep the sprite large and square on the projection, and leave the white margin around the code
|
|
127
|
+
intact — it is part of the symbol.
|
|
128
|
+
- Higher error correction survives worse optics but fits fewer characters per code, which means more
|
|
129
|
+
parts to carry. `M` is the default; set `Q` or `H` at startup if reading is unreliable:
|
|
130
|
+
`globalThis.__TWQP_QR_CONFIG__ = {errorCorrectionLevel: 'Q'};`
|
|
131
|
+
- To draw the code yourself instead of using a sprite skin, read
|
|
132
|
+
`pairing QR part [INDEX] of [SESSION] as data URI` or `... as SVG`. This path works even without a
|
|
133
|
+
renderer.
|
|
134
|
+
|
|
135
|
+
## 6. Reading parts
|
|
136
|
+
|
|
137
|
+
`scan pairing QR for [SESSION] from camera [CAMERA_ID]` holds one camera lease for the whole session
|
|
138
|
+
and keeps looking until every part has arrived. Reading the same part repeatedly is normal and
|
|
139
|
+
costs nothing. Codes belonging to something else — a poster, another session, the previous attempt —
|
|
140
|
+
are skipped silently.
|
|
141
|
+
|
|
142
|
+
Show progress while it runs:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
say (join (join (received parts of [pairing-1]) " / ") (required parts of [pairing-1]))
|
|
146
|
+
say (join "still needed: " (missing parts of [pairing-1]))
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`required parts` is 0 until the first part arrives, because the part count travels inside the parts.
|
|
150
|
+
|
|
151
|
+
If your project obtains QR text some other way, feed it in directly with
|
|
152
|
+
`receive pairing QR text [TEXT] for [SESSION]`. That is the primary entry point; camera scanning is
|
|
153
|
+
built on top of it.
|
|
154
|
+
|
|
155
|
+
## 7. Watching progress
|
|
156
|
+
|
|
157
|
+
`pairing phase of [SESSION]` reports where the exchange is:
|
|
158
|
+
|
|
159
|
+
| Phase | Meaning |
|
|
160
|
+
|---|---|
|
|
161
|
+
| `disabled` | The feature flag is off |
|
|
162
|
+
| `idle` | No session with that name |
|
|
163
|
+
| `creating-offer` | Hub: waiting for WebRTC to produce the offer |
|
|
164
|
+
| `offer-ready` | Hub: offer QR parts are ready to project |
|
|
165
|
+
| `awaiting-answer` | Hub: some answer parts have arrived |
|
|
166
|
+
| `answer-received` | Hub: the answer passed its checks |
|
|
167
|
+
| `awaiting-offer` | Camera: waiting for the first offer part |
|
|
168
|
+
| `receiving` | Camera: some offer parts have arrived |
|
|
169
|
+
| `offer-received` | Camera: the offer passed its checks |
|
|
170
|
+
| `creating-answer` | Camera: waiting for WebRTC to produce the answer |
|
|
171
|
+
| `answer-ready` | Camera: answer QR parts are ready to show |
|
|
172
|
+
| `connecting` | The code reached WebRTC; the connection is not up yet |
|
|
173
|
+
| `connected` | WebRTC reports an established connection |
|
|
174
|
+
| `cancelled` / `expired` / `failed` | The exchange ended |
|
|
175
|
+
|
|
176
|
+
**Carrying the code successfully is not the same as connecting.** `answer-received` and
|
|
177
|
+
`offer-received` mean the optical transport worked. `connected` means the network path worked. A
|
|
178
|
+
code can arrive perfectly and the connection still fail, because reachability depends on the network,
|
|
179
|
+
not on the QR codes.
|
|
180
|
+
|
|
181
|
+
## 8. Two or more camera machines
|
|
182
|
+
|
|
183
|
+
Pair them one at a time, each with its own session name and its own remote peer name:
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
start offer pairing [pairing-A] as [studio] to [cam-A]
|
|
187
|
+
...
|
|
188
|
+
start offer pairing [pairing-B] as [studio] to [cam-B]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
An answer from `cam-B` offered to session `pairing-A` is rejected: each answer carries the session
|
|
192
|
+
and the offer it replies to, so the connections cannot be swapped. `pairing sessions` lists the open
|
|
193
|
+
session names.
|
|
194
|
+
|
|
195
|
+
## 9. Timeouts, cancelling, retrying
|
|
196
|
+
|
|
197
|
+
- `set pairing timeout of [SESSION] to [SECONDS] seconds` — 1 to 3600, default 600. The deadline is
|
|
198
|
+
measured on this machine's own clock, so the two machines do not need synchronized time.
|
|
199
|
+
`remaining seconds of [SESSION]` counts down.
|
|
200
|
+
- `cancel pairing [SESSION]` — restores the display, releases the camera and the receive buffer, and
|
|
201
|
+
closes the peer connection **if it never connected**. An established connection stays up.
|
|
202
|
+
- `retry pairing [SESSION]` — cancels and starts a fresh exchange. The new exchange has a new id, so
|
|
203
|
+
a QR code photographed from the previous attempt is refused rather than applied.
|
|
204
|
+
|
|
205
|
+
Pressing the stop button releases displays and cameras and cancels exchanges in progress. It does
|
|
206
|
+
not disconnect an established connection.
|
|
207
|
+
|
|
208
|
+
## 10. When something goes wrong
|
|
209
|
+
|
|
210
|
+
`pairing error code of [SESSION]` gives a stable code; `pairing error message of [SESSION]` gives a
|
|
211
|
+
sentence for the operator. Neither ever contains the connection code itself.
|
|
212
|
+
|
|
213
|
+
| Code | What happened | What to do |
|
|
214
|
+
|---|---|---|
|
|
215
|
+
| `feature-disabled` | The startup flag is off | Set `__TWQP_FEATURE_FLAGS__` before loading |
|
|
216
|
+
| `webrtc-capability-missing` | `turbowarp-webrtc` is absent or older than v3 | Load it first; it must publish capability v3 |
|
|
217
|
+
| `qr-decoder-missing` / `camera-unavailable` | `turbowarp-jsqr` or `turbowarp-camera-source` is absent, or the camera failed | Load them first; check camera permission |
|
|
218
|
+
| `renderer-unavailable` | The sprite cannot take a temporary skin | Use a normal sprite, not the stage or a clone; or display the SVG yourself |
|
|
219
|
+
| `unsupported-protocol` | The code is not `twqr/1` | An old `twmp-qr/1` code or another product's QR |
|
|
220
|
+
| `hash-mismatch` / `length-mismatch` | Parts assembled but the message is damaged | Re-read all parts; raise error correction |
|
|
221
|
+
| `conflicting-part` | The same part number arrived with different content | Two different exchanges are being mixed; restart |
|
|
222
|
+
| `missing-parts` | Not every part has been read | `missing parts of [SESSION]` says which |
|
|
223
|
+
| `peer-mismatch` | The code names different machines | The wrong projection was scanned |
|
|
224
|
+
| `reply-mismatch` / `stale-exchange` | The code answers a different offer or a previous attempt | Photograph the current codes again |
|
|
225
|
+
| `already-accepted` | This exchange already handed its code to WebRTC | Nothing to do |
|
|
226
|
+
| `timeout` | The deadline passed | `retry pairing` |
|
|
227
|
+
| `webrtc-rejected` | WebRTC refused the code or the connection failed | A network problem, not a transport problem |
|
|
228
|
+
| `session-exists` / `session-limit` / `no-session` | Session name already open, too many open, or not open | Cancel a session, or check the name |
|
|
229
|
+
|
|
230
|
+
## 11. What this extension does not do
|
|
231
|
+
|
|
232
|
+
- **It is not authentication.** The hash detects damage. Anyone who can photograph the codes can
|
|
233
|
+
recompute it, and anyone who can see the projection can read the connection information. Manage
|
|
234
|
+
the visibility of the projection as an operational matter.
|
|
235
|
+
- **It does not guarantee reachability.** ICE, STUN and TURN belong to `turbowarp-webrtc`.
|
|
236
|
+
- **It does not carry video.** Camera data travels over WebRTC after the connection is established.
|
|
237
|
+
- **It does not lay out your screen.** Operator instructions, projection layout, and what happens
|
|
238
|
+
after the connection are the application's job.
|
|
239
|
+
|
|
240
|
+
## 12. Recording a hardware test
|
|
241
|
+
|
|
242
|
+
Optical conditions cannot be checked in CI. Record each run and do not treat an untested condition
|
|
243
|
+
as verified:
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
Date:
|
|
247
|
+
Hub machine: <model / OS / browser and version>
|
|
248
|
+
Camera machine: <model / OS / browser and version>
|
|
249
|
+
Carrier: <smartphone model / OS / camera app>
|
|
250
|
+
ICE mode: lan | stun
|
|
251
|
+
Network: same LAN / different segments / other
|
|
252
|
+
Offer: parts = / QR version = / error correction = / projector (size, distance, ambient light)
|
|
253
|
+
Answer: parts = / QR version = / error correction = / screen brightness
|
|
254
|
+
Reading: camera machine took s / hub took s / re-photographs =
|
|
255
|
+
Result: connected = yes|no / connection state = / test message exchanged = yes|no
|
|
256
|
+
Error: code = / circumstances =
|
|
257
|
+
Notes:
|
|
258
|
+
```
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# 移行と切戻し
|
|
2
|
+
|
|
3
|
+
[English](migration.md)
|
|
4
|
+
|
|
5
|
+
## 1. turbowarp-realtime-motion-capture からの移行
|
|
6
|
+
|
|
7
|
+
`turbowarp-realtime-motion-capture`は`twmp-qr/1`形式でOfferをQR搬送する経路を、独自の
|
|
8
|
+
`qrCourierPairing`フラグの内側に持っています。ブロックは`prepare offer QR`、
|
|
9
|
+
`show offer QR part`、`show next offer QR part`、`end offer QR display`、`offer QR state`、
|
|
10
|
+
`offer QR error`です。
|
|
11
|
+
|
|
12
|
+
この経路はOffer側だけです。カメラ側がOfferを受理してAnswerを返す部分は別の手段が必要で、そこが
|
|
13
|
+
本拡張の追加分です。
|
|
14
|
+
|
|
15
|
+
### 形式
|
|
16
|
+
|
|
17
|
+
`twqr/1`は`twmp-qr/1`と**互換ではなく**、読み取りもしません。`twmp-qr/1`はpeer名を1つしか持たず
|
|
18
|
+
reply-toも無いため、次を表現できません。
|
|
19
|
+
|
|
20
|
+
- 送信側が自分を呼ぶ名前と、相手を呼ぶ名前の区別
|
|
21
|
+
- Answerがどの Offer への応答か
|
|
22
|
+
|
|
23
|
+
本拡張では両方が必要です。前者は両端で同じ識別子を設定せずに済ませるため、後者はAnswerが誤った
|
|
24
|
+
交換へ適用されないためです。必須フィールドの追加は後方互換にできないため、新しい名前空間と
|
|
25
|
+
versionを与えました。
|
|
26
|
+
|
|
27
|
+
`twmp-qr/1`のQRを本拡張へ渡すと`unsupported-protocol`で拒否されます。読み取り互換モードは
|
|
28
|
+
設けていません。peerの対応付けを表現できないQRを受理することは、その対応を推測することであり、
|
|
29
|
+
推測を誤れば誤った端末同士が接続されます。曖昧な受理より明示的な拒否のほうが安全です。
|
|
30
|
+
|
|
31
|
+
同じ会場で両者を併用することはできます。相互にQRを読めませんが、失敗は黙って起きるのではなく
|
|
32
|
+
報告されます。
|
|
33
|
+
|
|
34
|
+
### ブロックの対応
|
|
35
|
+
|
|
36
|
+
| motion-capture | 本拡張 |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `prepare offer QR for peer [PEER]` | `start offer pairing [SESSION] as [LOCAL_PEER] to [REMOTE_PEER]` |
|
|
39
|
+
| `show offer QR part [INDEX] on this sprite` | `show pairing QR part [INDEX] of [SESSION] on this sprite` |
|
|
40
|
+
| `show next offer QR part on this sprite` | `show next pairing QR part of [SESSION] on this sprite` |
|
|
41
|
+
| `offer QR part count` | `pairing QR part count of [SESSION]` |
|
|
42
|
+
| `current offer QR part` | `current pairing QR part of [SESSION]` |
|
|
43
|
+
| `end offer QR display` | `end pairing QR display of [SESSION]` |
|
|
44
|
+
| `offer QR state` | `pairing phase of [SESSION]`(状態が増えています。利用ガイド参照) |
|
|
45
|
+
| `offer QR error` | `pairing error code of [SESSION]`と`pairing error message of [SESSION]` |
|
|
46
|
+
| — | Answer側の一式、読取り、取消、再試行、期限 |
|
|
47
|
+
|
|
48
|
+
プロジェクトを移すときの差分です。
|
|
49
|
+
|
|
50
|
+
- すべてのブロックがsession名を取ります。1つのプロジェクトで複数台とペアリングできます。
|
|
51
|
+
- peerの引数が、自分の呼称と相手の呼称に分かれます。
|
|
52
|
+
- `offer QR state`は`idle`、`generating-offer`、`rendering`、`displayed`、`error`を返していました。
|
|
53
|
+
`pairing phase`は往復の各段階に状態を持ち、QR読取り完了と接続成立を別々に報告します。
|
|
54
|
+
- 起動時フラグは`__TWMP_FEATURE_FLAGS__.qrCourierPairing`ではなく
|
|
55
|
+
`__TWQP_FEATURE_FLAGS__.qrCodePairing`です。誤り訂正の設定も`__TWMP_QR_CONFIG__`から
|
|
56
|
+
`__TWQP_QR_CONFIG__`に変わります。
|
|
57
|
+
|
|
58
|
+
### この移行に含まれないこと
|
|
59
|
+
|
|
60
|
+
本拡張は`turbowarp-realtime-motion-capture`を変更も削除もしません。同リポジトリのQR経路と
|
|
61
|
+
manual pairing経路はそのまま残ります。旧経路の廃止や`turbowarp-time-space-sync`側の責務調整は、
|
|
62
|
+
それぞれのリポジトリの別Issueとして扱います。
|
|
63
|
+
|
|
64
|
+
## 2. manual pairingへの切戻し
|
|
65
|
+
|
|
66
|
+
ペアリング用ブロックは経路の1つであり、必須ではありません。使うのをやめるには次のようにします。
|
|
67
|
+
|
|
68
|
+
1. `globalThis.__TWQP_FEATURE_FLAGS__ = {qrCodePairing: true};`の設定をやめる。
|
|
69
|
+
2. `turbowarp-webrtc`のブロックで直接ペアリングする。
|
|
70
|
+
- hub: `create offer code for peer [PEER]`、続いて`offer code for peer [PEER]`
|
|
71
|
+
- カメラ側: `accept offer code [CODE] as peer [PEER]`、続いて`answer code for peer [PEER]`
|
|
72
|
+
- hub: `accept answer code [CODE] for peer [PEER]`
|
|
73
|
+
|
|
74
|
+
接続情報の運搬方法は会場の事情に合わせて選びます。
|
|
75
|
+
3. 拡張自体は読み込んだままで構いません。フラグが無効ならブロックを公開せず、何もしません。
|
|
76
|
+
|
|
77
|
+
フラグの無効化は経路の選択です。成立済みの接続を切断することはなく、既存のpeer接続を閉じることも
|
|
78
|
+
ありません。
|
|
79
|
+
|
|
80
|
+
フラグは起動時に一度だけ読むため、変更は次回の読込みから有効になります。プロジェクトの実行中に
|
|
81
|
+
経路が変わると失敗の解釈が難しくなるため、意図的にそうしています。
|
|
82
|
+
|
|
83
|
+
## 3. 変更後の回帰確認
|
|
84
|
+
|
|
85
|
+
`pnpm run check`を実行します。型検査、lint、テスト、README生成検証、ビルド再現性、リポジトリ方針
|
|
86
|
+
検査、npm梱包のdry-runが含まれます。
|
|
87
|
+
|
|
88
|
+
これに加えて、搬送形式や状態機械に手を入れた場合は手動で確認してください。光学条件はCIで
|
|
89
|
+
検証できません。
|
|
90
|
+
|
|
91
|
+
- [ ] 単一partと複数partの交換が、どちらも完了する。
|
|
92
|
+
- [ ] 順不同で読んだpartと、同じpartの重複読取りが、正しく再構成される。
|
|
93
|
+
- [ ] 不足partが`missing parts of [SESSION]`に現れ、再構成されない。
|
|
94
|
+
- [ ] 破損したpartが`hash-mismatch`で失敗し、WebRTCへ何も渡らない。
|
|
95
|
+
- [ ] カメラ側2台を順に接続しても、Answerが入れ替わらない。
|
|
96
|
+
- [ ] 取消・期限切れ・再試行のいずれでも表示とカメラが解放され、再試行前に撮影したQRが拒否される。
|
|
97
|
+
- [ ] 表示終了、プロジェクト停止、スプライト削除のいずれでもスプライトが元のコスチュームに戻る。
|
|
98
|
+
- [ ] 同じカメラを他が保持していれば、session終了後もカメラが止まらない。
|
|
99
|
+
- [ ] フラグ無効でペアリング用ブロックが現れず、manual pairingが動く。
|
|
100
|
+
|
|
101
|
+
実機の試行は[利用ガイドの記録テンプレート](integration-guide.ja.md#12-実機検証の記録)で記録します。
|
|
102
|
+
|
|
103
|
+
## 4. 将来、搬送形式を変えるとき
|
|
104
|
+
|
|
105
|
+
別のenvelopeが必要になった場合は次の方針です。
|
|
106
|
+
|
|
107
|
+
- 受信側は`protocol`を完全一致で判定し、それ以外を拒否します。未知のversionは中途半端に読まれず
|
|
108
|
+
必ず拒否されます。
|
|
109
|
+
- 必須フィールドの追加はversionの変更であり、拡張ではありません。
|
|
110
|
+
- 旧形式の扱いと移行方法を、このページが`twmp-qr/1`について記しているのと同じ形で記載します。
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Migration and rollback
|
|
2
|
+
|
|
3
|
+
[日本語](migration.ja.md)
|
|
4
|
+
|
|
5
|
+
## 1. Coming from turbowarp-realtime-motion-capture
|
|
6
|
+
|
|
7
|
+
`turbowarp-realtime-motion-capture` carries an offer as QR codes with the `twmp-qr/1` format, behind
|
|
8
|
+
its own `qrCourierPairing` flag. Its blocks are `prepare offer QR`, `show offer QR part`,
|
|
9
|
+
`show next offer QR part`, `end offer QR display`, `offer QR state` and `offer QR error`.
|
|
10
|
+
|
|
11
|
+
That path only covers the offer. The camera machine has to accept the offer and return an answer by
|
|
12
|
+
some other means, which is what this extension adds.
|
|
13
|
+
|
|
14
|
+
### Format
|
|
15
|
+
|
|
16
|
+
`twqr/1` is **not compatible** with `twmp-qr/1` and does not read it. `twmp-qr/1` carries one peer
|
|
17
|
+
name and no reply-to, so it cannot express:
|
|
18
|
+
|
|
19
|
+
- which name the sender uses for itself versus for the receiver, and
|
|
20
|
+
- which offer an answer is replying to.
|
|
21
|
+
|
|
22
|
+
Both are required here: the first so the two machines never need matching identifiers, the second so
|
|
23
|
+
an answer cannot be applied to the wrong exchange. Adding required fields is not a backward
|
|
24
|
+
compatible change, so the format got a new namespace and version.
|
|
25
|
+
|
|
26
|
+
A `twmp-qr/1` code presented to this extension is refused with `unsupported-protocol`. There is no
|
|
27
|
+
read-compatibility mode: accepting a code that cannot express the peer mapping would mean guessing
|
|
28
|
+
it, and a wrong guess pairs the wrong machines. An explicit refusal is safer than an ambiguous
|
|
29
|
+
acceptance.
|
|
30
|
+
|
|
31
|
+
The two can be used in the same venue. They will not read each other's codes, but the failure is
|
|
32
|
+
reported rather than silent.
|
|
33
|
+
|
|
34
|
+
### Block mapping
|
|
35
|
+
|
|
36
|
+
| motion-capture | here |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `prepare offer QR for peer [PEER]` | `start offer pairing [SESSION] as [LOCAL_PEER] to [REMOTE_PEER]` |
|
|
39
|
+
| `show offer QR part [INDEX] on this sprite` | `show pairing QR part [INDEX] of [SESSION] on this sprite` |
|
|
40
|
+
| `show next offer QR part on this sprite` | `show next pairing QR part of [SESSION] on this sprite` |
|
|
41
|
+
| `offer QR part count` | `pairing QR part count of [SESSION]` |
|
|
42
|
+
| `current offer QR part` | `current pairing QR part of [SESSION]` |
|
|
43
|
+
| `end offer QR display` | `end pairing QR display of [SESSION]` |
|
|
44
|
+
| `offer QR state` | `pairing phase of [SESSION]` (more states; see the integration guide) |
|
|
45
|
+
| `offer QR error` | `pairing error code of [SESSION]` and `pairing error message of [SESSION]` |
|
|
46
|
+
| — | everything on the answer side, scanning, cancel, retry, timeout |
|
|
47
|
+
|
|
48
|
+
Differences to expect when porting a project:
|
|
49
|
+
|
|
50
|
+
- Every block takes a session name, so one project can pair several machines.
|
|
51
|
+
- The peer argument splits into a local name and a remote name.
|
|
52
|
+
- `offer QR state` returned `idle`, `generating-offer`, `rendering`, `displayed` or `error`.
|
|
53
|
+
`pairing phase` has a state per step of the round trip, and reports QR completion and connection
|
|
54
|
+
separately.
|
|
55
|
+
- The startup flag is `__TWQP_FEATURE_FLAGS__.qrCodePairing`, not
|
|
56
|
+
`__TWMP_FEATURE_FLAGS__.qrCourierPairing`. The error correction setting moves from
|
|
57
|
+
`__TWMP_QR_CONFIG__` to `__TWQP_QR_CONFIG__`.
|
|
58
|
+
|
|
59
|
+
### What is not part of this migration
|
|
60
|
+
|
|
61
|
+
This extension does not remove or change anything in `turbowarp-realtime-motion-capture`. Its QR
|
|
62
|
+
path and its manual pairing path stay as they are. Retiring the old path there, and adjusting
|
|
63
|
+
responsibilities in `turbowarp-time-space-sync`, are separate issues in those repositories.
|
|
64
|
+
|
|
65
|
+
## 2. Rolling back to manual pairing
|
|
66
|
+
|
|
67
|
+
The pairing blocks are a route, not a requirement. To stop using them:
|
|
68
|
+
|
|
69
|
+
1. Stop setting `globalThis.__TWQP_FEATURE_FLAGS__ = {qrCodePairing: true};`.
|
|
70
|
+
2. Pair with the `turbowarp-webrtc` blocks directly:
|
|
71
|
+
- hub: `create offer code for peer [PEER]`, then `offer code for peer [PEER]`;
|
|
72
|
+
- camera machine: `accept offer code [CODE] as peer [PEER]`, then `answer code for peer [PEER]`;
|
|
73
|
+
- hub: `accept answer code [CODE] for peer [PEER]`.
|
|
74
|
+
Move the codes by whatever means the venue allows.
|
|
75
|
+
3. Leave the extension loaded if you like. With the flag off it publishes no blocks and does
|
|
76
|
+
nothing.
|
|
77
|
+
|
|
78
|
+
Turning the flag off selects the other route. It never disconnects an established connection, and it
|
|
79
|
+
does not close peer connections that already exist.
|
|
80
|
+
|
|
81
|
+
Because the flag is read once at startup, changing it takes effect on the next load, not while a
|
|
82
|
+
project is running. That is deliberate: a route that could change mid-session would make failures
|
|
83
|
+
hard to interpret.
|
|
84
|
+
|
|
85
|
+
## 3. Regression checks after a change
|
|
86
|
+
|
|
87
|
+
Run `pnpm run check` — type checking, lint, the test suite, README generation, build reproducibility,
|
|
88
|
+
the repository policy check, and an npm pack dry run.
|
|
89
|
+
|
|
90
|
+
Beyond that, a change to the transport or the state machine should be re-verified by hand, because
|
|
91
|
+
optical conditions are not testable in CI:
|
|
92
|
+
|
|
93
|
+
- [ ] A single-part exchange and a multi-part exchange both complete.
|
|
94
|
+
- [ ] Parts read out of order, and the same part read repeatedly, still assemble.
|
|
95
|
+
- [ ] A missing part is reported by `missing parts of [SESSION]` and never assembles.
|
|
96
|
+
- [ ] A damaged part fails with `hash-mismatch` and nothing reaches WebRTC.
|
|
97
|
+
- [ ] Two camera machines pair one after another without their answers crossing.
|
|
98
|
+
- [ ] Cancel, timeout and retry each release the display and the camera, and a code photographed
|
|
99
|
+
before a retry is refused.
|
|
100
|
+
- [ ] The sprite returns to its original costume after ending the display, stopping the project, and
|
|
101
|
+
removing the sprite.
|
|
102
|
+
- [ ] Another holder of the same camera keeps it running after the session ends.
|
|
103
|
+
- [ ] With the flag off, no pairing blocks appear and manual pairing still works.
|
|
104
|
+
|
|
105
|
+
Record hardware runs with the template in the [integration guide](integration-guide.md#12-recording-a-hardware-test).
|
|
106
|
+
|
|
107
|
+
## 4. Changing the transport format later
|
|
108
|
+
|
|
109
|
+
If a future change needs a different envelope:
|
|
110
|
+
|
|
111
|
+
- Receivers match `protocol` exactly and refuse anything else, so an unknown version is always
|
|
112
|
+
rejected rather than half-read.
|
|
113
|
+
- Adding a required field is a version change, not an extension.
|
|
114
|
+
- Document the old format's treatment and the migration path here, as this page does for
|
|
115
|
+
`twmp-qr/1`.
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kubohiroya/turbowarp-webrtc-qrcode-pairing",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TurboWarp extension that pairs WebRTC peers by carrying offers and answers as QR codes.",
|
|
5
|
+
"author": "Hiroya Kubo <hiroya@cuc.ac.jp>",
|
|
6
|
+
"license": "MPL-2.0",
|
|
7
|
+
"homepage": "https://github.com/kubohiroya/turbowarp-webrtc-qrcode-pairing#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/kubohiroya/turbowarp-webrtc-qrcode-pairing.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/kubohiroya/turbowarp-webrtc-qrcode-pairing/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"turbowarp",
|
|
17
|
+
"scratch",
|
|
18
|
+
"extension",
|
|
19
|
+
"webrtc",
|
|
20
|
+
"qrcode",
|
|
21
|
+
"pairing",
|
|
22
|
+
"typescript"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"files": [
|
|
26
|
+
"dist/",
|
|
27
|
+
"docs/",
|
|
28
|
+
"schemas/",
|
|
29
|
+
"src/",
|
|
30
|
+
"templates/",
|
|
31
|
+
"README.md",
|
|
32
|
+
"README.ja.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public",
|
|
37
|
+
"registry": "https://registry.npmjs.org/"
|
|
38
|
+
},
|
|
39
|
+
"packageManager": "pnpm@11.11.0",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=22.18.0"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"dev": "vite build --watch",
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"build": "vite build",
|
|
47
|
+
"build:flagged": "node scripts/build-flagged.ts",
|
|
48
|
+
"lint": "eslint src tests scripts vite.config.ts",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"docs": "node scripts/generate-readme.ts",
|
|
51
|
+
"docs:check": "node scripts/check-readme.ts",
|
|
52
|
+
"template:readme": "node scripts/apply-readme-template.ts",
|
|
53
|
+
"repo:check": "node scripts/check-repo.ts",
|
|
54
|
+
"pack:check": "npm pack --dry-run --ignore-scripts",
|
|
55
|
+
"check": "pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run docs:check && pnpm run check:dist && pnpm run repo:check && pnpm run pack:check",
|
|
56
|
+
"check:dist": "pnpm run build && node scripts/check-dist.ts"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@eslint/js": "^9.39.5",
|
|
60
|
+
"@kubohiroya/turbowarp-jsqr": "0.3.0",
|
|
61
|
+
"@kubohiroya/vite-plugin-turbowarp-extension": "0.3.0",
|
|
62
|
+
"@types/node": "^24.13.3",
|
|
63
|
+
"@types/qrcode": "1.5.6",
|
|
64
|
+
"eslint": "^9.39.5",
|
|
65
|
+
"jsqr": "^1.4.0",
|
|
66
|
+
"typescript": "^5.9.3",
|
|
67
|
+
"typescript-eslint": "^8.66.0",
|
|
68
|
+
"vite": "^8.2.2",
|
|
69
|
+
"vitest": "^5.0.0"
|
|
70
|
+
},
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"qrcode": "1.5.4"
|
|
73
|
+
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"@kubohiroya/turbowarp-webrtc": ">=0.4.0",
|
|
76
|
+
"@kubohiroya/turbowarp-jsqr": "0.3.0",
|
|
77
|
+
"@kubohiroya/turbowarp-camera-source": "0.5.0"
|
|
78
|
+
},
|
|
79
|
+
"peerDependenciesMeta": {
|
|
80
|
+
"@kubohiroya/turbowarp-webrtc": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"@kubohiroya/turbowarp-jsqr": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"@kubohiroya/turbowarp-camera-source": {
|
|
87
|
+
"optional": true
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/kubohiroya/turbowarp-extension-template/main/schemas/extension-manifest.schema.json",
|
|
4
|
+
"title": "TurboWarp Extension API Manifest",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["formatVersion", "id", "blocks", "menus"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"formatVersion": {"const": 1},
|
|
10
|
+
"id": {"type": "string", "pattern": "^[a-z0-9]+$"},
|
|
11
|
+
"blocks": {
|
|
12
|
+
"type": "array",
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"additionalProperties": false,
|
|
16
|
+
"required": ["opcode", "blockType", "arguments"],
|
|
17
|
+
"properties": {
|
|
18
|
+
"opcode": {"type": "string", "minLength": 1},
|
|
19
|
+
"blockType": {"type": "string", "minLength": 1},
|
|
20
|
+
"arguments": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"required": ["id", "type"],
|
|
26
|
+
"properties": {
|
|
27
|
+
"id": {"type": "string", "minLength": 1},
|
|
28
|
+
"type": {"type": "string", "minLength": 1},
|
|
29
|
+
"menu": {"type": "string", "minLength": 1}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"menus": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"items": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"additionalProperties": false,
|
|
41
|
+
"required": ["id", "acceptReporters"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"id": {"type": "string", "minLength": 1},
|
|
44
|
+
"acceptReporters": {"type": "boolean"}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|