openrtc 2.3.1 → 2.5.1

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 (92) hide show
  1. package/README.md +77 -5
  2. package/dist/BrowserManagedBroadcastHost-ARCP5MGJ.js +1 -0
  3. package/dist/{ITransport-CjxCQ6q7.d.ts → Connection-CTpuImO-.d.ts} +610 -95
  4. package/dist/DelegatingRuntimeAdapter-D67Ozqo3.d.ts +154 -0
  5. package/dist/IpcRuntimeAdapter-Ddx0MtSI.d.ts +86 -0
  6. package/dist/WasmBridge-ZXBCXBBM.js +1 -0
  7. package/dist/{assembly-Bmi65-tK.d.ts → assembly-DvfsYFCL.d.ts} +41 -19
  8. package/dist/{capabilities-BPuynml5.d.ts → capabilities-CmzzxSvW.d.ts} +14 -7
  9. package/dist/chunk-2GX6GB6L.js +1 -0
  10. package/dist/chunk-2K47N3QP.js +2 -0
  11. package/dist/chunk-72NEEJQG.js +1 -0
  12. package/dist/chunk-AYHD4X2R.js +1 -0
  13. package/dist/chunk-BFN3AVHA.js +2 -0
  14. package/dist/chunk-BGWJHPBC.js +2 -0
  15. package/dist/chunk-CCCYE3XN.js +1 -0
  16. package/dist/chunk-EIJNERKZ.js +1 -0
  17. package/dist/chunk-FEF3DPPB.js +1 -0
  18. package/dist/chunk-GHAJW6SZ.js +2 -0
  19. package/dist/chunk-GTHSRQ7Y.js +2 -0
  20. package/dist/chunk-JPBW67B3.js +1 -0
  21. package/dist/chunk-LLZT3MR6.js +118 -0
  22. package/dist/chunk-N6LGTNUI.js +1 -0
  23. package/dist/{chunk-4MLJXEVV.js → chunk-NDR2ANQB.js} +1 -1
  24. package/dist/chunk-OT7IPLZE.js +1 -0
  25. package/dist/chunk-OWKAJPOI.js +1 -0
  26. package/dist/chunk-TYZS5LXQ.js +2 -0
  27. package/dist/chunk-VFDCIEP6.js +2 -0
  28. package/dist/chunk-VTYVIKTM.js +1 -0
  29. package/dist/chunk-XTYALGJN.js +2 -0
  30. package/dist/{chunk-KRJECX7X.js → chunk-Z4KJHIV5.js} +1 -1
  31. package/dist/chunk-Z54XUIKZ.js +1 -0
  32. package/dist/{controlPlane-KVFZ7BR5.js → controlPlane-IIPEJ77Q.js} +1 -1
  33. package/dist/coreClient.d.ts +3 -4
  34. package/dist/coreClient.js +1 -1
  35. package/dist/{deviceEnrollment-VACC64CQ.js → deviceEnrollment-ZQZ2WP5H.js} +2 -2
  36. package/dist/{deviceKey-Y35WSEQQ.js → deviceKey-EIMDBAQS.js} +1 -1
  37. package/dist/{dist-FIVA6EPA.js → dist-XO466BRG.js} +18 -0
  38. package/dist/index.d.ts +7 -7
  39. package/dist/index.js +1 -1
  40. package/dist/{ipc-CPnNwv6f.d.ts → ipc-DgzKqHH3.d.ts} +3 -0
  41. package/dist/ipcFactory-PRAJ7JP7.js +1 -0
  42. package/dist/native.d.ts +21 -0
  43. package/dist/native.js +1 -0
  44. package/dist/node-browser.d.ts +8 -0
  45. package/dist/node-browser.js +1 -0
  46. package/dist/node.d.ts +87 -0
  47. package/dist/node.js +2 -0
  48. package/dist/openrtc-VCS6V3AN.js +2 -0
  49. package/dist/openrtc_bg.wasm +0 -0
  50. package/dist/protocol-BSBjMb7p.d.ts +6 -0
  51. package/dist/room-ULZ2FGWV.js +1 -0
  52. package/dist/runtime/WasmRuntimeAdapter.d.ts +4 -5
  53. package/dist/runtime/WasmRuntimeAdapter.js +1 -1
  54. package/dist/runtime/device-status.d.ts +1 -2
  55. package/dist/runtime/factories.d.ts +352 -8
  56. package/dist/runtime/factories.js +1 -1
  57. package/dist/runtime/index.d.ts +15 -161
  58. package/dist/runtime/index.js +1 -1
  59. package/dist/server-browser.d.ts +14 -0
  60. package/dist/server-browser.js +1 -0
  61. package/dist/server.d.ts +44 -0
  62. package/dist/server.js +2 -0
  63. package/dist/service-node.d.ts +155 -0
  64. package/dist/service-node.js +1 -0
  65. package/dist/testing/native.d.ts +11 -0
  66. package/dist/testing/native.js +1 -0
  67. package/dist/testing.d.ts +47 -20
  68. package/dist/testing.js +1 -1
  69. package/dist/transport/index.d.ts +7 -18
  70. package/dist/transport/index.js +1 -1
  71. package/dist/types-Cyc-mPjk.d.ts +650 -0
  72. package/package.json +35 -3
  73. package/dist/DelegatingRuntimeAdapter-DE4LTGAJ.d.ts +0 -399
  74. package/dist/IpcRuntimeAdapter-CE0va9Tr.d.ts +0 -19
  75. package/dist/WasmBridge-75F6GKHG.js +0 -1
  76. package/dist/chunk-2L6ULC32.js +0 -1
  77. package/dist/chunk-2SUSPEC4.js +0 -1
  78. package/dist/chunk-7BJKDBLP.js +0 -2
  79. package/dist/chunk-AJQVMVYW.js +0 -1
  80. package/dist/chunk-BOS554IK.js +0 -2
  81. package/dist/chunk-HEAJ7ZUR.js +0 -1
  82. package/dist/chunk-HUG5AX7R.js +0 -1
  83. package/dist/chunk-Q6KXVTSH.js +0 -2
  84. package/dist/chunk-R35AWMNY.js +0 -1
  85. package/dist/chunk-UILOGUOU.js +0 -1
  86. package/dist/chunk-WA434EWD.js +0 -2
  87. package/dist/chunk-XAL4T6TB.js +0 -3
  88. package/dist/chunk-Z2IS6NH2.js +0 -2
  89. package/dist/ipcFactory-32R47AVK.js +0 -1
  90. package/dist/openrtc-MWLNTJI7.js +0 -2
  91. package/dist/types--jQzenZR.d.ts +0 -295
  92. package/dist/types-DDhMQkK8.d.ts +0 -621
