@qntx/nostr-wasm 0.14.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.mts +2 -2
- package/dist/browser.mjs +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{instance-6y1G4BjC.mjs → integrity-BfNJJNcA.mjs} +40 -6
- package/dist/integrity-BfNJJNcA.mjs.map +1 -0
- package/dist/{adapter-DMQvHVO_.d.mts → integrity-BmoamQip.d.mts} +7 -5
- package/dist/nostr_wasm.wasm +0 -0
- package/package.json +8 -7
- package/dist/instance-6y1G4BjC.mjs.map +0 -1
package/dist/browser.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as
|
|
1
|
+
import { i as NostrWasm, n as WasmPoisonedError, r as LoadNostrWasmOptions, t as NOSTR_WASM_INTEGRITY } from "./integrity-BmoamQip.mjs";
|
|
2
2
|
//#region src/load.browser.d.ts
|
|
3
3
|
/** Instantiate once. Repeats reuse the same module. Failure throws; no noble fallback. */
|
|
4
4
|
export declare function loadNostrWasm(opts?: LoadNostrWasmOptions): Promise<NostrWasm>;
|
|
5
5
|
//#endregion
|
|
6
|
-
export { type LoadNostrWasmOptions, type NostrWasm, WasmPoisonedError };
|
|
6
|
+
export { type LoadNostrWasmOptions, NOSTR_WASM_INTEGRITY, type NostrWasm, WasmPoisonedError };
|
|
7
7
|
//# sourceMappingURL=browser.d.mts.map
|
package/dist/browser.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as WasmPoisonedError, i as isWasmBytes, n as createNostrWasmLoader, r as fetchWasmUrl, t as NOSTR_WASM_INTEGRITY } from "./integrity-BfNJJNcA.mjs";
|
|
2
2
|
import { CryptoError } from "@qntx/nostr/core";
|
|
3
3
|
//#region src/load.browser.ts
|
|
4
4
|
async function wasmBytes(opts) {
|
|
@@ -14,6 +14,6 @@ async function loadNostrWasm(opts) {
|
|
|
14
14
|
return loader.loadNostrWasm(opts);
|
|
15
15
|
}
|
|
16
16
|
//#endregion
|
|
17
|
-
export { WasmPoisonedError, loadNostrWasm };
|
|
17
|
+
export { NOSTR_WASM_INTEGRITY, WasmPoisonedError, loadNostrWasm };
|
|
18
18
|
|
|
19
19
|
//# sourceMappingURL=browser.mjs.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as
|
|
1
|
+
import { i as NostrWasm, n as WasmPoisonedError, r as LoadNostrWasmOptions, t as NOSTR_WASM_INTEGRITY } from "./integrity-BmoamQip.mjs";
|
|
2
2
|
//#region src/load.d.ts
|
|
3
3
|
/** Instantiate once. Repeats reuse the same module. Failure throws; no noble fallback. */
|
|
4
4
|
export declare function loadNostrWasm(opts?: LoadNostrWasmOptions): Promise<NostrWasm>;
|
|
5
5
|
//#endregion
|
|
6
|
-
export { type LoadNostrWasmOptions, type NostrWasm, WasmPoisonedError };
|
|
6
|
+
export { type LoadNostrWasmOptions, NOSTR_WASM_INTEGRITY, type NostrWasm, WasmPoisonedError };
|
|
7
7
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as WasmPoisonedError, i as isWasmBytes, n as createNostrWasmLoader, r as fetchWasmUrl, t as NOSTR_WASM_INTEGRITY } from "./integrity-BfNJJNcA.mjs";
|
|
2
2
|
//#region src/load.ts
|
|
3
3
|
async function defaultWasmHref() {
|
|
4
4
|
return (await import("./nostr_wasm-BkgBL7qN.mjs")).default;
|
|
@@ -22,6 +22,6 @@ async function loadNostrWasm(opts) {
|
|
|
22
22
|
return loader.loadNostrWasm(opts);
|
|
23
23
|
}
|
|
24
24
|
//#endregion
|
|
25
|
-
export { WasmPoisonedError, loadNostrWasm };
|
|
25
|
+
export { NOSTR_WASM_INTEGRITY, WasmPoisonedError, loadNostrWasm };
|
|
26
26
|
|
|
27
27
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -21,7 +21,8 @@ function asExports(raw) {
|
|
|
21
21
|
nostr_verify: requireFn(raw["nostr_verify"], "nostr_verify"),
|
|
22
22
|
nostr_verify_serialized: requireFn(raw["nostr_verify_serialized"], "nostr_verify_serialized"),
|
|
23
23
|
nostr_sign: requireFn(raw["nostr_sign"], "nostr_sign"),
|
|
24
|
-
nostr_public_key: requireFn(raw["nostr_public_key"], "nostr_public_key")
|
|
24
|
+
nostr_public_key: requireFn(raw["nostr_public_key"], "nostr_public_key"),
|
|
25
|
+
nostr_ecdh_x: requireFn(raw["nostr_ecdh_x"], "nostr_ecdh_x")
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
@@ -96,13 +97,25 @@ async function instantiateCryptoWasm(bytes) {
|
|
|
96
97
|
if (imports.length > 0) throw new TypeError(`nostr_wasm module must not import anything, got ${imports.length} imports`);
|
|
97
98
|
const instance = await WebAssembly.instantiate(module, {});
|
|
98
99
|
const version = requireFn(instance.exports["nostr_abi_version"], "nostr_abi_version")();
|
|
99
|
-
if (version !==
|
|
100
|
+
if (version !== 4) throw new CryptoError(`unsupported nostr_wasm ABI version ${String(version)}, expected 4`);
|
|
100
101
|
const exports = asExports(instance.exports);
|
|
101
102
|
const { ptr, view } = scratch(exports, SEED_LEN);
|
|
102
103
|
view.set(randomBytes(SEED_LEN), 0);
|
|
103
104
|
if (exports.nostr_init(ptr) !== 0) throw new CryptoError("nostr_init failed to randomize the wasm context");
|
|
104
105
|
return exports;
|
|
105
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Returns the 32-byte x coordinate of the ECDH shared point, or an empty array on failure (invalid
|
|
109
|
+
* input). The wasm module zeroes the scratch copy of `seckey` before returning.
|
|
110
|
+
*/
|
|
111
|
+
function wasmEcdhX(exports, seckey, publicKeyX) {
|
|
112
|
+
const outOffset = 64;
|
|
113
|
+
const { ptr, view } = scratch(exports, 96);
|
|
114
|
+
view.set(seckey, 0);
|
|
115
|
+
view.set(publicKeyX, SECKEY_LEN);
|
|
116
|
+
if (exports.nostr_ecdh_x(ptr, ptr + SECKEY_LEN, ptr + outOffset) !== 0) return /* @__PURE__ */ new Uint8Array(0);
|
|
117
|
+
return takeOut(exports, ptr + outOffset, 32);
|
|
118
|
+
}
|
|
106
119
|
//#endregion
|
|
107
120
|
//#region src/adapter.ts
|
|
108
121
|
/** Wasm instance aborted; later calls on that instance fail. */
|
|
@@ -131,7 +144,7 @@ function runPoisoned(poison, fn) {
|
|
|
131
144
|
* `WasmPoisonedError`; any other backend error verifies `false` (the event is marked failed).
|
|
132
145
|
*/
|
|
133
146
|
function createWasmEventVerifier(verifySerialized, poison) {
|
|
134
|
-
const
|
|
147
|
+
const raw = (serializedUtf8, id, pubkey, sig) => {
|
|
135
148
|
try {
|
|
136
149
|
return runPoisoned(poison, () => verifySerialized(serializedUtf8, id, pubkey, sig));
|
|
137
150
|
} catch (error) {
|
|
@@ -139,7 +152,18 @@ function createWasmEventVerifier(verifySerialized, poison) {
|
|
|
139
152
|
return false;
|
|
140
153
|
}
|
|
141
154
|
};
|
|
142
|
-
return createEventVerifier(
|
|
155
|
+
return createEventVerifier({
|
|
156
|
+
verifySerialized: raw,
|
|
157
|
+
publicKey: () => {
|
|
158
|
+
throw new NostrError("wasm verifier backend: publicKey is unsupported");
|
|
159
|
+
},
|
|
160
|
+
sign: () => {
|
|
161
|
+
throw new NostrError("wasm verifier backend: sign is unsupported");
|
|
162
|
+
},
|
|
163
|
+
ecdhX: () => {
|
|
164
|
+
throw new NostrError("wasm verifier backend: ecdhX is unsupported");
|
|
165
|
+
}
|
|
166
|
+
});
|
|
143
167
|
}
|
|
144
168
|
//#endregion
|
|
145
169
|
//#region src/instance.ts
|
|
@@ -174,6 +198,13 @@ function bindExports(exports) {
|
|
|
174
198
|
const pk = wasmPublicKey(exports, seckey);
|
|
175
199
|
if (pk.length !== 32) throw new CryptoError("wasm publicKey failed");
|
|
176
200
|
return pk;
|
|
201
|
+
}),
|
|
202
|
+
ecdhX: (seckey, publicKeyX) => runPoisoned(poison, () => {
|
|
203
|
+
requireByteLength(seckey, 32, "secret key");
|
|
204
|
+
requireByteLength(publicKeyX, 32, "public key");
|
|
205
|
+
const x = wasmEcdhX(exports, seckey, publicKeyX);
|
|
206
|
+
if (x.length !== 32) throw new CryptoError("wasm ecdhX failed");
|
|
207
|
+
return x;
|
|
177
208
|
})
|
|
178
209
|
};
|
|
179
210
|
}
|
|
@@ -204,6 +235,9 @@ function createNostrWasmLoader(wasmBytes) {
|
|
|
204
235
|
};
|
|
205
236
|
}
|
|
206
237
|
//#endregion
|
|
207
|
-
|
|
238
|
+
//#region src/integrity.ts
|
|
239
|
+
const NOSTR_WASM_INTEGRITY = "sha256-p9Cj0Klb81RjKSm5EUX28CKmoXHhZqFRrdks6Pq5ejQ=";
|
|
240
|
+
//#endregion
|
|
241
|
+
export { WasmPoisonedError as a, isWasmBytes as i, createNostrWasmLoader as n, fetchWasmUrl as r, NOSTR_WASM_INTEGRITY as t };
|
|
208
242
|
|
|
209
|
-
//# sourceMappingURL=
|
|
243
|
+
//# sourceMappingURL=integrity-BfNJJNcA.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity-BfNJJNcA.mjs","names":[],"sources":["../src/abi.ts","../src/adapter.ts","../src/instance.ts","../src/integrity.ts"],"sourcesContent":["import { CryptoError, randomBytes } from \"@qntx/nostr/core\";\n\n/**\n * Byte-ABI version 3 surface exported by `nostr_wasm.wasm` (`packages/nostr-wasm/c/nostr_wasm.c`\n * over the shared `nostr_core.c` in `packages/nostr-native/cpp`). All inputs are caller-written\n * regions of a single scratch buffer; status codes are 0 ok/verified, 1 verification failed, 2\n * invalid input. `nostr_init` must succeed once with a 32-byte seed before the crypto ops run.\n */\nexport type CryptoWasmExports = {\n memory: WebAssembly.Memory;\n nostr_abi_version: () => number;\n nostr_init: (seed: number) => number;\n nostr_buffer: (len: number) => number;\n nostr_verify: (id: number, pubkey: number, sig: number) => number;\n nostr_verify_serialized: (\n ser: number,\n serLen: number,\n id: number,\n pubkey: number,\n sig: number,\n ) => number;\n nostr_sign: (id: number, seckey: number, aux: number, outSig: number) => number;\n nostr_public_key: (seckey: number, outPubkey: number) => number;\n nostr_ecdh_x: (seckey: number, pubkeyX: number, out32: number) => number;\n};\n\nexport const NOSTR_ABI_VERSION = 4;\n\nconst SEED_LEN = 32;\n\nconst ID_LEN = 32;\nconst PUBKEY_LEN = 32;\nconst SIG_LEN = 64;\nconst SECKEY_LEN = 32;\nconst AUX_LEN = 32;\n\nfunction requireMemory(value: WebAssembly.ExportValue | undefined): WebAssembly.Memory {\n if (!(value instanceof WebAssembly.Memory)) {\n throw new TypeError(\"wasm missing memory export\");\n }\n return value;\n}\n\n// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- T binds the declared ABI signature at each call site\nfunction requireFn<T extends (...args: number[]) => unknown>(\n value: WebAssembly.ExportValue | undefined,\n name: string,\n): T {\n if (typeof value !== \"function\") {\n throw new TypeError(`wasm missing ${name} export`);\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- wasm exports are opaque; each name is bound to the signature the ABI contract requires\n return value as T;\n}\n\nfunction asExports(raw: WebAssembly.Exports): CryptoWasmExports {\n return {\n memory: requireMemory(raw[\"memory\"]),\n nostr_abi_version: requireFn(raw[\"nostr_abi_version\"], \"nostr_abi_version\"),\n nostr_init: requireFn(raw[\"nostr_init\"], \"nostr_init\"),\n nostr_buffer: requireFn(raw[\"nostr_buffer\"], \"nostr_buffer\"),\n nostr_verify: requireFn(raw[\"nostr_verify\"], \"nostr_verify\"),\n nostr_verify_serialized: requireFn(raw[\"nostr_verify_serialized\"], \"nostr_verify_serialized\"),\n nostr_sign: requireFn(raw[\"nostr_sign\"], \"nostr_sign\"),\n nostr_public_key: requireFn(raw[\"nostr_public_key\"], \"nostr_public_key\"),\n nostr_ecdh_x: requireFn(raw[\"nostr_ecdh_x\"], \"nostr_ecdh_x\"),\n };\n}\n\n/**\n * Reserve `total` bytes in the wasm scratch region and return a fresh view. `nostr_buffer` returns\n * NULL when `memory.grow` fails; `__heap_base` is never 0, so 0 is unambiguously an allocation\n * failure — throw instead of writing at address 0. The view is taken after `nostr_buffer` ran, so a\n * growth inside it cannot leave a detached view in the caller's hands.\n */\nfunction scratch(exports: CryptoWasmExports, total: number): { ptr: number; view: Uint8Array } {\n const ptr = exports.nostr_buffer(total) >>> 0;\n if (ptr === 0) {\n throw new CryptoError(\"wasm scratch allocation failed\");\n }\n return { ptr, view: new Uint8Array(exports.memory.buffer, ptr, total) };\n}\n\n/** Copies `len` scratch bytes out; the view is re-acquired after the call. */\nfunction takeOut(exports: CryptoWasmExports, ptr: number, len: number): Uint8Array {\n const out = new Uint8Array(len);\n out.set(new Uint8Array(exports.memory.buffer, ptr, len));\n return out;\n}\n\nexport function wasmVerify(\n exports: CryptoWasmExports,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n): boolean {\n if (id.length !== ID_LEN || pubkey.length !== PUBKEY_LEN || sig.length !== SIG_LEN) {\n return false; // fixed-size ABI: a wrong-length input simply cannot verify\n }\n const { ptr, view } = scratch(exports, ID_LEN + PUBKEY_LEN + SIG_LEN);\n view.set(id, 0);\n view.set(pubkey, ID_LEN);\n view.set(sig, ID_LEN + PUBKEY_LEN);\n return exports.nostr_verify(ptr, ptr + ID_LEN, ptr + ID_LEN + PUBKEY_LEN) === 0;\n}\n\nexport function wasmVerifySerialized(\n exports: CryptoWasmExports,\n serialized: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n): boolean {\n if (id.length !== ID_LEN || pubkey.length !== PUBKEY_LEN || sig.length !== SIG_LEN) {\n return false;\n }\n const serLen = serialized.length;\n const { ptr, view } = scratch(exports, serLen + ID_LEN + PUBKEY_LEN + SIG_LEN);\n view.set(serialized, 0);\n view.set(id, serLen);\n view.set(pubkey, serLen + ID_LEN);\n view.set(sig, serLen + ID_LEN + PUBKEY_LEN);\n return (\n exports.nostr_verify_serialized(\n ptr,\n serLen,\n ptr + serLen,\n ptr + serLen + ID_LEN,\n ptr + serLen + ID_LEN + PUBKEY_LEN,\n ) === 0\n );\n}\n\n/**\n * Returns the 64-byte signature, or an empty array on failure (invalid input). The wasm module\n * zeroes the scratch copy of `seckey` before returning.\n */\nexport function wasmSign(\n exports: CryptoWasmExports,\n id: Uint8Array,\n seckey: Uint8Array,\n aux: Uint8Array,\n): Uint8Array {\n const outOffset = ID_LEN + SECKEY_LEN + AUX_LEN;\n const { ptr, view } = scratch(exports, outOffset + SIG_LEN);\n view.set(id, 0);\n view.set(seckey, ID_LEN);\n view.set(aux, ID_LEN + SECKEY_LEN);\n const status = exports.nostr_sign(ptr, ptr + ID_LEN, ptr + ID_LEN + SECKEY_LEN, ptr + outOffset);\n if (status !== 0) {\n return new Uint8Array(0);\n }\n return takeOut(exports, ptr + outOffset, SIG_LEN);\n}\n\n/**\n * Returns the 32-byte x-only public key, or an empty array on failure. The wasm module zeroes the\n * scratch copy of `seckey` before returning.\n */\nexport function wasmPublicKey(exports: CryptoWasmExports, seckey: Uint8Array): Uint8Array {\n const { ptr, view } = scratch(exports, SECKEY_LEN + PUBKEY_LEN);\n view.set(seckey, 0);\n const status = exports.nostr_public_key(ptr, ptr + SECKEY_LEN);\n if (status !== 0) {\n return new Uint8Array(0);\n }\n return takeOut(exports, ptr + SECKEY_LEN, PUBKEY_LEN);\n}\n\n/**\n * Compile and instantiate `nostr_wasm.wasm`. The module must declare no imports and must implement\n * ABI version {@link NOSTR_ABI_VERSION} — a mismatch means the artifact predates or postdates this\n * loader and instantiation fails.\n */\nexport async function instantiateCryptoWasm(\n bytes: ArrayBuffer | ArrayBufferView,\n): Promise<CryptoWasmExports> {\n const module = await WebAssembly.compile(bytes);\n const imports = WebAssembly.Module.imports(module);\n if (imports.length > 0) {\n throw new TypeError(\n `nostr_wasm module must not import anything, got ${imports.length} imports`,\n );\n }\n const instance = await WebAssembly.instantiate(module, {});\n const version = requireFn(instance.exports[\"nostr_abi_version\"], \"nostr_abi_version\")();\n if (version !== NOSTR_ABI_VERSION) {\n throw new CryptoError(\n `unsupported nostr_wasm ABI version ${String(version)}, expected ${NOSTR_ABI_VERSION}`,\n );\n }\n const exports = asExports(instance.exports);\n // Seed the context: 32 bytes of fresh randomness into the scratch buffer; the C side\n // randomizes the context and wipes the seed region on every path.\n const { ptr, view } = scratch(exports, SEED_LEN);\n view.set(randomBytes(SEED_LEN), 0);\n if (exports.nostr_init(ptr) !== 0) {\n throw new CryptoError(\"nostr_init failed to randomize the wasm context\");\n }\n return exports;\n}\n\n/**\n * Returns the 32-byte x coordinate of the ECDH shared point, or an empty array on failure (invalid\n * input). The wasm module zeroes the scratch copy of `seckey` before returning.\n */\nexport function wasmEcdhX(\n exports: CryptoWasmExports,\n seckey: Uint8Array,\n publicKeyX: Uint8Array,\n): Uint8Array {\n const outOffset = SECKEY_LEN + PUBKEY_LEN;\n const { ptr, view } = scratch(exports, outOffset + 32);\n view.set(seckey, 0);\n view.set(publicKeyX, SECKEY_LEN);\n const status = exports.nostr_ecdh_x(ptr, ptr + SECKEY_LEN, ptr + outOffset);\n if (status !== 0) {\n return new Uint8Array(0);\n }\n return takeOut(exports, ptr + outOffset, 32);\n}\n","import { NostrError, createEventVerifier } from \"@qntx/nostr/core\";\nimport type { EventVerifier, NostrBackend } from \"@qntx/nostr/core\";\n\n/** Wasm instance aborted; later calls on that instance fail. */\nexport class WasmPoisonedError extends NostrError {\n override name = \"WasmPoisonedError\";\n}\n\n/**\n * Run `fn` unless the instance is already poisoned; a `WebAssembly.RuntimeError` aborts the\n * instance, so it is converted to {@link WasmPoisonedError} and rethrown on every later call.\n */\nexport function runPoisoned<T>(poison: { error?: Error }, fn: () => T): T {\n if (poison.error !== undefined) {\n throw poison.error;\n }\n try {\n return fn();\n } catch (error) {\n if (error instanceof WebAssembly.RuntimeError) {\n poison.error = new WasmPoisonedError(\"wasm instance aborted\", {\n cause: error,\n });\n throw poison.error;\n }\n throw error;\n }\n}\n\n/**\n * Build the WASM event verifier on {@link createEventVerifier}. A `WebAssembly.RuntimeError` from\n * the module means the instance aborted — it poisons every later call with the same\n * `WasmPoisonedError`; any other backend error verifies `false` (the event is marked failed).\n */\nexport function createWasmEventVerifier(\n verifySerialized: NostrBackend[\"verifySerialized\"],\n poison: { error?: Error },\n): EventVerifier {\n const raw: NostrBackend[\"verifySerialized\"] = (serializedUtf8, id, pubkey, sig) => {\n try {\n return runPoisoned(poison, () => verifySerialized(serializedUtf8, id, pubkey, sig));\n } catch (error) {\n if (error instanceof WasmPoisonedError) {\n throw error;\n }\n return false;\n }\n };\n const backend: NostrBackend = {\n verifySerialized: raw,\n // createEventVerifier only calls verifySerialized; the rest of the shape is\n // required by NostrBackend and never invoked.\n publicKey: () => {\n throw new NostrError(\"wasm verifier backend: publicKey is unsupported\");\n },\n sign: () => {\n throw new NostrError(\"wasm verifier backend: sign is unsupported\");\n },\n ecdhX: () => {\n throw new NostrError(\"wasm verifier backend: ecdhX is unsupported\");\n },\n };\n return createEventVerifier(backend);\n}\n","import { CryptoError } from \"@qntx/nostr/core\";\nimport type { NostrBackend, EventVerifier } from \"@qntx/nostr/core\";\n\nimport {\n instantiateCryptoWasm,\n wasmEcdhX,\n wasmPublicKey,\n wasmSign,\n wasmVerify,\n wasmVerifySerialized,\n} from \"./abi.ts\";\nimport type { CryptoWasmExports } from \"./abi.ts\";\nimport { createWasmEventVerifier, runPoisoned } from \"./adapter.ts\";\n\nexport type LoadNostrWasmOptions = {\n /** Bytes, or a URL whose bytes will be read by the platform loader. */\n module?: ArrayBuffer | ArrayBufferView | URL | undefined;\n};\n\nexport type NostrWasm = NostrBackend & {\n verify: (id: Uint8Array, pubkey: Uint8Array, sig: Uint8Array) => boolean;\n verifyEvent: EventVerifier;\n};\n\nexport function isWasmBytes(value: unknown): value is ArrayBuffer | ArrayBufferView {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\nexport async function fetchWasmUrl(url: URL): Promise<Uint8Array> {\n const res = await fetch(url);\n if (!res.ok) {\n throw new CryptoError(`failed to fetch wasm: ${res.status} ${url.href}`);\n }\n return new Uint8Array(await res.arrayBuffer());\n}\n\nfunction requireByteLength(bytes: Uint8Array, expected: number, label: string): void {\n if (bytes.length !== expected) {\n throw new CryptoError(`invalid ${label} length: expected ${expected}, got ${bytes.length}`);\n }\n}\n\nfunction bindExports(exports: CryptoWasmExports): NostrWasm {\n const poison: { error?: Error } = {};\n const rawSerialized = (\n serializedUtf8: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n ): boolean => wasmVerifySerialized(exports, serializedUtf8, id, pubkey, sig);\n return {\n verify: (id, pubkey, sig) => runPoisoned(poison, () => wasmVerify(exports, id, pubkey, sig)),\n verifySerialized: (serializedUtf8, id, pubkey, sig) =>\n runPoisoned(poison, () => rawSerialized(serializedUtf8, id, pubkey, sig)),\n verifyEvent: createWasmEventVerifier(rawSerialized, poison),\n sign: (id, seckey, aux) =>\n runPoisoned(poison, () => {\n requireByteLength(id, 32, \"id\");\n requireByteLength(seckey, 32, \"secret key\");\n requireByteLength(aux, 32, \"aux\");\n const sig = wasmSign(exports, id, seckey, aux);\n if (sig.length !== 64) {\n throw new CryptoError(\"wasm sign failed\");\n }\n return sig;\n }),\n publicKey: (seckey) =>\n runPoisoned(poison, () => {\n requireByteLength(seckey, 32, \"secret key\");\n const pk = wasmPublicKey(exports, seckey);\n if (pk.length !== 32) {\n throw new CryptoError(\"wasm publicKey failed\");\n }\n return pk;\n }),\n ecdhX: (seckey, publicKeyX) =>\n runPoisoned(poison, () => {\n requireByteLength(seckey, 32, \"secret key\");\n requireByteLength(publicKeyX, 32, \"public key\");\n const x = wasmEcdhX(exports, seckey, publicKeyX);\n if (x.length !== 32) {\n throw new CryptoError(\"wasm ecdhX failed\");\n }\n return x;\n }),\n };\n}\n\n/** Builds a loader that interns a single instance per module slot. */\nexport function createNostrWasmLoader(\n wasmBytes: (opts?: LoadNostrWasmOptions) => Promise<ArrayBuffer | ArrayBufferView>,\n): {\n loadNostrWasm: (opts?: LoadNostrWasmOptions) => Promise<NostrWasm>;\n resetNostrWasm: () => void;\n} {\n let interned: Promise<NostrWasm> | undefined;\n\n async function instantiate(opts?: LoadNostrWasmOptions): Promise<NostrWasm> {\n const bytes = await wasmBytes(opts);\n const exports = await instantiateCryptoWasm(bytes);\n return bindExports(exports);\n }\n\n /** Instantiate once. Repeats reuse the same module. Failure throws; no noble fallback. */\n async function loadNostrWasm(opts?: LoadNostrWasmOptions): Promise<NostrWasm> {\n if (interned) {\n return interned;\n }\n const pending = instantiate(opts);\n interned = pending;\n try {\n return await pending;\n } catch (error) {\n if (interned === pending) {\n interned = undefined;\n }\n throw error;\n }\n }\n\n function resetNostrWasm(): void {\n interned = undefined;\n }\n\n return { loadNostrWasm, resetNostrWasm };\n}\n","/*\n * Subresource Integrity hash (`sha256-<base64>`) of the shipped `nostr_wasm.wasm`, regenerated\n * whenever build:wasm runs. Pass it to `fetch(url, { integrity })` so the browser refuses to\n * execute a tampered module:\n *\n * const res = await fetch(wasmUrl, { integrity: NOSTR_WASM_INTEGRITY });\n * const wasm = await loadNostrWasm({ module: await res.arrayBuffer() });\n *\n * The constant only guards fetch-time delivery — callers that want SRI enforcement must fetch\n * with `integrity` and pass `module`.\n */\nexport const NOSTR_WASM_INTEGRITY = \"sha256-p9Cj0Klb81RjKSm5EUX28CKmoXHhZqFRrdks6Pq5ejQ=\";\n"],"mappings":"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"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { EventVerifier,
|
|
1
|
+
import { EventVerifier, NostrBackend, NostrError } from "@qntx/nostr/core";
|
|
2
2
|
//#region src/instance.d.ts
|
|
3
3
|
type LoadNostrWasmOptions = {
|
|
4
4
|
/** Bytes, or a URL whose bytes will be read by the platform loader. */
|
|
5
5
|
module?: ArrayBuffer | ArrayBufferView | URL | undefined;
|
|
6
6
|
};
|
|
7
|
-
type NostrWasm =
|
|
7
|
+
type NostrWasm = NostrBackend & {
|
|
8
8
|
verify: (id: Uint8Array, pubkey: Uint8Array, sig: Uint8Array) => boolean;
|
|
9
|
-
verifySerialized: (serializedUtf8: Uint8Array, id: Uint8Array, pubkey: Uint8Array, sig: Uint8Array) => boolean;
|
|
10
9
|
verifyEvent: EventVerifier;
|
|
11
10
|
};
|
|
12
11
|
//#endregion
|
|
@@ -16,5 +15,8 @@ declare class WasmPoisonedError extends NostrError {
|
|
|
16
15
|
name: string;
|
|
17
16
|
}
|
|
18
17
|
//#endregion
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
//#region src/integrity.d.ts
|
|
19
|
+
declare const NOSTR_WASM_INTEGRITY = "sha256-p9Cj0Klb81RjKSm5EUX28CKmoXHhZqFRrdks6Pq5ejQ=";
|
|
20
|
+
//#endregion
|
|
21
|
+
export { NostrWasm as i, WasmPoisonedError as n, LoadNostrWasmOptions as r, NOSTR_WASM_INTEGRITY as t };
|
|
22
|
+
//# sourceMappingURL=integrity-BmoamQip.d.mts.map
|
package/dist/nostr_wasm.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qntx/nostr-wasm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "WebAssembly-accelerated cryptography backend for @qntx/nostr",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nostr",
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
|
-
"build
|
|
49
|
+
"build": "vp pack",
|
|
50
|
+
"build:wasm": "vp run @qntx/nostr#build && bash scripts/docker-build.sh && vp pack && publint && attw --pack . --profile esm-only",
|
|
50
51
|
"test:wasm": "vp test",
|
|
51
52
|
"bench:wasm": "BENCH=1 vp test tests/bench.ts",
|
|
52
53
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.src.json",
|
|
@@ -55,17 +56,17 @@
|
|
|
55
56
|
"devDependencies": {
|
|
56
57
|
"@arethetypeswrong/cli": "0.18.5",
|
|
57
58
|
"@noble/curves": "2.4.0",
|
|
58
|
-
"@qntx/nostr": "0.
|
|
59
|
-
"@qntx/nostr-native": "0.
|
|
59
|
+
"@qntx/nostr": "0.15.1",
|
|
60
|
+
"@qntx/nostr-native": "0.15.1",
|
|
60
61
|
"@qntx/tsconfig": "1.0.1",
|
|
61
|
-
"@types/node": "26.6.
|
|
62
|
+
"@types/node": "26.6.5",
|
|
62
63
|
"binaryen": "132.0.0",
|
|
63
64
|
"publint": "0.3.25",
|
|
64
65
|
"typescript": "7.0.2",
|
|
65
|
-
"vite-plus": "1.
|
|
66
|
+
"vite-plus": "1.3.0"
|
|
66
67
|
},
|
|
67
68
|
"peerDependencies": {
|
|
68
|
-
"@qntx/nostr": "^0.
|
|
69
|
+
"@qntx/nostr": "^0.15.1"
|
|
69
70
|
},
|
|
70
71
|
"devEngines": {
|
|
71
72
|
"packageManager": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"instance-6y1G4BjC.mjs","names":[],"sources":["../src/abi.ts","../src/adapter.ts","../src/instance.ts"],"sourcesContent":["import { CryptoError, randomBytes } from \"@qntx/nostr/core\";\n\n/**\n * Byte-ABI version 3 surface exported by `nostr_wasm.wasm` (`packages/nostr-wasm/c/nostr_wasm.c`\n * over the shared `nostr_core.c` in `packages/nostr-native/cpp`). All inputs are caller-written\n * regions of a single scratch buffer; status codes are 0 ok/verified, 1 verification failed, 2\n * invalid input. `nostr_init` must succeed once with a 32-byte seed before the crypto ops run.\n */\nexport type CryptoWasmExports = {\n memory: WebAssembly.Memory;\n nostr_abi_version: () => number;\n nostr_init: (seed: number) => number;\n nostr_buffer: (len: number) => number;\n nostr_verify: (id: number, pubkey: number, sig: number) => number;\n nostr_verify_serialized: (\n ser: number,\n serLen: number,\n id: number,\n pubkey: number,\n sig: number,\n ) => number;\n nostr_sign: (id: number, seckey: number, aux: number, outSig: number) => number;\n nostr_public_key: (seckey: number, outPubkey: number) => number;\n};\n\nexport const NOSTR_ABI_VERSION = 3;\n\nconst SEED_LEN = 32;\n\nconst ID_LEN = 32;\nconst PUBKEY_LEN = 32;\nconst SIG_LEN = 64;\nconst SECKEY_LEN = 32;\nconst AUX_LEN = 32;\n\nfunction requireMemory(value: WebAssembly.ExportValue | undefined): WebAssembly.Memory {\n if (!(value instanceof WebAssembly.Memory)) {\n throw new TypeError(\"wasm missing memory export\");\n }\n return value;\n}\n\n// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- T binds the declared ABI signature at each call site\nfunction requireFn<T extends (...args: number[]) => unknown>(\n value: WebAssembly.ExportValue | undefined,\n name: string,\n): T {\n if (typeof value !== \"function\") {\n throw new TypeError(`wasm missing ${name} export`);\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- wasm exports are opaque; each name is bound to the signature the ABI contract requires\n return value as T;\n}\n\nfunction asExports(raw: WebAssembly.Exports): CryptoWasmExports {\n return {\n memory: requireMemory(raw[\"memory\"]),\n nostr_abi_version: requireFn(raw[\"nostr_abi_version\"], \"nostr_abi_version\"),\n nostr_init: requireFn(raw[\"nostr_init\"], \"nostr_init\"),\n nostr_buffer: requireFn(raw[\"nostr_buffer\"], \"nostr_buffer\"),\n nostr_verify: requireFn(raw[\"nostr_verify\"], \"nostr_verify\"),\n nostr_verify_serialized: requireFn(raw[\"nostr_verify_serialized\"], \"nostr_verify_serialized\"),\n nostr_sign: requireFn(raw[\"nostr_sign\"], \"nostr_sign\"),\n nostr_public_key: requireFn(raw[\"nostr_public_key\"], \"nostr_public_key\"),\n };\n}\n\n/**\n * Reserve `total` bytes in the wasm scratch region and return a fresh view. `nostr_buffer` returns\n * NULL when `memory.grow` fails; `__heap_base` is never 0, so 0 is unambiguously an allocation\n * failure — throw instead of writing at address 0. The view is taken after `nostr_buffer` ran, so a\n * growth inside it cannot leave a detached view in the caller's hands.\n */\nfunction scratch(exports: CryptoWasmExports, total: number): { ptr: number; view: Uint8Array } {\n const ptr = exports.nostr_buffer(total) >>> 0;\n if (ptr === 0) {\n throw new CryptoError(\"wasm scratch allocation failed\");\n }\n return { ptr, view: new Uint8Array(exports.memory.buffer, ptr, total) };\n}\n\n/** Copies `len` scratch bytes out; the view is re-acquired after the call. */\nfunction takeOut(exports: CryptoWasmExports, ptr: number, len: number): Uint8Array {\n const out = new Uint8Array(len);\n out.set(new Uint8Array(exports.memory.buffer, ptr, len));\n return out;\n}\n\nexport function wasmVerify(\n exports: CryptoWasmExports,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n): boolean {\n if (id.length !== ID_LEN || pubkey.length !== PUBKEY_LEN || sig.length !== SIG_LEN) {\n return false; // fixed-size ABI: a wrong-length input simply cannot verify\n }\n const { ptr, view } = scratch(exports, ID_LEN + PUBKEY_LEN + SIG_LEN);\n view.set(id, 0);\n view.set(pubkey, ID_LEN);\n view.set(sig, ID_LEN + PUBKEY_LEN);\n return exports.nostr_verify(ptr, ptr + ID_LEN, ptr + ID_LEN + PUBKEY_LEN) === 0;\n}\n\nexport function wasmVerifySerialized(\n exports: CryptoWasmExports,\n serialized: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n): boolean {\n if (id.length !== ID_LEN || pubkey.length !== PUBKEY_LEN || sig.length !== SIG_LEN) {\n return false;\n }\n const serLen = serialized.length;\n const { ptr, view } = scratch(exports, serLen + ID_LEN + PUBKEY_LEN + SIG_LEN);\n view.set(serialized, 0);\n view.set(id, serLen);\n view.set(pubkey, serLen + ID_LEN);\n view.set(sig, serLen + ID_LEN + PUBKEY_LEN);\n return (\n exports.nostr_verify_serialized(\n ptr,\n serLen,\n ptr + serLen,\n ptr + serLen + ID_LEN,\n ptr + serLen + ID_LEN + PUBKEY_LEN,\n ) === 0\n );\n}\n\n/**\n * Returns the 64-byte signature, or an empty array on failure (invalid input). The wasm module\n * zeroes the scratch copy of `seckey` before returning.\n */\nexport function wasmSign(\n exports: CryptoWasmExports,\n id: Uint8Array,\n seckey: Uint8Array,\n aux: Uint8Array,\n): Uint8Array {\n const outOffset = ID_LEN + SECKEY_LEN + AUX_LEN;\n const { ptr, view } = scratch(exports, outOffset + SIG_LEN);\n view.set(id, 0);\n view.set(seckey, ID_LEN);\n view.set(aux, ID_LEN + SECKEY_LEN);\n const status = exports.nostr_sign(ptr, ptr + ID_LEN, ptr + ID_LEN + SECKEY_LEN, ptr + outOffset);\n if (status !== 0) {\n return new Uint8Array(0);\n }\n return takeOut(exports, ptr + outOffset, SIG_LEN);\n}\n\n/**\n * Returns the 32-byte x-only public key, or an empty array on failure. The wasm module zeroes the\n * scratch copy of `seckey` before returning.\n */\nexport function wasmPublicKey(exports: CryptoWasmExports, seckey: Uint8Array): Uint8Array {\n const { ptr, view } = scratch(exports, SECKEY_LEN + PUBKEY_LEN);\n view.set(seckey, 0);\n const status = exports.nostr_public_key(ptr, ptr + SECKEY_LEN);\n if (status !== 0) {\n return new Uint8Array(0);\n }\n return takeOut(exports, ptr + SECKEY_LEN, PUBKEY_LEN);\n}\n\n/**\n * Compile and instantiate `nostr_wasm.wasm`. The module must declare no imports and must implement\n * ABI version {@link NOSTR_ABI_VERSION} — a mismatch means the artifact predates or postdates this\n * loader and instantiation fails.\n */\nexport async function instantiateCryptoWasm(\n bytes: ArrayBuffer | ArrayBufferView,\n): Promise<CryptoWasmExports> {\n const module = await WebAssembly.compile(bytes);\n const imports = WebAssembly.Module.imports(module);\n if (imports.length > 0) {\n throw new TypeError(\n `nostr_wasm module must not import anything, got ${imports.length} imports`,\n );\n }\n const instance = await WebAssembly.instantiate(module, {});\n const version = requireFn(instance.exports[\"nostr_abi_version\"], \"nostr_abi_version\")();\n if (version !== NOSTR_ABI_VERSION) {\n throw new CryptoError(\n `unsupported nostr_wasm ABI version ${String(version)}, expected ${NOSTR_ABI_VERSION}`,\n );\n }\n const exports = asExports(instance.exports);\n // Seed the context: 32 bytes of fresh randomness into the scratch buffer; the C side\n // randomizes the context and wipes the seed region on every path.\n const { ptr, view } = scratch(exports, SEED_LEN);\n view.set(randomBytes(SEED_LEN), 0);\n if (exports.nostr_init(ptr) !== 0) {\n throw new CryptoError(\"nostr_init failed to randomize the wasm context\");\n }\n return exports;\n}\n","import { NostrError, createEventVerifier } from \"@qntx/nostr/core\";\nimport type { EventVerifier, SerializedEventVerifier } from \"@qntx/nostr/core\";\n\n/** Wasm instance aborted; later calls on that instance fail. */\nexport class WasmPoisonedError extends NostrError {\n override name = \"WasmPoisonedError\";\n}\n\n/**\n * Run `fn` unless the instance is already poisoned; a `WebAssembly.RuntimeError` aborts the\n * instance, so it is converted to {@link WasmPoisonedError} and rethrown on every later call.\n */\nexport function runPoisoned<T>(poison: { error?: Error }, fn: () => T): T {\n if (poison.error !== undefined) {\n throw poison.error;\n }\n try {\n return fn();\n } catch (error) {\n if (error instanceof WebAssembly.RuntimeError) {\n poison.error = new WasmPoisonedError(\"wasm instance aborted\", {\n cause: error,\n });\n throw poison.error;\n }\n throw error;\n }\n}\n\n/**\n * Build the WASM event verifier on {@link createEventVerifier}. A `WebAssembly.RuntimeError` from\n * the module means the instance aborted — it poisons every later call with the same\n * `WasmPoisonedError`; any other backend error verifies `false` (the event is marked failed).\n */\nexport function createWasmEventVerifier(\n verifySerialized: SerializedEventVerifier,\n poison: { error?: Error },\n): EventVerifier {\n const backend: SerializedEventVerifier = (serializedUtf8, id, pubkey, sig) => {\n try {\n return runPoisoned(poison, () => verifySerialized(serializedUtf8, id, pubkey, sig));\n } catch (error) {\n if (error instanceof WasmPoisonedError) {\n throw error;\n }\n return false;\n }\n };\n return createEventVerifier(backend);\n}\n","import { CryptoError } from \"@qntx/nostr/core\";\nimport type { SigningBackend, EventVerifier } from \"@qntx/nostr/core\";\n\nimport {\n instantiateCryptoWasm,\n wasmPublicKey,\n wasmSign,\n wasmVerify,\n wasmVerifySerialized,\n} from \"./abi.ts\";\nimport type { CryptoWasmExports } from \"./abi.ts\";\nimport { createWasmEventVerifier, runPoisoned } from \"./adapter.ts\";\n\nexport type LoadNostrWasmOptions = {\n /** Bytes, or a URL whose bytes will be read by the platform loader. */\n module?: ArrayBuffer | ArrayBufferView | URL | undefined;\n};\n\nexport type NostrWasm = SigningBackend & {\n verify: (id: Uint8Array, pubkey: Uint8Array, sig: Uint8Array) => boolean;\n verifySerialized: (\n serializedUtf8: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n ) => boolean;\n verifyEvent: EventVerifier;\n};\n\nexport function isWasmBytes(value: unknown): value is ArrayBuffer | ArrayBufferView {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\nexport async function fetchWasmUrl(url: URL): Promise<Uint8Array> {\n const res = await fetch(url);\n if (!res.ok) {\n throw new CryptoError(`failed to fetch wasm: ${res.status} ${url.href}`);\n }\n return new Uint8Array(await res.arrayBuffer());\n}\n\nfunction requireByteLength(bytes: Uint8Array, expected: number, label: string): void {\n if (bytes.length !== expected) {\n throw new CryptoError(`invalid ${label} length: expected ${expected}, got ${bytes.length}`);\n }\n}\n\nfunction bindExports(exports: CryptoWasmExports): NostrWasm {\n const poison: { error?: Error } = {};\n const rawSerialized = (\n serializedUtf8: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n ): boolean => wasmVerifySerialized(exports, serializedUtf8, id, pubkey, sig);\n return {\n verify: (id, pubkey, sig) => runPoisoned(poison, () => wasmVerify(exports, id, pubkey, sig)),\n verifySerialized: (serializedUtf8, id, pubkey, sig) =>\n runPoisoned(poison, () => rawSerialized(serializedUtf8, id, pubkey, sig)),\n verifyEvent: createWasmEventVerifier(rawSerialized, poison),\n sign: (id, seckey, aux) =>\n runPoisoned(poison, () => {\n requireByteLength(id, 32, \"id\");\n requireByteLength(seckey, 32, \"secret key\");\n requireByteLength(aux, 32, \"aux\");\n const sig = wasmSign(exports, id, seckey, aux);\n if (sig.length !== 64) {\n throw new CryptoError(\"wasm sign failed\");\n }\n return sig;\n }),\n publicKey: (seckey) =>\n runPoisoned(poison, () => {\n requireByteLength(seckey, 32, \"secret key\");\n const pk = wasmPublicKey(exports, seckey);\n if (pk.length !== 32) {\n throw new CryptoError(\"wasm publicKey failed\");\n }\n return pk;\n }),\n };\n}\n\n/** Builds a loader that interns a single instance per module slot. */\nexport function createNostrWasmLoader(\n wasmBytes: (opts?: LoadNostrWasmOptions) => Promise<ArrayBuffer | ArrayBufferView>,\n): {\n loadNostrWasm: (opts?: LoadNostrWasmOptions) => Promise<NostrWasm>;\n resetNostrWasm: () => void;\n} {\n let interned: Promise<NostrWasm> | undefined;\n\n async function instantiate(opts?: LoadNostrWasmOptions): Promise<NostrWasm> {\n const bytes = await wasmBytes(opts);\n const exports = await instantiateCryptoWasm(bytes);\n return bindExports(exports);\n }\n\n /** Instantiate once. Repeats reuse the same module. Failure throws; no noble fallback. */\n async function loadNostrWasm(opts?: LoadNostrWasmOptions): Promise<NostrWasm> {\n if (interned) {\n return interned;\n }\n const pending = instantiate(opts);\n interned = pending;\n try {\n return await pending;\n } catch (error) {\n if (interned === pending) {\n interned = undefined;\n }\n throw error;\n }\n }\n\n function resetNostrWasm(): void {\n interned = undefined;\n }\n\n return { loadNostrWasm, resetNostrWasm };\n}\n"],"mappings":";AA2BA,MAAM,WAAW;AAEjB,MAAM,SAAS;AACf,MAAM,aAAa;AACnB,MAAM,UAAU;AAChB,MAAM,aAAa;AAGnB,SAAS,cAAc,OAAgE;CACrF,IAAI,EAAE,iBAAiB,YAAY,SACjC,MAAM,IAAI,UAAU,4BAA4B;CAElD,OAAO;AACT;AAGA,SAAS,UACP,OACA,MACG;CACH,IAAI,OAAO,UAAU,YACnB,MAAM,IAAI,UAAU,gBAAgB,KAAK,QAAQ;CAGnD,OAAO;AACT;AAEA,SAAS,UAAU,KAA6C;CAC9D,OAAO;EACL,QAAQ,cAAc,IAAI,SAAS;EACnC,mBAAmB,UAAU,IAAI,sBAAsB,mBAAmB;EAC1E,YAAY,UAAU,IAAI,eAAe,YAAY;EACrD,cAAc,UAAU,IAAI,iBAAiB,cAAc;EAC3D,cAAc,UAAU,IAAI,iBAAiB,cAAc;EAC3D,yBAAyB,UAAU,IAAI,4BAA4B,yBAAyB;EAC5F,YAAY,UAAU,IAAI,eAAe,YAAY;EACrD,kBAAkB,UAAU,IAAI,qBAAqB,kBAAkB;CACzE;AACF;;;;;;;AAQA,SAAS,QAAQ,SAA4B,OAAkD;CAC7F,MAAM,MAAM,QAAQ,aAAa,KAAK,MAAM;CAC5C,IAAI,QAAQ,GACV,MAAM,IAAI,YAAY,gCAAgC;CAExD,OAAO;EAAE;EAAK,MAAM,IAAI,WAAW,QAAQ,OAAO,QAAQ,KAAK,KAAK;CAAE;AACxE;;AAGA,SAAS,QAAQ,SAA4B,KAAa,KAAyB;CACjF,MAAM,MAAM,IAAI,WAAW,GAAG;CAC9B,IAAI,IAAI,IAAI,WAAW,QAAQ,OAAO,QAAQ,KAAK,GAAG,CAAC;CACvD,OAAO;AACT;AAEA,SAAgB,WACd,SACA,IACA,QACA,KACS;CACT,IAAI,GAAG,WAAW,UAAU,OAAO,WAAW,cAAc,IAAI,WAAW,SACzE,OAAO;CAET,MAAM,EAAE,KAAK,SAAS,QAAQ,SAAS,GAA6B;CACpE,KAAK,IAAI,IAAI,CAAC;CACd,KAAK,IAAI,QAAQ,MAAM;CACvB,KAAK,IAAI,KAAK,EAAmB;CACjC,OAAO,QAAQ,aAAa,KAAK,MAAM,QAAQ,MAAM,SAAS,UAAU,MAAM;AAChF;AAEA,SAAgB,qBACd,SACA,YACA,IACA,QACA,KACS;CACT,IAAI,GAAG,WAAW,UAAU,OAAO,WAAW,cAAc,IAAI,WAAW,SACzE,OAAO;CAET,MAAM,SAAS,WAAW;CAC1B,MAAM,EAAE,KAAK,SAAS,QAAQ,SAAS,SAAS,SAAS,aAAa,OAAO;CAC7E,KAAK,IAAI,YAAY,CAAC;CACtB,KAAK,IAAI,IAAI,MAAM;CACnB,KAAK,IAAI,QAAQ,SAAS,MAAM;CAChC,KAAK,IAAI,KAAK,SAAS,SAAS,UAAU;CAC1C,OACE,QAAQ,wBACN,KACA,QACA,MAAM,QACN,MAAM,SAAS,QACf,MAAM,SAAS,SAAS,UAC1B,MAAM;AAEV;;;;;AAMA,SAAgB,SACd,SACA,IACA,QACA,KACY;CACZ,MAAM,YAAY;CAClB,MAAM,EAAE,KAAK,SAAS,QAAQ,SAAS,GAAmB;CAC1D,KAAK,IAAI,IAAI,CAAC;CACd,KAAK,IAAI,QAAQ,MAAM;CACvB,KAAK,IAAI,KAAK,EAAmB;CAEjC,IADe,QAAQ,WAAW,KAAK,MAAM,QAAQ,MAAM,SAAS,YAAY,MAAM,SAC7E,MAAM,GACb,uBAAO,IAAI,WAAW,CAAC;CAEzB,OAAO,QAAQ,SAAS,MAAM,WAAW,OAAO;AAClD;;;;;AAMA,SAAgB,cAAc,SAA4B,QAAgC;CACxF,MAAM,EAAE,KAAK,SAAS,QAAQ,SAAS,EAAuB;CAC9D,KAAK,IAAI,QAAQ,CAAC;CAElB,IADe,QAAQ,iBAAiB,KAAK,MAAM,UAC1C,MAAM,GACb,uBAAO,IAAI,WAAW,CAAC;CAEzB,OAAO,QAAQ,SAAS,MAAM,YAAY,UAAU;AACtD;;;;;;AAOA,eAAsB,sBACpB,OAC4B;CAC5B,MAAM,SAAS,MAAM,YAAY,QAAQ,KAAK;CAC9C,MAAM,UAAU,YAAY,OAAO,QAAQ,MAAM;CACjD,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,UACR,mDAAmD,QAAQ,OAAO,SACpE;CAEF,MAAM,WAAW,MAAM,YAAY,YAAY,QAAQ,CAAC,CAAC;CACzD,MAAM,UAAU,UAAU,SAAS,QAAQ,sBAAsB,mBAAmB,CAAC,CAAC;CACtF,IAAI,YAAA,GACF,MAAM,IAAI,YACR,sCAAsC,OAAO,OAAO,EAAE,aACxD;CAEF,MAAM,UAAU,UAAU,SAAS,OAAO;CAG1C,MAAM,EAAE,KAAK,SAAS,QAAQ,SAAS,QAAQ;CAC/C,KAAK,IAAI,YAAY,QAAQ,GAAG,CAAC;CACjC,IAAI,QAAQ,WAAW,GAAG,MAAM,GAC9B,MAAM,IAAI,YAAY,iDAAiD;CAEzE,OAAO;AACT;;;;AClMA,IAAa,oBAAb,cAAuC,WAAW;CAChD,OAAgB;AAClB;;;;;AAMA,SAAgB,YAAe,QAA2B,IAAgB;CACxE,IAAI,OAAO,UAAU,KAAA,GACnB,MAAM,OAAO;CAEf,IAAI;EACF,OAAO,GAAG;CACZ,SAAS,OAAO;EACd,IAAI,iBAAiB,YAAY,cAAc;GAC7C,OAAO,QAAQ,IAAI,kBAAkB,yBAAyB,EAC5D,OAAO,MACT,CAAC;GACD,MAAM,OAAO;EACf;EACA,MAAM;CACR;AACF;;;;;;AAOA,SAAgB,wBACd,kBACA,QACe;CACf,MAAM,WAAoC,gBAAgB,IAAI,QAAQ,QAAQ;EAC5E,IAAI;GACF,OAAO,YAAY,cAAc,iBAAiB,gBAAgB,IAAI,QAAQ,GAAG,CAAC;EACpF,SAAS,OAAO;GACd,IAAI,iBAAiB,mBACnB,MAAM;GAER,OAAO;EACT;CACF;CACA,OAAO,oBAAoB,OAAO;AACpC;;;ACpBA,SAAgB,YAAY,OAAwD;CAClF,OAAO,iBAAiB,eAAe,YAAY,OAAO,KAAK;AACjE;AAEA,eAAsB,aAAa,KAA+B;CAChE,MAAM,MAAM,MAAM,MAAM,GAAG;CAC3B,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,YAAY,yBAAyB,IAAI,OAAO,GAAG,IAAI,MAAM;CAEzE,OAAO,IAAI,WAAW,MAAM,IAAI,YAAY,CAAC;AAC/C;AAEA,SAAS,kBAAkB,OAAmB,UAAkB,OAAqB;CACnF,IAAI,MAAM,WAAW,UACnB,MAAM,IAAI,YAAY,WAAW,MAAM,oBAAoB,SAAS,QAAQ,MAAM,QAAQ;AAE9F;AAEA,SAAS,YAAY,SAAuC;CAC1D,MAAM,SAA4B,CAAC;CACnC,MAAM,iBACJ,gBACA,IACA,QACA,QACY,qBAAqB,SAAS,gBAAgB,IAAI,QAAQ,GAAG;CAC3E,OAAO;EACL,SAAS,IAAI,QAAQ,QAAQ,YAAY,cAAc,WAAW,SAAS,IAAI,QAAQ,GAAG,CAAC;EAC3F,mBAAmB,gBAAgB,IAAI,QAAQ,QAC7C,YAAY,cAAc,cAAc,gBAAgB,IAAI,QAAQ,GAAG,CAAC;EAC1E,aAAa,wBAAwB,eAAe,MAAM;EAC1D,OAAO,IAAI,QAAQ,QACjB,YAAY,cAAc;GACxB,kBAAkB,IAAI,IAAI,IAAI;GAC9B,kBAAkB,QAAQ,IAAI,YAAY;GAC1C,kBAAkB,KAAK,IAAI,KAAK;GAChC,MAAM,MAAM,SAAS,SAAS,IAAI,QAAQ,GAAG;GAC7C,IAAI,IAAI,WAAW,IACjB,MAAM,IAAI,YAAY,kBAAkB;GAE1C,OAAO;EACT,CAAC;EACH,YAAY,WACV,YAAY,cAAc;GACxB,kBAAkB,QAAQ,IAAI,YAAY;GAC1C,MAAM,KAAK,cAAc,SAAS,MAAM;GACxC,IAAI,GAAG,WAAW,IAChB,MAAM,IAAI,YAAY,uBAAuB;GAE/C,OAAO;EACT,CAAC;CACL;AACF;;AAGA,SAAgB,sBACd,WAIA;CACA,IAAI;CAEJ,eAAe,YAAY,MAAiD;EAG1E,OAAO,YAAY,MADG,sBAAsB,MADxB,UAAU,IAAI,CACe,CACvB;CAC5B;;CAGA,eAAe,cAAc,MAAiD;EAC5E,IAAI,UACF,OAAO;EAET,MAAM,UAAU,YAAY,IAAI;EAChC,WAAW;EACX,IAAI;GACF,OAAO,MAAM;EACf,SAAS,OAAO;GACd,IAAI,aAAa,SACf,WAAW,KAAA;GAEb,MAAM;EACR;CACF;CAEA,SAAS,iBAAuB;EAC9B,WAAW,KAAA;CACb;CAEA,OAAO;EAAE;EAAe;CAAe;AACzC"}
|