emusks 2.0.18 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +19 -2
- package/src/cycletls.js +2 -1
- package/src/flow.js +69 -5
- package/src/helpers/index.js +4 -0
- package/src/helpers/jetfuel.js +175 -0
- package/src/helpers/juicebox/chunk-BNv3lrIs.js +1 -0
- package/src/helpers/juicebox/index.js +30 -0
- package/src/helpers/juicebox/juicebox-sdk_bg.wasm +0 -0
- package/src/helpers/juicebox/sdk.js +1 -0
- package/src/helpers/xchat-call-media.js +127 -0
- package/src/helpers/xchat-calls.js +553 -0
- package/src/helpers/xchat-crypto.js +324 -0
- package/src/helpers/xchat-group-calls.js +340 -0
- package/src/helpers/xchat-juicebox.js +41 -0
- package/src/helpers/xchat-queries.js +3 -0
- package/src/helpers/xchat.js +794 -0
- package/src/index.js +2 -0
- package/src/instrumentation.js +124 -0
- package/src/jetfuel.js +92 -0
- package/src/parsers/jetfuel.js +226 -0
- package/src/v1.1.js +0 -11
- package/build/graphql.js +0 -19
- package/build/v1.1.js +0 -28
- package/build/v2.js +0 -28
- package/bun.lock +0 -93
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<img src="https://emusks.tiago.zip/icon.svg" width="120">
|
|
2
2
|
<h1>emusks: Reverse-engineered Twitter API client</h1>
|
|
3
3
|
|
|
4
|
-
Log in and interact with the unofficial X API using any client identity
|
|
4
|
+
Log in and interact with the unofficial X API using any client identity - web, Android, iOS, or TweetDeck. Covers tweets, users, DMs, communities, spaces, and XChat (X's end-to-end encrypted chat): send encrypted DMs in one call.
|
|
5
5
|
|
|
6
6
|
officially dmca'd by twitter™ 🏆 • includes a few leaked ads bearers
|
|
7
7
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emusks",
|
|
3
|
-
"version": "2.0
|
|
4
|
-
"description": "Reverse-engineered Twitter API client. Log in and interact with the unofficial X API using any client identity
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Reverse-engineered Twitter API client. Log in and interact with the unofficial X API using any client identity - web, Android, iOS, or TweetDeck",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"client",
|
|
7
7
|
"reverse-engineering",
|
|
@@ -24,8 +24,25 @@
|
|
|
24
24
|
"exports": {
|
|
25
25
|
".": "./src/index.js"
|
|
26
26
|
},
|
|
27
|
+
"files": [
|
|
28
|
+
"src",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
27
31
|
"dependencies": {
|
|
28
32
|
"cycletls": "^2.0.5",
|
|
33
|
+
"linkedom": "^0.18.12",
|
|
34
|
+
"tweetnacl": "^1.0.3",
|
|
29
35
|
"x-client-transaction-id": "^0.2.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@roamhq/wrtc": "^0.10.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"@roamhq/wrtc": {
|
|
42
|
+
"optional": true
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@roamhq/wrtc": "^0.10.0"
|
|
30
47
|
}
|
|
31
48
|
}
|
package/src/cycletls.js
CHANGED
package/src/flow.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import getCycleTLS from "./cycletls.js";
|
|
2
2
|
import clients from "./clients.js";
|
|
3
|
+
import { solveJsInstrumentation } from "./instrumentation.js";
|
|
3
4
|
|
|
4
5
|
const BASE_URL = "https://api.x.com/1.1/onboarding/task.json";
|
|
5
6
|
const GUEST_ACTIVATE_URL = "https://api.x.com/1.1/guest/activate.json";
|
|
@@ -103,6 +104,33 @@ class CookieSession {
|
|
|
103
104
|
|
|
104
105
|
return response;
|
|
105
106
|
}
|
|
107
|
+
|
|
108
|
+
async get(url, options = {}) {
|
|
109
|
+
const headers = options.headers || {};
|
|
110
|
+
const cookieString = this.getCookieString();
|
|
111
|
+
if (cookieString) {
|
|
112
|
+
headers["Cookie"] = cookieString;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const response = await this.cycleTLS(
|
|
116
|
+
url,
|
|
117
|
+
{
|
|
118
|
+
ja3: clients.web.fingerprints.ja3,
|
|
119
|
+
ja4r: clients.web.fingerprints.ja4r,
|
|
120
|
+
userAgent: USER_AGENT,
|
|
121
|
+
headers,
|
|
122
|
+
proxy: this.proxy || undefined,
|
|
123
|
+
},
|
|
124
|
+
"get",
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const setCookie = response.headers?.["Set-Cookie"] || response.headers?.["set-cookie"];
|
|
128
|
+
if (setCookie) {
|
|
129
|
+
this.parseCookies(setCookie);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return response;
|
|
133
|
+
}
|
|
106
134
|
}
|
|
107
135
|
|
|
108
136
|
function getFlowHeaders(guestToken) {
|
|
@@ -200,12 +228,39 @@ async function initFlow(session, guestToken) {
|
|
|
200
228
|
return [flowToken, headers, data];
|
|
201
229
|
}
|
|
202
230
|
|
|
203
|
-
|
|
231
|
+
const DEFAULT_JS_INST_URL = "https://twitter.com/i/js_inst?c_name=ui_metrics";
|
|
232
|
+
|
|
233
|
+
async function fetchChallengeJs(session, url) {
|
|
234
|
+
const response = await session.get(url, {
|
|
235
|
+
Accept: "*/*",
|
|
236
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
237
|
+
Referer: "https://x.com/",
|
|
238
|
+
"User-Agent": USER_AGENT,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
if (response.status !== 200) {
|
|
242
|
+
throw new Error(`Failed to fetch js_instrumentation challenge: ${response.status}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
let body = response.body ?? response.data;
|
|
246
|
+
if (body && typeof body === "object") {
|
|
247
|
+
body = Buffer.from(body.data ?? body).toString("utf8");
|
|
248
|
+
}
|
|
249
|
+
if (typeof body !== "string" || body.length === 0) {
|
|
250
|
+
throw new Error("js_instrumentation challenge returned an empty body");
|
|
251
|
+
}
|
|
252
|
+
return body;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function submitJsInstrumentation(session, flowToken, headers, subtask) {
|
|
256
|
+
const url = subtask?.js_instrumentation?.url || DEFAULT_JS_INST_URL;
|
|
257
|
+
const challengeJs = await fetchChallengeJs(session, url);
|
|
258
|
+
const response = solveJsInstrumentation(challengeJs);
|
|
259
|
+
|
|
204
260
|
return await makeRequest(session, headers, flowToken, {
|
|
205
261
|
subtask_id: "LoginJsInstrumentationSubtask",
|
|
206
262
|
js_instrumentation: {
|
|
207
|
-
response
|
|
208
|
-
'{"rf":{"a4fc506d24bb4843c48a1966940c2796bf4fb7617a2d515ad3297b7df6b459b6":121,"bff66e16f1d7ea28c04653dc32479cf416a9c8b67c80cb8ad533b2a44fee82a3":-1,"ac4008077a7e6ca03210159dbe2134dea72a616f03832178314bb9931645e4f7":-22,"c3a8a81a9b2706c6fec42c771da65a9597c537b8e4d9b39e8e58de9fe31ff239":-12},"s":"ZHYaDA9iXRxOl2J3AZ9cc23iJx-Fg5E82KIBA_fgeZFugZGYzRtf8Bl3EUeeYgsK30gLFD2jTQx9fAMsnYCw0j8ahEy4Pb5siM5zD6n7YgOeWmFFaXoTwaGY4H0o-jQnZi5yWZRAnFi4lVuCVouNz_xd2BO2sobCO7QuyOsOxQn2CWx7bjD8vPAzT5BS1mICqUWyjZDjLnRZJU6cSQG5YFIHEPBa8Kj-v1JFgkdAfAMIdVvP7C80HWoOqYivQR7IBuOAI4xCeLQEdxlGeT-JYStlP9dcU5St7jI6ExyMeQnRicOcxXLXsan8i5Joautk2M8dAJFByzBaG4wtrPhQ3QAAAZEi-_t7"}',
|
|
263
|
+
response,
|
|
209
264
|
link: "next_link",
|
|
210
265
|
},
|
|
211
266
|
});
|
|
@@ -306,7 +361,8 @@ async function resolve(staticValue, onRequest, type) {
|
|
|
306
361
|
}
|
|
307
362
|
|
|
308
363
|
export default async function flowLogin(opts) {
|
|
309
|
-
const { username, password, email, phone, onRequest, proxy } = opts;
|
|
364
|
+
const { username, password, email, phone, onRequest, proxy, debug } = opts;
|
|
365
|
+
const log = debug ? (...a) => console.error("[flow]", ...a) : () => {};
|
|
310
366
|
|
|
311
367
|
if (!username) throw new Error("username is required for flow login");
|
|
312
368
|
if (!password) throw new Error("password is required for flow login");
|
|
@@ -315,6 +371,7 @@ export default async function flowLogin(opts) {
|
|
|
315
371
|
const session = new CookieSession(cycleTLS, proxy);
|
|
316
372
|
|
|
317
373
|
const guestToken = await getGuestToken(session);
|
|
374
|
+
log("guest token", guestToken);
|
|
318
375
|
|
|
319
376
|
let [flowToken, headers, data] = await initFlow(session, guestToken);
|
|
320
377
|
headers["X-Guest-Token"] = guestToken;
|
|
@@ -324,16 +381,23 @@ export default async function flowLogin(opts) {
|
|
|
324
381
|
while (!isLoginComplete(data) && step < MAX_FLOW_STEPS) {
|
|
325
382
|
step++;
|
|
326
383
|
const subtaskIds = getSubtaskIds(data);
|
|
384
|
+
log(`step ${step}: subtasks [${subtaskIds.join(", ")}]`);
|
|
327
385
|
|
|
328
386
|
if (subtaskIds.length === 0) {
|
|
329
387
|
throw new Error("Login flow returned no subtasks and login is not complete");
|
|
330
388
|
}
|
|
331
389
|
|
|
332
390
|
const current = subtaskIds[0];
|
|
391
|
+
const currentSubtask = data.subtasks.find((s) => s.subtask_id === current);
|
|
333
392
|
|
|
334
393
|
switch (current) {
|
|
335
394
|
case "LoginJsInstrumentationSubtask":
|
|
336
|
-
[flowToken, data] = await submitJsInstrumentation(
|
|
395
|
+
[flowToken, data] = await submitJsInstrumentation(
|
|
396
|
+
session,
|
|
397
|
+
flowToken,
|
|
398
|
+
headers,
|
|
399
|
+
currentSubtask,
|
|
400
|
+
);
|
|
337
401
|
break;
|
|
338
402
|
|
|
339
403
|
case "LoginEnterUserIdentifierSSO":
|
package/src/helpers/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as account from "./account.js";
|
|
|
2
2
|
import * as bookmarks from "./bookmarks.js";
|
|
3
3
|
import * as communities from "./communities.js";
|
|
4
4
|
import * as dms from "./dms.js";
|
|
5
|
+
import * as jetfuel from "./jetfuel.js";
|
|
5
6
|
import * as lists from "./lists.js";
|
|
6
7
|
import * as media from "./media.js";
|
|
7
8
|
import * as notifications from "./notifications.js";
|
|
@@ -13,6 +14,7 @@ import * as topics from "./topics.js";
|
|
|
13
14
|
import * as trends from "./trends.js";
|
|
14
15
|
import * as tweets from "./tweets.js";
|
|
15
16
|
import * as users from "./users.js";
|
|
17
|
+
import * as xchat from "./xchat.js";
|
|
16
18
|
|
|
17
19
|
function namespace(proto, name, methods) {
|
|
18
20
|
Object.defineProperty(proto, name, {
|
|
@@ -48,4 +50,6 @@ export default function initHelpers(proto) {
|
|
|
48
50
|
namespace(proto, "topics", topics);
|
|
49
51
|
namespace(proto, "media", media);
|
|
50
52
|
namespace(proto, "syndication", syndication);
|
|
53
|
+
namespace(proto, "xchat", xchat);
|
|
54
|
+
namespace(proto, "jetfuel", jetfuel);
|
|
51
55
|
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildTimelineToken,
|
|
3
|
+
decodeJetfuel,
|
|
4
|
+
decodeTimelineToken,
|
|
5
|
+
extractTimelineTokens,
|
|
6
|
+
} from "../parsers/jetfuel.js";
|
|
7
|
+
import parseTimeline from "../parsers/timeline.js";
|
|
8
|
+
|
|
9
|
+
export const ENGAGEMENTS = ["Likes", "Replies", "Bookmarks", "Quotes", "VideoQualityViews"];
|
|
10
|
+
|
|
11
|
+
export const PERIODS = ["Daily", "Weekly", "Monthly"];
|
|
12
|
+
|
|
13
|
+
export const COUNTRIES = [
|
|
14
|
+
"All", "USA", "CAN", "GBR", "AUS", "DEU", "FRA", "JPN", "BRA", "IND", "MEX",
|
|
15
|
+
"ESP", "ITA", "KOR", "NLD", "ARG", "POL", "TUR", "IDN", "SAU", "ZAF", "NGA",
|
|
16
|
+
"PHL", "THA", "VNM", "EGY", "PAK", "MYS", "SGP", "ARE", "COL", "CHL", "SWE",
|
|
17
|
+
"NOR", "DNK", "FIN", "BEL", "AUT", "CHE", "PRT", "GRC", "IRL", "NZL", "ISR",
|
|
18
|
+
"RUS", "UKR", "CZE", "ROU", "HUN",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
function assertEnum(value, allowed, label) {
|
|
22
|
+
if (!allowed.includes(value)) {
|
|
23
|
+
throw new Error(`invalid ${label} "${value}", expected one of: ${allowed.join(", ")}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function page(route, opts = {}) {
|
|
28
|
+
const res = await this.jf(route, opts);
|
|
29
|
+
return { ...decodeJetfuel(res.buffer), status: res.status, contentType: res.contentType };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function remote(route, opts = {}) {
|
|
33
|
+
return await page.call(this, route, opts);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function raw(route, opts = {}) {
|
|
37
|
+
return await this.jf(route, opts);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function decode(buffer) {
|
|
41
|
+
return decodeJetfuel(buffer);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function topPostsTimelineId({ engagement = "Likes", period = "Daily", country = "All" } = {}) {
|
|
45
|
+
assertEnum(engagement, ENGAGEMENTS, "engagement");
|
|
46
|
+
assertEnum(period, PERIODS, "period");
|
|
47
|
+
return buildTimelineToken({ engagement, country, period });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function topPostsFeed({ engagement = "Likes", period = "Daily", country = "All" } = {}) {
|
|
51
|
+
assertEnum(engagement, ENGAGEMENTS, "engagement");
|
|
52
|
+
assertEnum(period, PERIODS, "period");
|
|
53
|
+
|
|
54
|
+
const params = { engagement, period };
|
|
55
|
+
if (country && country !== "All") params.country = country;
|
|
56
|
+
|
|
57
|
+
const res = await this.jf("creators/inspiration/remote/urt", { params });
|
|
58
|
+
const tokens = extractTimelineTokens(res.buffer);
|
|
59
|
+
const token = tokens[0] || null;
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
status: res.status,
|
|
63
|
+
token,
|
|
64
|
+
params: token ? decodeTimelineToken(token) : null,
|
|
65
|
+
decoded: decodeJetfuel(res.buffer),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function topPosts(opts = {}) {
|
|
70
|
+
const { engagement = "Likes", period = "Daily", country = "All", count = 20, cursor, viaRemote, raw: returnRaw } = opts;
|
|
71
|
+
assertEnum(engagement, ENGAGEMENTS, "engagement");
|
|
72
|
+
assertEnum(period, PERIODS, "period");
|
|
73
|
+
|
|
74
|
+
let timelineId;
|
|
75
|
+
if (viaRemote) {
|
|
76
|
+
const feed = await topPostsFeed.call(this, { engagement, period, country });
|
|
77
|
+
if (!feed.token) throw new Error("inspiration feed returned no timeline token");
|
|
78
|
+
timelineId = feed.token;
|
|
79
|
+
} else {
|
|
80
|
+
timelineId = buildTimelineToken({ engagement, country, period });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const variables = {
|
|
84
|
+
timelineId,
|
|
85
|
+
count,
|
|
86
|
+
withQuickPromoteEligibilityTweetFields: true,
|
|
87
|
+
};
|
|
88
|
+
if (cursor) variables.cursor = cursor;
|
|
89
|
+
|
|
90
|
+
const res = await this.graphql("GenericTimelineById", { variables });
|
|
91
|
+
if (returnRaw) return res;
|
|
92
|
+
|
|
93
|
+
const parsed = parseTimeline(res);
|
|
94
|
+
return { ...parsed, engagement, period, country, timelineId };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function inspirationPage(opts = {}) {
|
|
98
|
+
return await page.call(this, "creators/inspiration/top_posts", opts);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function stories(opts = {}) {
|
|
102
|
+
return await page.call(this, "stories/home", opts);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function storiesRemote({ category } = {}, opts = {}) {
|
|
106
|
+
const params = { ...opts.params };
|
|
107
|
+
if (category) params.category = category;
|
|
108
|
+
return await remote.call(this, "stories/storiesRemote", { ...opts, params });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function newsArticle(id, opts = {}) {
|
|
112
|
+
if (!id) throw new Error("newsArticle requires an article id");
|
|
113
|
+
return await page.call(this, `news/article/id/${id}`, opts);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const SPORT_EVENTS = {
|
|
117
|
+
nba: { noun: "game", home: true },
|
|
118
|
+
nfl: { noun: "game", home: true },
|
|
119
|
+
f1: { noun: "race", home: true },
|
|
120
|
+
nhl: { noun: "game", home: false },
|
|
121
|
+
soccer: { noun: "match", home: false },
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export async function sportHome(sport, opts = {}) {
|
|
125
|
+
if (!sport) throw new Error("sportHome requires a sport");
|
|
126
|
+
return await page.call(this, `${sport}/home`, opts);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function league(sport, opts = {}) {
|
|
130
|
+
if (!sport) throw new Error("league requires a sport");
|
|
131
|
+
return await page.call(this, `${sport}/league/home`, opts);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function game({ sport, id, noun, home } = {}, opts = {}) {
|
|
135
|
+
if (!sport || !id) throw new Error("game requires { sport, id }");
|
|
136
|
+
const shape = SPORT_EVENTS[sport] || {};
|
|
137
|
+
const eventNoun = noun ?? shape.noun ?? "game";
|
|
138
|
+
const hasHome = home ?? shape.home ?? false;
|
|
139
|
+
const route = `${sport}/${eventNoun}${hasHome ? "/home" : ""}/id/${id}`;
|
|
140
|
+
return await page.call(this, route, opts);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function event(id, opts = {}) {
|
|
144
|
+
if (!id) throw new Error("event requires an id");
|
|
145
|
+
return await page.call(this, "events/event", { ...opts, params: { id, ...opts.params } });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function brackets(opts = {}) {
|
|
149
|
+
return await page.call(this, "brackets/home", opts);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function bracketView(userId, opts = {}) {
|
|
153
|
+
return await page.call(this, userId ? `brackets/view/id/${userId}` : "brackets/view", opts);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function creatorsStudio(opts = {}) {
|
|
157
|
+
return await page.call(this, "creators/studio", opts);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function onboardingTopics(opts = {}) {
|
|
161
|
+
return await remote.call(this, "onboarding/remotes/topics", opts);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export async function health(opts = {}) {
|
|
165
|
+
const res = await this.jf("health", { ...opts, origin: "jf" });
|
|
166
|
+
return res.text();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function ogImageUrl(tweetId) {
|
|
170
|
+
return `https://jf.x.com/images/post/${tweetId}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function mediaPreviewUrl(tweetId) {
|
|
174
|
+
return `https://jf.x.com/images/media-preview/${tweetId}`;
|
|
175
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(e&&(t=e(e=0)),t),s=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),c=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},l=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},u=(n,r,a)=>(a=n==null?{}:e(i(n)),l(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),d=e=>a.call(e,`module.exports`)?e[`module.exports`]:l(t({},`__esModule`,{value:!0}),e);export{u as a,d as i,o as n,c as r,s as t};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const wasmPath = join(dir, "juicebox-sdk_bg.wasm");
|
|
7
|
+
|
|
8
|
+
let authTokenResolver = () => {
|
|
9
|
+
throw new Error("juicebox: auth-token resolver not set");
|
|
10
|
+
};
|
|
11
|
+
export function setAuthTokenResolver(fn) {
|
|
12
|
+
authTokenResolver = fn;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
globalThis.JuiceboxGetAuthToken = (realmId) => Promise.resolve(authTokenResolver(realmId));
|
|
16
|
+
|
|
17
|
+
const origFetch = globalThis.fetch.bind(globalThis);
|
|
18
|
+
globalThis.fetch = (input, init) => {
|
|
19
|
+
const url = typeof input === "string" ? input : (input?.url ?? String(input));
|
|
20
|
+
if (url.includes("juicebox-sdk_bg") && url.endsWith(".wasm")) {
|
|
21
|
+
return Promise.resolve(
|
|
22
|
+
new Response(readFileSync(wasmPath), { headers: { "Content-Type": "application/wasm" } }),
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return origFetch(input, init);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const glue = await import("./sdk.js");
|
|
29
|
+
|
|
30
|
+
export const { Client, Configuration, RecoverError, RecoverErrorReason } = glue;
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,r as t}from"./chunk-BNv3lrIs.js";var n,r=e((()=>{n=`/assets/juicebox-sdk_bg-B54z_Bsk.wasm`})),i,a=e((()=>{i=async(e={},t)=>{let n;if(t.startsWith(`data:`)){let r=t.replace(/^data:.*?base64,/,``),i;if(typeof Buffer==`function`&&typeof Buffer.from==`function`)i=Buffer.from(r,`base64`);else if(typeof atob==`function`){let e=atob(r);i=new Uint8Array(e.length);for(let t=0;t<e.length;t++)i[t]=e.charCodeAt(t)}else throw Error(`Cannot decode base64-encoded data URL`);n=await WebAssembly.instantiate(i,e)}else{let r=await fetch(t),i=r.headers.get(`Content-Type`)||``;if(`instantiateStreaming`in WebAssembly&&i.startsWith(`application/wasm`))n=await WebAssembly.instantiateStreaming(r,e);else{let t=await r.arrayBuffer();n=await WebAssembly.instantiate(t,e)}}return n.instance.exports}}));function o(e){V=e}function s(e){return H[e]}function c(e){e<132||(H[e]=U,U=e)}function l(e){let t=s(e);return c(e),t}function u(){return(G===null||G.byteLength===0)&&(G=new Uint8Array(V.memory.buffer)),G}function d(e,t,n){if(n===void 0){let n=K.encode(e),r=t(n.length,1)>>>0;return u().subarray(r,r+n.length).set(n),W=n.length,r}let r=e.length,i=t(r,1)>>>0,a=u(),o=0;for(;o<r;o++){let t=e.charCodeAt(o);if(t>127)break;a[i+o]=t}if(o!==r){o!==0&&(e=e.slice(o)),i=n(i,r,r=o+e.length*3,1)>>>0;let t=u().subarray(i+o,i+r),a=mt(e,t);o+=a.written}return W=o,i}function f(e){return e==null}function p(){return(q===null||q.byteLength===0)&&(q=new Int32Array(V.memory.buffer)),q}function m(e,t){return e>>>=0,J.decode(u().subarray(e,e+t))}function h(e){U===H.length&&H.push(H.length+1);let t=U;return U=H[t],H[t]=e,t}function ee(){return(Y===null||Y.byteLength===0)&&(Y=new Float64Array(V.memory.buffer)),Y}function g(e){let t=typeof e;if(t==`number`||t==`boolean`||e==null)return`${e}`;if(t==`string`)return`"${e}"`;if(t==`symbol`){let t=e.description;return t==null?`Symbol`:`Symbol(${t})`}if(t==`function`){let t=e.name;return typeof t==`string`&&t.length>0?`Function(${t})`:`Function`}if(Array.isArray(e)){let t=e.length,n=`[`;t>0&&(n+=g(e[0]));for(let r=1;r<t;r++)n+=`, `+g(e[r]);return n+=`]`,n}let n=/\[object ([^\]]+)\]/.exec(toString.call(e)),r;if(n.length>1)r=n[1];else return toString.call(e);if(r==`Object`)try{return`Object(`+JSON.stringify(e)+`)`}catch{return`Object`}return e instanceof Error?`${e.name}: ${e.message}\n${e.stack}`:r}function te(e,t,n,r){let i={a:e,b:t,cnt:1,dtor:n},a=(...e)=>{i.cnt++;let t=i.a;i.a=0;try{return r(t,i.b,...e)}finally{--i.cnt===0?V.__wbindgen_export_2.get(i.dtor)(t,i.b):i.a=t}};return a.original=i,a}function ne(e,t){V.wasm_bindgen__convert__closures__invoke0_mut__hcf60f8fe499c96ac(e,t)}function re(e,t,n){V._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h664375554e075ae3(e,t,h(n))}function ie(e,t){if(!(e instanceof t))throw Error(`expected instance of ${t.name}`);return e.ptr}function _(e,t){let n=t(e.length*1,1)>>>0;return u().set(e,n/1),W=e.length,n}function v(e,t){try{return e.apply(this,t)}catch(e){V.__wbindgen_exn_store(h(e))}}function ae(e,t,n,r){V.wasm_bindgen__convert__closures__invoke2_mut__h35a3737c9c8313d7(e,t,h(n),h(r))}function oe(e){l(e)}function se(e){return h(Z.__wrap(e))}function ce(e){let t=l(e).original;return t.cnt--==1?(t.a=0,!0):!1}function le(e,t){let n=s(t),r=typeof n==`string`?n:void 0;var i=f(r)?0:d(r,V.__wbindgen_malloc,V.__wbindgen_realloc),a=W;p()[e/4+1]=a,p()[e/4+0]=i}function ue(e){return typeof s(e)==`string`}function de(e){let t=s(e);return typeof t==`object`&&!!t}function fe(e){return s(e)===void 0}function pe(e,t){return s(e)in s(t)}function y(e,t){return h(Error(m(e,t)))}function b(e){return h(fetch(s(e)))}function x(){return v(function(e,t){return h(setTimeout(s(e),t))},arguments)}function S(){return v(function(e){return h(JuiceboxGetAuthToken(l(e)))},arguments)}function C(){return h(Error())}function w(e,t){let n=s(t).stack,r=d(n,V.__wbindgen_malloc,V.__wbindgen_realloc),i=W;p()[e/4+1]=i,p()[e/4+0]=r}function T(e,t){let n,r;try{n=e,r=t,console.error(m(e,t))}finally{V.__wbindgen_free(n,r,1)}}function E(e){queueMicrotask(s(e))}function D(e){let t=s(e).queueMicrotask;return h(t)}function O(e){return typeof s(e)==`function`}function k(e){return h(s(e))}function A(e,t){return s(e)==s(t)}function j(e){let t=s(e);return typeof t==`boolean`?+!!t:2}function M(e,t){let n=s(t),r=typeof n==`number`?n:void 0;ee()[e/8+1]=f(r)?0:r,p()[e/4+0]=!f(r)}function N(e){return+s(e)}function P(e){return h(e)}function F(e,t){return h(m(e,t))}function I(e,t){let n=s(e)[s(t)];return h(n)}function L(){return Date.now()}function R(e){return s(e).now()}function z(e){let t=s(e).headers;return h(t)}function B(){return v(function(e,t,n){return h(new Request(m(e,t),s(n)))},arguments)}function me(){return v(function(e,t,n,r,i){s(e).set(m(t,n),m(r,i))},arguments)}function he(e){return h(s(e).arrayBuffer())}function ge(e){let t;try{t=s(e)instanceof Response}catch{t=!1}return t}function _e(e){return s(e).status}function ve(e){let t=s(e).headers;return h(t)}function ye(){return v(function(e){return h(s(e).blob())},arguments)}function be(e){let t=s(e).crypto;return h(t)}function xe(e){let t=s(e).process;return h(t)}function Se(e){let t=s(e).versions;return h(t)}function Ce(e){let t=s(e).node;return h(t)}function we(e){let t=s(e).msCrypto;return h(t)}function Te(){return v(function(){let e=module.require;return h(e)},arguments)}function Ee(){return v(function(e,t){s(e).randomFillSync(l(t))},arguments)}function De(){return v(function(e,t){s(e).getRandomValues(s(t))},arguments)}function Oe(e,t){let n=s(e)[t>>>0];return h(n)}function ke(e){return s(e).length}function Ae(e,t){return h(Function(m(e,t)))}function je(e){let t=s(e).next;return h(t)}function Me(){return v(function(e){return h(s(e).next())},arguments)}function Ne(e){return s(e).done}function Pe(e){let t=s(e).value;return h(t)}function Fe(){let e=Symbol.iterator;return h(e)}function Ie(){return v(function(e,t){return h(Reflect.get(s(e),s(t)))},arguments)}function Le(){return v(function(e,t){return h(s(e).call(s(t)))},arguments)}function Re(){return h({})}function ze(){return v(function(){let e=self.self;return h(e)},arguments)}function Be(){return v(function(){let e=window.window;return h(e)},arguments)}function Ve(){return v(function(){let e=globalThis.globalThis;return h(e)},arguments)}function He(){return v(function(){let e=global.global;return h(e)},arguments)}function Ue(e){return h(Array.from(s(e)))}function We(e){return Array.isArray(s(e))}function Ge(e){let t;try{t=s(e)instanceof ArrayBuffer}catch{t=!1}return t}function Ke(){return v(function(e,t,n){return h(s(e).call(s(t),s(n)))},arguments)}function qe(e){return Number.isSafeInteger(s(e))}function Je(e){return h(Object.entries(s(e)))}function Ye(e,t){try{var n={a:e,b:t};return h(new Promise((e,t)=>{let r=n.a;n.a=0;try{return ae(r,n.b,e,t)}finally{n.a=r}}))}finally{n.a=n.b=0}}function Xe(e){return h(Promise.resolve(s(e)))}function Ze(e,t){return h(s(e).then(s(t)))}function Qe(e,t,n){return h(s(e).then(s(t),s(n)))}function $e(e){let t=s(e).buffer;return h(t)}function et(e,t,n){return h(new Uint8Array(s(e),t>>>0,n>>>0))}function tt(e){return h(new Uint8Array(s(e)))}function nt(e,t,n){s(e).set(s(t),n>>>0)}function rt(e){return s(e).length}function it(e){let t;try{t=s(e)instanceof Uint8Array}catch{t=!1}return t}function at(e){return h(new Uint8Array(e>>>0))}function ot(e,t,n){return h(s(e).subarray(t>>>0,n>>>0))}function st(){return v(function(e){return h(JSON.stringify(s(e)))},arguments)}function ct(){return v(function(e,t,n){return Reflect.set(s(e),s(t),s(n))},arguments)}function lt(e,t){let n=d(g(s(t)),V.__wbindgen_malloc,V.__wbindgen_realloc),r=W;p()[e/4+1]=r,p()[e/4+0]=n}function ut(e,t){throw Error(m(e,t))}function dt(){let e=V.memory;return h(e)}function ft(e,t,n){return h(te(e,t,123,ne))}function pt(e,t,n){return h(te(e,t,217,re))}var V,H,U,W,G,K,mt,q,J,Y,ht,gt,_t,vt,yt,X,Z,Q=e((()=>{H=Array(128).fill(void 0),H.push(void 0,null,!0,!1),U=H.length,W=0,G=null,K=new(typeof TextEncoder>`u`?(0,module.require)(`util`).TextEncoder:TextEncoder)(`utf-8`),mt=typeof K.encodeInto==`function`?function(e,t){return K.encodeInto(e,t)}:function(e,t){let n=K.encode(e);return t.set(n),{read:e.length,written:n.length}},q=null,J=new(typeof TextDecoder>`u`?(0,module.require)(`util`).TextDecoder:TextDecoder)(`utf-8`,{ignoreBOM:!0,fatal:!0}),J.decode(),Y=null,ht=Object.freeze({InvalidAuth:0,0:`InvalidAuth`,UpgradeRequired:1,1:`UpgradeRequired`,RateLimitExceeded:2,2:`RateLimitExceeded`,Assertion:3,3:`Assertion`,Transient:4,4:`Transient`}),gt=Object.freeze({InvalidAuth:0,0:`InvalidAuth`,UpgradeRequired:1,1:`UpgradeRequired`,RateLimitExceeded:2,2:`RateLimitExceeded`,Assertion:3,3:`Assertion`,Transient:4,4:`Transient`}),_t=Object.freeze({InvalidPin:0,0:`InvalidPin`,NotRegistered:1,1:`NotRegistered`,InvalidAuth:2,2:`InvalidAuth`,UpgradeRequired:3,3:`UpgradeRequired`,RateLimitExceeded:4,4:`RateLimitExceeded`,Assertion:5,5:`Assertion`,Transient:6,6:`Transient`}),vt=class{__destroy_into_raw(){let e=this.__wbg_ptr;return this.__wbg_ptr=0,e}free(){let e=this.__destroy_into_raw();V.__wbg_authtokengenerator_free(e)}constructor(e){let t=V.authtokengenerator_new(h(e));return this.__wbg_ptr=t>>>0,this}vend(e,t){let n,r;try{let o=V.__wbindgen_add_to_stack_pointer(-16),s=d(e,V.__wbindgen_malloc,V.__wbindgen_realloc),c=W,l=d(t,V.__wbindgen_malloc,V.__wbindgen_realloc),u=W;V.authtokengenerator_vend(o,this.__wbg_ptr,s,c,l,u);var i=p()[o/4+0],a=p()[o/4+1];return n=i,r=a,m(i,a)}finally{V.__wbindgen_add_to_stack_pointer(16),V.__wbindgen_free(n,r,1)}}static random_secret_id(){let e,t;try{let i=V.__wbindgen_add_to_stack_pointer(-16);V.authtokengenerator_random_secret_id(i);var n=p()[i/4+0],r=p()[i/4+1];return e=n,t=r,m(n,r)}finally{V.__wbindgen_add_to_stack_pointer(16),V.__wbindgen_free(e,t,1)}}},yt=class{__destroy_into_raw(){let e=this.__wbg_ptr;return this.__wbg_ptr=0,e}free(){let e=this.__destroy_into_raw();V.__wbg_client_free(e)}constructor(e,t){ie(e,X);var n=e.__destroy_into_raw();let r=V.client_new(n,h(t));return this.__wbg_ptr=r>>>0,this}register(e,t,n,r){let i=_(e,V.__wbindgen_malloc),a=W,o=_(t,V.__wbindgen_malloc),s=W,c=_(n,V.__wbindgen_malloc),u=W;return l(V.client_register(this.__wbg_ptr,i,a,o,s,c,u,r))}recover(e,t){let n=_(e,V.__wbindgen_malloc),r=W,i=_(t,V.__wbindgen_malloc),a=W;return l(V.client_recover(this.__wbg_ptr,n,r,i,a))}delete(){return l(V.client_delete(this.__wbg_ptr))}},X=class{__destroy_into_raw(){let e=this.__wbg_ptr;return this.__wbg_ptr=0,e}free(){let e=this.__destroy_into_raw();V.__wbg_configuration_free(e)}constructor(e){let t=V.configuration_new(h(e));return this.__wbg_ptr=t>>>0,this}},Z=class e{static __wrap(t){t>>>=0;let n=Object.create(e.prototype);return n.__wbg_ptr=t,n}__destroy_into_raw(){let e=this.__wbg_ptr;return this.__wbg_ptr=0,e}free(){let e=this.__destroy_into_raw();V.__wbg_recovererror_free(e)}get reason(){return V.__wbg_get_recovererror_reason(this.__wbg_ptr)}set reason(e){V.__wbg_set_recovererror_reason(this.__wbg_ptr,e)}get guesses_remaining(){let e=V.__wbg_get_recovererror_guesses_remaining(this.__wbg_ptr);return e===16777215?void 0:e}set guesses_remaining(e){V.__wbg_set_recovererror_guesses_remaining(this.__wbg_ptr,f(e)?16777215:e)}}})),bt=t({__wbg_authtokengenerator_free:()=>Pt,__wbg_client_free:()=>At,__wbg_configuration_free:()=>Ct,__wbg_get_recovererror_guesses_remaining:()=>Dt,__wbg_get_recovererror_reason:()=>Tt,__wbg_recovererror_free:()=>wt,__wbg_set_recovererror_guesses_remaining:()=>Ot,__wbg_set_recovererror_reason:()=>Et,__wbindgen_add_to_stack_pointer:()=>Wt,__wbindgen_exn_store:()=>Ut,__wbindgen_export_2:()=>Bt,__wbindgen_free:()=>Gt,__wbindgen_malloc:()=>Rt,__wbindgen_realloc:()=>zt,_dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h664375554e075ae3:()=>Ht,authtokengenerator_new:()=>Ft,authtokengenerator_random_secret_id:()=>Lt,authtokengenerator_vend:()=>It,client_delete:()=>$,client_new:()=>jt,client_recover:()=>Nt,client_register:()=>Mt,configuration_new:()=>kt,memory:()=>St,wasm_bindgen__convert__closures__invoke0_mut__hcf60f8fe499c96ac:()=>Vt,wasm_bindgen__convert__closures__invoke2_mut__h35a3737c9c8313d7:()=>Kt}),xt,St,Ct,wt,Tt,Et,Dt,Ot,kt,At,jt,Mt,Nt,$,Pt,Ft,It,Lt,Rt,zt,Bt,Vt,Ht,Ut,Wt,Gt,Kt,qt=e((async()=>{r(),a(),Q(),URL=globalThis.URL,xt=await i({"./juicebox-sdk_bg.js":{__wbindgen_object_drop_ref:oe,__wbg_recovererror_new:se,__wbindgen_cb_drop:ce,__wbindgen_string_get:le,__wbindgen_is_string:ue,__wbindgen_is_object:de,__wbindgen_is_undefined:fe,__wbindgen_in:pe,__wbindgen_error_new:y,__wbg_fetch_d70cd3e887817ac6:b,__wbg_setTimeout_deb7786d942a7ab5:x,__wbg_JuiceboxGetAuthToken_c2d75c5bf1346f44:S,__wbg_new_abda76e883ba8a5f:C,__wbg_stack_658279fe44541cf6:w,__wbg_error_f851667af71bcfc6:T,__wbg_queueMicrotask_4d890031a6a5a50c:E,__wbg_queueMicrotask_adae4bc085237231:D,__wbindgen_is_function:O,__wbindgen_object_clone_ref:k,__wbindgen_jsval_loose_eq:A,__wbindgen_boolean_get:j,__wbindgen_number_get:M,__wbindgen_as_number:N,__wbindgen_number_new:P,__wbindgen_string_new:F,__wbg_getwithrefkey_4a92a5eca60879b9:I,__wbg_now_0343d9c3e0e8eedc:L,__wbg_now_b724952e890dc703:R,__wbg_headers_d135d2bb8cc60413:z,__wbg_newwithstrandinit_f581dff0d19a8b03:B,__wbg_set_27f236f6d7a28c29:me,__wbg_arrayBuffer_a9d862b05aaee2f9:he,__wbg_instanceof_Response_4c3b1446206114d1:ge,__wbg_status_d6d47ad2837621eb:_e,__wbg_headers_24def508a7518df9:ve,__wbg_blob_c6537f3e31e66dad:ye,__wbg_crypto_58f13aa23ffcb166:be,__wbg_process_5b786e71d465a513:xe,__wbg_versions_c2ab80650590b6a2:Se,__wbg_node_523d7bd03ef69fba:Ce,__wbg_msCrypto_abcb1295e768d1f2:we,__wbg_require_2784e593a4674877:Te,__wbg_randomFillSync_a0d98aa11c81fe89:Ee,__wbg_getRandomValues_504510b5564925af:De,__wbg_get_f01601b5a68d10e3:Oe,__wbg_length_1009b1af0c481d7b:ke,__wbg_newnoargs_c62ea9419c21fbac:Ae,__wbg_next_9b877f231f476d01:je,__wbg_next_6529ee0cca8d57ed:Me,__wbg_done_5fe336b092d60cf2:Ne,__wbg_value_0c248a78fdc8e19f:Pe,__wbg_iterator_db7ca081358d4fb2:Fe,__wbg_get_7b48513de5dc5ea4:Ie,__wbg_call_90c26b09837aba1c:Le,__wbg_new_9fb8d994e1c0aaac:Re,__wbg_self_f0e34d89f33b99fd:ze,__wbg_window_d3b084224f4774d7:Be,__wbg_globalThis_9caa27ff917c6860:Ve,__wbg_global_35dfdd59a4da3e74:He,__wbg_from_71add2e723d1f1b2:Ue,__wbg_isArray_74fb723e24f76012:We,__wbg_instanceof_ArrayBuffer_e7d53d51371448e2:Ge,__wbg_call_5da1969d7cd31ccd:Ke,__wbg_isSafeInteger_f93fde0dca9820f8:qe,__wbg_entries_9e2e2aa45aa5094a:Je,__wbg_new_60f57089c7563e81:Ye,__wbg_resolve_6e1c6553a82f85b7:Xe,__wbg_then_3ab08cd4fbb91ae9:Ze,__wbg_then_8371cc12cfedc5a2:Qe,__wbg_buffer_a448f833075b71ba:$e,__wbg_newwithbyteoffsetandlength_d0482f893617af71:et,__wbg_new_8f67e318f15d7254:tt,__wbg_set_2357bf09366ee480:nt,__wbg_length_1d25fa9e4ac21ce7:rt,__wbg_instanceof_Uint8Array_bced6f43aed8c1aa:it,__wbg_newwithlength_6c2df9e2f3028c43:at,__wbg_subarray_2e940e41c0f5a1d9:ot,__wbg_stringify_e1b19966d964d242:st,__wbg_set_759f75cd92b612d2:ct,__wbindgen_debug_string:lt,__wbindgen_throw:ut,__wbindgen_memory:dt,__wbindgen_closure_wrapper610:ft,__wbindgen_closure_wrapper898:pt}},n),{memory:St,__wbg_configuration_free:Ct,__wbg_recovererror_free:wt,__wbg_get_recovererror_reason:Tt,__wbg_set_recovererror_reason:Et,__wbg_get_recovererror_guesses_remaining:Dt,__wbg_set_recovererror_guesses_remaining:Ot,configuration_new:kt,__wbg_client_free:At,client_new:jt,client_register:Mt,client_recover:Nt,client_delete:$,__wbg_authtokengenerator_free:Pt,authtokengenerator_new:Ft,authtokengenerator_vend:It,authtokengenerator_random_secret_id:Lt,__wbindgen_malloc:Rt,__wbindgen_realloc:zt,__wbindgen_export_2:Bt,wasm_bindgen__convert__closures__invoke0_mut__hcf60f8fe499c96ac:Vt,_dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h664375554e075ae3:Ht,__wbindgen_exn_store:Ut,__wbindgen_add_to_stack_pointer:Wt,__wbindgen_free:Gt,wasm_bindgen__convert__closures__invoke2_mut__h35a3737c9c8313d7:Kt}=xt}));await e((async()=>{await qt(),Q(),Q(),o(bt)}))();export{vt as AuthTokenGenerator,yt as Client,X as Configuration,ht as DeleteError,Z as RecoverError,_t as RecoverErrorReason,gt as RegisterError,S as __wbg_JuiceboxGetAuthToken_c2d75c5bf1346f44,he as __wbg_arrayBuffer_a9d862b05aaee2f9,ye as __wbg_blob_c6537f3e31e66dad,$e as __wbg_buffer_a448f833075b71ba,Ke as __wbg_call_5da1969d7cd31ccd,Le as __wbg_call_90c26b09837aba1c,be as __wbg_crypto_58f13aa23ffcb166,Ne as __wbg_done_5fe336b092d60cf2,Je as __wbg_entries_9e2e2aa45aa5094a,T as __wbg_error_f851667af71bcfc6,b as __wbg_fetch_d70cd3e887817ac6,Ue as __wbg_from_71add2e723d1f1b2,De as __wbg_getRandomValues_504510b5564925af,Ie as __wbg_get_7b48513de5dc5ea4,Oe as __wbg_get_f01601b5a68d10e3,I as __wbg_getwithrefkey_4a92a5eca60879b9,Ve as __wbg_globalThis_9caa27ff917c6860,He as __wbg_global_35dfdd59a4da3e74,ve as __wbg_headers_24def508a7518df9,z as __wbg_headers_d135d2bb8cc60413,Ge as __wbg_instanceof_ArrayBuffer_e7d53d51371448e2,ge as __wbg_instanceof_Response_4c3b1446206114d1,it as __wbg_instanceof_Uint8Array_bced6f43aed8c1aa,We as __wbg_isArray_74fb723e24f76012,qe as __wbg_isSafeInteger_f93fde0dca9820f8,Fe as __wbg_iterator_db7ca081358d4fb2,ke as __wbg_length_1009b1af0c481d7b,rt as __wbg_length_1d25fa9e4ac21ce7,we as __wbg_msCrypto_abcb1295e768d1f2,Ye as __wbg_new_60f57089c7563e81,tt as __wbg_new_8f67e318f15d7254,Re as __wbg_new_9fb8d994e1c0aaac,C as __wbg_new_abda76e883ba8a5f,Ae as __wbg_newnoargs_c62ea9419c21fbac,et as __wbg_newwithbyteoffsetandlength_d0482f893617af71,at as __wbg_newwithlength_6c2df9e2f3028c43,B as __wbg_newwithstrandinit_f581dff0d19a8b03,Me as __wbg_next_6529ee0cca8d57ed,je as __wbg_next_9b877f231f476d01,Ce as __wbg_node_523d7bd03ef69fba,L as __wbg_now_0343d9c3e0e8eedc,R as __wbg_now_b724952e890dc703,xe as __wbg_process_5b786e71d465a513,E as __wbg_queueMicrotask_4d890031a6a5a50c,D as __wbg_queueMicrotask_adae4bc085237231,Ee as __wbg_randomFillSync_a0d98aa11c81fe89,se as __wbg_recovererror_new,Te as __wbg_require_2784e593a4674877,Xe as __wbg_resolve_6e1c6553a82f85b7,ze as __wbg_self_f0e34d89f33b99fd,x as __wbg_setTimeout_deb7786d942a7ab5,nt as __wbg_set_2357bf09366ee480,me as __wbg_set_27f236f6d7a28c29,ct as __wbg_set_759f75cd92b612d2,o as __wbg_set_wasm,w as __wbg_stack_658279fe44541cf6,_e as __wbg_status_d6d47ad2837621eb,st as __wbg_stringify_e1b19966d964d242,ot as __wbg_subarray_2e940e41c0f5a1d9,Ze as __wbg_then_3ab08cd4fbb91ae9,Qe as __wbg_then_8371cc12cfedc5a2,Pe as __wbg_value_0c248a78fdc8e19f,Se as __wbg_versions_c2ab80650590b6a2,Be as __wbg_window_d3b084224f4774d7,N as __wbindgen_as_number,j as __wbindgen_boolean_get,ce as __wbindgen_cb_drop,ft as __wbindgen_closure_wrapper610,pt as __wbindgen_closure_wrapper898,lt as __wbindgen_debug_string,y as __wbindgen_error_new,pe as __wbindgen_in,O as __wbindgen_is_function,de as __wbindgen_is_object,ue as __wbindgen_is_string,fe as __wbindgen_is_undefined,A as __wbindgen_jsval_loose_eq,dt as __wbindgen_memory,M as __wbindgen_number_get,P as __wbindgen_number_new,k as __wbindgen_object_clone_ref,oe as __wbindgen_object_drop_ref,le as __wbindgen_string_get,F as __wbindgen_string_new,ut as __wbindgen_throw};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
const FFMPEG = process.env.FFMPEG_PATH || "ffmpeg";
|
|
4
|
+
|
|
5
|
+
function decodeAll(args) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const ff = spawn(FFMPEG, ["-hide_banner", "-loglevel", "error", ...args]);
|
|
8
|
+
const chunks = [];
|
|
9
|
+
const err = [];
|
|
10
|
+
ff.stdout.on("data", (d) => chunks.push(d));
|
|
11
|
+
ff.stderr.on("data", (d) => err.push(d));
|
|
12
|
+
ff.on("error", reject);
|
|
13
|
+
ff.on("close", (code) => {
|
|
14
|
+
if (code === 0) return resolve(Buffer.concat(chunks));
|
|
15
|
+
reject(new Error(`ffmpeg exited ${code}: ${Buffer.concat(err).toString().slice(0, 400)}`));
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Decode any audio file to raw PCM and push it into an RTCAudioSource at 10ms cadence.
|
|
21
|
+
// Returns { done: Promise, stop() }.
|
|
22
|
+
export async function streamAudioFile(source, filePath, opts = {}) {
|
|
23
|
+
const sampleRate = opts.sampleRate ?? 48000;
|
|
24
|
+
const channels = opts.channels ?? 1;
|
|
25
|
+
const samplesPerFrame = (sampleRate / 100) * channels; // 10ms
|
|
26
|
+
const bytesPerFrame = samplesPerFrame * 2;
|
|
27
|
+
const pcm = await decodeAll(["-i", filePath, "-f", "s16le", "-acodec", "pcm_s16le", "-ac", String(channels), "-ar", String(sampleRate), "pipe:1"]);
|
|
28
|
+
|
|
29
|
+
let offset = 0;
|
|
30
|
+
let timer = null;
|
|
31
|
+
let resolveDone;
|
|
32
|
+
const done = new Promise((r) => (resolveDone = r));
|
|
33
|
+
const tick = () => {
|
|
34
|
+
if (offset >= pcm.length) {
|
|
35
|
+
clearInterval(timer);
|
|
36
|
+
resolveDone({ frames: Math.ceil(pcm.length / bytesPerFrame) });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const samples = new Int16Array(samplesPerFrame); // own 960-byte buffer, no aliasing
|
|
40
|
+
Buffer.from(samples.buffer).set(pcm.subarray(offset, Math.min(offset + bytesPerFrame, pcm.length)));
|
|
41
|
+
offset += bytesPerFrame;
|
|
42
|
+
source.onData({ samples, sampleRate, bitsPerSample: 16, channelCount: channels, numberOfFrames: sampleRate / 100 });
|
|
43
|
+
};
|
|
44
|
+
timer = setInterval(tick, 10);
|
|
45
|
+
return { done, stop: () => { if (timer) clearInterval(timer); resolveDone?.({ stopped: true }); } };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Decode any video file to raw I420 frames and push them into an RTCVideoSource at the given fps.
|
|
49
|
+
export async function streamVideoFile(source, filePath, opts = {}) {
|
|
50
|
+
const width = opts.width ?? 320;
|
|
51
|
+
const height = opts.height ?? 240;
|
|
52
|
+
const fps = opts.fps ?? 15;
|
|
53
|
+
const frameBytes = (width * height * 3) / 2;
|
|
54
|
+
const raw = await decodeAll(["-i", filePath, "-f", "rawvideo", "-pix_fmt", "yuv420p", "-s", `${width}x${height}`, "-r", String(fps), "pipe:1"]);
|
|
55
|
+
|
|
56
|
+
let offset = 0;
|
|
57
|
+
let timer = null;
|
|
58
|
+
let resolveDone;
|
|
59
|
+
const done = new Promise((r) => (resolveDone = r));
|
|
60
|
+
const tick = () => {
|
|
61
|
+
if (offset + frameBytes > raw.length) {
|
|
62
|
+
clearInterval(timer);
|
|
63
|
+
resolveDone({ frames: Math.floor(raw.length / frameBytes) });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const data = new Uint8ClampedArray(raw.subarray(offset, offset + frameBytes));
|
|
67
|
+
offset += frameBytes;
|
|
68
|
+
source.onFrame({ width, height, data });
|
|
69
|
+
};
|
|
70
|
+
timer = setInterval(tick, Math.round(1000 / fps));
|
|
71
|
+
return { done, stop: () => { if (timer) clearInterval(timer); resolveDone?.({ stopped: true }); } };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Pipe a received RTCAudioSink's PCM frames into a file (wav by default). Returns { stop() }.
|
|
75
|
+
export function recordAudioSink(sink, filePath, opts = {}) {
|
|
76
|
+
let ff = null;
|
|
77
|
+
let sampleRate = opts.sampleRate ?? 48000;
|
|
78
|
+
let channels = opts.channels ?? 1;
|
|
79
|
+
let frames = 0;
|
|
80
|
+
const start = (sr, ch) => {
|
|
81
|
+
sampleRate = sr;
|
|
82
|
+
channels = ch;
|
|
83
|
+
ff = spawn(FFMPEG, ["-hide_banner", "-loglevel", "error", "-y", "-f", "s16le", "-ar", String(sr), "-ac", String(ch), "-i", "pipe:0", filePath]);
|
|
84
|
+
};
|
|
85
|
+
sink.ondata = (d) => {
|
|
86
|
+
if (!ff) start(d.sampleRate, d.channelCount);
|
|
87
|
+
frames++;
|
|
88
|
+
ff.stdin.write(Buffer.from(d.samples.buffer, d.samples.byteOffset, d.samples.byteLength));
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
get frames() { return frames; },
|
|
92
|
+
stop: () => new Promise((res) => {
|
|
93
|
+
sink.stop?.();
|
|
94
|
+
if (!ff) return res({ frames: 0 });
|
|
95
|
+
ff.stdin.end();
|
|
96
|
+
ff.on("close", () => res({ frames, sampleRate, channels }));
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Pipe a received RTCVideoSink's I420 frames into a file (mp4 by default). Returns { stop() }.
|
|
102
|
+
export function recordVideoSink(sink, filePath, opts = {}) {
|
|
103
|
+
let ff = null;
|
|
104
|
+
let fps = opts.fps ?? 15;
|
|
105
|
+
let frames = 0;
|
|
106
|
+
let dims = null;
|
|
107
|
+
const start = (w, h) => {
|
|
108
|
+
dims = { w, h };
|
|
109
|
+
ff = spawn(FFMPEG, ["-hide_banner", "-loglevel", "error", "-y", "-f", "rawvideo", "-pix_fmt", "yuv420p", "-s", `${w}x${h}`, "-r", String(fps), "-i", "pipe:0", "-pix_fmt", "yuv420p", filePath]);
|
|
110
|
+
};
|
|
111
|
+
sink.onframe = ({ frame }) => {
|
|
112
|
+
if (!ff) start(frame.width, frame.height);
|
|
113
|
+
if (frame.width !== dims.w || frame.height !== dims.h) return;
|
|
114
|
+
frames++;
|
|
115
|
+
ff.stdin.write(Buffer.from(frame.data.buffer, frame.data.byteOffset, frame.data.byteLength));
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
get frames() { return frames; },
|
|
119
|
+
get dims() { return dims; },
|
|
120
|
+
stop: () => new Promise((res) => {
|
|
121
|
+
sink.stop?.();
|
|
122
|
+
if (!ff) return res({ frames: 0 });
|
|
123
|
+
ff.stdin.end();
|
|
124
|
+
ff.on("close", () => res({ frames, dims }));
|
|
125
|
+
}),
|
|
126
|
+
};
|
|
127
|
+
}
|