package/README.md CHANGED
@@ -100,6 +100,75 @@ const team = await rtc.rooms.join('team', {
100
100
  Every long-lived capability has `close()`; spaces and rooms also have
101
101
  `leave()`. One handle activates exactly one avenue.
102
102
 
103
+ ## Developer service nodes
104
+
105
+ `openrtc/service-node` exposes a preview WebSocket-server-like application
106
+ surface for browser/WASM. Browser code supplies a callback
107
+ that obtains a short-lived authority grant from the developer backend; it must
108
+ never receive a developer `sk_*` key. Trusted Node/server code can mint that
109
+ grant with `openrtc/server`.
110
+
111
+ ```ts
112
+ import { ServiceNode } from 'openrtc/service-node';
113
+
114
+ const service = new ServiceNode({
115
+ client: rtc,
116
+ apiKey,
117
+ serviceId: 'world-simulation',
118
+ generation: 1,
119
+ shardIds: ['zone-a'],
120
+ deviceId: await rtc.devices.localId(),
121
+ deviceSigner,
122
+ assignmentSigner,
123
+ grantIssuer: (request) => fetch('/api/openrtc/authority-grant', {
124
+ method: 'POST',
125
+ body: JSON.stringify(request),
126
+ }).then((response) => response.json()),
127
+ });
128
+
129
+ service.onConnection(({ connection }) => console.log(connection.id));
130
+ await service.listen('world-42');
131
+ ```
132
+
133
+ Trusted Node.js authorities use the dedicated Rust host through `openrtc/node`;
134
+ no application IPC bridge or native addon is required:
135
+
136
+ ```ts
137
+ import { ServiceNode } from 'openrtc/node';
138
+
139
+ const service = new ServiceNode({
140
+ apiKey: process.env.OPENRTC_API_KEY!,
141
+ secretKey: process.env.OPENRTC_SECRET_KEY!,
142
+ serviceId: 'world-simulation',
143
+ generation: 1,
144
+ shardIds: ['zone-a'],
145
+ deviceId: 'world-simulation-1',
146
+ stateDirectory: '/var/lib/world-simulation/openrtc', // private and persistent
147
+ });
148
+
149
+ service.onConnection(({ connection }) => {
150
+ connection.onMessage((message) => console.log(connection.peerId, message));
151
+ });
152
+ await service.listen('world-42');
153
+ ```
154
+
155
+ The released package must contain the signed native artifact for the current
156
+ `process.platform`/`process.arch`; a missing artifact fails closed. A reviewed
157
+ source checkout can instead opt into `buildFromSource: true` with an absolute
158
+ `sourceRoot`. The developer secret is sent only over the child's stdin, and
159
+ the Node layer only demultiplexes bounded application messages and authority
160
+ assignments. The Rust peer-session actor remains the sole lifecycle owner.
161
+ This preview Node facade intentionally owns one room and exposes only
162
+ message-oriented `send`/`onMessage`/`onClose` plus assignment publication; it
163
+ does not yet claim the browser facade's streams, media, or full Room
164
+ namespaces. Browser resolution of `openrtc/node` fails closed; browser code
165
+ uses WASM plus delegated grants through `openrtc/service-node`. Native Rust
166
+ uses `ControlPlane::join_authority_room` directly and does not require
167
+ JavaScript.
168
+
169
+ The authority surface remains preview-only until signed prebuilt artifacts and
170
+ source-bound browser, Node, and native Rust authority E2E lanes pass.
171
+
103
172
  ## Optional trust adapters
104
173
 
105
174
  Attestation is evaluated at device enrollment, key rotation, recovery, or risk
@@ -166,8 +235,11 @@ overrides live only in `openrtc/testing`.
166
235
 
167
236
  Relay, durable membership, managed attestation, MoQ, and BLE require both
168
237
  portal enablement and explicit runtime opt-in. Avenues default to 8 peers.
169
- Reviewed non-room avenues may request 9-12 peers. Reviewed rooms may request up
170
- to 99 peers with `advancedFanout: true`; OpenRTC then maintains a symmetric
171
- four-neighbor overlay and forwards named-channel messages with bounded hops and
172
- duplicate suppression. `state()` is intentionally unavailable in sparse rooms;
173
- use a named channel with application-owned latest-state revisions.
238
+ Reviewed non-room avenues may request 9-12 peers. Rooms default to
239
+ `architecture: 'auto'` and `payload: 'reliable'`. Small rooms use a direct
240
+ mesh. A room may declare `payload: 'latest-state'` so auto can use the reviewed
241
+ four-neighbor sparse graph through 50 members. Reliable rooms above the safe
242
+ mesh limit require
243
+ the explicitly enabled, content-blind managed path. `advancedFanout: true` is a
244
+ one-release alias for fixed sparse latest-state delivery. `state()` remains
245
+ unavailable in sparse rooms; use a named channel with application revisions.
@@ -0,0 +1 @@
1
+ import {b,a as a$2}from'./chunk-BGWJHPBC.js';import {a}from'./chunk-AYHD4X2R.js';import {b as b$1,a as a$1}from'./chunk-LLZT3MR6.js';import'./chunk-JPBW67B3.js';var y=class{constructor(e){this.deps=e;this.active=null;this.commands=Promise.resolve();}apply(e){let t=this.commands.then(()=>this.applyOrdered(e));return this.commands=t.catch(()=>{}),t}dispose(){this.retire();}async applyOrdered(e){if(e.type==="open"){await this.open(e);return}if(this.isCurrent(e)){if(e.type==="publish"){if(!this.active||!["owner","publisher","processor"].includes(this.active.role))throw new Error("broadcast role cannot publish");let t=await this.duplex(e.sourceSlot,true),r=this.active.writers.get(e.sourceSlot);r||(r=t.writable.getWriter(),this.active.writers.set(e.sourceSlot,r)),await b(r.write(e.object),2e3,"broadcast object write");return}if(e.type==="subscribe"){if(!this.active||!["owner","subscriber","processor"].includes(this.active.role))throw new Error("broadcast role cannot subscribe");await Promise.all(e.sourceSlots.map(async t=>{let r=await this.duplex(t,false),a=this.active;this.readObjects(a,t,r.readable).catch(async()=>{if(!(this.active!==a||a.closed))try{await this.deps.observe({type:"failed",broadcastGeneration:a.broadcastGeneration,grantGeneration:a.grantGeneration,retryable:!1,code:"relay-object-rejected"});}catch{}});}));return}this.retire(),await this.deps.observe({type:"closed",broadcastGeneration:e.broadcastGeneration,grantGeneration:e.grantGeneration,code:e.reason});}}async open(e){this.retire();try{let t=this.deps.openSession?await this.deps.openSession(e.relay.relayUrl,e.relay.token):await A(e.relay.relayUrl,e.relay.token),r={broadcastGeneration:e.broadcastGeneration,grantGeneration:e.grantGeneration,role:e.role,namespace:e.relay.namespace,session:t,duplexes:new Map,writers:new Map,closed:!1};if(this.active=r,t.onClose(()=>{this.active!==r||r.closed||this.deps.observe({type:"failed",broadcastGeneration:r.broadcastGeneration,grantGeneration:r.grantGeneration,retryable:!0,code:"relay-session-closed"});}),await b(t.init(),1e4,"broadcast MoQ Draft 14 setup"),await b(t.waitForSetup(),5e3,"broadcast MoQ Draft 14 setup"),this.active!==r)return;await this.deps.observe({type:"opened",broadcastGeneration:r.broadcastGeneration,grantGeneration:r.grantGeneration});}catch{this.matches(e)&&(this.retire(),await this.deps.observe({type:"failed",broadcastGeneration:e.broadcastGeneration,grantGeneration:e.grantGeneration,retryable:true,code:"relay-open-failed"}));}}async duplex(e,t){let r=this.active;if(!r)throw new Error("broadcast session is unavailable");let a=`${t?"pub":"sub"}:${e}`,o=r.duplexes.get(a);if(o)return o;let n=`${r.namespace}/source/${e}`,l=`${r.namespace}/control/${r.grantGeneration}/${e}`,c=await b(r.session.openObjectStreamDuplex(t?n:l,t?l:n,"openrtc-broadcast-1"),12e3,"broadcast MoQ Draft 14 object stream");if(this.active!==r)throw c.close(),new Error("broadcast generation retired during setup");return r.duplexes.set(a,c),c}async readObjects(e,t,r){let a=r.getReader();try{for(;this.active===e;){let o=await a.read();if(o.done)break;await this.deps.onObject({broadcastGeneration:e.broadcastGeneration,grantGeneration:e.grantGeneration,sourceSlot:t,object:o.value})&&await this.deps.observe({type:"usage",broadcastGeneration:e.broadcastGeneration,grantGeneration:e.grantGeneration,deliveredBytes:o.value.byteLength});}}finally{a.releaseLock();}}isCurrent(e){return this.matches(e)&&this.active?.closed===false}matches(e){return this.active?.broadcastGeneration===e.broadcastGeneration&&this.active.grantGeneration===e.grantGeneration}retire(){let e=this.active;if(this.active=null,!!e){e.closed=true;for(let t of e.writers.values())try{t.releaseLock();}catch{}for(let t of e.duplexes.values())try{t.close();}catch{}e.session.close();}}};async function A(m,e){let t=new URL(m);if(t.protocol!=="https:"||t.search||t.hash||!/^[A-Za-z0-9._-]{16,16384}$/.test(e))throw new Error("broadcast relay access is invalid");let r=new URL(t.toString());r.pathname=`${r.pathname.replace(/\/+$/,"")}/${encodeURIComponent(e)}`;let a=new WebTransport(r.toString());return await b(a.ready,15e3,"broadcast relay connection"),new a$2(a,t)}var g=class{constructor(e,t,r,a,o){this.wasm=e;this.handleId=t;this.drain=o;this.incoming=null;this.incomingWriters=new Map;this.sources=new Map;this.closed=false;let n=a[0]??"",l={beginOpenRtcMediaPublication:async s=>{let d=this.wasm.__beginOpenRtcBroadcastPublication(this.handleId,n,s.publicationId||void 0,s.kind,s.codec,s.clockRate,s.codedWidth,s.codedHeight,s.channels);return await this.drain(),{...d,control:new Uint8Array(d.control??[])}},encodeOpenRtcMediaSample:async s=>(this.wasm.__publishOpenRtcBroadcastSample(this.handleId,s.publicationId,s.timestampUs,s.durationUs,s.keyframe,s.discardable,s.payload),await this.drain(),new Uint8Array),pauseOpenRtcMediaPublication:async s=>{this.wasm.__pauseOpenRtcBroadcastPublication(this.handleId,s);},retireOpenRtcMediaPublication:async s=>{this.wasm.__retireOpenRtcBroadcastPublication(this.handleId,s);},retireOpenRtcMediaReceiver:async(s,d)=>{this.wasm.__retireOpenRtcMediaReceiver(s,d);},decodeOpenRtcMediaControl:async s=>this.wasm.__decodeOpenRtcMediaControl(s),decodeOpenRtcMediaChunk:async s=>{let d=this.wasm.__decodeOpenRtcMediaChunk(s);return {...d,payload:new Uint8Array(d.payload)}}},c={createBidirectionalStream:async()=>({protocol:"openrtc-media/1",readable:new ReadableStream({start(s){s.close();}}),writable:new WritableStream})};if(this.media=new b$1(c,l,(s,d)=>(this.incoming=d,()=>{this.incoming=null;})),["owner","publisher","processor"].includes(r)&&!n)throw new Error("[OpenRTC] Broadcast publisher grant has no source slot.")}addTrack(e,t){return this.media.addTrack(e,t)}removeTrack(e){return this.media.removeTrack(e)}onTrack(e){return this.media.onTrack(t=>e({...t,source:{id:this.sources.get(t.receiver.id)??"unknown"}}))}senders(){return this.media.senders()}receivers(){return this.media.receivers()}async accept(e,t){if(this.closed)return false;let r=this.wasm.__acceptOpenRtcBroadcastObject(this.handleId,e,t);if(!r)return false;let a=r.publicationId;if(this.sources.set(a,r.sourceSlot),r.control){let n=this.incomingWriters.get(a);if(r.controlType==="stop")return n?.generation===r.mediaGeneration&&(this.incomingWriters.delete(a),await n.writer.close().catch(()=>{})),this.sources.delete(a),true;if(r.controlType!=="publish"||n&&n.generation>=r.mediaGeneration)return true;n&&await n.writer.close().catch(()=>{});let l=new TransformStream,c=l.writable.getWriter();return this.incomingWriters.set(a,{generation:r.mediaGeneration,writer:c}),this.incoming?.({peerId:r.sourceSlot,protocol:"openrtc-media/1",readable:l.readable,writable:new WritableStream}),await c.write(a$1(new Uint8Array(r.control))),true}if(!r.media)return true;let o=this.incomingWriters.get(a);return !o||o.generation!==r.mediaGeneration||await o.writer.write(a$1(new Uint8Array(r.media))),true}async close(){this.closed||(this.closed=true,await Promise.all([...this.incomingWriters.values()].map(({writer:e})=>e.close().catch(()=>{}))),this.incomingWriters.clear(),this.sources.clear(),await this.media.close());}};var M=class{constructor(e){this.deps=e;this.wasmPromise=null;this.sessions=new Set;this.publishers=new Set;}async open(e){return this.openResolved(e)}async create(e){let t=e.auth??this.deps.defaultAuth;if(!t||!this.deps.manage||!this.deps.deviceId)throw new Error("Managed broadcast creation requires authenticated control-plane access.");let r=await this.deps.manage({action:"create",deviceId:await this.deps.deviceId(),auth:t,create:e});if(!r)throw new Error("Managed broadcast creation returned no grant.");return r}async renew(e,t){let r=await this.manageGrant("renew",e,t);if(!r)throw new Error("Managed broadcast renewal returned no grant.");return r}async invite(e,t,r){let a=await this.manage("invite",r,{grant:e,invitation:t});if(!a)throw new Error("Managed broadcast invitation returned no token.");return a}async accept(e,t){let r=await this.manage("accept",t,{invite:e});if(!r)throw new Error("Managed broadcast invitation acceptance returned no grant.");return r}async revoke(e,t,r){await this.manage("revoke",r,{grant:e,invite:t});}async leave(e,t){await this.manageGrant("leave",e,t);}async closeGrant(e,t){await this.manageGrant("close",e,t);}async preparePublisher(){let e=await this.wasm(),t=a.create(e);this.publishers.add(t);let r=false;return {verificationKey:t.verificationKey.slice(),open:a=>{if(r)throw new Error("broadcast publisher signer is closed");return this.openResolved(a,t)},close:async()=>{r||(r=true,t.close(),this.publishers.delete(t));}}}async close(){await Promise.all([...this.sessions].map(e=>e.close()));for(let e of this.publishers)e.close();this.publishers.clear();}wasm(){return this.wasmPromise??(this.wasmPromise=this.deps.loadWasm())}async manageGrant(e,t,r){return this.manage(e,r,{grant:t})}async manage(e,t,r){let a=t??this.deps.defaultAuth;if(!a||!this.deps.manage||!this.deps.deviceId)throw new Error(`Managed broadcast ${e} requires authenticated control-plane access.`);return this.deps.manage({action:e,deviceId:await this.deps.deviceId(),auth:a,...r})}async openResolved(e,t){let[r,a]=await Promise.all([this.wasm(),this.deps.resolve(e,t?.verificationKey)]),o=Date.now(),n;if(t)n=await t.open(a,o);else {let i=r.__prepareOpenRtcBroadcastGrant(a.token,a.issuerPublicKey,o),v=await a.signBindingChallenge(i.signingBytes);n=r.__openOpenRtcBroadcast(a.token,a.issuerPublicKey,i.handle,v,void 0,o);}let l={...a.relay},c=n.state,s=false,d=false,h=false,u=null,f=new y({openSession:this.deps.openSession,observe:async i=>{if(!s){i.type==="opened"&&(c="live"),i.type==="failed"&&(c="retrying"),i.type==="closed"&&(c="closed");try{r.__observeOpenRtcBroadcastAdapter(n.handleId,i);}finally{await w();}}},onObject:async i=>s||!u?false:u.accept(i.sourceSlot,i.object)}),w=async()=>{if(!s){if(d){h=true;return}d=true;try{do{h=!1;let i=r.__takeOpenRtcBroadcastActions(n.handleId,32);for(let v of i)await f.apply(this.adapterAction(v,l));i.length===32&&(h=!0);}while(h)}finally{d=false;}}};u=new g(r,n.handleId,n.role,n.sourceSlots??[],w);let b={id:n.id,role:n.role,get state(){return c},media:u,getStats:async()=>({...r.__getOpenRtcBroadcastStats(n.handleId)}),close:async()=>{s||(s=true,f.dispose(),await u?.close(),r.__closeOpenRtcBroadcast(n.handleId),this.sessions.delete(b));}};this.sessions.add(b);try{return await w(),b}catch(i){throw await b.close(),i}}adapterAction(e,t){let r={broadcastGeneration:e.broadcastGeneration,grantGeneration:e.grantGeneration};if(e.type==="open"&&e.role)return {type:"open",role:e.role,relay:t,...r};if(e.type==="publish"&&e.sourceSlot&&e.object)return {type:"publish",sourceSlot:e.sourceSlot,object:e.object,discardable:e.discardable===true,control:e.control===true,...r};if(e.type==="subscribe"&&e.sourceSlots)return {type:"subscribe",sourceSlots:e.sourceSlots,...r};if(e.type==="close"&&e.reason)return {type:"close",reason:e.reason,...r};throw new Error("Rust returned an invalid broadcast adapter action")}};export{M as BrowserManagedBroadcastHost};