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 { 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 { 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 { 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): AsyncGenerator { for (const c of chunks) yield c; } export async function readAll(stream: ReadableStream): Promise { 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; }