chore(repo): initialize WebDrop monorepo
Signed-off-by: JustMe <mr.toing@outlook.com>
This commit is contained in:
commit
53768a195d
39 files changed
+9063
No files matched your search
@@ -0,0 +1,90 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { fromHex, toBase64Url, toHex, type Bytes } from '../src/bytes.js';
|
||||
import { ChannelSession, collectFrames, type EncodedFrame, type ItemPlan } from '../src/index.js';
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export const vectors: any = JSON.parse(readFileSync(new URL('../../../test-vectors/vectors.json', import.meta.url), 'utf8'));
|
||||
|
||||
export const h = (s: string): Bytes => fromHex(s);
|
||||
export { toHex };
|
||||
|
||||
/** 向量里的固定输入。 */
|
||||
export const inputs = {
|
||||
channelId: h(vectors.inputs.channel_id),
|
||||
channelIdB64: vectors.inputs.channel_id_b64 as string,
|
||||
k: h(vectors.inputs.k),
|
||||
a: h(vectors.inputs.a),
|
||||
w: h(vectors.inputs.w),
|
||||
endpointSecret: h(vectors.inputs.endpoint_secret),
|
||||
itemA: h(vectors.inputs.item_a),
|
||||
itemB: h(vectors.inputs.item_b),
|
||||
itemC: h(vectors.inputs.item_c),
|
||||
};
|
||||
|
||||
export const ORIGIN = 'https://app.example.test';
|
||||
|
||||
/** 用向量里的 channel_id / K(/A) 打开一个会话,终端密钥也取向量值。 */
|
||||
export function openVectorSession(opts: { owner?: boolean } = {}): Promise<ChannelSession> {
|
||||
const parts = [`k=${toBase64Url(inputs.k)}`];
|
||||
if (opts.owner) parts.push(`a=${toBase64Url(inputs.a)}`);
|
||||
return ChannelSession.open(`${ORIGIN}/c/${inputs.channelIdB64}#${parts.join('&')}`, { endpointSecret: inputs.endpointSecret });
|
||||
}
|
||||
|
||||
export async function framesOf(plan: ItemPlan): Promise<EncodedFrame[]> {
|
||||
return collectFrames(plan);
|
||||
}
|
||||
|
||||
/** 随机但可复现的字节(线性同余),测试里需要「看起来随机」的内容时用。 */
|
||||
export function pseudoRandom(length: number, seed = 1): Bytes {
|
||||
const out = new Uint8Array(length);
|
||||
let x = seed >>> 0 || 1;
|
||||
for (let i = 0; i < length; i++) {
|
||||
x = (Math.imul(x, 1664525) + 1013904223) >>> 0;
|
||||
out[i] = x >>> 24;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 把字节流在随机位置切成小块,模拟网络分片。 */
|
||||
export function* splitAt(bytes: Uint8Array, seed = 7): Generator<Uint8Array> {
|
||||
let x = seed >>> 0 || 1;
|
||||
let offset = 0;
|
||||
while (offset < bytes.length) {
|
||||
x = (Math.imul(x, 1664525) + 1013904223) >>> 0;
|
||||
const size = 1 + (x >>> 8) % 4096;
|
||||
yield bytes.subarray(offset, offset + size);
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
export async function* asyncChunks(chunks: Iterable<Uint8Array>): AsyncGenerator<Uint8Array> {
|
||||
for (const c of chunks) yield c;
|
||||
}
|
||||
|
||||
export async function readAll(stream: ReadableStream<Uint8Array>): Promise<Bytes> {
|
||||
const parts: Uint8Array[] = [];
|
||||
const reader = stream.getReader();
|
||||
for (;;) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
parts.push(value);
|
||||
}
|
||||
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
||||
let o = 0;
|
||||
for (const p of parts) {
|
||||
out.set(p, o);
|
||||
o += p.length;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function concatAll(parts: Uint8Array[]): Bytes {
|
||||
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
||||
let o = 0;
|
||||
for (const p of parts) {
|
||||
out.set(p, o);
|
||||
o += p.length;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Reference in new issue
Block a user