@roboflow/inference-sdk 0.1.6 → 0.1.7

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/dist/index.es.js CHANGED
@@ -1,20 +1,24 @@
1
- var T = Object.defineProperty;
2
- var O = (a, e, t) => e in a ? T(a, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[e] = t;
3
- var u = (a, e, t) => O(a, typeof e != "symbol" ? e + "" : e, t);
4
- class _ {
1
+ var W = Object.defineProperty;
2
+ var U = (o, e, t) => e in o ? W(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t;
3
+ var p = (o, e, t) => U(o, typeof e != "symbol" ? e + "" : e, t);
4
+ var b;
5
+ const I = typeof process < "u" && ((b = process.env) != null && b.RF_API_BASE_URL) ? process.env.RF_API_BASE_URL : "https://api.roboflow.com", O = [
6
+ "https://serverless.roboflow.com"
7
+ ];
8
+ class v {
5
9
  /**
6
10
  * @private
7
11
  * Use InferenceHTTPClient.init() instead
8
12
  */
9
13
  constructor(e, t = "https://serverless.roboflow.com") {
10
- u(this, "apiKey");
11
- u(this, "serverUrl");
14
+ p(this, "apiKey");
15
+ p(this, "serverUrl");
12
16
  this.apiKey = e, this.serverUrl = t;
13
17
  }
14
18
  static init({ apiKey: e, serverUrl: t }) {
15
19
  if (!e)
16
20
  throw new Error("apiKey is required");
17
- return new _(e, t);
21
+ return new v(e, t);
18
22
  }
19
23
  /**
20
24
  * Initialize a WebRTC worker pipeline
@@ -44,59 +48,59 @@ class _ {
44
48
  async initializeWebrtcWorker({
45
49
  offer: e,
46
50
  workflowSpec: t,
47
- workspaceName: n,
48
- workflowId: r,
49
- config: o = {}
51
+ workspaceName: r,
52
+ workflowId: a,
53
+ config: n = {}
50
54
  }) {
51
55
  if (!e || !e.sdp || !e.type)
52
56
  throw new Error("offer with sdp and type is required");
53
- const i = !!t, s = !!(n && r);
57
+ const i = !!t, s = !!(r && a);
54
58
  if (!i && !s)
55
59
  throw new Error("Either workflowSpec OR (workspaceName + workflowId) is required");
56
60
  if (i && s)
57
61
  throw new Error("Provide either workflowSpec OR (workspaceName + workflowId), not both");
58
62
  const {
59
- imageInputName: d = "image",
63
+ imageInputName: l = "image",
60
64
  streamOutputNames: c = [],
61
- dataOutputNames: l = ["string"],
62
- threadPoolWorkers: p = 4,
63
- workflowsParameters: y = {},
64
- iceServers: w,
65
- processingTimeout: k,
66
- requestedPlan: S,
67
- requestedRegion: f
68
- } = o, g = {
65
+ dataOutputNames: h = ["string"],
66
+ threadPoolWorkers: y = 4,
67
+ workflowsParameters: f = {},
68
+ iceServers: d,
69
+ processingTimeout: S,
70
+ requestedPlan: _,
71
+ requestedRegion: g
72
+ } = n, R = {
69
73
  type: "WorkflowConfiguration",
70
- image_input_name: d,
71
- workflows_parameters: y,
72
- workflows_thread_pool_workers: p,
74
+ image_input_name: l,
75
+ workflows_parameters: f,
76
+ workflows_thread_pool_workers: y,
73
77
  cancel_thread_pool_tasks_on_exit: !0,
74
78
  video_metadata_input_name: "video_metadata"
75
79
  };
76
- i ? g.workflow_specification = t : (g.workspace_name = n, g.workflow_id = r);
80
+ i ? R.workflow_specification = t : (R.workspace_name = r, R.workflow_id = a);
77
81
  const m = {
78
- workflow_configuration: g,
82
+ workflow_configuration: R,
79
83
  api_key: this.apiKey,
80
84
  webrtc_realtime_processing: !0,
81
85
  webrtc_offer: {
82
86
  sdp: e.sdp,
83
87
  type: e.type
84
88
  },
85
- webrtc_config: w ? { iceServers: w } : null,
89
+ webrtc_config: d ? { iceServers: d } : null,
86
90
  stream_output: c,
87
- data_output: l
91
+ data_output: h
88
92
  };
89
- k !== void 0 && (m.processing_timeout = k), S !== void 0 && (m.requested_plan = S), f !== void 0 && (m.requested_region = f), console.trace("payload", m);
90
- const h = await fetch(`${this.serverUrl}/initialise_webrtc_worker`, {
93
+ S !== void 0 && (m.processing_timeout = S), _ !== void 0 && (m.requested_plan = _), g !== void 0 && (m.requested_region = g), console.trace("payload", m);
94
+ const u = await fetch(`${this.serverUrl}/initialise_webrtc_worker`, {
91
95
  method: "POST",
92
96
  headers: { "Content-Type": "application/json" },
93
97
  body: JSON.stringify(m)
94
98
  });
95
- if (!h.ok) {
96
- const E = await h.text().catch(() => "");
97
- throw new Error(`initialise_webrtc_worker failed (${h.status}): ${E}`);
99
+ if (!u.ok) {
100
+ const w = await u.text().catch(() => "");
101
+ throw new Error(`initialise_webrtc_worker failed (${u.status}): ${w}`);
98
102
  }
99
- return await h.json();
103
+ return await u.json();
100
104
  }
101
105
  async terminatePipeline({ pipelineId: e }) {
102
106
  if (!e)
@@ -109,8 +113,56 @@ class _ {
109
113
  }
110
114
  );
111
115
  }
116
+ /**
117
+ * Fetch TURN server configuration from Roboflow API
118
+ *
119
+ * This automatically fetches TURN server credentials for improved WebRTC
120
+ * connectivity through firewalls and NAT. Only applicable when using
121
+ * Roboflow serverless infrastructure.
122
+ *
123
+ * @returns Promise resolving to ICE server configuration, or null if not applicable
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * const client = InferenceHTTPClient.init({ apiKey: "your-api-key" });
128
+ * const iceServers = await client.fetchTurnConfig();
129
+ * // Returns: [{ urls: ["turn:..."], username: "...", credential: "..." }]
130
+ * ```
131
+ */
132
+ async fetchTurnConfig() {
133
+ if (!O.includes(this.serverUrl))
134
+ return null;
135
+ try {
136
+ const e = await fetch(
137
+ `${I}/webrtc_turn_config?api_key=${this.apiKey}`,
138
+ {
139
+ method: "GET",
140
+ headers: { "Content-Type": "application/json" }
141
+ }
142
+ );
143
+ if (!e.ok)
144
+ return console.warn(`[RFWebRTC] Failed to fetch TURN config (${e.status}), using defaults`), null;
145
+ const t = await e.json();
146
+ let r;
147
+ if (Array.isArray(t))
148
+ r = t;
149
+ else if (t.iceServers && Array.isArray(t.iceServers))
150
+ r = t.iceServers;
151
+ else if (t.urls)
152
+ r = [t];
153
+ else
154
+ return console.warn("[RFWebRTC] Invalid TURN config format, using defaults"), null;
155
+ return r.map((n) => ({
156
+ urls: Array.isArray(n.urls) ? n.urls : [n.urls],
157
+ username: n.username,
158
+ credential: n.credential
159
+ }));
160
+ } catch (e) {
161
+ return console.warn("[RFWebRTC] Error fetching TURN config:", e), null;
162
+ }
163
+ }
112
164
  }
113
- const K = {
165
+ const $ = {
114
166
  /**
115
167
  * Create a connector that uses API key directly
116
168
  *
@@ -129,33 +181,36 @@ const K = {
129
181
  * const answer = await connector.connectWrtc(offer, wrtcParams);
130
182
  * ```
131
183
  */
132
- withApiKey(a, e = {}) {
184
+ withApiKey(o, e = {}) {
133
185
  const { serverUrl: t } = e;
134
- return typeof window < "u" && console.warn(
186
+ typeof window < "u" && console.warn(
135
187
  "[Security Warning] Using API key directly in browser will expose it. Use connectors.withProxyUrl() for production. See: https://docs.roboflow.com/api-reference/authentication#securing-your-api-key"
136
- ), {
137
- connectWrtc: async (n, r) => {
138
- const o = _.init({ apiKey: a, serverUrl: t });
139
- return console.log("wrtcParams", r), await o.initializeWebrtcWorker({
140
- offer: n,
141
- workflowSpec: r.workflowSpec,
142
- workspaceName: r.workspaceName,
143
- workflowId: r.workflowId,
144
- config: {
145
- imageInputName: r.imageInputName,
146
- streamOutputNames: r.streamOutputNames,
147
- dataOutputNames: r.dataOutputNames,
148
- threadPoolWorkers: r.threadPoolWorkers,
149
- workflowsParameters: r.workflowsParameters,
150
- iceServers: r.iceServers,
151
- processingTimeout: r.processingTimeout,
152
- requestedPlan: r.requestedPlan,
153
- requestedRegion: r.requestedRegion
154
- }
155
- });
156
- },
188
+ );
189
+ const r = v.init({ apiKey: o, serverUrl: t });
190
+ return {
191
+ connectWrtc: async (a, n) => (console.log("wrtcParams", n), await r.initializeWebrtcWorker({
192
+ offer: a,
193
+ workflowSpec: n.workflowSpec,
194
+ workspaceName: n.workspaceName,
195
+ workflowId: n.workflowId,
196
+ config: {
197
+ imageInputName: n.imageInputName,
198
+ streamOutputNames: n.streamOutputNames,
199
+ dataOutputNames: n.dataOutputNames,
200
+ threadPoolWorkers: n.threadPoolWorkers,
201
+ workflowsParameters: n.workflowsParameters,
202
+ iceServers: n.iceServers,
203
+ processingTimeout: n.processingTimeout,
204
+ requestedPlan: n.requestedPlan,
205
+ requestedRegion: n.requestedRegion
206
+ }
207
+ })),
208
+ /**
209
+ * Fetch TURN server configuration for improved WebRTC connectivity
210
+ */
211
+ getIceServers: async () => await r.fetchTurnConfig(),
157
212
  // Store apiKey for cleanup
158
- _apiKey: a,
213
+ _apiKey: o,
159
214
  _serverUrl: t
160
215
  };
161
216
  },
@@ -165,24 +220,41 @@ const K = {
165
220
  * Your backend receives the offer and wrtcParams, adds the secret API key,
166
221
  * and forwards to Roboflow. This keeps your API key secure.
167
222
  *
168
- * @param proxyUrl - Backend proxy endpoint URL
169
- * @param options - Additional options (reserved for future use)
170
- * @returns Connector with connectWrtc method
223
+ * For improved WebRTC connectivity through firewalls, implement a separate
224
+ * endpoint for TURN server configuration that calls `fetchTurnConfig()`.
225
+ *
226
+ * @param proxyUrl - Backend proxy endpoint URL for WebRTC initialization
227
+ * @param options - Additional options
228
+ * @param options.turnConfigUrl - Optional URL for fetching TURN server configuration
229
+ * @returns Connector with connectWrtc and optional getIceServers methods
171
230
  *
172
231
  * @example
173
232
  * ```typescript
174
- * const connector = connectors.withProxyUrl('/api/init-webrtc');
175
- * const answer = await connector.connectWrtc(offer, wrtcParams);
233
+ * // Frontend: Create connector with TURN config endpoint
234
+ * const connector = connectors.withProxyUrl('/api/init-webrtc', {
235
+ * turnConfigUrl: '/api/turn-config'
236
+ * });
176
237
  * ```
177
238
  *
178
239
  * @example
179
- * Backend implementation (Express):
240
+ * Backend implementation (Express) with TURN server support:
180
241
  * ```typescript
242
+ * // Endpoint for TURN configuration (called first by SDK)
243
+ * app.get('/api/turn-config', async (req, res) => {
244
+ * const client = InferenceHTTPClient.init({
245
+ * apiKey: process.env.ROBOFLOW_API_KEY
246
+ * });
247
+ * const iceServers = await client.fetchTurnConfig();
248
+ * res.json({ iceServers });
249
+ * });
250
+ *
251
+ * // Endpoint for WebRTC initialization
181
252
  * app.post('/api/init-webrtc', async (req, res) => {
182
253
  * const { offer, wrtcParams } = req.body;
183
254
  * const client = InferenceHTTPClient.init({
184
255
  * apiKey: process.env.ROBOFLOW_API_KEY
185
256
  * });
257
+ *
186
258
  * const answer = await client.initializeWebrtcWorker({
187
259
  * offer,
188
260
  * workflowSpec: wrtcParams.workflowSpec,
@@ -200,70 +272,87 @@ const K = {
200
272
  * requestedRegion: wrtcParams.requestedRegion
201
273
  * }
202
274
  * });
275
+ *
203
276
  * res.json(answer);
204
277
  * });
205
278
  * ```
206
279
  */
207
- withProxyUrl(a, e = {}) {
280
+ withProxyUrl(o, e = {}) {
281
+ const { turnConfigUrl: t } = e;
208
282
  return {
209
- connectWrtc: async (t, n) => {
210
- const r = await fetch(a, {
283
+ connectWrtc: async (r, a) => {
284
+ const n = await fetch(o, {
211
285
  method: "POST",
212
286
  headers: { "Content-Type": "application/json" },
213
287
  body: JSON.stringify({
214
- offer: t,
215
- wrtcParams: n
288
+ offer: r,
289
+ wrtcParams: a
216
290
  })
217
291
  });
218
- if (!r.ok) {
219
- const o = await r.text().catch(() => "");
220
- throw new Error(`Proxy request failed (${r.status}): ${o}`);
292
+ if (!n.ok) {
293
+ const i = await n.text().catch(() => "");
294
+ throw new Error(`Proxy request failed (${n.status}): ${i}`);
221
295
  }
222
- return await r.json();
223
- }
296
+ return await n.json();
297
+ },
298
+ /**
299
+ * Fetch TURN server configuration from the proxy backend
300
+ * Only available if turnConfigUrl was provided
301
+ */
302
+ getIceServers: t ? async () => {
303
+ try {
304
+ const r = await fetch(t, {
305
+ method: "GET",
306
+ headers: { "Content-Type": "application/json" }
307
+ });
308
+ return r.ok ? (await r.json()).iceServers || null : (console.warn(`[RFWebRTC] Failed to fetch TURN config from proxy (${r.status})`), null);
309
+ } catch (r) {
310
+ return console.warn("[RFWebRTC] Error fetching TURN config from proxy:", r), null;
311
+ }
312
+ } : void 0
224
313
  };
225
314
  }
226
315
  };
227
- async function W(a = { video: !0 }) {
316
+ async function P(o = { video: !0 }) {
228
317
  try {
229
- console.log("[RFStreams] requesting with", a);
230
- const e = await navigator.mediaDevices.getUserMedia(a);
318
+ console.log("[RFStreams] requesting with", o);
319
+ const e = await navigator.mediaDevices.getUserMedia(o);
231
320
  return console.log("[RFStreams] got stream", e.getVideoTracks().map((t) => ({ id: t.id, label: t.label }))), e;
232
321
  } catch (e) {
233
322
  console.warn("[RFStreams] failed, falling back", e);
234
323
  const t = await navigator.mediaDevices.getUserMedia({ video: !0, audio: !1 });
235
- return console.log("[RFStreams] fallback stream", t.getVideoTracks().map((n) => ({ id: n.id, label: n.label }))), t;
324
+ return console.log("[RFStreams] fallback stream", t.getVideoTracks().map((r) => ({ id: r.id, label: r.label }))), t;
236
325
  }
237
326
  }
238
- function v(a) {
239
- a && (a.getTracks().forEach((e) => e.stop()), console.log("[RFStreams] Stream stopped"));
327
+ function C(o) {
328
+ o && (o.getTracks().forEach((e) => e.stop()), console.log("[RFStreams] Stream stopped"));
240
329
  }
241
- const j = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
330
+ const B = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
242
331
  __proto__: null,
243
- stopStream: v,
244
- useCamera: W
245
- }, Symbol.toStringTag, { value: "Module" })), F = 12;
246
- class b {
332
+ stopStream: C,
333
+ useCamera: P
334
+ }, Symbol.toStringTag, { value: "Module" })), N = 12;
335
+ class T {
247
336
  constructor() {
248
- u(this, "pendingFrames", /* @__PURE__ */ new Map());
337
+ p(this, "pendingFrames", /* @__PURE__ */ new Map());
249
338
  }
250
339
  /**
251
340
  * Process an incoming chunk and return the complete message if all chunks received
252
341
  */
253
- processChunk(e, t, n, r) {
254
- if (n === 1)
255
- return r;
342
+ processChunk(e, t, r, a) {
343
+ if (r === 1)
344
+ return a;
256
345
  this.pendingFrames.has(e) || this.pendingFrames.set(e, {
257
346
  chunks: /* @__PURE__ */ new Map(),
258
- totalChunks: n
347
+ totalChunks: r
259
348
  });
260
- const o = this.pendingFrames.get(e);
261
- if (o.chunks.set(t, r), o.chunks.size === n) {
262
- const i = Array.from(o.chunks.values()).reduce((c, l) => c + l.length, 0), s = new Uint8Array(i);
263
- let d = 0;
264
- for (let c = 0; c < n; c++) {
265
- const l = o.chunks.get(c);
266
- s.set(l, d), d += l.length;
349
+ const n = this.pendingFrames.get(e);
350
+ if (n.chunks.set(t, a), n.chunks.size === r) {
351
+ const i = Array.from(n.chunks.values()).reduce((c, h) => c + h.length, 0), s = new Uint8Array(i);
352
+ let l = 0;
353
+ for (let c = 0; c < r; c++) {
354
+ const h = n.chunks.get(c);
355
+ s.set(h, l), l += h.length;
267
356
  }
268
357
  return this.pendingFrames.delete(e), s;
269
358
  }
@@ -276,38 +365,38 @@ class b {
276
365
  this.pendingFrames.clear();
277
366
  }
278
367
  }
279
- function C(a) {
280
- const e = new DataView(a), t = e.getUint32(0, !0), n = e.getUint32(4, !0), r = e.getUint32(8, !0), o = new Uint8Array(a, F);
281
- return { frameId: t, chunkIndex: n, totalChunks: r, payload: o };
368
+ function E(o) {
369
+ const e = new DataView(o), t = e.getUint32(0, !0), r = e.getUint32(4, !0), a = e.getUint32(8, !0), n = new Uint8Array(o, N);
370
+ return { frameId: t, chunkIndex: r, totalChunks: a, payload: n };
282
371
  }
283
- async function P(a, e = 6e3) {
284
- if (a.iceGatheringState === "complete") return;
372
+ async function L(o, e = 6e3) {
373
+ if (o.iceGatheringState === "complete") return;
285
374
  let t = !1;
286
- const n = (r) => {
287
- r.candidate && r.candidate.type === "srflx" && (t = !0);
375
+ const r = (a) => {
376
+ a.candidate && a.candidate.type === "srflx" && (t = !0);
288
377
  };
289
- a.addEventListener("icecandidate", n);
378
+ o.addEventListener("icecandidate", r);
290
379
  try {
291
380
  await Promise.race([
292
- new Promise((r) => {
293
- const o = () => {
294
- a.iceGatheringState === "complete" && (a.removeEventListener("icegatheringstatechange", o), r());
381
+ new Promise((a) => {
382
+ const n = () => {
383
+ o.iceGatheringState === "complete" && (o.removeEventListener("icegatheringstatechange", n), a());
295
384
  };
296
- a.addEventListener("icegatheringstatechange", o);
385
+ o.addEventListener("icegatheringstatechange", n);
297
386
  }),
298
- new Promise((r, o) => {
387
+ new Promise((a, n) => {
299
388
  setTimeout(() => {
300
- t ? r() : (console.error("[ICE] timeout with NO srflx candidate! Connection may fail."), o(new Error("ICE gathering timeout without srflx candidate")));
389
+ t ? a() : (console.error("[ICE] timeout with NO srflx candidate! Connection may fail."), n(new Error("ICE gathering timeout without srflx candidate")));
301
390
  }, e);
302
391
  })
303
392
  ]);
304
393
  } finally {
305
- a.removeEventListener("icecandidate", n);
394
+ o.removeEventListener("icecandidate", r);
306
395
  }
307
396
  }
308
- function I(a) {
397
+ function x(o) {
309
398
  return new Promise((e) => {
310
- a.addEventListener("track", (t) => {
399
+ o.addEventListener("track", (t) => {
311
400
  t.streams && t.streams[0] && e(t.streams[0]);
312
401
  });
313
402
  });
@@ -315,71 +404,71 @@ function I(a) {
315
404
  const D = [
316
405
  { urls: ["stun:stun.l.google.com:19302"] }
317
406
  ];
318
- async function x(a, e) {
319
- const t = e ?? D, n = new RTCPeerConnection({
407
+ async function A(o, e) {
408
+ const t = e ?? D, r = new RTCPeerConnection({
320
409
  iceServers: t
321
410
  });
322
411
  try {
323
- n.addTransceiver("video", { direction: "recvonly" });
412
+ r.addTransceiver("video", { direction: "recvonly" });
324
413
  } catch (s) {
325
414
  console.warn("[RFWebRTC] Could not add transceiver:", s);
326
415
  }
327
- a.getVideoTracks().forEach((s) => {
416
+ o.getVideoTracks().forEach((s) => {
328
417
  try {
329
418
  s.contentHint = "detail";
330
419
  } catch {
331
420
  }
332
- n.addTrack(s, a);
421
+ r.addTrack(s, o);
333
422
  });
334
- const r = I(n), o = n.createDataChannel("roboflow-control", {
423
+ const a = x(r), n = r.createDataChannel("roboflow-control", {
335
424
  ordered: !0
336
- }), i = await n.createOffer();
337
- return await n.setLocalDescription(i), await P(n), {
338
- pc: n,
339
- offer: n.localDescription,
340
- remoteStreamPromise: r,
341
- dataChannel: o
425
+ }), i = await r.createOffer();
426
+ return await r.setLocalDescription(i), await L(r), {
427
+ pc: r,
428
+ offer: r.localDescription,
429
+ remoteStreamPromise: a,
430
+ dataChannel: n
342
431
  };
343
432
  }
344
- async function N(a) {
345
- const e = a.getSenders().find((n) => n.track && n.track.kind === "video");
433
+ async function q(o) {
434
+ const e = o.getSenders().find((r) => r.track && r.track.kind === "video");
346
435
  if (!e) return;
347
436
  const t = e.getParameters();
348
437
  t.encodings = t.encodings || [{}], t.encodings[0].scaleResolutionDownBy = 1;
349
438
  try {
350
439
  await e.setParameters(t);
351
- } catch (n) {
352
- console.warn("[RFWebRTC] Failed to set encoding parameters:", n);
440
+ } catch (r) {
441
+ console.warn("[RFWebRTC] Failed to set encoding parameters:", r);
353
442
  }
354
443
  }
355
- class R {
444
+ class F {
356
445
  /** @private */
357
- constructor(e, t, n, r, o, i, s) {
358
- u(this, "pc");
359
- u(this, "_localStream");
360
- u(this, "remoteStreamPromise");
361
- u(this, "pipelineId");
362
- u(this, "apiKey");
363
- u(this, "dataChannel");
364
- u(this, "reassembler");
365
- this.pc = e, this._localStream = t, this.remoteStreamPromise = n, this.pipelineId = r, this.apiKey = o, this.dataChannel = i, this.reassembler = new b(), this.dataChannel.binaryType = "arraybuffer", s && (this.dataChannel.addEventListener("open", () => {
366
- }), this.dataChannel.addEventListener("message", (d) => {
446
+ constructor(e, t, r, a, n, i, s) {
447
+ p(this, "pc");
448
+ p(this, "_localStream");
449
+ p(this, "remoteStreamPromise");
450
+ p(this, "pipelineId");
451
+ p(this, "apiKey");
452
+ p(this, "dataChannel");
453
+ p(this, "reassembler");
454
+ this.pc = e, this._localStream = t, this.remoteStreamPromise = r, this.pipelineId = a, this.apiKey = n, this.dataChannel = i, this.reassembler = new T(), this.dataChannel.binaryType = "arraybuffer", s && (this.dataChannel.addEventListener("open", () => {
455
+ }), this.dataChannel.addEventListener("message", (l) => {
367
456
  try {
368
- if (d.data instanceof ArrayBuffer) {
369
- const { frameId: c, chunkIndex: l, totalChunks: p, payload: y } = C(d.data), w = this.reassembler.processChunk(c, l, p, y);
370
- if (w) {
371
- const S = new TextDecoder("utf-8").decode(w), f = JSON.parse(S);
372
- s(f);
457
+ if (l.data instanceof ArrayBuffer) {
458
+ const { frameId: c, chunkIndex: h, totalChunks: y, payload: f } = E(l.data), d = this.reassembler.processChunk(c, h, y, f);
459
+ if (d) {
460
+ const _ = new TextDecoder("utf-8").decode(d), g = JSON.parse(_);
461
+ s(g);
373
462
  }
374
463
  } else {
375
- const c = JSON.parse(d.data);
464
+ const c = JSON.parse(l.data);
376
465
  s(c);
377
466
  }
378
467
  } catch (c) {
379
468
  console.error("[RFWebRTC] Failed to parse data channel message:", c);
380
469
  }
381
- }), this.dataChannel.addEventListener("error", (d) => {
382
- console.error("[RFWebRTC] Data channel error:", d);
470
+ }), this.dataChannel.addEventListener("error", (l) => {
471
+ console.error("[RFWebRTC] Data channel error:", l);
383
472
  }), this.dataChannel.addEventListener("close", () => {
384
473
  this.reassembler.clear();
385
474
  }));
@@ -430,7 +519,7 @@ class R {
430
519
  * ```
431
520
  */
432
521
  async cleanup() {
433
- this.reassembler.clear(), this.pipelineId && this.apiKey && await _.init({ apiKey: this.apiKey }).terminatePipeline({ pipelineId: this.pipelineId }), this.pc && this.pc.connectionState !== "closed" && this.pc.close(), v(this._localStream);
522
+ this.reassembler.clear(), this.pipelineId && this.apiKey && await v.init({ apiKey: this.apiKey }).terminatePipeline({ pipelineId: this.pipelineId }), this.pc && this.pc.connectionState !== "closed" && this.pc.close(), C(this._localStream);
434
523
  }
435
524
  /**
436
525
  * Reconfigure pipeline outputs at runtime
@@ -485,47 +574,59 @@ class R {
485
574
  }
486
575
  }
487
576
  }
488
- async function U({
489
- source: a,
577
+ async function j({
578
+ source: o,
490
579
  connector: e,
491
580
  wrtcParams: t,
492
- onData: n,
493
- options: r = {}
581
+ onData: r,
582
+ options: a = {}
494
583
  }) {
495
- var f;
584
+ var m;
496
585
  if (!e || typeof e.connectWrtc != "function")
497
586
  throw new Error("connector must have a connectWrtc method");
498
- const o = a, { pc: i, offer: s, remoteStreamPromise: d, dataChannel: c } = await x(
499
- o,
500
- t.iceServers
501
- ), l = await e.connectWrtc(
502
- { sdp: s.sdp, type: s.type },
503
- t
504
- ), p = { sdp: l.sdp, type: l.type };
505
- if (!(p != null && p.sdp) || !(p != null && p.type))
506
- throw console.error("[RFWebRTC] Invalid answer from server:", l), new Error("connector.connectWrtc must return answer with sdp and type");
507
- const y = ((f = l == null ? void 0 : l.context) == null ? void 0 : f.pipeline_id) || null;
508
- await i.setRemoteDescription(p), await new Promise((g, m) => {
509
- const h = () => {
510
- i.connectionState === "connected" ? (i.removeEventListener("connectionstatechange", h), g()) : i.connectionState === "failed" && (i.removeEventListener("connectionstatechange", h), m(new Error("WebRTC connection failed")));
587
+ const n = o;
588
+ let i = t.iceServers;
589
+ if ((!i || i.length === 0) && e.getIceServers)
590
+ try {
591
+ const u = await e.getIceServers();
592
+ u && u.length > 0 && (i = u, console.log("[RFWebRTC] Using TURN servers from connector"));
593
+ } catch (u) {
594
+ console.warn("[RFWebRTC] Failed to fetch TURN config, using defaults:", u);
595
+ }
596
+ const { pc: s, offer: l, remoteStreamPromise: c, dataChannel: h } = await A(
597
+ n,
598
+ i
599
+ ), y = {
600
+ ...t,
601
+ iceServers: i
602
+ }, f = await e.connectWrtc(
603
+ { sdp: l.sdp, type: l.type },
604
+ y
605
+ ), d = { sdp: f.sdp, type: f.type };
606
+ if (!(d != null && d.sdp) || !(d != null && d.type))
607
+ throw console.error("[RFWebRTC] Invalid answer from server:", f), new Error("connector.connectWrtc must return answer with sdp and type");
608
+ const S = ((m = f == null ? void 0 : f.context) == null ? void 0 : m.pipeline_id) || null;
609
+ await s.setRemoteDescription(d), await new Promise((u, k) => {
610
+ const w = () => {
611
+ s.connectionState === "connected" ? (s.removeEventListener("connectionstatechange", w), u()) : s.connectionState === "failed" && (s.removeEventListener("connectionstatechange", w), k(new Error("WebRTC connection failed")));
511
612
  };
512
- i.addEventListener("connectionstatechange", h), h(), setTimeout(() => {
513
- i.removeEventListener("connectionstatechange", h), m(new Error("WebRTC connection timeout after 30s"));
613
+ s.addEventListener("connectionstatechange", w), w(), setTimeout(() => {
614
+ s.removeEventListener("connectionstatechange", w), k(new Error("WebRTC connection timeout after 30s"));
514
615
  }, 3e4);
515
- }), r.disableInputStreamDownscaling !== !1 && await N(i);
516
- const k = e._apiKey || null;
517
- return new R(i, o, d, y, k, c, n);
616
+ }), a.disableInputStreamDownscaling !== !1 && await q(s);
617
+ const g = e._apiKey || null;
618
+ return new F(s, n, c, S, g, h, r);
518
619
  }
519
- const $ = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
620
+ const M = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
520
621
  __proto__: null,
521
- ChunkReassembler: b,
522
- RFWebRTCConnection: R,
523
- parseBinaryHeader: C,
524
- useStream: U
622
+ ChunkReassembler: T,
623
+ RFWebRTCConnection: F,
624
+ parseBinaryHeader: E,
625
+ useStream: j
525
626
  }, Symbol.toStringTag, { value: "Module" }));
526
627
  export {
527
- _ as InferenceHTTPClient,
528
- K as connectors,
529
- j as streams,
530
- $ as webrtc
628
+ v as InferenceHTTPClient,
629
+ $ as connectors,
630
+ B as streams,
631
+ M as webrtc
531
632
  };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- (function(l,s){typeof exports=="object"&&typeof module<"u"?s(exports):typeof define=="function"&&define.amd?define(["exports"],s):(l=typeof globalThis<"u"?globalThis:l||self,s(l.RoboflowClient={}))})(this,function(l){"use strict";var K=Object.defineProperty;var j=(l,s,y)=>s in l?K(l,s,{enumerable:!0,configurable:!0,writable:!0,value:y}):l[s]=y;var f=(l,s,y)=>j(l,typeof s!="symbol"?s+"":s,y);class s{constructor(e,t="https://serverless.roboflow.com"){f(this,"apiKey");f(this,"serverUrl");this.apiKey=e,this.serverUrl=t}static init({apiKey:e,serverUrl:t}){if(!e)throw new Error("apiKey is required");return new s(e,t)}async initializeWebrtcWorker({offer:e,workflowSpec:t,workspaceName:r,workflowId:n,config:a={}}){if(!e||!e.sdp||!e.type)throw new Error("offer with sdp and type is required");const i=!!t,c=!!(r&&n);if(!i&&!c)throw new Error("Either workflowSpec OR (workspaceName + workflowId) is required");if(i&&c)throw new Error("Provide either workflowSpec OR (workspaceName + workflowId), not both");const{imageInputName:p="image",streamOutputNames:d=[],dataOutputNames:u=["string"],threadPoolWorkers:h=4,workflowsParameters:_={},iceServers:S,processingTimeout:b,requestedPlan:v,requestedRegion:g}=a,k={type:"WorkflowConfiguration",image_input_name:p,workflows_parameters:_,workflows_thread_pool_workers:h,cancel_thread_pool_tasks_on_exit:!0,video_metadata_input_name:"video_metadata"};i?k.workflow_specification=t:(k.workspace_name=r,k.workflow_id=n);const w={workflow_configuration:k,api_key:this.apiKey,webrtc_realtime_processing:!0,webrtc_offer:{sdp:e.sdp,type:e.type},webrtc_config:S?{iceServers:S}:null,stream_output:d,data_output:u};b!==void 0&&(w.processing_timeout=b),v!==void 0&&(w.requested_plan=v),g!==void 0&&(w.requested_region=g),console.trace("payload",w);const m=await fetch(`${this.serverUrl}/initialise_webrtc_worker`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(w)});if(!m.ok){const q=await m.text().catch(()=>"");throw new Error(`initialise_webrtc_worker failed (${m.status}): ${q}`)}return await m.json()}async terminatePipeline({pipelineId:e}){if(!e)throw new Error("pipelineId is required");await fetch(`${this.serverUrl}/inference_pipelines/${e}/terminate?api_key=${this.apiKey}`,{method:"POST",headers:{"Content-Type":"application/json"}})}}const y={withApiKey(o,e={}){const{serverUrl:t}=e;return typeof window<"u"&&console.warn("[Security Warning] Using API key directly in browser will expose it. Use connectors.withProxyUrl() for production. See: https://docs.roboflow.com/api-reference/authentication#securing-your-api-key"),{connectWrtc:async(r,n)=>{const a=s.init({apiKey:o,serverUrl:t});return console.log("wrtcParams",n),await a.initializeWebrtcWorker({offer:r,workflowSpec:n.workflowSpec,workspaceName:n.workspaceName,workflowId:n.workflowId,config:{imageInputName:n.imageInputName,streamOutputNames:n.streamOutputNames,dataOutputNames:n.dataOutputNames,threadPoolWorkers:n.threadPoolWorkers,workflowsParameters:n.workflowsParameters,iceServers:n.iceServers,processingTimeout:n.processingTimeout,requestedPlan:n.requestedPlan,requestedRegion:n.requestedRegion}})},_apiKey:o,_serverUrl:t}},withProxyUrl(o,e={}){return{connectWrtc:async(t,r)=>{const n=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:t,wrtcParams:r})});if(!n.ok){const a=await n.text().catch(()=>"");throw new Error(`Proxy request failed (${n.status}): ${a}`)}return await n.json()}}}};async function O(o={video:!0}){try{console.log("[RFStreams] requesting with",o);const e=await navigator.mediaDevices.getUserMedia(o);return console.log("[RFStreams] got stream",e.getVideoTracks().map(t=>({id:t.id,label:t.label}))),e}catch(e){console.warn("[RFStreams] failed, falling back",e);const t=await navigator.mediaDevices.getUserMedia({video:!0,audio:!1});return console.log("[RFStreams] fallback stream",t.getVideoTracks().map(r=>({id:r.id,label:r.label}))),t}}function C(o){o&&(o.getTracks().forEach(e=>e.stop()),console.log("[RFStreams] Stream stopped"))}const W=Object.freeze(Object.defineProperty({__proto__:null,stopStream:C,useCamera:O},Symbol.toStringTag,{value:"Module"})),P=12;class T{constructor(){f(this,"pendingFrames",new Map)}processChunk(e,t,r,n){if(r===1)return n;this.pendingFrames.has(e)||this.pendingFrames.set(e,{chunks:new Map,totalChunks:r});const a=this.pendingFrames.get(e);if(a.chunks.set(t,n),a.chunks.size===r){const i=Array.from(a.chunks.values()).reduce((d,u)=>d+u.length,0),c=new Uint8Array(i);let p=0;for(let d=0;d<r;d++){const u=a.chunks.get(d);c.set(u,p),p+=u.length}return this.pendingFrames.delete(e),c}return null}clear(){this.pendingFrames.clear()}}function R(o){const e=new DataView(o),t=e.getUint32(0,!0),r=e.getUint32(4,!0),n=e.getUint32(8,!0),a=new Uint8Array(o,P);return{frameId:t,chunkIndex:r,totalChunks:n,payload:a}}async function F(o,e=6e3){if(o.iceGatheringState==="complete")return;let t=!1;const r=n=>{n.candidate&&n.candidate.type==="srflx"&&(t=!0)};o.addEventListener("icecandidate",r);try{await Promise.race([new Promise(n=>{const a=()=>{o.iceGatheringState==="complete"&&(o.removeEventListener("icegatheringstatechange",a),n())};o.addEventListener("icegatheringstatechange",a)}),new Promise((n,a)=>{setTimeout(()=>{t?n():(console.error("[ICE] timeout with NO srflx candidate! Connection may fail."),a(new Error("ICE gathering timeout without srflx candidate")))},e)})])}finally{o.removeEventListener("icecandidate",r)}}function I(o){return new Promise(e=>{o.addEventListener("track",t=>{t.streams&&t.streams[0]&&e(t.streams[0])})})}const D=[{urls:["stun:stun.l.google.com:19302"]}];async function N(o,e){const t=e??D,r=new RTCPeerConnection({iceServers:t});try{r.addTransceiver("video",{direction:"recvonly"})}catch(c){console.warn("[RFWebRTC] Could not add transceiver:",c)}o.getVideoTracks().forEach(c=>{try{c.contentHint="detail"}catch{}r.addTrack(c,o)});const n=I(r),a=r.createDataChannel("roboflow-control",{ordered:!0}),i=await r.createOffer();return await r.setLocalDescription(i),await F(r),{pc:r,offer:r.localDescription,remoteStreamPromise:n,dataChannel:a}}async function U(o){const e=o.getSenders().find(r=>r.track&&r.track.kind==="video");if(!e)return;const t=e.getParameters();t.encodings=t.encodings||[{}],t.encodings[0].scaleResolutionDownBy=1;try{await e.setParameters(t)}catch(r){console.warn("[RFWebRTC] Failed to set encoding parameters:",r)}}class E{constructor(e,t,r,n,a,i,c){f(this,"pc");f(this,"_localStream");f(this,"remoteStreamPromise");f(this,"pipelineId");f(this,"apiKey");f(this,"dataChannel");f(this,"reassembler");this.pc=e,this._localStream=t,this.remoteStreamPromise=r,this.pipelineId=n,this.apiKey=a,this.dataChannel=i,this.reassembler=new T,this.dataChannel.binaryType="arraybuffer",c&&(this.dataChannel.addEventListener("open",()=>{}),this.dataChannel.addEventListener("message",p=>{try{if(p.data instanceof ArrayBuffer){const{frameId:d,chunkIndex:u,totalChunks:h,payload:_}=R(p.data),S=this.reassembler.processChunk(d,u,h,_);if(S){const v=new TextDecoder("utf-8").decode(S),g=JSON.parse(v);c(g)}}else{const d=JSON.parse(p.data);c(d)}}catch(d){console.error("[RFWebRTC] Failed to parse data channel message:",d)}}),this.dataChannel.addEventListener("error",p=>{console.error("[RFWebRTC] Data channel error:",p)}),this.dataChannel.addEventListener("close",()=>{this.reassembler.clear()}))}async remoteStream(){return await this.remoteStreamPromise}localStream(){return this._localStream}async cleanup(){this.reassembler.clear(),this.pipelineId&&this.apiKey&&await s.init({apiKey:this.apiKey}).terminatePipeline({pipelineId:this.pipelineId}),this.pc&&this.pc.connectionState!=="closed"&&this.pc.close(),C(this._localStream)}reconfigureOutputs(e){const t={};e.streamOutput!==void 0&&(t.stream_output=e.streamOutput),e.dataOutput!==void 0&&(t.data_output=e.dataOutput),this.sendData(t)}sendData(e){if(this.dataChannel.readyState!=="open"){console.warn("[RFWebRTC] Data channel is not open. Current state:",this.dataChannel.readyState);return}try{const t=typeof e=="string"?e:JSON.stringify(e);this.dataChannel.send(t)}catch(t){console.error("[RFWebRTC] Failed to send data:",t)}}}async function L({source:o,connector:e,wrtcParams:t,onData:r,options:n={}}){var g;if(!e||typeof e.connectWrtc!="function")throw new Error("connector must have a connectWrtc method");const a=o,{pc:i,offer:c,remoteStreamPromise:p,dataChannel:d}=await N(a,t.iceServers),u=await e.connectWrtc({sdp:c.sdp,type:c.type},t),h={sdp:u.sdp,type:u.type};if(!(h!=null&&h.sdp)||!(h!=null&&h.type))throw console.error("[RFWebRTC] Invalid answer from server:",u),new Error("connector.connectWrtc must return answer with sdp and type");const _=((g=u==null?void 0:u.context)==null?void 0:g.pipeline_id)||null;await i.setRemoteDescription(h),await new Promise((k,w)=>{const m=()=>{i.connectionState==="connected"?(i.removeEventListener("connectionstatechange",m),k()):i.connectionState==="failed"&&(i.removeEventListener("connectionstatechange",m),w(new Error("WebRTC connection failed")))};i.addEventListener("connectionstatechange",m),m(),setTimeout(()=>{i.removeEventListener("connectionstatechange",m),w(new Error("WebRTC connection timeout after 30s"))},3e4)}),n.disableInputStreamDownscaling!==!1&&await U(i);const b=e._apiKey||null;return new E(i,a,p,_,b,d,r)}const x=Object.freeze(Object.defineProperty({__proto__:null,ChunkReassembler:T,RFWebRTCConnection:E,parseBinaryHeader:R,useStream:L},Symbol.toStringTag,{value:"Module"}));l.InferenceHTTPClient=s,l.connectors=y,l.streams=W,l.webrtc=x,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
1
+ (function(c,u){typeof exports=="object"&&typeof module<"u"?u(exports):typeof define=="function"&&define.amd?define(["exports"],u):(c=typeof globalThis<"u"?globalThis:c||self,u(c.RoboflowClient={}))})(this,function(c){"use strict";var B=Object.defineProperty;var M=(c,u,S)=>u in c?B(c,u,{enumerable:!0,configurable:!0,writable:!0,value:S}):c[u]=S;var h=(c,u,S)=>M(c,typeof u!="symbol"?u+"":u,S);var U;const u=typeof process<"u"&&((U=process.env)!=null&&U.RF_API_BASE_URL)?process.env.RF_API_BASE_URL:"https://api.roboflow.com",S=["https://serverless.roboflow.com"];class _{constructor(e,t="https://serverless.roboflow.com"){h(this,"apiKey");h(this,"serverUrl");this.apiKey=e,this.serverUrl=t}static init({apiKey:e,serverUrl:t}){if(!e)throw new Error("apiKey is required");return new _(e,t)}async initializeWebrtcWorker({offer:e,workflowSpec:t,workspaceName:r,workflowId:a,config:n={}}){if(!e||!e.sdp||!e.type)throw new Error("offer with sdp and type is required");const s=!!t,i=!!(r&&a);if(!s&&!i)throw new Error("Either workflowSpec OR (workspaceName + workflowId) is required");if(s&&i)throw new Error("Provide either workflowSpec OR (workspaceName + workflowId), not both");const{imageInputName:d="image",streamOutputNames:l=[],dataOutputNames:m=["string"],threadPoolWorkers:v=4,workflowsParameters:w={},iceServers:p,processingTimeout:b,requestedPlan:k,requestedRegion:R}=n,T={type:"WorkflowConfiguration",image_input_name:d,workflows_parameters:w,workflows_thread_pool_workers:v,cancel_thread_pool_tasks_on_exit:!0,video_metadata_input_name:"video_metadata"};s?T.workflow_specification=t:(T.workspace_name=r,T.workflow_id=a);const g={workflow_configuration:T,api_key:this.apiKey,webrtc_realtime_processing:!0,webrtc_offer:{sdp:e.sdp,type:e.type},webrtc_config:p?{iceServers:p}:null,stream_output:l,data_output:m};b!==void 0&&(g.processing_timeout=b),k!==void 0&&(g.requested_plan=k),R!==void 0&&(g.requested_region=R),console.trace("payload",g);const f=await fetch(`${this.serverUrl}/initialise_webrtc_worker`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(g)});if(!f.ok){const y=await f.text().catch(()=>"");throw new Error(`initialise_webrtc_worker failed (${f.status}): ${y}`)}return await f.json()}async terminatePipeline({pipelineId:e}){if(!e)throw new Error("pipelineId is required");await fetch(`${this.serverUrl}/inference_pipelines/${e}/terminate?api_key=${this.apiKey}`,{method:"POST",headers:{"Content-Type":"application/json"}})}async fetchTurnConfig(){if(!S.includes(this.serverUrl))return null;try{const e=await fetch(`${u}/webrtc_turn_config?api_key=${this.apiKey}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok)return console.warn(`[RFWebRTC] Failed to fetch TURN config (${e.status}), using defaults`),null;const t=await e.json();let r;if(Array.isArray(t))r=t;else if(t.iceServers&&Array.isArray(t.iceServers))r=t.iceServers;else if(t.urls)r=[t];else return console.warn("[RFWebRTC] Invalid TURN config format, using defaults"),null;return r.map(n=>({urls:Array.isArray(n.urls)?n.urls:[n.urls],username:n.username,credential:n.credential}))}catch(e){return console.warn("[RFWebRTC] Error fetching TURN config:",e),null}}}const O={withApiKey(o,e={}){const{serverUrl:t}=e;typeof window<"u"&&console.warn("[Security Warning] Using API key directly in browser will expose it. Use connectors.withProxyUrl() for production. See: https://docs.roboflow.com/api-reference/authentication#securing-your-api-key");const r=_.init({apiKey:o,serverUrl:t});return{connectWrtc:async(a,n)=>(console.log("wrtcParams",n),await r.initializeWebrtcWorker({offer:a,workflowSpec:n.workflowSpec,workspaceName:n.workspaceName,workflowId:n.workflowId,config:{imageInputName:n.imageInputName,streamOutputNames:n.streamOutputNames,dataOutputNames:n.dataOutputNames,threadPoolWorkers:n.threadPoolWorkers,workflowsParameters:n.workflowsParameters,iceServers:n.iceServers,processingTimeout:n.processingTimeout,requestedPlan:n.requestedPlan,requestedRegion:n.requestedRegion}})),getIceServers:async()=>await r.fetchTurnConfig(),_apiKey:o,_serverUrl:t}},withProxyUrl(o,e={}){const{turnConfigUrl:t}=e;return{connectWrtc:async(r,a)=>{const n=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:r,wrtcParams:a})});if(!n.ok){const s=await n.text().catch(()=>"");throw new Error(`Proxy request failed (${n.status}): ${s}`)}return await n.json()},getIceServers:t?async()=>{try{const r=await fetch(t,{method:"GET",headers:{"Content-Type":"application/json"}});return r.ok?(await r.json()).iceServers||null:(console.warn(`[RFWebRTC] Failed to fetch TURN config from proxy (${r.status})`),null)}catch(r){return console.warn("[RFWebRTC] Error fetching TURN config from proxy:",r),null}}:void 0}}};async function P(o={video:!0}){try{console.log("[RFStreams] requesting with",o);const e=await navigator.mediaDevices.getUserMedia(o);return console.log("[RFStreams] got stream",e.getVideoTracks().map(t=>({id:t.id,label:t.label}))),e}catch(e){console.warn("[RFStreams] failed, falling back",e);const t=await navigator.mediaDevices.getUserMedia({video:!0,audio:!1});return console.log("[RFStreams] fallback stream",t.getVideoTracks().map(r=>({id:r.id,label:r.label}))),t}}function E(o){o&&(o.getTracks().forEach(e=>e.stop()),console.log("[RFStreams] Stream stopped"))}const N=Object.freeze(Object.defineProperty({__proto__:null,stopStream:E,useCamera:P},Symbol.toStringTag,{value:"Module"})),L=12;class F{constructor(){h(this,"pendingFrames",new Map)}processChunk(e,t,r,a){if(r===1)return a;this.pendingFrames.has(e)||this.pendingFrames.set(e,{chunks:new Map,totalChunks:r});const n=this.pendingFrames.get(e);if(n.chunks.set(t,a),n.chunks.size===r){const s=Array.from(n.chunks.values()).reduce((l,m)=>l+m.length,0),i=new Uint8Array(s);let d=0;for(let l=0;l<r;l++){const m=n.chunks.get(l);i.set(m,d),d+=m.length}return this.pendingFrames.delete(e),i}return null}clear(){this.pendingFrames.clear()}}function W(o){const e=new DataView(o),t=e.getUint32(0,!0),r=e.getUint32(4,!0),a=e.getUint32(8,!0),n=new Uint8Array(o,L);return{frameId:t,chunkIndex:r,totalChunks:a,payload:n}}async function D(o,e=6e3){if(o.iceGatheringState==="complete")return;let t=!1;const r=a=>{a.candidate&&a.candidate.type==="srflx"&&(t=!0)};o.addEventListener("icecandidate",r);try{await Promise.race([new Promise(a=>{const n=()=>{o.iceGatheringState==="complete"&&(o.removeEventListener("icegatheringstatechange",n),a())};o.addEventListener("icegatheringstatechange",n)}),new Promise((a,n)=>{setTimeout(()=>{t?a():(console.error("[ICE] timeout with NO srflx candidate! Connection may fail."),n(new Error("ICE gathering timeout without srflx candidate")))},e)})])}finally{o.removeEventListener("icecandidate",r)}}function x(o){return new Promise(e=>{o.addEventListener("track",t=>{t.streams&&t.streams[0]&&e(t.streams[0])})})}const A=[{urls:["stun:stun.l.google.com:19302"]}];async function j(o,e){const t=e??A,r=new RTCPeerConnection({iceServers:t});try{r.addTransceiver("video",{direction:"recvonly"})}catch(i){console.warn("[RFWebRTC] Could not add transceiver:",i)}o.getVideoTracks().forEach(i=>{try{i.contentHint="detail"}catch{}r.addTrack(i,o)});const a=x(r),n=r.createDataChannel("roboflow-control",{ordered:!0}),s=await r.createOffer();return await r.setLocalDescription(s),await D(r),{pc:r,offer:r.localDescription,remoteStreamPromise:a,dataChannel:n}}async function q(o){const e=o.getSenders().find(r=>r.track&&r.track.kind==="video");if(!e)return;const t=e.getParameters();t.encodings=t.encodings||[{}],t.encodings[0].scaleResolutionDownBy=1;try{await e.setParameters(t)}catch(r){console.warn("[RFWebRTC] Failed to set encoding parameters:",r)}}class I{constructor(e,t,r,a,n,s,i){h(this,"pc");h(this,"_localStream");h(this,"remoteStreamPromise");h(this,"pipelineId");h(this,"apiKey");h(this,"dataChannel");h(this,"reassembler");this.pc=e,this._localStream=t,this.remoteStreamPromise=r,this.pipelineId=a,this.apiKey=n,this.dataChannel=s,this.reassembler=new F,this.dataChannel.binaryType="arraybuffer",i&&(this.dataChannel.addEventListener("open",()=>{}),this.dataChannel.addEventListener("message",d=>{try{if(d.data instanceof ArrayBuffer){const{frameId:l,chunkIndex:m,totalChunks:v,payload:w}=W(d.data),p=this.reassembler.processChunk(l,m,v,w);if(p){const k=new TextDecoder("utf-8").decode(p),R=JSON.parse(k);i(R)}}else{const l=JSON.parse(d.data);i(l)}}catch(l){console.error("[RFWebRTC] Failed to parse data channel message:",l)}}),this.dataChannel.addEventListener("error",d=>{console.error("[RFWebRTC] Data channel error:",d)}),this.dataChannel.addEventListener("close",()=>{this.reassembler.clear()}))}async remoteStream(){return await this.remoteStreamPromise}localStream(){return this._localStream}async cleanup(){this.reassembler.clear(),this.pipelineId&&this.apiKey&&await _.init({apiKey:this.apiKey}).terminatePipeline({pipelineId:this.pipelineId}),this.pc&&this.pc.connectionState!=="closed"&&this.pc.close(),E(this._localStream)}reconfigureOutputs(e){const t={};e.streamOutput!==void 0&&(t.stream_output=e.streamOutput),e.dataOutput!==void 0&&(t.data_output=e.dataOutput),this.sendData(t)}sendData(e){if(this.dataChannel.readyState!=="open"){console.warn("[RFWebRTC] Data channel is not open. Current state:",this.dataChannel.readyState);return}try{const t=typeof e=="string"?e:JSON.stringify(e);this.dataChannel.send(t)}catch(t){console.error("[RFWebRTC] Failed to send data:",t)}}}async function K({source:o,connector:e,wrtcParams:t,onData:r,options:a={}}){var g;if(!e||typeof e.connectWrtc!="function")throw new Error("connector must have a connectWrtc method");const n=o;let s=t.iceServers;if((!s||s.length===0)&&e.getIceServers)try{const f=await e.getIceServers();f&&f.length>0&&(s=f,console.log("[RFWebRTC] Using TURN servers from connector"))}catch(f){console.warn("[RFWebRTC] Failed to fetch TURN config, using defaults:",f)}const{pc:i,offer:d,remoteStreamPromise:l,dataChannel:m}=await j(n,s),v={...t,iceServers:s},w=await e.connectWrtc({sdp:d.sdp,type:d.type},v),p={sdp:w.sdp,type:w.type};if(!(p!=null&&p.sdp)||!(p!=null&&p.type))throw console.error("[RFWebRTC] Invalid answer from server:",w),new Error("connector.connectWrtc must return answer with sdp and type");const b=((g=w==null?void 0:w.context)==null?void 0:g.pipeline_id)||null;await i.setRemoteDescription(p),await new Promise((f,C)=>{const y=()=>{i.connectionState==="connected"?(i.removeEventListener("connectionstatechange",y),f()):i.connectionState==="failed"&&(i.removeEventListener("connectionstatechange",y),C(new Error("WebRTC connection failed")))};i.addEventListener("connectionstatechange",y),y(),setTimeout(()=>{i.removeEventListener("connectionstatechange",y),C(new Error("WebRTC connection timeout after 30s"))},3e4)}),a.disableInputStreamDownscaling!==!1&&await q(i);const R=e._apiKey||null;return new I(i,n,l,b,R,m,r)}const $=Object.freeze(Object.defineProperty({__proto__:null,ChunkReassembler:F,RFWebRTCConnection:I,parseBinaryHeader:W,useStream:K},Symbol.toStringTag,{value:"Module"}));c.InferenceHTTPClient=_,c.connectors=O,c.streams=N,c.webrtc=$,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})});
@@ -99,6 +99,14 @@ export interface WebRTCParams {
99
99
  }
100
100
  export interface Connector {
101
101
  connectWrtc(offer: WebRTCOffer, wrtcParams: WebRTCParams): Promise<WebRTCWorkerResponse>;
102
+ /**
103
+ * Fetch ICE servers (TURN configuration) for WebRTC connections
104
+ * This should be called BEFORE creating the RTCPeerConnection to ensure
105
+ * proper NAT traversal configuration.
106
+ *
107
+ * @returns Promise resolving to ICE server configuration, or null/undefined if not available
108
+ */
109
+ getIceServers?(): Promise<RTCIceServerConfig[] | null>;
102
110
  _apiKey?: string;
103
111
  _serverUrl?: string;
104
112
  }
@@ -149,6 +157,23 @@ export declare class InferenceHTTPClient {
149
157
  terminatePipeline({ pipelineId }: {
150
158
  pipelineId: string;
151
159
  }): Promise<void>;
160
+ /**
161
+ * Fetch TURN server configuration from Roboflow API
162
+ *
163
+ * This automatically fetches TURN server credentials for improved WebRTC
164
+ * connectivity through firewalls and NAT. Only applicable when using
165
+ * Roboflow serverless infrastructure.
166
+ *
167
+ * @returns Promise resolving to ICE server configuration, or null if not applicable
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const client = InferenceHTTPClient.init({ apiKey: "your-api-key" });
172
+ * const iceServers = await client.fetchTurnConfig();
173
+ * // Returns: [{ urls: ["turn:..."], username: "...", credential: "..." }]
174
+ * ```
175
+ */
176
+ fetchTurnConfig(): Promise<RTCIceServerConfig[] | null>;
152
177
  }
153
178
  /**
154
179
  * Connectors for establishing WebRTC connections to Roboflow
@@ -181,24 +206,41 @@ export declare const connectors: {
181
206
  * Your backend receives the offer and wrtcParams, adds the secret API key,
182
207
  * and forwards to Roboflow. This keeps your API key secure.
183
208
  *
184
- * @param proxyUrl - Backend proxy endpoint URL
185
- * @param options - Additional options (reserved for future use)
186
- * @returns Connector with connectWrtc method
209
+ * For improved WebRTC connectivity through firewalls, implement a separate
210
+ * endpoint for TURN server configuration that calls `fetchTurnConfig()`.
211
+ *
212
+ * @param proxyUrl - Backend proxy endpoint URL for WebRTC initialization
213
+ * @param options - Additional options
214
+ * @param options.turnConfigUrl - Optional URL for fetching TURN server configuration
215
+ * @returns Connector with connectWrtc and optional getIceServers methods
187
216
  *
188
217
  * @example
189
218
  * ```typescript
190
- * const connector = connectors.withProxyUrl('/api/init-webrtc');
191
- * const answer = await connector.connectWrtc(offer, wrtcParams);
219
+ * // Frontend: Create connector with TURN config endpoint
220
+ * const connector = connectors.withProxyUrl('/api/init-webrtc', {
221
+ * turnConfigUrl: '/api/turn-config'
222
+ * });
192
223
  * ```
193
224
  *
194
225
  * @example
195
- * Backend implementation (Express):
226
+ * Backend implementation (Express) with TURN server support:
196
227
  * ```typescript
228
+ * // Endpoint for TURN configuration (called first by SDK)
229
+ * app.get('/api/turn-config', async (req, res) => {
230
+ * const client = InferenceHTTPClient.init({
231
+ * apiKey: process.env.ROBOFLOW_API_KEY
232
+ * });
233
+ * const iceServers = await client.fetchTurnConfig();
234
+ * res.json({ iceServers });
235
+ * });
236
+ *
237
+ * // Endpoint for WebRTC initialization
197
238
  * app.post('/api/init-webrtc', async (req, res) => {
198
239
  * const { offer, wrtcParams } = req.body;
199
240
  * const client = InferenceHTTPClient.init({
200
241
  * apiKey: process.env.ROBOFLOW_API_KEY
201
242
  * });
243
+ *
202
244
  * const answer = await client.initializeWebrtcWorker({
203
245
  * offer,
204
246
  * workflowSpec: wrtcParams.workflowSpec,
@@ -216,10 +258,13 @@ export declare const connectors: {
216
258
  * requestedRegion: wrtcParams.requestedRegion
217
259
  * }
218
260
  * });
261
+ *
219
262
  * res.json(answer);
220
263
  * });
221
264
  * ```
222
265
  */
223
- withProxyUrl(proxyUrl: string, options?: Record<string, any>): Connector;
266
+ withProxyUrl(proxyUrl: string, options?: {
267
+ turnConfigUrl?: string;
268
+ }): Connector;
224
269
  };
225
270
  //# sourceMappingURL=inference-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inference-api.d.ts","sourceRoot":"","sources":["../src/inference-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inference-api.d.ts","sourceRoot":"","sources":["../src/inference-api.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"webrtc.d.ts","sourceRoot":"","sources":["../src/webrtc.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,SAAS,EAAE,YAAY,EAAsB,MAAM,iBAAiB,CAAC;AASnG;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAGN;IAEf;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI;IAqC9G;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAQxI;AAED,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,YAAY,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAmID;;;;GAIG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,WAAW,CAAmB;IAEtC,eAAe;gBAEb,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,WAAW,EACxB,mBAAmB,EAAE,OAAO,CAAC,WAAW,CAAC,EACzC,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,WAAW,EAAE,cAAc,EAC3B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI;IAsD9B;;;;;;;;;;;OAWG;IACG,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC;IAI1C;;;;;;;;;;;OAWG;IACH,WAAW,IAAI,WAAW;IAI1B;;;;;;;;;;;;;;OAcG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,kBAAkB,CAAC,MAAM,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAclG;;;OAGG;IACH,OAAO,CAAC,QAAQ;CAajB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAsB,SAAS,CAAC,EAC9B,MAAM,EACN,SAAS,EACT,UAAU,EACV,MAAM,EACN,OAAY,EACb,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAqE/C"}
1
+ {"version":3,"file":"webrtc.d.ts","sourceRoot":"","sources":["../src/webrtc.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,SAAS,EAAE,YAAY,EAAsB,MAAM,iBAAiB,CAAC;AASnG;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAGN;IAEf;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI;IAqC9G;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAQxI;AAED,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,YAAY,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAmID;;;;GAIG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,WAAW,CAAmB;IAEtC,eAAe;gBAEb,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,WAAW,EACxB,mBAAmB,EAAE,OAAO,CAAC,WAAW,CAAC,EACzC,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,WAAW,EAAE,cAAc,EAC3B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI;IAsD9B;;;;;;;;;;;OAWG;IACG,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC;IAI1C;;;;;;;;;;;OAWG;IACH,WAAW,IAAI,WAAW;IAI1B;;;;;;;;;;;;;;OAcG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,kBAAkB,CAAC,MAAM,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAclG;;;OAGG;IACH,OAAO,CAAC,QAAQ;CAajB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAsB,SAAS,CAAC,EAC9B,MAAM,EACN,SAAS,EACT,UAAU,EACV,MAAM,EACN,OAAY,EACb,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA0F/C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roboflow/inference-sdk",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Lightweight client for Roboflow's hosted inference API with WebRTC streaming support",
5
5
  "keywords": [
6
6
  "roboflow",