91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
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;
|
|
}
|