Create "working" example
This commit is contained in:
3
gobot-gui/api/.vscode/settings.json
vendored
Normal file
3
gobot-gui/api/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"deno.enable": true
|
||||||
|
}
|
||||||
0
gobot-gui/api/asd.json
Normal file
0
gobot-gui/api/asd.json
Normal file
@@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"dev": "deno run --watch main.ts"
|
"dev": "deno run --watch main.ts"
|
||||||
|
},
|
||||||
|
"imports": {
|
||||||
|
"@earthstar/dns-sd": "jsr:@earthstar/dns-sd@^3.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
169
gobot-gui/api/deno.lock
generated
Normal file
169
gobot-gui/api/deno.lock
generated
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
{
|
||||||
|
"version": "3",
|
||||||
|
"packages": {
|
||||||
|
"specifiers": {
|
||||||
|
"jsr:@earthstar/dns-sd": "jsr:@earthstar/dns-sd@3.1.0",
|
||||||
|
"jsr:@oak/commons@0.7": "jsr:@oak/commons@0.7.0",
|
||||||
|
"jsr:@oak/oak@14": "jsr:@oak/oak@14.2.0",
|
||||||
|
"jsr:@std/assert": "jsr:@std/assert@0.218.2",
|
||||||
|
"jsr:@std/assert@0.218": "jsr:@std/assert@0.218.2",
|
||||||
|
"jsr:@std/assert@^0.218.2": "jsr:@std/assert@0.218.2",
|
||||||
|
"jsr:@std/bytes@0.218": "jsr:@std/bytes@0.218.2",
|
||||||
|
"jsr:@std/bytes@^0.218.2": "jsr:@std/bytes@0.218.2",
|
||||||
|
"jsr:@std/bytes@^0.224.0": "jsr:@std/bytes@0.224.0",
|
||||||
|
"jsr:@std/crypto": "jsr:@std/crypto@0.218.2",
|
||||||
|
"jsr:@std/crypto@0.218": "jsr:@std/crypto@0.218.2",
|
||||||
|
"jsr:@std/encoding": "jsr:@std/encoding@0.218.2",
|
||||||
|
"jsr:@std/encoding@0.218": "jsr:@std/encoding@0.218.2",
|
||||||
|
"jsr:@std/encoding@^0.218.2": "jsr:@std/encoding@0.218.2",
|
||||||
|
"jsr:@std/fmt@^0.218.2": "jsr:@std/fmt@0.218.2",
|
||||||
|
"jsr:@std/http@0.218": "jsr:@std/http@0.218.2",
|
||||||
|
"jsr:@std/io@0.218": "jsr:@std/io@0.218.2",
|
||||||
|
"jsr:@std/media-types@0.218": "jsr:@std/media-types@0.218.2",
|
||||||
|
"jsr:@std/path@0.218": "jsr:@std/path@0.218.2",
|
||||||
|
"npm:@types/node": "npm:@types/node@18.16.19",
|
||||||
|
"npm:@types/sdp-transform": "npm:@types/sdp-transform@2.4.9",
|
||||||
|
"npm:path-to-regexp@6.2.1": "npm:path-to-regexp@6.2.1",
|
||||||
|
"npm:sdp-transform@2.14.2": "npm:sdp-transform@2.14.2"
|
||||||
|
},
|
||||||
|
"jsr": {
|
||||||
|
"@earthstar/dns-sd@3.1.0": {
|
||||||
|
"integrity": "8b76b7ccfe230677f6177227e0250bff350b3b19604b3d8c534c1e2e3318d411",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/bytes@^0.224.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@oak/commons@0.7.0": {
|
||||||
|
"integrity": "4bd889b3dc9ddac1b602034d88c137f06de7078775961b51081beb5f175c120b"
|
||||||
|
},
|
||||||
|
"@oak/oak@14.2.0": {
|
||||||
|
"integrity": "b683b089693004ac3bca80b52159b3e9ad214dc8246ff5dc61ba658da78bc166",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@oak/commons@0.7",
|
||||||
|
"jsr:@std/assert@0.218",
|
||||||
|
"jsr:@std/bytes@0.218",
|
||||||
|
"jsr:@std/crypto@0.218",
|
||||||
|
"jsr:@std/encoding@0.218",
|
||||||
|
"jsr:@std/http@0.218",
|
||||||
|
"jsr:@std/io@0.218",
|
||||||
|
"jsr:@std/media-types@0.218",
|
||||||
|
"jsr:@std/path@0.218",
|
||||||
|
"npm:path-to-regexp@6.2.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/assert@0.218.2": {
|
||||||
|
"integrity": "7f0a5a1a8cf86607cd6c2c030584096e1ffad27fc9271429a8cb48cfbdee5eaf",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/fmt@^0.218.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/bytes@0.218.2": {
|
||||||
|
"integrity": "91fe54b232dcca73856b79a817247f4a651dbb60d51baafafb6408c137241670"
|
||||||
|
},
|
||||||
|
"@std/bytes@0.224.0": {
|
||||||
|
"integrity": "a2250e1d0eb7d1c5a426f21267ab9bdeac2447fa87a3d0d1a467d3f7a6058e49"
|
||||||
|
},
|
||||||
|
"@std/crypto@0.218.2": {
|
||||||
|
"integrity": "8c5031a3a1c3ac3bed3c0d4bed2fe7e7faedcb673bbfa0edd10570c8452f5cd2",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert@^0.218.2",
|
||||||
|
"jsr:@std/encoding@^0.218.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/encoding@0.218.2": {
|
||||||
|
"integrity": "da55a763c29bf0dbf06fd286430b358266eb99c28789d89fe9a3e28edecb8d8e"
|
||||||
|
},
|
||||||
|
"@std/fmt@0.218.2": {
|
||||||
|
"integrity": "99526449d2505aa758b6cbef81e7dd471d8b28ec0dcb1491d122b284c548788a"
|
||||||
|
},
|
||||||
|
"@std/http@0.218.2": {
|
||||||
|
"integrity": "54223b62702e665b9dab6373ea2e51235e093ef47228d21cfa0469ee5ac75c9b",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert@^0.218.2",
|
||||||
|
"jsr:@std/encoding@^0.218.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/io@0.218.2": {
|
||||||
|
"integrity": "c64fbfa087b7c9d4d386c5672f291f607d88cb7d44fc299c20c713e345f2785f",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/bytes@^0.218.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/media-types@0.218.2": {
|
||||||
|
"integrity": "1ed3bd2a05e44bad3fc2bab1767d0ce7f2fd68baee62a980751ce51633acb788"
|
||||||
|
},
|
||||||
|
"@std/path@0.218.2": {
|
||||||
|
"integrity": "b568fd923d9e53ad76d17c513e7310bda8e755a3e825e6289a0ce536404e2662",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert@^0.218.2"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm": {
|
||||||
|
"@types/node@18.16.19": {
|
||||||
|
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==",
|
||||||
|
"dependencies": {}
|
||||||
|
},
|
||||||
|
"@types/sdp-transform@2.4.9": {
|
||||||
|
"integrity": "sha512-bVr+/OoZZy7wrHlNcEAAa6PAgKA4BoXPYVN2EijMC5WnGgQ4ZEuixmKnVs2roiAvr7RhIFVH17QD27cojgIZCg==",
|
||||||
|
"dependencies": {}
|
||||||
|
},
|
||||||
|
"path-to-regexp@6.2.1": {
|
||||||
|
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==",
|
||||||
|
"dependencies": {}
|
||||||
|
},
|
||||||
|
"sdp-transform@2.14.2": {
|
||||||
|
"integrity": "sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA==",
|
||||||
|
"dependencies": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redirects": {
|
||||||
|
"https://deno.land/std/testing/asserts.ts": "https://deno.land/std@0.224.0/testing/asserts.ts",
|
||||||
|
"https://deno.land/x/port/mod.ts": "https://deno.land/x/port@1.0.0/mod.ts"
|
||||||
|
},
|
||||||
|
"remote": {
|
||||||
|
"https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
|
||||||
|
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
|
||||||
|
"https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
|
||||||
|
"https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
|
||||||
|
"https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
|
||||||
|
"https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
|
||||||
|
"https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
|
||||||
|
"https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
|
||||||
|
"https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
|
||||||
|
"https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
|
||||||
|
"https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e",
|
||||||
|
"https://deno.land/std@0.224.0/testing/asserts.ts": "d0cdbabadc49cc4247a50732ee0df1403fdcd0f95360294ad448ae8c240f3f5c",
|
||||||
|
"https://deno.land/x/free_port@v1.2.0/mod.ts": "512646732aaea41fbfd1f210f3ae82660f38251777d189d290da331d0235a58e",
|
||||||
|
"https://deno.land/x/port@1.0.0/mod.ts": "2dc04ce1ccf133ae09205e30b550044c4c6f64a1a7d00ea91c66dbb9f6cc00f5",
|
||||||
|
"https://deno.land/x/port@1.0.0/types.ts": "42d6ae4147d5d67408d60209da070ddfa79ec8389c6cab1b8002df0cf6c03af6"
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@earthstar/dns-sd@^3.1.0"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
export function add(a: number, b: number): number {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Learn more at https://deno.land/manual/examples/module_metadata#concepts
|
|
||||||
if (import.meta.main) {
|
|
||||||
console.log("Add 2 + 3 =", add(2, 3));
|
|
||||||
}
|
|
||||||
10
gobot-gui/api/output.sdp
Normal file
10
gobot-gui/api/output.sdp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
v=0
|
||||||
|
o=- 0 0 IN IP4 127.0.0.1
|
||||||
|
s=No Name
|
||||||
|
c=IN IP4 127.0.0.1
|
||||||
|
t=0 0
|
||||||
|
a=tool:libavformat 60.16.100
|
||||||
|
m=video 1234 RTP/AVP 96
|
||||||
|
b=AS:3000
|
||||||
|
a=rtpmap:96 MP4V-ES/90000
|
||||||
|
a=fmtp:96 profile-level-id=1
|
||||||
12
gobot-gui/api/scripts/ffmpeg_stream.bash
Normal file
12
gobot-gui/api/scripts/ffmpeg_stream.bash
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#/usr/bin/bash
|
||||||
|
|
||||||
|
SDP_FILE="output.sdp"
|
||||||
|
|
||||||
|
ffmpeg \
|
||||||
|
-stream_loop -1 \
|
||||||
|
-rtbufsize 2M \
|
||||||
|
-re \
|
||||||
|
-i "test/Big_Buck_Bunny_1080_10s_10MB.mp4" \
|
||||||
|
-b:v 3M \
|
||||||
|
-sdp_file $SDP_FILE \
|
||||||
|
-f rtp rtp://127.0.0.1:1234
|
||||||
48
gobot-gui/api/src/iceCandiate.ts
Normal file
48
gobot-gui/api/src/iceCandiate.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
export interface IceCandidate {
|
||||||
|
foundation: string;
|
||||||
|
componentId: number;
|
||||||
|
transport: string;
|
||||||
|
priority: number;
|
||||||
|
ipAddress: string;
|
||||||
|
port: number;
|
||||||
|
candidateType: string;
|
||||||
|
relatedAddress: string | null;
|
||||||
|
relatedPort: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function parseIceCandidate(candidate: string): IceCandidate {
|
||||||
|
const parts = candidate.split(' ');
|
||||||
|
const iceCandidate: IceCandidate = {
|
||||||
|
foundation: parts[0].split(':')[1],
|
||||||
|
componentId: Number.parseInt(parts[1]),
|
||||||
|
transport: parts[2],
|
||||||
|
priority: Number.parseInt(parts[3]),
|
||||||
|
ipAddress: parts[4],
|
||||||
|
port: Number.parseInt(parts[5]),
|
||||||
|
candidateType: parts[7],
|
||||||
|
relatedAddress: null,
|
||||||
|
relatedPort: null
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 8; i < parts.length; i++) {
|
||||||
|
if (parts[i] === 'raddr') {
|
||||||
|
iceCandidate.relatedAddress = parts[i + 1];
|
||||||
|
} else if (parts[i] === 'rport') {
|
||||||
|
iceCandidate.relatedPort = Number.parseInt(parts[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iceCandidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findHighestPriorityCandidate(candidates: IceCandidate[]): IceCandidate | null {
|
||||||
|
if (candidates.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates.reduce((highest, candidate) =>
|
||||||
|
candidate.priority > highest.priority ? candidate : highest
|
||||||
|
);
|
||||||
|
}
|
||||||
145
gobot-gui/api/src/main.ts
Normal file
145
gobot-gui/api/src/main.ts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import { Application,Router } from "jsr:@oak/oak@14";
|
||||||
|
import { crypto } from "jsr:@std/crypto";
|
||||||
|
import { randomBytes } from "node:crypto";
|
||||||
|
import { encodeHex } from "jsr:@std/encoding/hex";
|
||||||
|
// @deno-types="npm:@types/sdp-transform"
|
||||||
|
import sdpTransform from "npm:sdp-transform@2.14.2";
|
||||||
|
import { IceCandidate, findHighestPriorityCandidate, parseIceCandidate } from "./iceCandiate.ts";
|
||||||
|
import { browse, MulticastInterface } from "jsr:@earthstar/dns-sd";
|
||||||
|
|
||||||
|
const app = new Application();
|
||||||
|
const router = new Router({
|
||||||
|
prefix: "/api",
|
||||||
|
});
|
||||||
|
|
||||||
|
const rtpIncommingConnection = Deno.listenDatagram({ port: 1234, transport: "udp" });
|
||||||
|
let outgoingAddresses: Map<string, Deno.Addr> = new Map();
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
for await (const [data, addr] of rtpIncommingConnection) {
|
||||||
|
//console.log(data.length);
|
||||||
|
|
||||||
|
outgoingAddresses.forEach((outgoingAddr) => {
|
||||||
|
rtpIncommingConnection.send(data, outgoingAddr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
async function getFingerprint(): Promise<string> {
|
||||||
|
const cert = randomBytes(32);
|
||||||
|
const hash = encodeHex(await crypto.subtle.digest("SHA-256", cert));
|
||||||
|
return hash.split(/(..)/g).filter(s => s !== "").join(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolveMDNS(address: string): Promise<string | undefined> {
|
||||||
|
const pingCommand = new Deno.Command("timeout", {
|
||||||
|
args: ["2", "ping", "-c", "1", address],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { code, stdout, stderr } = await pingCommand.output();
|
||||||
|
|
||||||
|
if (code !== 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const realAddr = (new TextDecoder().decode(stdout))
|
||||||
|
.match(/\((.*)\):/)?.at(1);
|
||||||
|
|
||||||
|
return realAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findWorkingIceCandiate(candidates: IceCandidate[]): Promise<{ candiate: IceCandidate, ipAddress: string } | undefined> {
|
||||||
|
const workingCandiates: {
|
||||||
|
candiate: IceCandidate,
|
||||||
|
ipAddress: string,
|
||||||
|
priority: number
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
|
candidates = candidates.sort((a, b) => a.priority - b.priority);
|
||||||
|
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
if(candidate.ipAddress === undefined || candidate.transport.toLowerCase() !== "udp") continue;
|
||||||
|
|
||||||
|
const ipAddr = await resolveMDNS(candidate.ipAddress);
|
||||||
|
console.log(ipAddr, candidate.ipAddress);
|
||||||
|
if (ipAddr) {
|
||||||
|
workingCandiates.push({
|
||||||
|
candiate: candidate,
|
||||||
|
ipAddress: ipAddr,
|
||||||
|
priority: candidate.priority
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workingCandiates.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
return workingCandiates.reduce((highest, candidate) =>
|
||||||
|
candidate.priority > highest.priority ? candidate : highest
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
router.post("/webrtc-offer", async (ctx) => {
|
||||||
|
const body = await ctx.request.body.json();
|
||||||
|
const clientSdp = sdpTransform.parse(body.offer.sdp);
|
||||||
|
const clientIceRaw: {
|
||||||
|
candidate: string,
|
||||||
|
sdpMid: string,
|
||||||
|
sdpMLineIndex: number
|
||||||
|
usernameFragment: string
|
||||||
|
}[] = body.ice;
|
||||||
|
|
||||||
|
const clientIce = clientIceRaw.map((ice) => parseIceCandidate(ice.candidate));
|
||||||
|
const remoteIceCandiate = await findWorkingIceCandiate(clientIce);
|
||||||
|
|
||||||
|
if (!remoteIceCandiate) {
|
||||||
|
ctx.response.status = 400;
|
||||||
|
ctx.response.type = "application/json";
|
||||||
|
ctx.response.body = { error: "None of the provided IceCandiates where reachable" };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientAddr: Deno.NetAddr = {
|
||||||
|
transport: "udp",
|
||||||
|
hostname: remoteIceCandiate.ipAddress,
|
||||||
|
port: remoteIceCandiate.candiate.port
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(clientAddr);
|
||||||
|
|
||||||
|
const ffmpegSdp = sdpTransform.parse(await Deno.readTextFile("./output.sdp"));
|
||||||
|
ffmpegSdp.media[0].fingerprint = {
|
||||||
|
type: "sha-256",
|
||||||
|
hash: await getFingerprint()
|
||||||
|
}
|
||||||
|
|
||||||
|
ffmpegSdp.iceUfrag = encodeHex(randomBytes(32));
|
||||||
|
ffmpegSdp.icePwd = encodeHex(randomBytes(32));
|
||||||
|
ffmpegSdp.media[0].fmtp = [];
|
||||||
|
ffmpegSdp.
|
||||||
|
//ffmpegSdp.media[0].rtcpMux = "";
|
||||||
|
|
||||||
|
const sessionId = clientSdp.origin?.sessionId;
|
||||||
|
if (!sessionId) {
|
||||||
|
ctx.response.status = 400;
|
||||||
|
ctx.response.type = "application/json";
|
||||||
|
ctx.response.body = { error: "sessionId is undefined" };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
outgoingAddresses.set(sessionId.toString(), clientAddr);
|
||||||
|
|
||||||
|
ctx.response.type = "application/json";
|
||||||
|
ctx.response.body = {
|
||||||
|
"type": "answer",
|
||||||
|
"sdp": sdpTransform.write(ffmpegSdp)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.use(router.routes());
|
||||||
|
app.use(router.allowedMethods());
|
||||||
|
|
||||||
|
app.listen({ port: 3000 });
|
||||||
BIN
gobot-gui/api/test/Big_Buck_Bunny_1080_10s_10MB.mp4
Normal file
BIN
gobot-gui/api/test/Big_Buck_Bunny_1080_10s_10MB.mp4
Normal file
Binary file not shown.
114
gobot-gui/api/test/iceCandiates.test.ts
Normal file
114
gobot-gui/api/test/iceCandiates.test.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { assertEquals } from "jsr:@std/assert";
|
||||||
|
import { IceCandidate, parseIceCandidate } from '../src/iceCandiate.ts';
|
||||||
|
|
||||||
|
const iceCandidates = [
|
||||||
|
"candidate:842163049 1 udp 1677729535 192.168.1.2 3478 typ host",
|
||||||
|
"candidate:842163049 2 udp 1677729534 192.168.1.3 3479 typ srflx raddr 192.168.1.2 rport 3478",
|
||||||
|
"candidate:842163049 1 tcp 1677729533 192.168.1.4 3480 typ relay raddr 192.168.1.3 rport 3479",
|
||||||
|
"candidate:842163049 2 tcp 1677729532 192.168.1.5 3481 typ host",
|
||||||
|
"candidate:842163049 1 udp 1677729531 192.168.1.6 3482 typ srflx rport 3480",
|
||||||
|
"candidate:842163049 2 udp 1677729530 192.168.1.7 3483 typ relay raddr 192.168.1.5",
|
||||||
|
"candidate:842163049 1 tcp 1677729529 192.168.1.8 3484 typ host",
|
||||||
|
"candidate:842163049 2 tcp 1677729528 192.168.1.9 3485 typ srflx raddr 192.168.1.6 rport 3482"
|
||||||
|
];
|
||||||
|
|
||||||
|
const parsedExamples: IceCandidate[] = [
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 1,
|
||||||
|
transport: "udp",
|
||||||
|
priority: 1677729535,
|
||||||
|
ipAddress: "192.168.1.2",
|
||||||
|
port: 3478,
|
||||||
|
candidateType: "host",
|
||||||
|
relatedAddress: null,
|
||||||
|
relatedPort: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 2,
|
||||||
|
transport: "udp",
|
||||||
|
priority: 1677729534,
|
||||||
|
ipAddress: "192.168.1.3",
|
||||||
|
port: 3479,
|
||||||
|
candidateType: "srflx",
|
||||||
|
relatedAddress: "192.168.1.2",
|
||||||
|
relatedPort: 3478
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 1,
|
||||||
|
transport: "tcp",
|
||||||
|
priority: 1677729533,
|
||||||
|
ipAddress: "192.168.1.4",
|
||||||
|
port: 3480,
|
||||||
|
candidateType: "relay",
|
||||||
|
relatedAddress: "192.168.1.3",
|
||||||
|
relatedPort: 3479
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 2,
|
||||||
|
transport: "tcp",
|
||||||
|
priority: 1677729532,
|
||||||
|
ipAddress: "192.168.1.5",
|
||||||
|
port: 3481,
|
||||||
|
candidateType: "host",
|
||||||
|
relatedAddress: null,
|
||||||
|
relatedPort: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 1,
|
||||||
|
transport: "udp",
|
||||||
|
priority: 1677729531,
|
||||||
|
ipAddress: "192.168.1.6",
|
||||||
|
port: 3482,
|
||||||
|
candidateType: "srflx",
|
||||||
|
relatedPort: 3480,
|
||||||
|
relatedAddress: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 2,
|
||||||
|
transport: "udp",
|
||||||
|
priority: 1677729530,
|
||||||
|
ipAddress: "192.168.1.7",
|
||||||
|
port: 3483,
|
||||||
|
candidateType: "relay",
|
||||||
|
relatedAddress: "192.168.1.5",
|
||||||
|
relatedPort: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 1,
|
||||||
|
transport: "tcp",
|
||||||
|
priority: 1677729529,
|
||||||
|
ipAddress: "192.168.1.8",
|
||||||
|
port: 3484,
|
||||||
|
candidateType: "host",
|
||||||
|
relatedAddress: null,
|
||||||
|
relatedPort: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 2,
|
||||||
|
transport: "tcp",
|
||||||
|
priority: 1677729528,
|
||||||
|
ipAddress: "192.168.1.9",
|
||||||
|
port: 3485,
|
||||||
|
candidateType: "srflx",
|
||||||
|
relatedAddress: "192.168.1.6",
|
||||||
|
relatedPort: 3482
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
Deno.test("parseIceCandidate should correctly parse ICE candidates", async (t) => {
|
||||||
|
for (const [index, candidate] of iceCandidates.entries()) {
|
||||||
|
await t.step(`${index}-${candidate}`, () => {
|
||||||
|
const parsed = parseIceCandidate(candidate);
|
||||||
|
console.log(candidate);
|
||||||
|
assertEquals(parsed, parsedExamples[index]);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
111
gobot-gui/frontend/src/lib/Feedtes.svelte
Normal file
111
gobot-gui/frontend/src/lib/Feedtes.svelte
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
let videoElementIn: HTMLVideoElement;
|
||||||
|
let videoElementOut: HTMLVideoElement;
|
||||||
|
let peerConnection: RTCPeerConnection;
|
||||||
|
let incomingStream: MediaStream;
|
||||||
|
let outgoingStream: MediaStream;
|
||||||
|
|
||||||
|
let offerBase64Out: string;
|
||||||
|
let offerBase64In: string;
|
||||||
|
let answerBase64Out: string;
|
||||||
|
let answerBase64In: string;
|
||||||
|
let iceCandidate: RTCIceCandidate[] = [];
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
async function getIce(d: RTCSessionDescription | RTCSessionDescriptionInit): Promise<RTCIceCandidate[]> {
|
||||||
|
let icecandidate: RTCIceCandidate[] = [];
|
||||||
|
peerConnection.addEventListener("icecandidate", (event) => {
|
||||||
|
if(event.candidate) {
|
||||||
|
icecandidate.push(event.candidate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await peerConnection.setLocalDescription(d);
|
||||||
|
|
||||||
|
console.log("2asd");
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
if(peerConnection.iceGatheringState === 'complete') {
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
peerConnection.addEventListener('icegatheringstatechange', () => {
|
||||||
|
console.log(peerConnection.iceGatheringState);
|
||||||
|
if(peerConnection.iceGatheringState === 'complete') {
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("3asd");
|
||||||
|
|
||||||
|
return icecandidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function start_rx() {
|
||||||
|
peerConnection = new RTCPeerConnection({
|
||||||
|
certificates: [],
|
||||||
|
iceServers: [],
|
||||||
|
iceTransportPolicy: "all",
|
||||||
|
// @ts-ignore
|
||||||
|
rtcpMuxPolicy: 'negotiate' // Workaround, typescript doesn't know about this property
|
||||||
|
});
|
||||||
|
|
||||||
|
const offer = await peerConnection.createOffer();
|
||||||
|
|
||||||
|
iceCandidate = await getIce(offer);
|
||||||
|
console.log("asd");
|
||||||
|
console.log(iceCandidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function start_tx() {
|
||||||
|
console.log("Starting TX");
|
||||||
|
peerConnection = new RTCPeerConnection({
|
||||||
|
certificates: [],
|
||||||
|
iceServers: [],
|
||||||
|
iceTransportPolicy: "all",
|
||||||
|
// @ts-ignore
|
||||||
|
rtcpMuxPolicy: 'negotiate' // Workaround, typescript doesn't know about this property
|
||||||
|
});
|
||||||
|
|
||||||
|
iceCandidate = await getIce();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copy_to_clip_board(s: string) {
|
||||||
|
await navigator.clipboard.writeText(s);
|
||||||
|
console.log("Copied to clipboard");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
|
||||||
|
<video bind:this={videoElementIn} autoplay playsinline></video>
|
||||||
|
<video bind:this={videoElementOut} autoplay playsinline></video>
|
||||||
|
|
||||||
|
<button on:click={start_tx}>Start TX</button>
|
||||||
|
<button on:click={start_rx}>Start RX</button>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<p>Offer: {offerBase64Out}</p>
|
||||||
|
<button on:click={() => copy_to_clip_board(offerBase64Out)}>Copy Offer</button>
|
||||||
|
<input type="text" bind:value={offerBase64In}>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<p>Answere: {answerBase64Out}</p>
|
||||||
|
<button on:click={() => copy_to_clip_board(answerBase64Out)}>Copy Answer</button>
|
||||||
|
<input type="text" bind:value={offerBase64In}>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<p>Ice Candiates</p>
|
||||||
|
{#each iceCandidate as ice}
|
||||||
|
<p>{ice.address}, {ice.port}, {ice.protocol}</p>
|
||||||
|
{/each}
|
||||||
|
</main>
|
||||||
@@ -3,11 +3,25 @@
|
|||||||
|
|
||||||
let peerConnection: RTCPeerConnection;
|
let peerConnection: RTCPeerConnection;
|
||||||
let videoElement: HTMLVideoElement;
|
let videoElement: HTMLVideoElement;
|
||||||
|
let incommingStream: MediaStream;
|
||||||
|
|
||||||
async function negotiateWebRTC(peerConn: RTCPeerConnection) {
|
async function negotiateWebRTC(peerConn: RTCPeerConnection) {
|
||||||
|
incommingStream = new MediaStream();
|
||||||
|
videoElement.srcObject = incommingStream;
|
||||||
peerConn.addTransceiver('video', { direction: 'recvonly' });
|
peerConn.addTransceiver('video', { direction: 'recvonly' });
|
||||||
|
|
||||||
const offer = await peerConn.createOffer();
|
const offer = await peerConn.createOffer({
|
||||||
|
offerToReceiveVideo: true
|
||||||
|
});
|
||||||
|
let icecandidate: RTCIceCandidate[] = [];
|
||||||
|
|
||||||
|
peerConnection.addEventListener("icecandidate", (event) => {
|
||||||
|
if(event.candidate) {
|
||||||
|
icecandidate.push(event.candidate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// This does ICE gathering
|
||||||
await peerConn.setLocalDescription(offer);
|
await peerConn.setLocalDescription(offer);
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
@@ -15,9 +29,9 @@
|
|||||||
resolve(null);
|
resolve(null);
|
||||||
} else {
|
} else {
|
||||||
peerConn.addEventListener('icegatheringstatechange', () => {
|
peerConn.addEventListener('icegatheringstatechange', () => {
|
||||||
console.log(peerConn.iceGatheringState);
|
if(peerConn.iceGatheringState === 'complete') {
|
||||||
if(peerConn.iceGatheringState === 'complete')
|
|
||||||
resolve(null);
|
resolve(null);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -27,34 +41,50 @@
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ offer })
|
body: JSON.stringify({ offer, ice: icecandidate})
|
||||||
})).json();
|
})).json();
|
||||||
|
|
||||||
|
//console.log(remoteOffer.sdp);
|
||||||
|
|
||||||
await peerConn.setRemoteDescription(remoteOffer);
|
await peerConn.setRemoteDescription(remoteOffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
peerConnection = new RTCPeerConnection({
|
peerConnection = new RTCPeerConnection({
|
||||||
iceServers: []
|
certificates: [],
|
||||||
|
iceServers: [],
|
||||||
|
iceTransportPolicy: "all",
|
||||||
|
// @ts-ignore
|
||||||
|
rtcpMuxPolicy: 'negotiate' // Workaround, typescript doesn't know about this property
|
||||||
});
|
});
|
||||||
|
|
||||||
peerConnection.addEventListener('track', (event) => {
|
peerConnection.addEventListener('track', (event) => {
|
||||||
console.log('track', event);
|
event.streams[0].getTracks().forEach(track => {
|
||||||
videoElement.srcObject = event.streams[0];
|
incommingStream.addTrack(track);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
peerConnection.addEventListener("icecandidate", (event) => {
|
||||||
|
if(event.candidate) {
|
||||||
|
console.log(event.candidate.candidate);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await negotiateWebRTC(peerConnection);
|
await negotiateWebRTC(peerConnection);
|
||||||
peerConnection.onconnectionstatechange = () => {
|
|
||||||
console.log("PC State:", peerConnection.connectionState);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function start_playing() {
|
||||||
|
videoElement.play();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<video bind:this={videoElement}></video>
|
<!-- svelte-ignore a11y-media-has-caption -->
|
||||||
|
<video bind:this={videoElement} autoplay playsinline></video>
|
||||||
|
|
||||||
<button>stop</button>
|
<button>stop</button>
|
||||||
<button>start</button>
|
<button on:click={start_playing}>start</button>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import Feedtes from "$lib/Feedtes.svelte";
|
||||||
import LiveFeed from "$lib/LiveFeed.svelte";
|
import LiveFeed from "$lib/LiveFeed.svelte";
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<Feedtes/>
|
||||||
|
|
||||||
|
<!--
|
||||||
<LiveFeed/>
|
<LiveFeed/>
|
||||||
|
-->
|
||||||
BIN
gobot-gui/webrtc_test/.gitignore
(Stored with Git LFS)
vendored
Normal file
BIN
gobot-gui/webrtc_test/.gitignore
(Stored with Git LFS)
vendored
Normal file
Binary file not shown.
1
gobot-gui/webrtc_test/.npmrc
Normal file
1
gobot-gui/webrtc_test/.npmrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
engine-strict=true
|
||||||
BIN
gobot-gui/webrtc_test/README.md
(Stored with Git LFS)
Normal file
BIN
gobot-gui/webrtc_test/README.md
(Stored with Git LFS)
Normal file
Binary file not shown.
3
gobot-gui/webrtc_test/api/.vscode/settings.json
vendored
Normal file
3
gobot-gui/webrtc_test/api/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"deno.enable": true
|
||||||
|
}
|
||||||
0
gobot-gui/webrtc_test/api/a
Normal file
0
gobot-gui/webrtc_test/api/a
Normal file
5
gobot-gui/webrtc_test/api/deno.json
Normal file
5
gobot-gui/webrtc_test/api/deno.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"dev": "deno run -A --unstable-net --watch main.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
104
gobot-gui/webrtc_test/api/deno.lock
generated
Normal file
104
gobot-gui/webrtc_test/api/deno.lock
generated
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
{
|
||||||
|
"version": "3",
|
||||||
|
"packages": {
|
||||||
|
"specifiers": {
|
||||||
|
"jsr:@oak/commons@0.7": "jsr:@oak/commons@0.7.0",
|
||||||
|
"jsr:@oak/oak@14": "jsr:@oak/oak@14.2.0",
|
||||||
|
"jsr:@std/assert@0.218": "jsr:@std/assert@0.218.2",
|
||||||
|
"jsr:@std/assert@^0.218.2": "jsr:@std/assert@0.218.2",
|
||||||
|
"jsr:@std/bytes@0.218": "jsr:@std/bytes@0.218.2",
|
||||||
|
"jsr:@std/bytes@^0.218.2": "jsr:@std/bytes@0.218.2",
|
||||||
|
"jsr:@std/crypto@0.218": "jsr:@std/crypto@0.218.2",
|
||||||
|
"jsr:@std/encoding@0.218": "jsr:@std/encoding@0.218.2",
|
||||||
|
"jsr:@std/encoding@^0.218.2": "jsr:@std/encoding@0.218.2",
|
||||||
|
"jsr:@std/http@0.218": "jsr:@std/http@0.218.2",
|
||||||
|
"jsr:@std/io@0.218": "jsr:@std/io@0.218.2",
|
||||||
|
"jsr:@std/media-types@0.218": "jsr:@std/media-types@0.218.2",
|
||||||
|
"jsr:@std/path@0.218": "jsr:@std/path@0.218.2",
|
||||||
|
"npm:@types/node": "npm:@types/node@18.16.19",
|
||||||
|
"npm:@types/sdp-transform": "npm:@types/sdp-transform@2.4.9",
|
||||||
|
"npm:path-to-regexp@6.2.1": "npm:path-to-regexp@6.2.1",
|
||||||
|
"npm:sdp-transform@2.14.2": "npm:sdp-transform@2.14.2"
|
||||||
|
},
|
||||||
|
"jsr": {
|
||||||
|
"@oak/commons@0.7.0": {
|
||||||
|
"integrity": "4bd889b3dc9ddac1b602034d88c137f06de7078775961b51081beb5f175c120b"
|
||||||
|
},
|
||||||
|
"@oak/oak@14.2.0": {
|
||||||
|
"integrity": "b683b089693004ac3bca80b52159b3e9ad214dc8246ff5dc61ba658da78bc166",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@oak/commons@0.7",
|
||||||
|
"jsr:@std/assert@0.218",
|
||||||
|
"jsr:@std/bytes@0.218",
|
||||||
|
"jsr:@std/crypto@0.218",
|
||||||
|
"jsr:@std/encoding@0.218",
|
||||||
|
"jsr:@std/http@0.218",
|
||||||
|
"jsr:@std/io@0.218",
|
||||||
|
"jsr:@std/media-types@0.218",
|
||||||
|
"jsr:@std/path@0.218",
|
||||||
|
"npm:path-to-regexp@6.2.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/assert@0.218.2": {
|
||||||
|
"integrity": "7f0a5a1a8cf86607cd6c2c030584096e1ffad27fc9271429a8cb48cfbdee5eaf"
|
||||||
|
},
|
||||||
|
"@std/bytes@0.218.2": {
|
||||||
|
"integrity": "91fe54b232dcca73856b79a817247f4a651dbb60d51baafafb6408c137241670"
|
||||||
|
},
|
||||||
|
"@std/crypto@0.218.2": {
|
||||||
|
"integrity": "8c5031a3a1c3ac3bed3c0d4bed2fe7e7faedcb673bbfa0edd10570c8452f5cd2",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert@^0.218.2",
|
||||||
|
"jsr:@std/encoding@^0.218.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/encoding@0.218.2": {
|
||||||
|
"integrity": "da55a763c29bf0dbf06fd286430b358266eb99c28789d89fe9a3e28edecb8d8e"
|
||||||
|
},
|
||||||
|
"@std/http@0.218.2": {
|
||||||
|
"integrity": "54223b62702e665b9dab6373ea2e51235e093ef47228d21cfa0469ee5ac75c9b",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert@^0.218.2",
|
||||||
|
"jsr:@std/encoding@^0.218.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/io@0.218.2": {
|
||||||
|
"integrity": "c64fbfa087b7c9d4d386c5672f291f607d88cb7d44fc299c20c713e345f2785f",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/bytes@^0.218.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/media-types@0.218.2": {
|
||||||
|
"integrity": "1ed3bd2a05e44bad3fc2bab1767d0ce7f2fd68baee62a980751ce51633acb788"
|
||||||
|
},
|
||||||
|
"@std/path@0.218.2": {
|
||||||
|
"integrity": "b568fd923d9e53ad76d17c513e7310bda8e755a3e825e6289a0ce536404e2662",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert@^0.218.2"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm": {
|
||||||
|
"@types/node@18.16.19": {
|
||||||
|
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==",
|
||||||
|
"dependencies": {}
|
||||||
|
},
|
||||||
|
"@types/sdp-transform@2.4.9": {
|
||||||
|
"integrity": "sha512-bVr+/OoZZy7wrHlNcEAAa6PAgKA4BoXPYVN2EijMC5WnGgQ4ZEuixmKnVs2roiAvr7RhIFVH17QD27cojgIZCg==",
|
||||||
|
"dependencies": {}
|
||||||
|
},
|
||||||
|
"path-to-regexp@6.2.1": {
|
||||||
|
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==",
|
||||||
|
"dependencies": {}
|
||||||
|
},
|
||||||
|
"sdp-transform@2.14.2": {
|
||||||
|
"integrity": "sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA==",
|
||||||
|
"dependencies": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remote": {
|
||||||
|
"https://deno.land/std@0.224.0/encoding/_util.ts": "beacef316c1255da9bc8e95afb1fa56ed69baef919c88dc06ae6cb7a6103d376",
|
||||||
|
"https://deno.land/std@0.224.0/encoding/hex.ts": "6270f25e5d85f99fcf315278670ba012b04b7c94b67715b53f30d03249687c07"
|
||||||
|
}
|
||||||
|
}
|
||||||
66
gobot-gui/webrtc_test/api/main.ts
Normal file
66
gobot-gui/webrtc_test/api/main.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { Application, Router } from "jsr:@oak/oak@14";
|
||||||
|
import { router_webrtc } from "./webrtc_fake.ts";
|
||||||
|
|
||||||
|
const app = new Application();
|
||||||
|
const router = new Router({
|
||||||
|
prefix: "/api",
|
||||||
|
});
|
||||||
|
|
||||||
|
let offer: any = {};
|
||||||
|
let answer: any = {};
|
||||||
|
|
||||||
|
router.post("/offer", async (ctx) => {
|
||||||
|
const body = await ctx.request.body.json();
|
||||||
|
if (body) {
|
||||||
|
offer = body;
|
||||||
|
ctx.response.status = 200;
|
||||||
|
ctx.response.body = { message: "Answer stored successfully" };
|
||||||
|
} else {
|
||||||
|
ctx.response.status = 400;
|
||||||
|
ctx.response.body = { error: "Invalid JSON" };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/answer", async (ctx) => {
|
||||||
|
const body = await ctx.request.body.json();
|
||||||
|
if (body) {
|
||||||
|
answer = body;
|
||||||
|
ctx.response.status = 200;
|
||||||
|
ctx.response.body = { message: "Answer stored successfully" };
|
||||||
|
} else {
|
||||||
|
ctx.response.status = 400;
|
||||||
|
ctx.response.body = { error: "Invalid JSON" };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/offer", (ctx) => {
|
||||||
|
ctx.response.body = offer;
|
||||||
|
if(Object.keys(answer).length === 0) {
|
||||||
|
ctx.response.status = 204;
|
||||||
|
} else {
|
||||||
|
offer = {};
|
||||||
|
ctx.response.status = 200;
|
||||||
|
}
|
||||||
|
ctx.response.headers.set("Content-Type", "application/json");
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/answer", (ctx) => {
|
||||||
|
ctx.response.body = answer;
|
||||||
|
if(Object.keys(answer).length === 0) {
|
||||||
|
ctx.response.status = 204;
|
||||||
|
} else {
|
||||||
|
answer = {};
|
||||||
|
ctx.response.status = 200;
|
||||||
|
}
|
||||||
|
ctx.response.headers.set("Content-Type", "application/json");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(router_webrtc.routes());
|
||||||
|
app.use(router_webrtc.allowedMethods());
|
||||||
|
|
||||||
|
app.use(router.routes());
|
||||||
|
app.use(router.allowedMethods());
|
||||||
|
|
||||||
|
app.listen({
|
||||||
|
port: 8080,
|
||||||
|
});
|
||||||
8
gobot-gui/webrtc_test/api/output.sdp
Normal file
8
gobot-gui/webrtc_test/api/output.sdp
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
v=0
|
||||||
|
o=- 0 0 IN IP4 127.0.0.1
|
||||||
|
s=No Name
|
||||||
|
t=0 0
|
||||||
|
m=video 1234 RTP/AVP 96
|
||||||
|
b=AS:3000
|
||||||
|
a=rtpmap:96 H264/90000
|
||||||
|
a=fmtp:96 packetization-mode=1
|
||||||
14
gobot-gui/webrtc_test/api/scripts/ffmpeg_stream.bash
Normal file
14
gobot-gui/webrtc_test/api/scripts/ffmpeg_stream.bash
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#/usr/bin/bash
|
||||||
|
|
||||||
|
SDP_FILE="output.sdp"
|
||||||
|
|
||||||
|
ffmpeg \
|
||||||
|
-stream_loop -1 \
|
||||||
|
-rtbufsize 2M \
|
||||||
|
-re \
|
||||||
|
-i "test/Big_Buck_Bunny_1080_10s_10MB.mp4" \
|
||||||
|
-b:v 3M \
|
||||||
|
-sdp_file $SDP_FILE \
|
||||||
|
-vcodec libx264 \
|
||||||
|
-acodec aac \
|
||||||
|
-f rtp rtp://0.0.0.0:1234
|
||||||
48
gobot-gui/webrtc_test/api/src/iceCandiate.ts
Normal file
48
gobot-gui/webrtc_test/api/src/iceCandiate.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
export interface IceCandidate {
|
||||||
|
foundation: string;
|
||||||
|
componentId: number;
|
||||||
|
transport: string;
|
||||||
|
priority: number;
|
||||||
|
ipAddress: string;
|
||||||
|
port: number;
|
||||||
|
candidateType: string;
|
||||||
|
relatedAddress: string | null;
|
||||||
|
relatedPort: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function parseIceCandidate(candidate: string): IceCandidate {
|
||||||
|
const parts = candidate.split(' ');
|
||||||
|
const iceCandidate: IceCandidate = {
|
||||||
|
foundation: parts[0].split(':')[1],
|
||||||
|
componentId: Number.parseInt(parts[1]),
|
||||||
|
transport: parts[2],
|
||||||
|
priority: Number.parseInt(parts[3]),
|
||||||
|
ipAddress: parts[4],
|
||||||
|
port: Number.parseInt(parts[5]),
|
||||||
|
candidateType: parts[7],
|
||||||
|
relatedAddress: null,
|
||||||
|
relatedPort: null
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 8; i < parts.length; i++) {
|
||||||
|
if (parts[i] === 'raddr') {
|
||||||
|
iceCandidate.relatedAddress = parts[i + 1];
|
||||||
|
} else if (parts[i] === 'rport') {
|
||||||
|
iceCandidate.relatedPort = Number.parseInt(parts[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iceCandidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findHighestPriorityCandidate(candidates: IceCandidate[]): IceCandidate | null {
|
||||||
|
if (candidates.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates.reduce((highest, candidate) =>
|
||||||
|
candidate.priority > highest.priority ? candidate : highest
|
||||||
|
);
|
||||||
|
}
|
||||||
BIN
gobot-gui/webrtc_test/api/test/Big_Buck_Bunny_1080_10s_10MB.mp4
Normal file
BIN
gobot-gui/webrtc_test/api/test/Big_Buck_Bunny_1080_10s_10MB.mp4
Normal file
Binary file not shown.
114
gobot-gui/webrtc_test/api/test/iceCandiates.test.ts
Normal file
114
gobot-gui/webrtc_test/api/test/iceCandiates.test.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { assertEquals } from "jsr:@std/assert";
|
||||||
|
import { IceCandidate, parseIceCandidate } from '../src/iceCandiate.ts';
|
||||||
|
|
||||||
|
const iceCandidates = [
|
||||||
|
"candidate:842163049 1 udp 1677729535 192.168.1.2 3478 typ host",
|
||||||
|
"candidate:842163049 2 udp 1677729534 192.168.1.3 3479 typ srflx raddr 192.168.1.2 rport 3478",
|
||||||
|
"candidate:842163049 1 tcp 1677729533 192.168.1.4 3480 typ relay raddr 192.168.1.3 rport 3479",
|
||||||
|
"candidate:842163049 2 tcp 1677729532 192.168.1.5 3481 typ host",
|
||||||
|
"candidate:842163049 1 udp 1677729531 192.168.1.6 3482 typ srflx rport 3480",
|
||||||
|
"candidate:842163049 2 udp 1677729530 192.168.1.7 3483 typ relay raddr 192.168.1.5",
|
||||||
|
"candidate:842163049 1 tcp 1677729529 192.168.1.8 3484 typ host",
|
||||||
|
"candidate:842163049 2 tcp 1677729528 192.168.1.9 3485 typ srflx raddr 192.168.1.6 rport 3482"
|
||||||
|
];
|
||||||
|
|
||||||
|
const parsedExamples: IceCandidate[] = [
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 1,
|
||||||
|
transport: "udp",
|
||||||
|
priority: 1677729535,
|
||||||
|
ipAddress: "192.168.1.2",
|
||||||
|
port: 3478,
|
||||||
|
candidateType: "host",
|
||||||
|
relatedAddress: null,
|
||||||
|
relatedPort: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 2,
|
||||||
|
transport: "udp",
|
||||||
|
priority: 1677729534,
|
||||||
|
ipAddress: "192.168.1.3",
|
||||||
|
port: 3479,
|
||||||
|
candidateType: "srflx",
|
||||||
|
relatedAddress: "192.168.1.2",
|
||||||
|
relatedPort: 3478
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 1,
|
||||||
|
transport: "tcp",
|
||||||
|
priority: 1677729533,
|
||||||
|
ipAddress: "192.168.1.4",
|
||||||
|
port: 3480,
|
||||||
|
candidateType: "relay",
|
||||||
|
relatedAddress: "192.168.1.3",
|
||||||
|
relatedPort: 3479
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 2,
|
||||||
|
transport: "tcp",
|
||||||
|
priority: 1677729532,
|
||||||
|
ipAddress: "192.168.1.5",
|
||||||
|
port: 3481,
|
||||||
|
candidateType: "host",
|
||||||
|
relatedAddress: null,
|
||||||
|
relatedPort: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 1,
|
||||||
|
transport: "udp",
|
||||||
|
priority: 1677729531,
|
||||||
|
ipAddress: "192.168.1.6",
|
||||||
|
port: 3482,
|
||||||
|
candidateType: "srflx",
|
||||||
|
relatedPort: 3480,
|
||||||
|
relatedAddress: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 2,
|
||||||
|
transport: "udp",
|
||||||
|
priority: 1677729530,
|
||||||
|
ipAddress: "192.168.1.7",
|
||||||
|
port: 3483,
|
||||||
|
candidateType: "relay",
|
||||||
|
relatedAddress: "192.168.1.5",
|
||||||
|
relatedPort: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 1,
|
||||||
|
transport: "tcp",
|
||||||
|
priority: 1677729529,
|
||||||
|
ipAddress: "192.168.1.8",
|
||||||
|
port: 3484,
|
||||||
|
candidateType: "host",
|
||||||
|
relatedAddress: null,
|
||||||
|
relatedPort: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foundation: "842163049",
|
||||||
|
componentId: 2,
|
||||||
|
transport: "tcp",
|
||||||
|
priority: 1677729528,
|
||||||
|
ipAddress: "192.168.1.9",
|
||||||
|
port: 3485,
|
||||||
|
candidateType: "srflx",
|
||||||
|
relatedAddress: "192.168.1.6",
|
||||||
|
relatedPort: 3482
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
Deno.test("parseIceCandidate should correctly parse ICE candidates", async (t) => {
|
||||||
|
for (const [index, candidate] of iceCandidates.entries()) {
|
||||||
|
await t.step(`${index}-${candidate}`, () => {
|
||||||
|
const parsed = parseIceCandidate(candidate);
|
||||||
|
console.log(candidate);
|
||||||
|
assertEquals(parsed, parsedExamples[index]);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
145
gobot-gui/webrtc_test/api/webrtc_fake.ts
Normal file
145
gobot-gui/webrtc_test/api/webrtc_fake.ts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import { Router } from "jsr:@oak/oak@14";
|
||||||
|
|
||||||
|
// @deno-types="npm:@types/sdp-transform"
|
||||||
|
import sdpTransform from "npm:sdp-transform@2.14.2";
|
||||||
|
import { encodeHex,} from "https://deno.land/std@0.224.0/encoding/hex.ts";
|
||||||
|
import { parseIceCandidate, IceCandidate } from "./src/iceCandiate.ts";
|
||||||
|
|
||||||
|
const router = new Router({
|
||||||
|
prefix: "/api",
|
||||||
|
});
|
||||||
|
|
||||||
|
export {router as router_webrtc};
|
||||||
|
|
||||||
|
let rxAddress: Deno.Addr | undefined = undefined;
|
||||||
|
|
||||||
|
const ffmpegSocket = Deno.listenDatagram({
|
||||||
|
hostname: "localhost",
|
||||||
|
port: 1234,
|
||||||
|
transport: "udp"
|
||||||
|
});
|
||||||
|
|
||||||
|
let ffmpegRxRun = true;
|
||||||
|
// deno-lint-ignore no-async-promise-executor
|
||||||
|
const _ffmpegRxPromose = new Promise(async (resolve, _reject) => {
|
||||||
|
while(ffmpegRxRun) {
|
||||||
|
const data = new Uint8Array(65536);
|
||||||
|
await ffmpegSocket.receive(data);
|
||||||
|
|
||||||
|
if(rxAddress !== undefined) {
|
||||||
|
await ffmpegSocket.send(data, rxAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
interface RTCIceCandidate {
|
||||||
|
candidate: string;
|
||||||
|
sdpMid: string;
|
||||||
|
sdpMLineIndex: number;
|
||||||
|
usernameFragment: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RTCSessionDescription {
|
||||||
|
sdp: string;
|
||||||
|
type: "offer" | "answer";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSDP(): Promise<string> {
|
||||||
|
return await Deno.readTextFile("./output.sdp");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFingerprint(): Promise<string> {
|
||||||
|
const cert = new Uint8Array(32);
|
||||||
|
crypto.getRandomValues(cert);
|
||||||
|
const hash = encodeHex(await crypto.subtle.digest("SHA-256", cert)).toUpperCase();
|
||||||
|
return hash.split(/(..)/g).filter((s: string) => s !== "").join(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function modifySDP(sdp: string): Promise<string> {
|
||||||
|
//let sdpObj = sdpTransform.parse(sdp);
|
||||||
|
const localAddress = "127.0.0.1";
|
||||||
|
|
||||||
|
let sdpObj: sdpTransform.SessionDescription = {
|
||||||
|
fingerprint: {
|
||||||
|
"type": "SHA-256",
|
||||||
|
"hash": await getFingerprint()
|
||||||
|
},
|
||||||
|
icePwd : "AAAABBBB",
|
||||||
|
iceUfrag: "AAAABBBB",
|
||||||
|
origin: {
|
||||||
|
username: "-",
|
||||||
|
sessionId: (Math.random()*10000000).toFixed(0),
|
||||||
|
sessionVersion: 0,
|
||||||
|
netType: "IN",
|
||||||
|
ipVer: 4,
|
||||||
|
address: localAddress,
|
||||||
|
},
|
||||||
|
timing: {
|
||||||
|
start: 0,
|
||||||
|
stop: 0
|
||||||
|
},
|
||||||
|
media: [
|
||||||
|
{
|
||||||
|
type: "video",
|
||||||
|
port: 1235,
|
||||||
|
protocol: "RTP/AVP",
|
||||||
|
direction: "sendrecv",
|
||||||
|
connection: {
|
||||||
|
ip: localAddress,
|
||||||
|
version: 4,
|
||||||
|
|
||||||
|
},
|
||||||
|
payloads: "126",
|
||||||
|
fmtp: [
|
||||||
|
|
||||||
|
],
|
||||||
|
rtp: [
|
||||||
|
{
|
||||||
|
payload: 126,
|
||||||
|
codec: "H264",
|
||||||
|
rate: 90000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(sdpObj);
|
||||||
|
|
||||||
|
return sdpTransform.write(sdpObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.post("/webrtc", async (ctx) => {
|
||||||
|
const body: {
|
||||||
|
ice: RTCIceCandidate[];
|
||||||
|
offer: RTCSessionDescription
|
||||||
|
} = await ctx.request.body.json();
|
||||||
|
|
||||||
|
const ice: RTCIceCandidate[] = [];
|
||||||
|
const answer: RTCSessionDescription = {
|
||||||
|
sdp: await modifySDP(await getSDP()),
|
||||||
|
type: "answer"
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(body.offer);
|
||||||
|
|
||||||
|
const remoteIce = body.ice.map((candidate: RTCIceCandidate) => parseIceCandidate(candidate.candidate))
|
||||||
|
.filter((c: IceCandidate | null) => c !== null)
|
||||||
|
.filter((c: IceCandidate | null) => c?.transport === "UDP" && c?.candidateType === "host")
|
||||||
|
|
||||||
|
//console.log(remoteIce);
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
ctx.response.status = 200;
|
||||||
|
ctx.response.body = {
|
||||||
|
ice: ice,
|
||||||
|
answer: answer
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
ctx.response.status = 400;
|
||||||
|
ctx.response.body = { error: "Invalid JSON" };
|
||||||
|
}
|
||||||
|
});
|
||||||
21
gobot-gui/webrtc_test/package.json
Normal file
21
gobot-gui/webrtc_test/package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "webrtc-test",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
|
"@sveltejs/kit": "^2.0.0",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||||
|
"svelte": "^5.0.0",
|
||||||
|
"svelte-check": "^4.0.0",
|
||||||
|
"typescript": "^5.0.0",
|
||||||
|
"vite": "^5.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
gobot-gui/webrtc_test/src/app.d.ts
vendored
Normal file
13
gobot-gui/webrtc_test/src/app.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// See https://svelte.dev/docs/kit/types#app
|
||||||
|
// for information about these interfaces
|
||||||
|
declare global {
|
||||||
|
namespace App {
|
||||||
|
// interface Error {}
|
||||||
|
// interface Locals {}
|
||||||
|
// interface PageData {}
|
||||||
|
// interface PageState {}
|
||||||
|
// interface Platform {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
12
gobot-gui/webrtc_test/src/app.html
Normal file
12
gobot-gui/webrtc_test/src/app.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
%sveltekit.head%
|
||||||
|
</head>
|
||||||
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
gobot-gui/webrtc_test/src/lib/index.ts
Normal file
1
gobot-gui/webrtc_test/src/lib/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
// place files you want to import through the `$lib` alias in this folder.
|
||||||
2
gobot-gui/webrtc_test/src/routes/+page.svelte
Normal file
2
gobot-gui/webrtc_test/src/routes/+page.svelte
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<h1>Welcome to SvelteKit</h1>
|
||||||
|
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
|
||||||
92
gobot-gui/webrtc_test/src/routes/recv/+page.svelte
Normal file
92
gobot-gui/webrtc_test/src/routes/recv/+page.svelte
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
let state = "IDLE";
|
||||||
|
|
||||||
|
let localStream: MediaStream;
|
||||||
|
let videoElement: HTMLVideoElement;
|
||||||
|
let pc: RTCPeerConnection;
|
||||||
|
|
||||||
|
async function start() {
|
||||||
|
state = "CONNECTING";
|
||||||
|
|
||||||
|
pc = new RTCPeerConnection({
|
||||||
|
iceServers: [
|
||||||
|
{urls: "stun:stun.l.google.com:19302"},
|
||||||
|
],
|
||||||
|
// @ts-ignore
|
||||||
|
rtcpMuxPolicy: "negotiate"
|
||||||
|
});
|
||||||
|
|
||||||
|
const offer = await pc.createOffer({
|
||||||
|
offerToReceiveVideo: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let iceCandidates: RTCIceCandidate[] = [];
|
||||||
|
pc.addEventListener("icecandidate", (event) => {
|
||||||
|
if (event.candidate) {
|
||||||
|
iceCandidates.push(event.candidate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await pc.setLocalDescription(offer);
|
||||||
|
state = "GATHERING ICE";
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
function onIce() {
|
||||||
|
if (pc.iceGatheringState === "complete") {
|
||||||
|
pc.removeEventListener("icegatheringstatechange", onIce);
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pc.iceGatheringState === "complete") {2
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
pc.addEventListener("icegatheringstatechange", onIce);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
localStream = new MediaStream();
|
||||||
|
videoElement.srcObject = localStream;
|
||||||
|
|
||||||
|
pc.addEventListener("track", (ev) => {
|
||||||
|
console.log("TRACK!");
|
||||||
|
localStream.addTrack(ev.track);
|
||||||
|
});
|
||||||
|
|
||||||
|
state = "WAITING FOR ANSWER";
|
||||||
|
const data = await (await fetch("/api/webrtc", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
offer: offer,
|
||||||
|
ice: iceCandidates,
|
||||||
|
}),
|
||||||
|
})).json();
|
||||||
|
|
||||||
|
|
||||||
|
let answer: RTCSessionDescription = data.answer;
|
||||||
|
let ice: RTCIceCandidate[] = data.ice;
|
||||||
|
console.log(answer)
|
||||||
|
|
||||||
|
state = "GOT ANSWER!";
|
||||||
|
|
||||||
|
await pc.setRemoteDescription(answer);
|
||||||
|
|
||||||
|
console.log(ice);
|
||||||
|
ice.forEach((candidate) => {
|
||||||
|
pc.addIceCandidate(candidate);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<video bind:this={videoElement} autoplay playsinline></video>
|
||||||
|
<button on:click={start}>Connect!</button>
|
||||||
|
<p>{state}</p>
|
||||||
|
</main>
|
||||||
98
gobot-gui/webrtc_test/src/routes/send/+page.svelte
Normal file
98
gobot-gui/webrtc_test/src/routes/send/+page.svelte
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
let state = "IDLE";
|
||||||
|
|
||||||
|
let localStream: MediaStream;
|
||||||
|
let videoElement: HTMLVideoElement;
|
||||||
|
let pc: RTCPeerConnection;
|
||||||
|
|
||||||
|
async function start() {
|
||||||
|
state = "WAITING FOR OFFER";
|
||||||
|
let offer: RTCSessionDescription = null;
|
||||||
|
let offerIceCandidates: RTCIceCandidate[] = [];
|
||||||
|
|
||||||
|
await new Promise(async (resolve) => {
|
||||||
|
while(true) {
|
||||||
|
const res = await (fetch("/api/offer"));
|
||||||
|
|
||||||
|
if(res.status === 200) {
|
||||||
|
const data = await res.json();
|
||||||
|
offer = data.offer;
|
||||||
|
offerIceCandidates = data.ice;
|
||||||
|
resolve(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise((r) => setTimeout(() => r(null), 250));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
pc = new RTCPeerConnection({
|
||||||
|
iceServers: []
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(offerIceCandidates);
|
||||||
|
|
||||||
|
state = "SET TRACKS";
|
||||||
|
|
||||||
|
localStream = new MediaStream();
|
||||||
|
let camStream = await navigator.mediaDevices.getUserMedia({video: true});
|
||||||
|
|
||||||
|
camStream.getTracks().forEach((track) => {
|
||||||
|
pc.addTrack(track, camStream);
|
||||||
|
localStream.addTrack(track);
|
||||||
|
});
|
||||||
|
|
||||||
|
videoElement.srcObject = localStream;
|
||||||
|
|
||||||
|
state = "GATHERING ICE";
|
||||||
|
let iceCandidates: RTCIceCandidate[] = [];
|
||||||
|
|
||||||
|
pc.addEventListener("icecandidate", (event) => {
|
||||||
|
if (event.candidate) {
|
||||||
|
iceCandidates.push(event.candidate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await pc.setRemoteDescription(offer);
|
||||||
|
offerIceCandidates.forEach((candidate) => pc.addIceCandidate(candidate));
|
||||||
|
let answer = await pc.createAnswer();
|
||||||
|
await pc.setLocalDescription(answer);
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
function onIce() {
|
||||||
|
if (pc.iceGatheringState === "complete") {
|
||||||
|
pc.removeEventListener("icegatheringstatechange", onIce);
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pc.iceGatheringState === "complete") {
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
pc.addEventListener("icegatheringstatechange", onIce);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
state = "SENDING ANSWER";
|
||||||
|
await fetch("/api/answer", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
answer: answer,
|
||||||
|
ice: iceCandidates,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<video bind:this={videoElement} autoplay playsinline></video>
|
||||||
|
<button on:click={start}>Connect!</button>
|
||||||
|
<p>{state}</p>
|
||||||
|
</main>
|
||||||
BIN
gobot-gui/webrtc_test/static/favicon.png
(Stored with Git LFS)
Normal file
BIN
gobot-gui/webrtc_test/static/favicon.png
(Stored with Git LFS)
Normal file
Binary file not shown.
18
gobot-gui/webrtc_test/svelte.config.js
Normal file
18
gobot-gui/webrtc_test/svelte.config.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import adapter from '@sveltejs/adapter-auto';
|
||||||
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
const config = {
|
||||||
|
// Consult https://svelte.dev/docs/kit/integrations#preprocessors
|
||||||
|
// for more information about preprocessors
|
||||||
|
preprocess: vitePreprocess(),
|
||||||
|
|
||||||
|
kit: {
|
||||||
|
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||||
|
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||||
|
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||||
|
adapter: adapter()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
19
gobot-gui/webrtc_test/tsconfig.json
Normal file
19
gobot-gui/webrtc_test/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"moduleResolution": "bundler"
|
||||||
|
}
|
||||||
|
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||||
|
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||||
|
//
|
||||||
|
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||||
|
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||||
|
}
|
||||||
12
gobot-gui/webrtc_test/vite.config.ts
Normal file
12
gobot-gui/webrtc_test/vite.config.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [sveltekit()],
|
||||||
|
server: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
proxy: {
|
||||||
|
'/api': 'http://localhost:8080'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
596
gobot-gui/webrtc_test/yarn.lock
Normal file
596
gobot-gui/webrtc_test/yarn.lock
Normal file
@@ -0,0 +1,596 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@ampproject/remapping@^2.3.0":
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
|
||||||
|
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/gen-mapping" "^0.3.5"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.24"
|
||||||
|
|
||||||
|
"@esbuild/aix-ppc64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
|
||||||
|
integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
|
||||||
|
|
||||||
|
"@esbuild/android-arm64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
|
||||||
|
integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
|
||||||
|
|
||||||
|
"@esbuild/android-arm@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
|
||||||
|
integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
|
||||||
|
|
||||||
|
"@esbuild/android-x64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
|
||||||
|
integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
|
||||||
|
|
||||||
|
"@esbuild/darwin-arm64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
|
||||||
|
integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
|
||||||
|
|
||||||
|
"@esbuild/darwin-x64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
|
||||||
|
integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
|
||||||
|
|
||||||
|
"@esbuild/freebsd-arm64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
|
||||||
|
integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
|
||||||
|
|
||||||
|
"@esbuild/freebsd-x64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
|
||||||
|
integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
|
||||||
|
|
||||||
|
"@esbuild/linux-arm64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
|
||||||
|
integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
|
||||||
|
|
||||||
|
"@esbuild/linux-arm@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
|
||||||
|
integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
|
||||||
|
|
||||||
|
"@esbuild/linux-ia32@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
|
||||||
|
integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
|
||||||
|
integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
|
||||||
|
|
||||||
|
"@esbuild/linux-mips64el@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
|
||||||
|
integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
|
||||||
|
|
||||||
|
"@esbuild/linux-ppc64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
|
||||||
|
integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
|
||||||
|
|
||||||
|
"@esbuild/linux-riscv64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
|
||||||
|
integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
|
||||||
|
|
||||||
|
"@esbuild/linux-s390x@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
|
||||||
|
integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
|
||||||
|
|
||||||
|
"@esbuild/linux-x64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
|
||||||
|
integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
|
||||||
|
|
||||||
|
"@esbuild/netbsd-x64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
|
||||||
|
integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
|
||||||
|
|
||||||
|
"@esbuild/openbsd-x64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
|
||||||
|
integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
|
||||||
|
|
||||||
|
"@esbuild/sunos-x64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
|
||||||
|
integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
|
||||||
|
|
||||||
|
"@esbuild/win32-arm64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
|
||||||
|
integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
|
||||||
|
|
||||||
|
"@esbuild/win32-ia32@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
|
||||||
|
integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
|
||||||
|
|
||||||
|
"@esbuild/win32-x64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
|
||||||
|
integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping@^0.3.5":
|
||||||
|
version "0.3.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
|
||||||
|
integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/set-array" "^1.2.1"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.24"
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri@^3.1.0":
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
|
||||||
|
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
|
||||||
|
|
||||||
|
"@jridgewell/set-array@^1.2.1":
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
|
||||||
|
integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15", "@jridgewell/sourcemap-codec@^1.5.0":
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||||
|
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
|
||||||
|
version "0.3.25"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
|
||||||
|
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/resolve-uri" "^3.1.0"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||||
|
|
||||||
|
"@polka/url@^1.0.0-next.24":
|
||||||
|
version "1.0.0-next.28"
|
||||||
|
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73"
|
||||||
|
integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm-eabi@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz#1661ff5ea9beb362795304cb916049aba7ac9c54"
|
||||||
|
integrity sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm64@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz#2ffaa91f1b55a0082b8a722525741aadcbd3971e"
|
||||||
|
integrity sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-arm64@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz#627007221b24b8cc3063703eee0b9177edf49c1f"
|
||||||
|
integrity sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-x64@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz#0605506142b9e796c370d59c5984ae95b9758724"
|
||||||
|
integrity sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz#62dfd196d4b10c0c2db833897164d2d319ee0cbb"
|
||||||
|
integrity sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz#53ce72aeb982f1f34b58b380baafaf6a240fddb3"
|
||||||
|
integrity sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-gnu@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz#1632990f62a75c74f43e4b14ab3597d7ed416496"
|
||||||
|
integrity sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-musl@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz#8c03a996efb41e257b414b2e0560b7a21f2d9065"
|
||||||
|
integrity sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-powerpc64le-gnu@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz#5b98729628d5bcc8f7f37b58b04d6845f85c7b5d"
|
||||||
|
integrity sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz#48e42e41f4cabf3573cfefcb448599c512e22983"
|
||||||
|
integrity sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-s390x-gnu@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz#e0b4f9a966872cb7d3e21b9e412a4b7efd7f0b58"
|
||||||
|
integrity sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-gnu@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz#78144741993100f47bd3da72fce215e077ae036b"
|
||||||
|
integrity sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-musl@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz#d9fe32971883cd1bd858336bd33a1c3ca6146127"
|
||||||
|
integrity sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-arm64-msvc@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz#71fa3ea369316db703a909c790743972e98afae5"
|
||||||
|
integrity sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-ia32-msvc@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz#653f5989a60658e17d7576a3996deb3902e342e2"
|
||||||
|
integrity sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz#0574d7e87b44ee8511d08cc7f914bcb802b70818"
|
||||||
|
integrity sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==
|
||||||
|
|
||||||
|
"@sveltejs/adapter-auto@^3.0.0":
|
||||||
|
version "3.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-auto/-/adapter-auto-3.3.0.tgz#34ebe894616a53c4bcee4a5e6cc0d8dd5acdeade"
|
||||||
|
integrity sha512-EJZqY7eMM+bdbR898Xt9ufawUHLPJu7w3wPr4Cc+T1iIDf3fufVLWg4C71OluIqsdJqv85E4biKuHo3XXIY0PQ==
|
||||||
|
dependencies:
|
||||||
|
import-meta-resolve "^4.1.0"
|
||||||
|
|
||||||
|
"@sveltejs/kit@^2.0.0":
|
||||||
|
version "2.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-2.7.2.tgz#9d799082a466fde4e227cd5bff2193907890f798"
|
||||||
|
integrity sha512-bFwrl+0bNr0/DHQZM0INwwSPNYqDjfsKRhUoa6rj9d8tDZzszBrJ3La6/HVFxWGONEigtG+SzHXa1BEa1BLdwA==
|
||||||
|
dependencies:
|
||||||
|
"@types/cookie" "^0.6.0"
|
||||||
|
cookie "^0.6.0"
|
||||||
|
devalue "^5.1.0"
|
||||||
|
esm-env "^1.0.0"
|
||||||
|
import-meta-resolve "^4.1.0"
|
||||||
|
kleur "^4.1.5"
|
||||||
|
magic-string "^0.30.5"
|
||||||
|
mrmime "^2.0.0"
|
||||||
|
sade "^1.8.1"
|
||||||
|
set-cookie-parser "^2.6.0"
|
||||||
|
sirv "^3.0.0"
|
||||||
|
tiny-glob "^0.2.9"
|
||||||
|
|
||||||
|
"@sveltejs/vite-plugin-svelte-inspector@^3.0.0-next.0||^3.0.0":
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.1.tgz#006bcab6ea90e09c65459133d4e3eaa6b1e83e28"
|
||||||
|
integrity sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==
|
||||||
|
dependencies:
|
||||||
|
debug "^4.3.7"
|
||||||
|
|
||||||
|
"@sveltejs/vite-plugin-svelte@^4.0.0":
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.0.tgz#4e7c2fe6fd262f6bbd7dc82085a76654cbaeafe5"
|
||||||
|
integrity sha512-kpVJwF+gNiMEsoHaw+FJL76IYiwBikkxYU83+BpqQLdVMff19KeRKLd2wisS8niNBMJ2omv5gG+iGDDwd8jzag==
|
||||||
|
dependencies:
|
||||||
|
"@sveltejs/vite-plugin-svelte-inspector" "^3.0.0-next.0||^3.0.0"
|
||||||
|
debug "^4.3.7"
|
||||||
|
deepmerge "^4.3.1"
|
||||||
|
kleur "^4.1.5"
|
||||||
|
magic-string "^0.30.12"
|
||||||
|
vitefu "^1.0.3"
|
||||||
|
|
||||||
|
"@types/cookie@^0.6.0":
|
||||||
|
version "0.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5"
|
||||||
|
integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==
|
||||||
|
|
||||||
|
"@types/estree@*", "@types/estree@1.0.6", "@types/estree@^1.0.1", "@types/estree@^1.0.5":
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
|
||||||
|
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
|
||||||
|
|
||||||
|
acorn-typescript@^1.4.13:
|
||||||
|
version "1.4.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-typescript/-/acorn-typescript-1.4.13.tgz#5f851c8bdda0aa716ffdd5f6ac084df8acc6f5ea"
|
||||||
|
integrity sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==
|
||||||
|
|
||||||
|
acorn@^8.12.1:
|
||||||
|
version "8.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.13.0.tgz#2a30d670818ad16ddd6a35d3842dacec9e5d7ca3"
|
||||||
|
integrity sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==
|
||||||
|
|
||||||
|
aria-query@^5.3.1:
|
||||||
|
version "5.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59"
|
||||||
|
integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==
|
||||||
|
|
||||||
|
axobject-query@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee"
|
||||||
|
integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==
|
||||||
|
|
||||||
|
chokidar@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41"
|
||||||
|
integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==
|
||||||
|
dependencies:
|
||||||
|
readdirp "^4.0.1"
|
||||||
|
|
||||||
|
cookie@^0.6.0:
|
||||||
|
version "0.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
|
||||||
|
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
|
||||||
|
|
||||||
|
debug@^4.3.7:
|
||||||
|
version "4.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
||||||
|
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
||||||
|
dependencies:
|
||||||
|
ms "^2.1.3"
|
||||||
|
|
||||||
|
deepmerge@^4.3.1:
|
||||||
|
version "4.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
|
||||||
|
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
|
||||||
|
|
||||||
|
devalue@^5.1.0:
|
||||||
|
version "5.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/devalue/-/devalue-5.1.1.tgz#a71887ac0f354652851752654e4bd435a53891ae"
|
||||||
|
integrity sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==
|
||||||
|
|
||||||
|
esbuild@^0.21.3:
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
|
||||||
|
integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
|
||||||
|
optionalDependencies:
|
||||||
|
"@esbuild/aix-ppc64" "0.21.5"
|
||||||
|
"@esbuild/android-arm" "0.21.5"
|
||||||
|
"@esbuild/android-arm64" "0.21.5"
|
||||||
|
"@esbuild/android-x64" "0.21.5"
|
||||||
|
"@esbuild/darwin-arm64" "0.21.5"
|
||||||
|
"@esbuild/darwin-x64" "0.21.5"
|
||||||
|
"@esbuild/freebsd-arm64" "0.21.5"
|
||||||
|
"@esbuild/freebsd-x64" "0.21.5"
|
||||||
|
"@esbuild/linux-arm" "0.21.5"
|
||||||
|
"@esbuild/linux-arm64" "0.21.5"
|
||||||
|
"@esbuild/linux-ia32" "0.21.5"
|
||||||
|
"@esbuild/linux-loong64" "0.21.5"
|
||||||
|
"@esbuild/linux-mips64el" "0.21.5"
|
||||||
|
"@esbuild/linux-ppc64" "0.21.5"
|
||||||
|
"@esbuild/linux-riscv64" "0.21.5"
|
||||||
|
"@esbuild/linux-s390x" "0.21.5"
|
||||||
|
"@esbuild/linux-x64" "0.21.5"
|
||||||
|
"@esbuild/netbsd-x64" "0.21.5"
|
||||||
|
"@esbuild/openbsd-x64" "0.21.5"
|
||||||
|
"@esbuild/sunos-x64" "0.21.5"
|
||||||
|
"@esbuild/win32-arm64" "0.21.5"
|
||||||
|
"@esbuild/win32-ia32" "0.21.5"
|
||||||
|
"@esbuild/win32-x64" "0.21.5"
|
||||||
|
|
||||||
|
esm-env@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/esm-env/-/esm-env-1.0.0.tgz#b124b40b180711690a4cb9b00d16573391950413"
|
||||||
|
integrity sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==
|
||||||
|
|
||||||
|
esrap@^1.2.2:
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/esrap/-/esrap-1.2.2.tgz#b9e3afee3f12238563a763b7fa86220de2c53203"
|
||||||
|
integrity sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||||
|
"@types/estree" "^1.0.1"
|
||||||
|
|
||||||
|
fdir@^6.2.0:
|
||||||
|
version "6.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.2.tgz#ddaa7ce1831b161bc3657bb99cb36e1622702689"
|
||||||
|
integrity sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==
|
||||||
|
|
||||||
|
fsevents@~2.3.2, fsevents@~2.3.3:
|
||||||
|
version "2.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||||
|
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||||
|
|
||||||
|
globalyzer@0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465"
|
||||||
|
integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==
|
||||||
|
|
||||||
|
globrex@^0.1.2:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
|
||||||
|
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
|
||||||
|
|
||||||
|
import-meta-resolve@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706"
|
||||||
|
integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==
|
||||||
|
|
||||||
|
is-reference@^3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.2.tgz#154747a01f45cd962404ee89d43837af2cba247c"
|
||||||
|
integrity sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==
|
||||||
|
dependencies:
|
||||||
|
"@types/estree" "*"
|
||||||
|
|
||||||
|
kleur@^4.1.5:
|
||||||
|
version "4.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
|
||||||
|
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
|
||||||
|
|
||||||
|
locate-character@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/locate-character/-/locate-character-3.0.0.tgz#0305c5b8744f61028ef5d01f444009e00779f974"
|
||||||
|
integrity sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==
|
||||||
|
|
||||||
|
magic-string@^0.30.11, magic-string@^0.30.12, magic-string@^0.30.5:
|
||||||
|
version "0.30.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.12.tgz#9eb11c9d072b9bcb4940a5b2c2e1a217e4ee1a60"
|
||||||
|
integrity sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||||
|
|
||||||
|
mri@^1.1.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
|
||||||
|
integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
|
||||||
|
|
||||||
|
mrmime@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4"
|
||||||
|
integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==
|
||||||
|
|
||||||
|
ms@^2.1.3:
|
||||||
|
version "2.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||||
|
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||||
|
|
||||||
|
nanoid@^3.3.7:
|
||||||
|
version "3.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
|
||||||
|
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
||||||
|
|
||||||
|
picocolors@^1.0.0, picocolors@^1.1.0:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||||
|
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||||
|
|
||||||
|
postcss@^8.4.43:
|
||||||
|
version "8.4.47"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365"
|
||||||
|
integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
|
||||||
|
dependencies:
|
||||||
|
nanoid "^3.3.7"
|
||||||
|
picocolors "^1.1.0"
|
||||||
|
source-map-js "^1.2.1"
|
||||||
|
|
||||||
|
readdirp@^4.0.1:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a"
|
||||||
|
integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==
|
||||||
|
|
||||||
|
rollup@^4.20.0:
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.24.0.tgz#c14a3576f20622ea6a5c9cad7caca5e6e9555d05"
|
||||||
|
integrity sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==
|
||||||
|
dependencies:
|
||||||
|
"@types/estree" "1.0.6"
|
||||||
|
optionalDependencies:
|
||||||
|
"@rollup/rollup-android-arm-eabi" "4.24.0"
|
||||||
|
"@rollup/rollup-android-arm64" "4.24.0"
|
||||||
|
"@rollup/rollup-darwin-arm64" "4.24.0"
|
||||||
|
"@rollup/rollup-darwin-x64" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-arm64-gnu" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-arm64-musl" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-powerpc64le-gnu" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-s390x-gnu" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-x64-gnu" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-x64-musl" "4.24.0"
|
||||||
|
"@rollup/rollup-win32-arm64-msvc" "4.24.0"
|
||||||
|
"@rollup/rollup-win32-ia32-msvc" "4.24.0"
|
||||||
|
"@rollup/rollup-win32-x64-msvc" "4.24.0"
|
||||||
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
sade@^1.7.4, sade@^1.8.1:
|
||||||
|
version "1.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"
|
||||||
|
integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==
|
||||||
|
dependencies:
|
||||||
|
mri "^1.1.0"
|
||||||
|
|
||||||
|
set-cookie-parser@^2.6.0:
|
||||||
|
version "2.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943"
|
||||||
|
integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==
|
||||||
|
|
||||||
|
sirv@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sirv/-/sirv-3.0.0.tgz#f8d90fc528f65dff04cb597a88609d4e8a4361ce"
|
||||||
|
integrity sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==
|
||||||
|
dependencies:
|
||||||
|
"@polka/url" "^1.0.0-next.24"
|
||||||
|
mrmime "^2.0.0"
|
||||||
|
totalist "^3.0.0"
|
||||||
|
|
||||||
|
source-map-js@^1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||||
|
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||||
|
|
||||||
|
svelte-check@^4.0.0:
|
||||||
|
version "4.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-4.0.5.tgz#5cd910c3b1d50f38159c17cc3bae127cbbb55c8d"
|
||||||
|
integrity sha512-icBTBZ3ibBaywbXUat3cK6hB5Du+Kq9Z8CRuyLmm64XIe2/r+lQcbuBx/IQgsbrC+kT2jQ0weVpZSSRIPwB6jQ==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.25"
|
||||||
|
chokidar "^4.0.1"
|
||||||
|
fdir "^6.2.0"
|
||||||
|
picocolors "^1.0.0"
|
||||||
|
sade "^1.7.4"
|
||||||
|
|
||||||
|
svelte@^5.0.0:
|
||||||
|
version "5.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/svelte/-/svelte-5.0.5.tgz#0e29f0510822f137e7fba245a2d40abe798fc91b"
|
||||||
|
integrity sha512-f4WBlP5g8W6pEoDfx741lewMlemy+LIGpEqjGPWqnHVP92wqlQXl87U5O5Bi2tkSUrO95OxOoqwU8qlqiHmFKA==
|
||||||
|
dependencies:
|
||||||
|
"@ampproject/remapping" "^2.3.0"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||||
|
"@types/estree" "^1.0.5"
|
||||||
|
acorn "^8.12.1"
|
||||||
|
acorn-typescript "^1.4.13"
|
||||||
|
aria-query "^5.3.1"
|
||||||
|
axobject-query "^4.1.0"
|
||||||
|
esm-env "^1.0.0"
|
||||||
|
esrap "^1.2.2"
|
||||||
|
is-reference "^3.0.2"
|
||||||
|
locate-character "^3.0.0"
|
||||||
|
magic-string "^0.30.11"
|
||||||
|
zimmerframe "^1.1.2"
|
||||||
|
|
||||||
|
tiny-glob@^0.2.9:
|
||||||
|
version "0.2.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2"
|
||||||
|
integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==
|
||||||
|
dependencies:
|
||||||
|
globalyzer "0.1.0"
|
||||||
|
globrex "^0.1.2"
|
||||||
|
|
||||||
|
totalist@^3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8"
|
||||||
|
integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==
|
||||||
|
|
||||||
|
typescript@^5.0.0:
|
||||||
|
version "5.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b"
|
||||||
|
integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
|
||||||
|
|
||||||
|
vite@^5.0.3:
|
||||||
|
version "5.4.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.10.tgz#d358a7bd8beda6cf0f3b7a450a8c7693a4f80c18"
|
||||||
|
integrity sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==
|
||||||
|
dependencies:
|
||||||
|
esbuild "^0.21.3"
|
||||||
|
postcss "^8.4.43"
|
||||||
|
rollup "^4.20.0"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.3.3"
|
||||||
|
|
||||||
|
vitefu@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-1.0.3.tgz#0467c75ee2be951c35246605b7fdbdbfd03b65d1"
|
||||||
|
integrity sha512-iKKfOMBHob2WxEJbqbJjHAkmYgvFDPhuqrO82om83S8RLk+17FtyMBfcyeH8GqD0ihShtkMW/zzJgiA51hCNCQ==
|
||||||
|
|
||||||
|
zimmerframe@^1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/zimmerframe/-/zimmerframe-1.1.2.tgz#5b75f1fa83b07ae2a428d51e50f58e2ae6855e5e"
|
||||||
|
integrity sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==
|
||||||
BIN
motor-control/motor-driver-breakout-v2/motor-driver-breakout-v2-backups/motor-driver-breakout-v2-2024-10-27_184032.zip
(Stored with Git LFS)
Normal file
BIN
motor-control/motor-driver-breakout-v2/motor-driver-breakout-v2-backups/motor-driver-breakout-v2-2024-10-27_184032.zip
(Stored with Git LFS)
Normal file
Binary file not shown.
Reference in New Issue
Block a user