@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.
Files changed (46) hide show
  1. package/LICENSE +373 -0
  2. package/README.ja.md +122 -0
  3. package/README.md +414 -0
  4. package/dist/extension-manifest.json +318 -0
  5. package/dist/webrtc-qrcode-pairing.js +4064 -0
  6. package/docs/architecture.ja.md +57 -0
  7. package/docs/architecture.md +65 -0
  8. package/docs/design/qr-pairing/README.md +16 -0
  9. package/docs/design/qr-pairing/architecture.md +191 -0
  10. package/docs/design/qr-pairing/block-api.md +123 -0
  11. package/docs/design/qr-pairing/dataflow.md +202 -0
  12. package/docs/design/qr-pairing/extraction-plan.md +134 -0
  13. package/docs/design/qr-pairing/interfaces.ts +473 -0
  14. package/docs/design/qr-pairing/requirements.md +152 -0
  15. package/docs/design/qr-pairing/test-plan.md +126 -0
  16. package/docs/index.html +34 -0
  17. package/docs/initialization-issue.ja.md +34 -0
  18. package/docs/integration-guide.ja.md +245 -0
  19. package/docs/integration-guide.md +258 -0
  20. package/docs/migration.ja.md +110 -0
  21. package/docs/migration.md +115 -0
  22. package/package.json +90 -0
  23. package/schemas/extension-manifest.schema.json +49 -0
  24. package/src/block-definitions.json +264 -0
  25. package/src/clock.ts +18 -0
  26. package/src/config/feature-flags.ts +18 -0
  27. package/src/config/qr-config.ts +22 -0
  28. package/src/config.ts +12 -0
  29. package/src/errors.ts +51 -0
  30. package/src/extension-manifest.ts +149 -0
  31. package/src/extension.ts +281 -0
  32. package/src/globals.d.ts +54 -0
  33. package/src/index.ts +8 -0
  34. package/src/pairing/controller.ts +836 -0
  35. package/src/pairing/limits.ts +12 -0
  36. package/src/pairing/types.ts +96 -0
  37. package/src/ports/display.ts +136 -0
  38. package/src/ports/qr-scan.ts +151 -0
  39. package/src/ports/webrtc.ts +61 -0
  40. package/src/qr/courier.ts +267 -0
  41. package/src/qr/envelope.ts +0 -0
  42. package/src/qr/hash.ts +13 -0
  43. package/src/qr/limits.ts +44 -0
  44. package/src/qr/svg.ts +34 -0
  45. package/templates/README.ja.md +183 -0
  46. package/templates/README.md +198 -0
