@roboflow/inference-sdk 0.1.7 → 0.1.9

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,24 +1,24 @@
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 = [
1
+ var A = Object.defineProperty;
2
+ var q = (r, e, t) => e in r ? A(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
+ var l = (r, e, t) => q(r, typeof e != "symbol" ? e + "" : e, t);
4
+ var L;
5
+ const K = typeof process < "u" && ((L = process.env) != null && L.RF_API_BASE_URL) ? process.env.RF_API_BASE_URL : "https://api.roboflow.com", B = [
6
6
  "https://serverless.roboflow.com"
7
7
  ];
8
- class v {
8
+ class E {
9
9
  /**
10
10
  * @private
11
11
  * Use InferenceHTTPClient.init() instead
12
12
  */
13
13
  constructor(e, t = "https://serverless.roboflow.com") {
14
- p(this, "apiKey");
15
- p(this, "serverUrl");
14
+ l(this, "apiKey");
15
+ l(this, "serverUrl");
16
16
  this.apiKey = e, this.serverUrl = t;
17
17
  }
18
18
  static init({ apiKey: e, serverUrl: t }) {
19
19
  if (!e)
20
20
  throw new Error("apiKey is required");
21
- return new v(e, t);
21
+ return new E(e, t);
22
22
  }
23
23
  /**
24
24
  * Initialize a WebRTC worker pipeline
@@ -48,59 +48,61 @@ class v {
48
48
  async initializeWebrtcWorker({
49
49
  offer: e,
50
50
  workflowSpec: t,
51
- workspaceName: r,
52
- workflowId: a,
53
- config: n = {}
51
+ workspaceName: n,
52
+ workflowId: o,
53
+ config: a = {}
54
54
  }) {
55
55
  if (!e || !e.sdp || !e.type)
56
56
  throw new Error("offer with sdp and type is required");
57
- const i = !!t, s = !!(r && a);
58
- if (!i && !s)
57
+ const i = !!t, u = !!(n && o);
58
+ if (!i && !u)
59
59
  throw new Error("Either workflowSpec OR (workspaceName + workflowId) is required");
60
- if (i && s)
60
+ if (i && u)
61
61
  throw new Error("Provide either workflowSpec OR (workspaceName + workflowId), not both");
62
62
  const {
63
- imageInputName: l = "image",
64
- streamOutputNames: c = [],
63
+ imageInputName: c = "image",
64
+ streamOutputNames: s = [],
65
65
  dataOutputNames: h = ["string"],
66
66
  threadPoolWorkers: y = 4,
67
67
  workflowsParameters: f = {},
68
68
  iceServers: d,
69
- processingTimeout: S,
70
- requestedPlan: _,
71
- requestedRegion: g
72
- } = n, R = {
69
+ processingTimeout: m,
70
+ requestedPlan: S,
71
+ requestedRegion: v,
72
+ realtimeProcessing: _ = !0,
73
+ rtspUrl: k
74
+ } = a, w = {
73
75
  type: "WorkflowConfiguration",
74
- image_input_name: l,
76
+ image_input_name: c,
75
77
  workflows_parameters: f,
76
78
  workflows_thread_pool_workers: y,
77
79
  cancel_thread_pool_tasks_on_exit: !0,
78
80
  video_metadata_input_name: "video_metadata"
79
81
  };
80
- i ? R.workflow_specification = t : (R.workspace_name = r, R.workflow_id = a);
81
- const m = {
82
- workflow_configuration: R,
82
+ i ? w.workflow_specification = t : (w.workspace_name = n, w.workflow_id = o);
83
+ const p = {
84
+ workflow_configuration: w,
83
85
  api_key: this.apiKey,
84
- webrtc_realtime_processing: !0,
86
+ webrtc_realtime_processing: _,
85
87
  webrtc_offer: {
86
88
  sdp: e.sdp,
87
89
  type: e.type
88
90
  },
89
91
  webrtc_config: d ? { iceServers: d } : null,
90
- stream_output: c,
92
+ stream_output: s,
91
93
  data_output: h
92
94
  };
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`, {
95
+ m !== void 0 && (p.processing_timeout = m), S !== void 0 && (p.requested_plan = S), v !== void 0 && (p.requested_region = v), k && (p.rtsp_url = k);
96
+ const R = await fetch(`${this.serverUrl}/initialise_webrtc_worker`, {
95
97
  method: "POST",
96
98
  headers: { "Content-Type": "application/json" },
97
- body: JSON.stringify(m)
99
+ body: JSON.stringify(p)
98
100
  });
99
- if (!u.ok) {
100
- const w = await u.text().catch(() => "");
101
- throw new Error(`initialise_webrtc_worker failed (${u.status}): ${w}`);
101
+ if (!R.ok) {
102
+ const b = await R.text().catch(() => "");
103
+ throw new Error(`initialise_webrtc_worker failed (${R.status}): ${b}`);
102
104
  }
103
- return await u.json();
105
+ return await R.json();
104
106
  }
105
107
  async terminatePipeline({ pipelineId: e }) {
106
108
  if (!e)
@@ -130,11 +132,11 @@ class v {
130
132
  * ```
131
133
  */
132
134
  async fetchTurnConfig() {
133
- if (!O.includes(this.serverUrl))
135
+ if (!B.includes(this.serverUrl))
134
136
  return null;
135
137
  try {
136
138
  const e = await fetch(
137
- `${I}/webrtc_turn_config?api_key=${this.apiKey}`,
139
+ `${K}/webrtc_turn_config?api_key=${this.apiKey}`,
138
140
  {
139
141
  method: "GET",
140
142
  headers: { "Content-Type": "application/json" }
@@ -143,26 +145,26 @@ class v {
143
145
  if (!e.ok)
144
146
  return console.warn(`[RFWebRTC] Failed to fetch TURN config (${e.status}), using defaults`), null;
145
147
  const t = await e.json();
146
- let r;
148
+ let n;
147
149
  if (Array.isArray(t))
148
- r = t;
150
+ n = t;
149
151
  else if (t.iceServers && Array.isArray(t.iceServers))
150
- r = t.iceServers;
152
+ n = t.iceServers;
151
153
  else if (t.urls)
152
- r = [t];
154
+ n = [t];
153
155
  else
154
156
  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
157
+ return n.map((a) => ({
158
+ urls: Array.isArray(a.urls) ? a.urls : [a.urls],
159
+ username: a.username,
160
+ credential: a.credential
159
161
  }));
160
162
  } catch (e) {
161
163
  return console.warn("[RFWebRTC] Error fetching TURN config:", e), null;
162
164
  }
163
165
  }
164
166
  }
165
- const $ = {
167
+ const ne = {
166
168
  /**
167
169
  * Create a connector that uses API key directly
168
170
  *
@@ -181,36 +183,38 @@ const $ = {
181
183
  * const answer = await connector.connectWrtc(offer, wrtcParams);
182
184
  * ```
183
185
  */
184
- withApiKey(o, e = {}) {
186
+ withApiKey(r, e = {}) {
185
187
  const { serverUrl: t } = e;
186
188
  typeof window < "u" && console.warn(
187
189
  "[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"
188
190
  );
189
- const r = v.init({ apiKey: o, serverUrl: t });
191
+ const n = E.init({ apiKey: r, serverUrl: t });
190
192
  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,
193
+ connectWrtc: async (o, a) => (console.debug("wrtcParams", a), await n.initializeWebrtcWorker({
194
+ offer: o,
195
+ workflowSpec: a.workflowSpec,
196
+ workspaceName: a.workspaceName,
197
+ workflowId: a.workflowId,
196
198
  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
199
+ imageInputName: a.imageInputName,
200
+ streamOutputNames: a.streamOutputNames,
201
+ dataOutputNames: a.dataOutputNames,
202
+ threadPoolWorkers: a.threadPoolWorkers,
203
+ workflowsParameters: a.workflowsParameters,
204
+ iceServers: a.iceServers,
205
+ processingTimeout: a.processingTimeout,
206
+ requestedPlan: a.requestedPlan,
207
+ requestedRegion: a.requestedRegion,
208
+ realtimeProcessing: a.realtimeProcessing,
209
+ rtspUrl: a.rtspUrl
206
210
  }
207
211
  })),
208
212
  /**
209
213
  * Fetch TURN server configuration for improved WebRTC connectivity
210
214
  */
211
- getIceServers: async () => await r.fetchTurnConfig(),
215
+ getIceServers: async () => await n.fetchTurnConfig(),
212
216
  // Store apiKey for cleanup
213
- _apiKey: o,
217
+ _apiKey: r,
214
218
  _serverUrl: t
215
219
  };
216
220
  },
@@ -277,23 +281,23 @@ const $ = {
277
281
  * });
278
282
  * ```
279
283
  */
280
- withProxyUrl(o, e = {}) {
284
+ withProxyUrl(r, e = {}) {
281
285
  const { turnConfigUrl: t } = e;
282
286
  return {
283
- connectWrtc: async (r, a) => {
284
- const n = await fetch(o, {
287
+ connectWrtc: async (n, o) => {
288
+ const a = await fetch(r, {
285
289
  method: "POST",
286
290
  headers: { "Content-Type": "application/json" },
287
291
  body: JSON.stringify({
288
- offer: r,
289
- wrtcParams: a
292
+ offer: n,
293
+ wrtcParams: o
290
294
  })
291
295
  });
292
- if (!n.ok) {
293
- const i = await n.text().catch(() => "");
294
- throw new Error(`Proxy request failed (${n.status}): ${i}`);
296
+ if (!a.ok) {
297
+ const i = await a.text().catch(() => "");
298
+ throw new Error(`Proxy request failed (${a.status}): ${i}`);
295
299
  }
296
- return await n.json();
300
+ return await a.json();
297
301
  },
298
302
  /**
299
303
  * Fetch TURN server configuration from the proxy backend
@@ -301,60 +305,100 @@ const $ = {
301
305
  */
302
306
  getIceServers: t ? async () => {
303
307
  try {
304
- const r = await fetch(t, {
308
+ const n = await fetch(t, {
305
309
  method: "GET",
306
310
  headers: { "Content-Type": "application/json" }
307
311
  });
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;
312
+ return n.ok ? (await n.json()).iceServers || null : (console.warn(`[RFWebRTC] Failed to fetch TURN config from proxy (${n.status})`), null);
313
+ } catch (n) {
314
+ return console.warn("[RFWebRTC] Error fetching TURN config from proxy:", n), null;
311
315
  }
312
316
  } : void 0
313
317
  };
314
318
  }
315
319
  };
316
- async function P(o = { video: !0 }) {
320
+ async function j(r = { video: !0 }) {
317
321
  try {
318
- console.log("[RFStreams] requesting with", o);
319
- const e = await navigator.mediaDevices.getUserMedia(o);
322
+ console.log("[RFStreams] requesting with", r);
323
+ const e = await navigator.mediaDevices.getUserMedia(r);
320
324
  return console.log("[RFStreams] got stream", e.getVideoTracks().map((t) => ({ id: t.id, label: t.label }))), e;
321
325
  } catch (e) {
322
326
  console.warn("[RFStreams] failed, falling back", e);
323
327
  const t = await navigator.mediaDevices.getUserMedia({ video: !0, audio: !1 });
324
- return console.log("[RFStreams] fallback stream", t.getVideoTracks().map((r) => ({ id: r.id, label: r.label }))), t;
328
+ return console.log("[RFStreams] fallback stream", t.getVideoTracks().map((n) => ({ id: n.id, label: n.label }))), t;
325
329
  }
326
330
  }
327
- function C(o) {
328
- o && (o.getTracks().forEach((e) => e.stop()), console.log("[RFStreams] Stream stopped"));
331
+ function P(r) {
332
+ r && (r.getTracks().forEach((e) => e.stop()), console.log("[RFStreams] Stream stopped"));
329
333
  }
330
- const B = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
334
+ const ae = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
331
335
  __proto__: null,
332
- stopStream: C,
333
- useCamera: P
334
- }, Symbol.toStringTag, { value: "Module" })), N = 12;
335
- class T {
336
+ stopStream: P,
337
+ useCamera: j
338
+ }, Symbol.toStringTag, { value: "Module" })), T = 49152, V = 262144, $ = 10;
339
+ function M(r) {
340
+ return new Promise((e) => setTimeout(e, r));
341
+ }
342
+ class O {
343
+ constructor(e, t) {
344
+ l(this, "file");
345
+ l(this, "channel");
346
+ l(this, "totalChunks");
347
+ l(this, "cancelled", !1);
348
+ this.file = e, this.channel = t, this.totalChunks = Math.ceil(e.size / T);
349
+ }
350
+ /**
351
+ * Cancel the upload
352
+ */
353
+ cancel() {
354
+ this.cancelled = !0;
355
+ }
356
+ /**
357
+ * Upload the file in chunks with backpressure handling
358
+ *
359
+ * @param onProgress - Optional callback for progress updates (bytesUploaded, totalBytes)
360
+ */
361
+ async upload(e) {
362
+ const t = this.file.size;
363
+ for (let n = 0; n < this.totalChunks; n++) {
364
+ if (this.cancelled)
365
+ throw new Error("Upload cancelled");
366
+ if (this.channel.readyState !== "open")
367
+ throw new Error("Video upload interrupted");
368
+ const o = n * T, a = Math.min(o + T, t), i = this.file.slice(o, a), u = new Uint8Array(await i.arrayBuffer()), c = new ArrayBuffer(8 + u.length), s = new DataView(c);
369
+ for (s.setUint32(0, n, !0), s.setUint32(4, this.totalChunks, !0), new Uint8Array(c, 8).set(u); this.channel.bufferedAmount > V; ) {
370
+ if (this.channel.readyState !== "open")
371
+ throw new Error("Video upload interrupted");
372
+ await M($);
373
+ }
374
+ this.channel.send(c), e && e(a, t);
375
+ }
376
+ }
377
+ }
378
+ const z = 12;
379
+ class D {
336
380
  constructor() {
337
- p(this, "pendingFrames", /* @__PURE__ */ new Map());
381
+ l(this, "pendingFrames", /* @__PURE__ */ new Map());
338
382
  }
339
383
  /**
340
384
  * Process an incoming chunk and return the complete message if all chunks received
341
385
  */
342
- processChunk(e, t, r, a) {
343
- if (r === 1)
344
- return a;
386
+ processChunk(e, t, n, o) {
387
+ if (n === 1)
388
+ return o;
345
389
  this.pendingFrames.has(e) || this.pendingFrames.set(e, {
346
390
  chunks: /* @__PURE__ */ new Map(),
347
- totalChunks: r
391
+ totalChunks: n
348
392
  });
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;
393
+ const a = this.pendingFrames.get(e);
394
+ if (a.chunks.set(t, o), a.chunks.size === n) {
395
+ const i = Array.from(a.chunks.values()).reduce((s, h) => s + h.length, 0), u = new Uint8Array(i);
396
+ let c = 0;
397
+ for (let s = 0; s < n; s++) {
398
+ const h = a.chunks.get(s);
399
+ u.set(h, c), c += h.length;
356
400
  }
357
- return this.pendingFrames.delete(e), s;
401
+ return this.pendingFrames.delete(e), u;
358
402
  }
359
403
  return null;
360
404
  }
@@ -365,113 +409,139 @@ class T {
365
409
  this.pendingFrames.clear();
366
410
  }
367
411
  }
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 };
412
+ function N(r) {
413
+ const e = new DataView(r), t = e.getUint32(0, !0), n = e.getUint32(4, !0), o = e.getUint32(8, !0), a = new Uint8Array(r, z);
414
+ return { frameId: t, chunkIndex: n, totalChunks: o, payload: a };
371
415
  }
372
- async function L(o, e = 6e3) {
373
- if (o.iceGatheringState === "complete") return;
416
+ async function H(r, e = 6e3) {
417
+ if (r.iceGatheringState === "complete") return;
374
418
  let t = !1;
375
- const r = (a) => {
376
- a.candidate && a.candidate.type === "srflx" && (t = !0);
419
+ const n = (o) => {
420
+ o.candidate && o.candidate.type === "srflx" && (t = !0);
377
421
  };
378
- o.addEventListener("icecandidate", r);
422
+ r.addEventListener("icecandidate", n);
379
423
  try {
380
424
  await Promise.race([
381
- new Promise((a) => {
382
- const n = () => {
383
- o.iceGatheringState === "complete" && (o.removeEventListener("icegatheringstatechange", n), a());
425
+ new Promise((o) => {
426
+ const a = () => {
427
+ r.iceGatheringState === "complete" && (r.removeEventListener("icegatheringstatechange", a), o());
384
428
  };
385
- o.addEventListener("icegatheringstatechange", n);
429
+ r.addEventListener("icegatheringstatechange", a);
386
430
  }),
387
- new Promise((a, n) => {
431
+ new Promise((o, a) => {
388
432
  setTimeout(() => {
389
- t ? a() : (console.error("[ICE] timeout with NO srflx candidate! Connection may fail."), n(new Error("ICE gathering timeout without srflx candidate")));
433
+ t ? o() : (console.error("[ICE] timeout with NO srflx candidate! Connection may fail."), a(new Error("ICE gathering timeout without srflx candidate")));
390
434
  }, e);
391
435
  })
392
436
  ]);
393
437
  } finally {
394
- o.removeEventListener("icecandidate", r);
438
+ r.removeEventListener("icecandidate", n);
395
439
  }
396
440
  }
397
- function x(o) {
441
+ function G(r) {
398
442
  return new Promise((e) => {
399
- o.addEventListener("track", (t) => {
443
+ r.addEventListener("track", (t) => {
400
444
  t.streams && t.streams[0] && e(t.streams[0]);
401
445
  });
402
446
  });
403
447
  }
404
- const D = [
448
+ const J = [
405
449
  { urls: ["stun:stun.l.google.com:19302"] }
406
450
  ];
407
- async function A(o, e) {
408
- const t = e ?? D, r = new RTCPeerConnection({
409
- iceServers: t
451
+ async function Z(r, e, t, n) {
452
+ if ([!!r, !!e, !!n].filter(Boolean).length !== 1)
453
+ throw new Error("Exactly one of localStream, file, or rtspUrl must be provided");
454
+ const c = t ?? J, s = new RTCPeerConnection({
455
+ iceServers: c
410
456
  });
411
457
  try {
412
- r.addTransceiver("video", { direction: "recvonly" });
413
- } catch (s) {
414
- console.warn("[RFWebRTC] Could not add transceiver:", s);
458
+ s.addTransceiver("video", { direction: "recvonly" });
459
+ } catch (m) {
460
+ console.warn("[RFWebRTC] Could not add transceiver:", m);
415
461
  }
416
- o.getVideoTracks().forEach((s) => {
462
+ r && r.getVideoTracks().forEach((m) => {
417
463
  try {
418
- s.contentHint = "detail";
464
+ m.contentHint = "detail";
419
465
  } catch {
420
466
  }
421
- r.addTrack(s, o);
467
+ s.addTrack(m, r);
422
468
  });
423
- const a = x(r), n = r.createDataChannel("roboflow-control", {
469
+ const h = G(s), y = s.createDataChannel("inference", {
424
470
  ordered: !0
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
471
+ });
472
+ let f;
473
+ e && (f = s.createDataChannel("video_upload"));
474
+ const d = await s.createOffer();
475
+ return await s.setLocalDescription(d), await H(s), {
476
+ pc: s,
477
+ offer: s.localDescription,
478
+ remoteStreamPromise: h,
479
+ dataChannel: y,
480
+ uploadChannel: f
431
481
  };
432
482
  }
433
- async function q(o) {
434
- const e = o.getSenders().find((r) => r.track && r.track.kind === "video");
483
+ async function Q(r) {
484
+ const e = r.getSenders().find((n) => n.track && n.track.kind === "video");
435
485
  if (!e) return;
436
486
  const t = e.getParameters();
437
487
  t.encodings = t.encodings || [{}], t.encodings[0].scaleResolutionDownBy = 1;
438
488
  try {
439
489
  await e.setParameters(t);
440
- } catch (r) {
441
- console.warn("[RFWebRTC] Failed to set encoding parameters:", r);
490
+ } catch (n) {
491
+ console.warn("[RFWebRTC] Failed to set encoding parameters:", n);
442
492
  }
443
493
  }
444
- class F {
494
+ function X(r, e = 3e4) {
495
+ return new Promise((t, n) => {
496
+ if (r.readyState === "open") {
497
+ t();
498
+ return;
499
+ }
500
+ const o = () => {
501
+ r.removeEventListener("open", o), r.removeEventListener("error", a), clearTimeout(i), t();
502
+ }, a = () => {
503
+ r.removeEventListener("open", o), r.removeEventListener("error", a), clearTimeout(i), n(new Error("Datachannel error"));
504
+ }, i = setTimeout(() => {
505
+ r.removeEventListener("open", o), r.removeEventListener("error", a), n(new Error("Datachannel open timeout"));
506
+ }, e);
507
+ r.addEventListener("open", o), r.addEventListener("error", a);
508
+ });
509
+ }
510
+ class x {
445
511
  /** @private */
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) => {
512
+ constructor(e, t, n, o, a, i) {
513
+ l(this, "pc");
514
+ l(this, "_localStream");
515
+ l(this, "remoteStreamPromise");
516
+ l(this, "pipelineId");
517
+ l(this, "apiKey");
518
+ l(this, "dataChannel");
519
+ l(this, "reassembler");
520
+ l(this, "uploadChannel");
521
+ l(this, "uploader");
522
+ l(this, "onComplete");
523
+ this.pc = e, this._localStream = i == null ? void 0 : i.localStream, this.remoteStreamPromise = t, this.pipelineId = n, this.apiKey = o, this.dataChannel = a, this.reassembler = new D(), this.uploadChannel = i == null ? void 0 : i.uploadChannel, this.onComplete = i == null ? void 0 : i.onComplete, this.dataChannel.binaryType = "arraybuffer";
524
+ const u = i == null ? void 0 : i.onData;
525
+ u && (this.dataChannel.addEventListener("message", (c) => {
456
526
  try {
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);
527
+ if (c.data instanceof ArrayBuffer) {
528
+ const { frameId: s, chunkIndex: h, totalChunks: y, payload: f } = N(c.data), d = this.reassembler.processChunk(s, h, y, f);
459
529
  if (d) {
460
- const _ = new TextDecoder("utf-8").decode(d), g = JSON.parse(_);
461
- s(g);
530
+ const S = new TextDecoder("utf-8").decode(d), v = JSON.parse(S);
531
+ u(v);
462
532
  }
463
533
  } else {
464
- const c = JSON.parse(l.data);
465
- s(c);
534
+ const s = JSON.parse(c.data);
535
+ u(s);
466
536
  }
467
- } catch (c) {
468
- console.error("[RFWebRTC] Failed to parse data channel message:", c);
537
+ } catch (s) {
538
+ console.error("[RFWebRTC] Failed to parse data channel message:", s);
469
539
  }
470
- }), this.dataChannel.addEventListener("error", (l) => {
471
- console.error("[RFWebRTC] Data channel error:", l);
472
- }), this.dataChannel.addEventListener("close", () => {
473
- this.reassembler.clear();
474
- }));
540
+ }), this.dataChannel.addEventListener("error", (c) => {
541
+ console.error("[RFWebRTC] Data channel error:", c);
542
+ })), this.dataChannel.addEventListener("close", () => {
543
+ this.reassembler.clear(), this.onComplete && this.onComplete();
544
+ });
475
545
  }
476
546
  /**
477
547
  * Get the remote stream (processed video from Roboflow)
@@ -491,13 +561,15 @@ class F {
491
561
  /**
492
562
  * Get the local stream (original camera)
493
563
  *
494
- * @returns The local MediaStream
564
+ * @returns The local MediaStream, or undefined if using file upload mode
495
565
  *
496
566
  * @example
497
567
  * ```typescript
498
568
  * const conn = await useStream({ ... });
499
569
  * const localStream = conn.localStream();
500
- * videoElement.srcObject = localStream;
570
+ * if (localStream) {
571
+ * videoElement.srcObject = localStream;
572
+ * }
501
573
  * ```
502
574
  */
503
575
  localStream() {
@@ -507,7 +579,7 @@ class F {
507
579
  * Cleanup and close connection
508
580
  *
509
581
  * Terminates the pipeline on Roboflow, closes the peer connection,
510
- * and stops the local media stream.
582
+ * and stops the local media stream (if applicable).
511
583
  *
512
584
  * @returns Promise that resolves when cleanup is complete
513
585
  *
@@ -519,7 +591,39 @@ class F {
519
591
  * ```
520
592
  */
521
593
  async cleanup() {
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);
594
+ if (this.uploader && this.uploader.cancel(), this.reassembler.clear(), this.pipelineId && this.apiKey)
595
+ try {
596
+ await E.init({ apiKey: this.apiKey }).terminatePipeline({ pipelineId: this.pipelineId });
597
+ } catch (e) {
598
+ console.warn("[RFWebRTC] Failed to terminate pipeline:", e);
599
+ }
600
+ this.pc && this.pc.connectionState !== "closed" && this.pc.close(), this._localStream && P(this._localStream);
601
+ }
602
+ /**
603
+ * Start uploading a file through the connection
604
+ *
605
+ * @param file - The file to upload
606
+ * @param onProgress - Optional callback for progress updates (bytesUploaded, totalBytes)
607
+ * @returns Promise that resolves when upload is complete
608
+ * @throws Error if no upload channel is available
609
+ *
610
+ * @example
611
+ * ```typescript
612
+ * await connection.startUpload(videoFile, (uploaded, total) => {
613
+ * console.log(`Upload progress: ${(uploaded / total * 100).toFixed(1)}%`);
614
+ * });
615
+ * ```
616
+ */
617
+ async startUpload(e, t) {
618
+ if (!this.uploadChannel)
619
+ throw new Error("No upload channel available. This connection was not created for file uploads.");
620
+ await X(this.uploadChannel), this.uploader = new O(e, this.uploadChannel), await this.uploader.upload(t);
621
+ }
622
+ /**
623
+ * Cancel any ongoing file upload
624
+ */
625
+ cancelUpload() {
626
+ this.uploader && this.uploader.cancel();
523
627
  }
524
628
  /**
525
629
  * Reconfigure pipeline outputs at runtime
@@ -574,59 +678,136 @@ class F {
574
678
  }
575
679
  }
576
680
  }
577
- async function j({
578
- source: o,
579
- connector: e,
580
- wrtcParams: t,
581
- onData: r,
582
- options: a = {}
681
+ async function F({
682
+ source: r,
683
+ rtspUrl: e,
684
+ connector: t,
685
+ wrtcParams: n,
686
+ onData: o,
687
+ onComplete: a,
688
+ onFileUploadProgress: i,
689
+ options: u = {}
583
690
  }) {
584
- var m;
585
- if (!e || typeof e.connectWrtc != "function")
691
+ var W;
692
+ if (!t || typeof t.connectWrtc != "function")
586
693
  throw new Error("connector must have a connectWrtc method");
587
- const n = o;
588
- let i = t.iceServers;
589
- if ((!i || i.length === 0) && e.getIceServers)
694
+ const c = !!e, s = !c && r instanceof File, h = !c && !s && r ? r : void 0, y = s ? r : void 0;
695
+ let f = n.iceServers;
696
+ if ((!f || f.length === 0) && t.getIceServers)
590
697
  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);
698
+ const g = await t.getIceServers();
699
+ g && g.length > 0 && (f = g, console.log("[RFWebRTC] Using TURN servers from connector"));
700
+ } catch (g) {
701
+ console.warn("[RFWebRTC] Failed to fetch TURN config, using defaults:", g);
595
702
  }
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")));
703
+ const { pc: d, offer: m, remoteStreamPromise: S, dataChannel: v, uploadChannel: _ } = await Z(
704
+ h,
705
+ y,
706
+ f,
707
+ e
708
+ ), k = {
709
+ ...n,
710
+ iceServers: f,
711
+ realtimeProcessing: n.realtimeProcessing ?? !s,
712
+ rtspUrl: e
713
+ }, w = await t.connectWrtc(
714
+ { sdp: m.sdp, type: m.type },
715
+ k
716
+ ), p = { sdp: w.sdp, type: w.type };
717
+ if (!(p != null && p.sdp) || !(p != null && p.type))
718
+ throw console.error("[RFWebRTC] Invalid answer from server:", w), new Error("connector.connectWrtc must return answer with sdp and type");
719
+ const R = ((W = w == null ? void 0 : w.context) == null ? void 0 : W.pipeline_id) || null;
720
+ await d.setRemoteDescription(p), await new Promise((g, I) => {
721
+ const C = () => {
722
+ d.connectionState === "connected" ? (d.removeEventListener("connectionstatechange", C), g()) : d.connectionState === "failed" && (d.removeEventListener("connectionstatechange", C), I(new Error("WebRTC connection failed")));
612
723
  };
613
- s.addEventListener("connectionstatechange", w), w(), setTimeout(() => {
614
- s.removeEventListener("connectionstatechange", w), k(new Error("WebRTC connection timeout after 30s"));
724
+ d.addEventListener("connectionstatechange", C), C(), setTimeout(() => {
725
+ d.removeEventListener("connectionstatechange", C), I(new Error("WebRTC connection timeout after 30s"));
615
726
  }, 3e4);
616
- }), a.disableInputStreamDownscaling !== !1 && await q(s);
617
- const g = e._apiKey || null;
618
- return new F(s, n, c, S, g, h, r);
727
+ }), h && u.disableInputStreamDownscaling !== !1 && await Q(d);
728
+ const U = t._apiKey || null, b = new x(
729
+ d,
730
+ S,
731
+ R,
732
+ U,
733
+ v,
734
+ {
735
+ localStream: h,
736
+ uploadChannel: _,
737
+ onData: o,
738
+ onComplete: a
739
+ }
740
+ );
741
+ return y && _ && b.startUpload(y, i).catch((g) => {
742
+ console.error("[RFWebRTC] Upload error:", g);
743
+ }), b;
744
+ }
745
+ async function Y({
746
+ source: r,
747
+ connector: e,
748
+ wrtcParams: t,
749
+ onData: n,
750
+ options: o = {}
751
+ }) {
752
+ if (r instanceof File)
753
+ throw new Error("useStream requires a MediaStream. Use useVideoFile for File uploads.");
754
+ return F({
755
+ source: r,
756
+ connector: e,
757
+ wrtcParams: t,
758
+ onData: n,
759
+ options: o
760
+ });
761
+ }
762
+ async function ee({
763
+ file: r,
764
+ connector: e,
765
+ wrtcParams: t,
766
+ onData: n,
767
+ onUploadProgress: o,
768
+ onComplete: a
769
+ }) {
770
+ return F({
771
+ source: r,
772
+ connector: e,
773
+ wrtcParams: {
774
+ ...t,
775
+ realtimeProcessing: t.realtimeProcessing ?? !0
776
+ },
777
+ onData: n,
778
+ onComplete: a,
779
+ onFileUploadProgress: o
780
+ });
781
+ }
782
+ async function te({
783
+ rtspUrl: r,
784
+ connector: e,
785
+ wrtcParams: t,
786
+ onData: n
787
+ }) {
788
+ if (!r.startsWith("rtsp://") && !r.startsWith("rtsps://"))
789
+ throw new Error("Invalid RTSP URL: must start with rtsp:// or rtsps://");
790
+ return F({
791
+ rtspUrl: r,
792
+ connector: e,
793
+ wrtcParams: t,
794
+ onData: n
795
+ });
619
796
  }
620
- const M = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
797
+ const oe = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
621
798
  __proto__: null,
622
- ChunkReassembler: T,
623
- RFWebRTCConnection: F,
624
- parseBinaryHeader: E,
625
- useStream: j
799
+ ChunkReassembler: D,
800
+ FileUploader: O,
801
+ RFWebRTCConnection: x,
802
+ parseBinaryHeader: N,
803
+ useRtspStream: te,
804
+ useStream: Y,
805
+ useVideoFile: ee
626
806
  }, Symbol.toStringTag, { value: "Module" }));
627
807
  export {
628
- v as InferenceHTTPClient,
629
- $ as connectors,
630
- B as streams,
631
- M as webrtc
808
+ E as InferenceHTTPClient,
809
+ ne as connectors,
810
+ ae as streams,
811
+ oe as webrtc
632
812
  };
813
+ //# sourceMappingURL=index.es.js.map