@roboflow/inference-sdk 0.1.5 → 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 +283 -182
- package/dist/index.js +1 -1
- package/dist/inference-api.d.ts +52 -7
- package/dist/inference-api.d.ts.map +1 -1
- package/dist/webrtc.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.es.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
|
|
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
|
-
|
|
11
|
-
|
|
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
|
|
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:
|
|
48
|
-
workflowId:
|
|
49
|
-
config:
|
|
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 = !!(
|
|
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:
|
|
63
|
+
imageInputName: l = "image",
|
|
60
64
|
streamOutputNames: c = [],
|
|
61
|
-
dataOutputNames:
|
|
62
|
-
threadPoolWorkers:
|
|
63
|
-
workflowsParameters:
|
|
64
|
-
iceServers:
|
|
65
|
-
processingTimeout:
|
|
66
|
-
requestedPlan:
|
|
67
|
-
requestedRegion:
|
|
68
|
-
} =
|
|
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:
|
|
71
|
-
workflows_parameters:
|
|
72
|
-
workflows_thread_pool_workers:
|
|
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 ?
|
|
80
|
+
i ? R.workflow_specification = t : (R.workspace_name = r, R.workflow_id = a);
|
|
77
81
|
const m = {
|
|
78
|
-
workflow_configuration:
|
|
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:
|
|
89
|
+
webrtc_config: d ? { iceServers: d } : null,
|
|
86
90
|
stream_output: c,
|
|
87
|
-
data_output:
|
|
91
|
+
data_output: h
|
|
88
92
|
};
|
|
89
|
-
|
|
90
|
-
const
|
|
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 (!
|
|
96
|
-
const
|
|
97
|
-
throw new Error(`initialise_webrtc_worker failed (${
|
|
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
|
|
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
|
|
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(
|
|
184
|
+
withApiKey(o, e = {}) {
|
|
133
185
|
const { serverUrl: t } = e;
|
|
134
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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:
|
|
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
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
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
|
-
*
|
|
175
|
-
* const
|
|
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(
|
|
280
|
+
withProxyUrl(o, e = {}) {
|
|
281
|
+
const { turnConfigUrl: t } = e;
|
|
208
282
|
return {
|
|
209
|
-
connectWrtc: async (
|
|
210
|
-
const
|
|
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:
|
|
215
|
-
wrtcParams:
|
|
288
|
+
offer: r,
|
|
289
|
+
wrtcParams: a
|
|
216
290
|
})
|
|
217
291
|
});
|
|
218
|
-
if (!
|
|
219
|
-
const
|
|
220
|
-
throw new Error(`Proxy request failed (${
|
|
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
|
|
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
|
|
316
|
+
async function P(o = { video: !0 }) {
|
|
228
317
|
try {
|
|
229
|
-
console.log("[RFStreams] requesting with",
|
|
230
|
-
const e = await navigator.mediaDevices.getUserMedia(
|
|
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((
|
|
324
|
+
return console.log("[RFStreams] fallback stream", t.getVideoTracks().map((r) => ({ id: r.id, label: r.label }))), t;
|
|
236
325
|
}
|
|
237
326
|
}
|
|
238
|
-
function
|
|
239
|
-
|
|
327
|
+
function C(o) {
|
|
328
|
+
o && (o.getTracks().forEach((e) => e.stop()), console.log("[RFStreams] Stream stopped"));
|
|
240
329
|
}
|
|
241
|
-
const
|
|
330
|
+
const B = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
242
331
|
__proto__: null,
|
|
243
|
-
stopStream:
|
|
244
|
-
useCamera:
|
|
245
|
-
}, Symbol.toStringTag, { value: "Module" })),
|
|
246
|
-
class
|
|
332
|
+
stopStream: C,
|
|
333
|
+
useCamera: P
|
|
334
|
+
}, Symbol.toStringTag, { value: "Module" })), N = 12;
|
|
335
|
+
class T {
|
|
247
336
|
constructor() {
|
|
248
|
-
|
|
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,
|
|
254
|
-
if (
|
|
255
|
-
return
|
|
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:
|
|
347
|
+
totalChunks: r
|
|
259
348
|
});
|
|
260
|
-
const
|
|
261
|
-
if (
|
|
262
|
-
const i = Array.from(
|
|
263
|
-
let
|
|
264
|
-
for (let c = 0; c <
|
|
265
|
-
const
|
|
266
|
-
s.set(
|
|
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
|
|
280
|
-
const e = new DataView(
|
|
281
|
-
return { frameId: t, chunkIndex:
|
|
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
|
|
284
|
-
if (
|
|
372
|
+
async function L(o, e = 6e3) {
|
|
373
|
+
if (o.iceGatheringState === "complete") return;
|
|
285
374
|
let t = !1;
|
|
286
|
-
const
|
|
287
|
-
|
|
375
|
+
const r = (a) => {
|
|
376
|
+
a.candidate && a.candidate.type === "srflx" && (t = !0);
|
|
288
377
|
};
|
|
289
|
-
|
|
378
|
+
o.addEventListener("icecandidate", r);
|
|
290
379
|
try {
|
|
291
380
|
await Promise.race([
|
|
292
|
-
new Promise((
|
|
293
|
-
const
|
|
294
|
-
|
|
381
|
+
new Promise((a) => {
|
|
382
|
+
const n = () => {
|
|
383
|
+
o.iceGatheringState === "complete" && (o.removeEventListener("icegatheringstatechange", n), a());
|
|
295
384
|
};
|
|
296
|
-
|
|
385
|
+
o.addEventListener("icegatheringstatechange", n);
|
|
297
386
|
}),
|
|
298
|
-
new Promise((
|
|
387
|
+
new Promise((a, n) => {
|
|
299
388
|
setTimeout(() => {
|
|
300
|
-
t ?
|
|
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
|
-
|
|
394
|
+
o.removeEventListener("icecandidate", r);
|
|
306
395
|
}
|
|
307
396
|
}
|
|
308
|
-
function
|
|
397
|
+
function x(o) {
|
|
309
398
|
return new Promise((e) => {
|
|
310
|
-
|
|
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
|
|
319
|
-
const t = e ?? D,
|
|
407
|
+
async function A(o, e) {
|
|
408
|
+
const t = e ?? D, r = new RTCPeerConnection({
|
|
320
409
|
iceServers: t
|
|
321
410
|
});
|
|
322
411
|
try {
|
|
323
|
-
|
|
412
|
+
r.addTransceiver("video", { direction: "recvonly" });
|
|
324
413
|
} catch (s) {
|
|
325
414
|
console.warn("[RFWebRTC] Could not add transceiver:", s);
|
|
326
415
|
}
|
|
327
|
-
|
|
416
|
+
o.getVideoTracks().forEach((s) => {
|
|
328
417
|
try {
|
|
329
418
|
s.contentHint = "detail";
|
|
330
419
|
} catch {
|
|
331
420
|
}
|
|
332
|
-
|
|
421
|
+
r.addTrack(s, o);
|
|
333
422
|
});
|
|
334
|
-
const
|
|
423
|
+
const a = x(r), n = r.createDataChannel("roboflow-control", {
|
|
335
424
|
ordered: !0
|
|
336
|
-
}), i = await
|
|
337
|
-
return await
|
|
338
|
-
pc:
|
|
339
|
-
offer:
|
|
340
|
-
remoteStreamPromise:
|
|
341
|
-
dataChannel:
|
|
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
|
|
345
|
-
const e =
|
|
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 (
|
|
352
|
-
console.warn("[RFWebRTC] Failed to set encoding parameters:",
|
|
440
|
+
} catch (r) {
|
|
441
|
+
console.warn("[RFWebRTC] Failed to set encoding parameters:", r);
|
|
353
442
|
}
|
|
354
443
|
}
|
|
355
|
-
class
|
|
444
|
+
class F {
|
|
356
445
|
/** @private */
|
|
357
|
-
constructor(e, t,
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
this.pc = e, this._localStream = t, this.remoteStreamPromise =
|
|
366
|
-
}), this.dataChannel.addEventListener("message", (
|
|
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 (
|
|
369
|
-
const { frameId: c, chunkIndex:
|
|
370
|
-
if (
|
|
371
|
-
const
|
|
372
|
-
s(
|
|
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(
|
|
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", (
|
|
382
|
-
console.error("[RFWebRTC] Data channel error:",
|
|
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
|
|
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
|
|
489
|
-
source:
|
|
577
|
+
async function j({
|
|
578
|
+
source: o,
|
|
490
579
|
connector: e,
|
|
491
580
|
wrtcParams: t,
|
|
492
|
-
onData:
|
|
493
|
-
options:
|
|
581
|
+
onData: r,
|
|
582
|
+
options: a = {}
|
|
494
583
|
}) {
|
|
495
|
-
var
|
|
584
|
+
var m;
|
|
496
585
|
if (!e || typeof e.connectWrtc != "function")
|
|
497
586
|
throw new Error("connector must have a connectWrtc method");
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
-
|
|
513
|
-
|
|
613
|
+
s.addEventListener("connectionstatechange", w), w(), setTimeout(() => {
|
|
614
|
+
s.removeEventListener("connectionstatechange", w), k(new Error("WebRTC connection timeout after 30s"));
|
|
514
615
|
}, 3e4);
|
|
515
|
-
}),
|
|
516
|
-
const
|
|
517
|
-
return new
|
|
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
|
|
620
|
+
const M = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
520
621
|
__proto__: null,
|
|
521
|
-
ChunkReassembler:
|
|
522
|
-
RFWebRTCConnection:
|
|
523
|
-
parseBinaryHeader:
|
|
524
|
-
useStream:
|
|
622
|
+
ChunkReassembler: T,
|
|
623
|
+
RFWebRTCConnection: F,
|
|
624
|
+
parseBinaryHeader: E,
|
|
625
|
+
useStream: j
|
|
525
626
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
526
627
|
export {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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"})});
|
package/dist/inference-api.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
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
|
-
*
|
|
191
|
-
* const
|
|
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?:
|
|
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":"
|
|
1
|
+
{"version":3,"file":"inference-api.d.ts","sourceRoot":"","sources":["../src/inference-api.ts"],"names":[],"mappings":"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"}
|
package/dist/webrtc.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|