// Checks the browser writers against SPEC §5, *Candidate capabilities*: `pack` // and `packMulti` refuse a manifest that names a candidate in any place an // envelope can name one (the WAI1 capability or fallback; a WAI2 component in // either role), and still write an envelope that names none. The candidates are // read from the committed export wai/registry/capabilities.json, so a newly // registered candidate is checked without editing this script. They also refuse a // manifest that names a member twice in one object (SPEC §2), which no reader reads, // and one longer than SPEC §2's reader limit, while writing a long one within it. // // node verify_pack.mjs [path/to/wai_web.js] [path/to/capabilities.json] // The package path is relative to this directory (as in the other verify scripts); // the export path is relative to the working directory (default: this tree's). // Exit 0 iff every check holds. import { readFileSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; const here = dirname(fileURLToPath(import.meta.url)); const pkg = resolve(here, process.argv[2] ?? './pkg-node/wai_web.js'); const exportPath = process.argv[3] ? resolve(process.argv[3]) : resolve(here, '../../registry/capabilities.json'); const wai = await import(pathToFileURL(pkg).href); const candidates = JSON.parse(readFileSync(exportPath, 'utf8')).capabilities .filter((c) => c.status === 'candidate') .map((c) => c.capability); let pass = true; const fail = (msg) => { pass = false; console.log(`FAIL ${msg}`); }; const bytes = new Uint8Array([1, 2, 3]); const v1 = (capability, fallback) => JSON.stringify({ wai: '1.0', media: 'binary', intent: 'improve', model_requirement: fallback ? { capability, fallback } : { capability }, conditioning: { kind: 'bytes' }, target: null, }); const v2 = (components) => JSON.stringify({ wai: '1.1', media: 'video', intent: 'improve', target: null, renditions: components.map(([capability, base]) => base ? { capability, kind: 'bytes', role: 'companion', companion_of: base } : { capability, kind: 'bytes' }), }); function refuses(what, write) { try { write(); fail(`${what}: written, but a candidate MUST NOT be emitted`); } catch (e) { if (!String(e.message ?? e).includes('candidate')) fail(`${what}: refused for another reason: ${e.message ?? e}`); } } if (candidates.length === 0) fail(`no candidate in ${exportPath}`); for (const cand of candidates) { refuses(`pack, ${cand} as the capability`, () => wai.pack(v1(cand), bytes)); refuses(`pack, ${cand} as the fallback`, () => wai.pack(v1('wai.image.png', cand), bytes)); refuses(`packMulti, ${cand} as a rendition`, () => wai.packMulti(v2([[cand]]), [bytes])); refuses(`packMulti, ${cand} as a companion`, () => wai.packMulti(v2([['wai.video.av1'], [cand, 'wai.video.av1']]), [bytes, bytes])); } // A manifest that names a member twice in one object is refused (SPEC §2): every // reader refuses it, so a writer never emits it. function refusesTwice(what, write) { try { write(); fail(`${what}: written, but no reader reads it`); } catch (e) { if (!String(e.message ?? e).includes('twice')) fail(`${what}: refused for another reason: ${e.message ?? e}`); } } const v1Text = v1('wai.image.png'); refusesTwice('pack, a member twice at the top', () => wai.pack(v1Text.slice(0, -1) + ',"zz":1,"zz":2}', bytes)); refusesTwice('pack, a member twice in target', () => wai.pack(v1Text.replace('"target":null', '"target":{"w":1,"w":2}'), bytes)); refusesTwice('pack, a member twice in model_requirement', () => wai.pack(v1Text.replace('"capability":"wai.image.png"', '"capability":"wai.image.png","q":1,"q":2'), bytes)); refusesTwice('packMulti, a member twice in an entry', () => wai.packMulti(v2([['wai.video.av1']]).replace('"kind":"bytes"', '"kind":"bytes","zz":1,"zz":2'), [bytes])); // SPEC §2, *Reader limits*: a manifest of 3 MiB is written as given; one of 2^27 + 1 // bytes, one byte past the limit, is refused (`manifest_too_large`), as every // reference reader refuses it. const MAX = 1 << 27; const LONG = 3 << 20; // `text` with a member `pad` added so that it is exactly `len` bytes (all ASCII). const padTo = (text, len) => text.slice(0, -1) + ',"pad":"' + 'x'.repeat(len - text.length - 9) + '"}'; function refusesTooLarge(what, write) { try { write(); fail(`${what}: written, but it is past the reader limit`); } catch (e) { if (!String(e.message ?? e).includes('manifest_too_large')) fail(`${what}: refused for another reason: ${e.message ?? e}`); } } const v2Text = v2([['wai.video.av1']]); try { for (const [what, env] of [['pack', wai.pack(padTo(v1Text, LONG), bytes)], ['packMulti', wai.packMulti(padTo(v2Text, LONG), [bytes])]]) { if (new DataView(env.buffer, env.byteOffset, env.byteLength).getUint32(4, true) !== LONG) fail(`${what}: a 3 MiB manifest was not written as given`); } } catch (e) { fail(`a manifest within the reader limit was refused: ${e.message ?? e}`); } refusesTooLarge('pack, a manifest one byte past the limit', () => wai.pack(padTo(v1Text, MAX + 1), bytes)); refusesTooLarge('packMulti, a manifest one byte past the limit', () => wai.packMulti(padTo(v2Text, MAX + 1), [bytes])); // An envelope that names no candidate is still written, and parses back. try { const one = wai.pack(v1('wai.image.png', 'wai.image.jpeg'), bytes); if (wai.detectVersion(one) !== 1) fail('pack: the written envelope is not WAI1'); const two = wai.packMulti(v2([['wai.video.av1'], ['wai.meta.hdr10plus', 'wai.video.av1']]), [bytes, bytes]); if (wai.detectVersion(two) !== 2) fail('packMulti: the written envelope is not WAI2'); } catch (e) { fail(`an envelope with no candidate was refused: ${e.message ?? e}`); } console.log(pass ? `PASS pack and packMulti refuse every candidate (${candidates.join(', ')}), a member named twice and a manifest past the reader limit` : '\n✗ a browser writer emits what no reader may take'); process.exit(pass ? 0 : 1);