package/README.md ADDED
@@ -0,0 +1,414 @@
1
+ # TurboWarp-WebRTC-QRCode-Pairing
2
+
3
+ [日本語](README.ja.md)
4
+
5
+ A TurboWarp extension for exchanging WebRTC connection information through QR codes, including a smartphone carrying an image between machines. Initialized from `turbowarp-extension-template` 0.4.0.
6
+
7
+ **User guide:** [English](https://kubohiroya.github.io/turbowarp-webrtc-qrcode-pairing/)
8
+
9
+ ## What it does
10
+
11
+ - Encodes a WebRTC Offer or Answer into QR envelopes, splitting large ones across several codes.
12
+ - Displays one part at a time on a sprite, so an operator can photograph each part of a projection.
13
+ - Accepts decoded parts in any order, tolerates repeated readings, and reports which parts are still missing.
14
+ - Verifies length and hash before a code reaches WebRTC, and hands each verified code over exactly once.
15
+ - Tracks which offer an answer replies to, so two camera machines never receive each other's connection.
16
+ - Reports transport progress and connection state as separate states, plus errors, cancellation, retry and deadlines.
17
+
18
+ Version 0.1.0 is local package metadata; it does not imply an npm release.
19
+
20
+ > [!NOTE]
21
+ > The pairing blocks are behind a startup feature flag that is off by default. See
22
+ > [Feature flag and rollback](#feature-flag-and-rollback).
23
+
24
+ ## Documentation
25
+
26
+ | Document | Contents |
27
+ |---|---|
28
+ | [Integration guide](docs/integration-guide.md) ([日本語](docs/integration-guide.ja.md)) | Setup, block sequences for both machines, showing and scanning parts, several camera machines, error codes, hardware test record |
29
+ | [Migration and rollback](docs/migration.md) ([日本語](docs/migration.ja.md)) | Coming from `turbowarp-realtime-motion-capture`, returning to manual pairing, regression checks |
30
+ | [Architecture](docs/architecture.md) ([日本語](docs/architecture.ja.md)) | Runtime structure, build outputs, extension API manifest |
31
+ | [Design records](docs/design/qr-pairing/README.md) | Requirements, data flow, type contracts, block contract, extraction plan, verification plan |
32
+
33
+ ## Intended workflow
34
+
35
+ 1. The integration machine creates an Offer and displays its QR codes through a projector.
36
+ 2. A camera machine scans them, accepts the Offer and displays Answer QR codes.
37
+ 3. An operator photographs the Answer with a smartphone and carries the images to the integration machine.
38
+ 4. The integration machine scans those images, accepts the Answer and confirms the connection.
39
+
40
+ The smartphone carries signaling information optically. Camera data is sent over WebRTC after connection establishment. Applications provide operator instructions and presentation choices.
41
+
42
+ ## Package boundaries
43
+
44
+ | Package | Responsibility |
45
+ |---|---|
46
+ | `turbowarp-webrtc` | Offer/Answer creation and acceptance, ICE and WebRTC connections |
47
+ | `turbowarp-webrtc-qrcode-pairing` | QR transport protocol, multipart display/assembly and pairing orchestration |
48
+ | `turbowarp-jsqr` | Decode QR codes from camera frames |
49
+ | `turbowarp-camera-source` | Camera acquisition and frame access |
50
+ | `turbowarp-time-space-sync` | Optical time correspondence and camera placement calibration |
51
+ | `turbowarp-time-space-sync-app` | Verification app for the time-space-sync extension |
52
+ | `turbowarp-camera-calibration-app` | Lens calibration; produces intrinsic profiles as files |
53
+ | `turbowarp-webrtc-qrcode-pairing-app` | Standalone pairing example and connection verification |
54
+ | `turbowarp-realtime-motion-capture-app`, `turbowarp-photogrammetry-app` | Production venue workflow; embed this extension directly |
55
+
56
+ Pairing is usable independently of time/space calibration and motion capture. `turbowarp-webrtc`, `turbowarp-jsqr` and `turbowarp-camera-source` are companion extensions this one checks for at runtime.
57
+
58
+ A pairing session cannot be handed from one app to another. Opening a different SB3 re-evaluates its bundle, so the WebRTC extension instance is rebuilt and the existing `RTCPeerConnection` becomes unreachable from blocks; a saved SDP cannot restore it either. Consumer apps therefore embed this extension and pair inside their own flow, and `turbowarp-webrtc-qrcode-pairing-app` exists for verification rather than as a stage of the production workflow.
59
+
60
+ ## Transport format
61
+
62
+ QR parts use the `twqr/1` envelope: protocol, session, sender and target peer names, message kind, message id, reply-to, part index and count, message length, SHA-256 hash and payload.
63
+
64
+ The hash detects optical damage. It is not authentication: anyone who can photograph the codes can recompute it. Treat a projected pairing code as visible to everyone who can see the projection.
65
+
66
+ `twmp-qr/1` from `turbowarp-realtime-motion-capture` is the format this one grew out of, but the two do not interoperate. `twmp-qr/1` carries a single peer name and no reply-to, so it cannot express the peer mapping and answer tracking this extension needs. A `twmp-qr/1` code is rejected with `unsupported-protocol` rather than accepted in a compatibility mode, because an ambiguous acceptance could pair the wrong machines.
67
+
68
+ Limits: at most 64 parts, 4096 payload characters per part, and 131072 characters per message. Character counts equal byte counts because payloads are printable ASCII. At error correction level M the binding limit is the part count, which allows about 126000 characters.
69
+
70
+ ## Feature flag and rollback
71
+
72
+ The pairing blocks are behind `qrCodePairing`, read once at startup and off by default:
73
+
74
+ ```js
75
+ globalThis.__TWQP_FEATURE_FLAGS__ = {qrCodePairing: true};
76
+ ```
77
+
78
+ Set it before the extension loads. While the flag is off, `getInfo` publishes no blocks and `pairing phase` reports `disabled`.
79
+
80
+ To roll back, stop setting the flag and pair with the `turbowarp-webrtc` blocks directly: `create offer code`, `accept offer code`, `answer code` and `accept answer code`. Turning the flag off selects the other route; it never disconnects an established connection.
81
+
82
+ Error correction level can be fixed at startup the same way:
83
+
84
+ ```js
85
+ globalThis.__TWQP_QR_CONFIG__ = {errorCorrectionLevel: 'Q'};
86
+ ```
87
+
88
+ ## Requirements and safety
89
+
90
+ - Node.js >=22.18.0 and pnpm 11.11.0.
91
+ - Companion extensions: `turbowarp-webrtc` for the connection, `turbowarp-jsqr` for decoding and `turbowarp-camera-source` for camera frames. Load them before this one.
92
+ - `turbowarp-webrtc` must publish runtime capability v3, which adds `acceptOffer`, `getAnswer`, `acceptAnswer`, `connectionState`, `hasPeer` and `closePeer`. Version 0.4.0 publishes it; 0.3.0 and earlier publish v2, which can produce an offer but not accept one, so a round trip cannot complete.
93
+ - Carrying a code by QR does not make a connection reachable. Network conditions, ICE and STUN/TURN belong to `turbowarp-webrtc`.
94
+
95
+ > [!IMPORTANT]
96
+ > This extension must run without the sandbox because it reaches the companion extensions through
97
+ > `Scratch.vm.runtime` and swaps sprite skins through the renderer. Load unsandboxed
98
+ > extension code only from a source you trust.
99
+
100
+ ## Development
101
+
102
+ ```bash
103
+ pnpm install --frozen-lockfile
104
+ pnpm run check
105
+ pnpm run dev
106
+ ```
107
+
108
+ `pnpm run check` runs type checking, lint, template tests, generated README validation, bundle reproducibility, repository policy checks and an npm package dry run. There is no separate format script in the upstream template.
109
+
110
+ Build output: `dist/webrtc-qrcode-pairing.js` and `dist/extension-manifest.json`. Load the JavaScript file as a custom TurboWarp extension. The manifest always records every declared block, including blocks the feature flag hides at runtime: it is the build-time contract, not the runtime state.
111
+
112
+ Package name and version for future consumers (not a claim of publication): `@kubohiroya/turbowarp-webrtc-qrcode-pairing@0.1.0`.
113
+
114
+ ## Block reference
115
+
116
+ <!-- BEGIN GENERATED BLOCKS -->
117
+
118
+ ### `start offer pairing [SESSION] as [LOCAL_PEER] to [REMOTE_PEER]`
119
+
120
+ Creates a WebRTC offer for the integration machine and prepares its QR parts.
121
+
122
+ | Property | Value |
123
+ |---|---|
124
+ | Type | Command |
125
+ | Opcode | `startOfferPairing` |
126
+ | `SESSION` | String, default: `pairing-1` |
127
+ | `LOCAL_PEER` | String, default: `hub` |
128
+ | `REMOTE_PEER` | String, default: `camera-1` |
129
+
130
+ ### `start answer pairing [SESSION] as [LOCAL_PEER]`
131
+
132
+ Waits for an offer on the camera machine. Leave the name empty to adopt the name the offer assigns.
133
+
134
+ | Property | Value |
135
+ |---|---|
136
+ | Type | Command |
137
+ | Opcode | `startAnswerPairing` |
138
+ | `SESSION` | String, default: `pairing-1` |
139
+ | `LOCAL_PEER` | String, default: `` |
140
+
141
+ ### `receive pairing QR text [TEXT] for [SESSION]`
142
+
143
+ Accepts one decoded QR text. Repeated readings of the same part are ignored.
144
+
145
+ | Property | Value |
146
+ |---|---|
147
+ | Type | Command |
148
+ | Opcode | `ingestPairingQrText` |
149
+ | `TEXT` | String, default: `` |
150
+ | `SESSION` | String, default: `pairing-1` |
151
+
152
+ ### `scan pairing QR for [SESSION] from camera [CAMERA_ID]`
153
+
154
+ Reads parts from the named camera until the exchange has every part it needs.
155
+
156
+ | Property | Value |
157
+ |---|---|
158
+ | Type | Command |
159
+ | Opcode | `scanPairingQrFromCamera` |
160
+ | `SESSION` | String, default: `pairing-1` |
161
+ | `CAMERA_ID` | String, default: `default` |
162
+
163
+ ### `received parts of [SESSION]`
164
+
165
+ Returns how many distinct parts have been accepted.
166
+
167
+ | Property | Value |
168
+ |---|---|
169
+ | Type | Reporter |
170
+ | Opcode | `pairingReceivedParts` |
171
+ | `SESSION` | String, default: `pairing-1` |
172
+
173
+ ### `required parts of [SESSION]`
174
+
175
+ Returns how many parts the incoming message has, or zero before the first part arrives.
176
+
177
+ | Property | Value |
178
+ |---|---|
179
+ | Type | Reporter |
180
+ | Opcode | `pairingRequiredParts` |
181
+ | `SESSION` | String, default: `pairing-1` |
182
+
183
+ ### `missing parts of [SESSION]`
184
+
185
+ Returns the one-based part numbers that have not been read yet, separated by commas.
186
+
187
+ | Property | Value |
188
+ |---|---|
189
+ | Type | Reporter |
190
+ | Opcode | `pairingMissingParts` |
191
+ | `SESSION` | String, default: `pairing-1` |
192
+
193
+ ### `show pairing QR part [INDEX] of [SESSION] on this sprite`
194
+
195
+ Shows the selected one-based part using a temporary sprite skin.
196
+
197
+ | Property | Value |
198
+ |---|---|
199
+ | Type | Command |
200
+ | Opcode | `showPairingQrPart` |
201
+ | `INDEX` | Number, default: `1` |
202
+ | `SESSION` | String, default: `pairing-1` |
203
+
204
+ ### `show next pairing QR part of [SESSION] on this sprite`
205
+
206
+ Shows the next part and wraps from the last part back to the first.
207
+
208
+ | Property | Value |
209
+ |---|---|
210
+ | Type | Command |
211
+ | Opcode | `showNextPairingQrPart` |
212
+ | `SESSION` | String, default: `pairing-1` |
213
+
214
+ ### `pairing QR part count of [SESSION]`
215
+
216
+ Returns how many parts are prepared for display, or zero when none are.
217
+
218
+ | Property | Value |
219
+ |---|---|
220
+ | Type | Reporter |
221
+ | Opcode | `pairingQrPartCount` |
222
+ | `SESSION` | String, default: `pairing-1` |
223
+
224
+ ### `current pairing QR part of [SESSION]`
225
+
226
+ Returns the one-based part selected for display, or zero when none is selected.
227
+
228
+ | Property | Value |
229
+ |---|---|
230
+ | Type | Reporter |
231
+ | Opcode | `pairingQrCurrentPart` |
232
+ | `SESSION` | String, default: `pairing-1` |
233
+
234
+ ### `pairing QR part [INDEX] of [SESSION] as SVG`
235
+
236
+ Returns the part as SVG markup so a project can display it its own way.
237
+
238
+ | Property | Value |
239
+ |---|---|
240
+ | Type | Reporter |
241
+ | Opcode | `pairingQrPartSvg` |
242
+ | `INDEX` | Number, default: `1` |
243
+ | `SESSION` | String, default: `pairing-1` |
244
+
245
+ ### `pairing QR part [INDEX] of [SESSION] as data URI`
246
+
247
+ Returns the part as a base64 SVG data URI for costumes and HTML images.
248
+
249
+ | Property | Value |
250
+ |---|---|
251
+ | Type | Reporter |
252
+ | Opcode | `pairingQrPartDataUri` |
253
+ | `INDEX` | Number, default: `1` |
254
+ | `SESSION` | String, default: `pairing-1` |
255
+
256
+ ### `end pairing QR display of [SESSION]`
257
+
258
+ Restores the sprites this session changed. The session itself stays open.
259
+
260
+ | Property | Value |
261
+ |---|---|
262
+ | Type | Command |
263
+ | Opcode | `endPairingQrDisplay` |
264
+ | `SESSION` | String, default: `pairing-1` |
265
+
266
+ ### `pairing phase of [SESSION]`
267
+
268
+ Returns the session phase, such as offer-ready, receiving, answer-ready, connecting, or connected.
269
+
270
+ | Property | Value |
271
+ |---|---|
272
+ | Type | Reporter |
273
+ | Opcode | `pairingPhase` |
274
+ | `SESSION` | String, default: `pairing-1` |
275
+
276
+ ### `pairing connection state of [SESSION]`
277
+
278
+ Returns the WebRTC connection state reported for this session's peer.
279
+
280
+ | Property | Value |
281
+ |---|---|
282
+ | Type | Reporter |
283
+ | Opcode | `pairingConnectionState` |
284
+ | `SESSION` | String, default: `pairing-1` |
285
+
286
+ ### `pairing [SESSION] connected?`
287
+
288
+ Reports whether WebRTC has established the connection for this session.
289
+
290
+ | Property | Value |
291
+ |---|---|
292
+ | Type | Boolean |
293
+ | Opcode | `isPairingConnected` |
294
+ | `SESSION` | String, default: `pairing-1` |
295
+
296
+ ### `wait until pairing [SESSION] is connected`
297
+
298
+ Waits for the connection, or fails when the session is cancelled, times out, or fails.
299
+
300
+ | Property | Value |
301
+ |---|---|
302
+ | Type | Command |
303
+ | Opcode | `waitUntilPairingConnected` |
304
+ | `SESSION` | String, default: `pairing-1` |
305
+
306
+ ### `pairing error code of [SESSION]`
307
+
308
+ Returns the latest error code, such as hash-mismatch or peer-mismatch, or an empty string.
309
+
310
+ | Property | Value |
311
+ |---|---|
312
+ | Type | Reporter |
313
+ | Opcode | `pairingError` |
314
+ | `SESSION` | String, default: `pairing-1` |
315
+
316
+ ### `pairing error message of [SESSION]`
317
+
318
+ Returns the latest error message. Pairing codes and QR payloads are never included.
319
+
320
+ | Property | Value |
321
+ |---|---|
322
+ | Type | Reporter |
323
+ | Opcode | `pairingErrorMessage` |
324
+ | `SESSION` | String, default: `pairing-1` |
325
+
326
+ ### `local peer of [SESSION]`
327
+
328
+ Returns the name this machine uses for itself in this exchange.
329
+
330
+ | Property | Value |
331
+ |---|---|
332
+ | Type | Reporter |
333
+ | Opcode | `pairingLocalPeer` |
334
+ | `SESSION` | String, default: `pairing-1` |
335
+
336
+ ### `remote peer of [SESSION]`
337
+
338
+ Returns the name this machine uses for the other machine, which is also its WebRTC peer name.
339
+
340
+ | Property | Value |
341
+ |---|---|
342
+ | Type | Reporter |
343
+ | Opcode | `pairingRemotePeer` |
344
+ | `SESSION` | String, default: `pairing-1` |
345
+
346
+ ### `exchange id of [SESSION]`
347
+
348
+ Returns the identifier of the current offer and answer exchange. A retry allocates a new one.
349
+
350
+ | Property | Value |
351
+ |---|---|
352
+ | Type | Reporter |
353
+ | Opcode | `pairingExchangeId` |
354
+ | `SESSION` | String, default: `pairing-1` |
355
+
356
+ ### `remaining seconds of [SESSION]`
357
+
358
+ Returns the seconds left before the exchange times out.
359
+
360
+ | Property | Value |
361
+ |---|---|
362
+ | Type | Reporter |
363
+ | Opcode | `pairingRemainingSeconds` |
364
+ | `SESSION` | String, default: `pairing-1` |
365
+
366
+ ### `pairing sessions`
367
+
368
+ Returns the open session names, separated by commas.
369
+
370
+ | Property | Value |
371
+ |---|---|
372
+ | Type | Reporter |
373
+ | Opcode | `pairingSessions` |
374
+
375
+ ### `cancel pairing [SESSION]`
376
+
377
+ Cancels the exchange and releases displays, buffers, and unconnected peers. Established connections stay up.
378
+
379
+ | Property | Value |
380
+ |---|---|
381
+ | Type | Command |
382
+ | Opcode | `cancelPairing` |
383
+ | `SESSION` | String, default: `pairing-1` |
384
+
385
+ ### `retry pairing [SESSION]`
386
+
387
+ Starts a new exchange for the same session. QR codes from the previous exchange stop being accepted.
388
+
389
+ | Property | Value |
390
+ |---|---|
391
+ | Type | Command |
392
+ | Opcode | `retryPairing` |
393
+ | `SESSION` | String, default: `pairing-1` |
394
+
395
+ ### `set pairing timeout of [SESSION] to [SECONDS] seconds`
396
+
397
+ Sets how long the exchange waits, measured on this machine's own clock.
398
+
399
+ | Property | Value |
400
+ |---|---|
401
+ | Type | Command |
402
+ | Opcode | `setPairingTimeout` |
403
+ | `SESSION` | String, default: `pairing-1` |
404
+ | `SECONDS` | Number, default: `600` |
405
+
406
+ <!-- END GENERATED BLOCKS -->
407
+
408
+ ## Task management
409
+
410
+ Track implementation in this repository's GitHub Issues. Design documents live in [docs/design/qr-pairing](docs/design/qr-pairing/README.md): requirements, architecture, data flow, type contracts, the block contract, the extraction plan and the verification plan.
411
+
412
+ ## License
413
+
414
+ SPDX-License-Identifier: MPL-2.0