@qntx/wallet-worker 0.3.0 → 0.5.0
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/README.md +9 -2
- package/dist/{browser-gx857d3g.js → browser-1vpxmqwn.js} +5 -5
- package/dist/browser.mjs +3 -3
- package/dist/browser.mjs.map +1 -1
- package/dist/kernel.manifest.json +26 -33
- package/dist/kernel.node.js +8609 -415
- package/dist/kernel.worker.js +8610 -416
- package/dist/{worker-7sztn879.js → node-366p4fej.js} +4256 -221
- package/dist/{worker-30nrftxx.js → node-gsare3mw.js} +1 -1
- package/dist/node.mjs +81 -830
- package/dist/node.mjs.map +1 -1
- package/dist/{signer-tgawen1v.js → signer-gs57sjgx.js} +37 -29
- package/dist/spark.wasm +0 -0
- package/dist/{node-7sztn879.js → worker-366p4fej.js} +4256 -221
- package/dist/{node-30nrftxx.js → worker-gsare3mw.js} +1 -1
- package/package.json +13 -8
- package/dist/index-cgstn3zw.js +0 -26
- package/dist/node-nhrchcf5.js +0 -3979
- package/dist/worker-nhrchcf5.js +0 -3979
package/README.md
CHANGED
|
@@ -75,8 +75,15 @@ const wallet = await createNodeWallet({
|
|
|
75
75
|
update the app, do not reset.
|
|
76
76
|
- The Node store's RPC failures surface as `unavailable` and precondition failures as
|
|
77
77
|
`conflict` — neither hangs a kernel call.
|
|
78
|
-
- The Web host is a **hot wallet**. CSP needs `worker-src 'self'
|
|
79
|
-
`
|
|
78
|
+
- The Web host is a **hot wallet**. CSP needs `worker-src 'self'`. The host spawns the kernel
|
|
79
|
+
with `new Worker(new URL("kernel.worker.js", import.meta.url))`, which bundlers emit as a
|
|
80
|
+
worker entry with its chunks and `spark.wasm`. Consumer setup: Vite —
|
|
81
|
+
`worker: { format: "es" }` (required to keep the Spark backend lazy; the default `"iife"`
|
|
82
|
+
still runs but ships Spark inside the initial worker) and dev-server
|
|
83
|
+
`optimizeDeps.exclude: ["@qntx/wallet-worker"]`; webpack 5 — nothing.
|
|
84
|
+
- `dist/kernel.manifest.json` pins the artifacts the package was built from. Consumer bundlers
|
|
85
|
+
re-bundle the worker, so the shipped bytes differ from the manifest's; `spark.wasm` is still
|
|
86
|
+
verified by its integrity constant when the kernel loads it.
|
|
80
87
|
- `@qntx/wallet/host` is for host packages only — not application API.
|
|
81
88
|
|
|
82
89
|
## License
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SparkError,
|
|
3
3
|
SparkCryptoError
|
|
4
|
-
} from "./worker-
|
|
4
|
+
} from "./worker-gsare3mw.js";
|
|
5
5
|
|
|
6
|
-
// ../../node_modules/.bun/@qntx+spark@0.
|
|
6
|
+
// ../../node_modules/.bun/@qntx+spark@0.5.2/node_modules/@qntx/spark/dist/crypto.mjs
|
|
7
7
|
var SPARK_ABI_VERSION = 1;
|
|
8
8
|
|
|
9
|
-
// ../../node_modules/.bun/@qntx+spark-wasm@0.
|
|
9
|
+
// ../../node_modules/.bun/@qntx+spark-wasm@0.5.2+6548168f04baafa8/node_modules/@qntx/spark-wasm/dist/integrity-DPI575pP.mjs
|
|
10
10
|
var OUT_PARAMS_LEN = 8;
|
|
11
11
|
var INTERNAL_ERROR = 8;
|
|
12
12
|
function requireMemory(value) {
|
|
@@ -175,9 +175,9 @@ function bindSparkExports(exports) {
|
|
|
175
175
|
splitSecretWithProofs: (secret, threshold, shares, random) => run(() => wasmSplitSecretWithProofs(exports, secret, threshold, shares, random))
|
|
176
176
|
};
|
|
177
177
|
}
|
|
178
|
-
var SPARK_WASM_INTEGRITY = "sha256-
|
|
178
|
+
var SPARK_WASM_INTEGRITY = "sha256-OBAVP0QjuY/JyeQh6Ay3XnczAKsMYrWoHdCJ73RSPeQ=";
|
|
179
179
|
|
|
180
|
-
// ../../node_modules/.bun/@qntx+spark-wasm@0.
|
|
180
|
+
// ../../node_modules/.bun/@qntx+spark-wasm@0.5.2+6548168f04baafa8/node_modules/@qntx/spark-wasm/dist/browser.mjs
|
|
181
181
|
async function fetchWasmUrl(url) {
|
|
182
182
|
const res = await fetch(url);
|
|
183
183
|
if (!res.ok)
|
package/dist/browser.mjs
CHANGED
|
@@ -182,13 +182,12 @@ async function createWebWallet(options) {
|
|
|
182
182
|
t: "init",
|
|
183
183
|
idleMinutes: autoLock.idleMinutes
|
|
184
184
|
};
|
|
185
|
-
const kernelUrl = new URL("kernel.worker.js", import.meta.url);
|
|
186
185
|
const ceremony = createWebAuthnPasskey({
|
|
187
186
|
id: options.passkey.rpId,
|
|
188
187
|
name: options.passkey.rpName
|
|
189
188
|
});
|
|
190
189
|
const spawn = () => {
|
|
191
|
-
const worker = new Worker(
|
|
190
|
+
const worker = new Worker(new URL("kernel.worker.js", import.meta.url), {
|
|
192
191
|
type: "module",
|
|
193
192
|
name: "qntx-wallet"
|
|
194
193
|
});
|
|
@@ -208,7 +207,7 @@ async function createWebWallet(options) {
|
|
|
208
207
|
},
|
|
209
208
|
onExit: (handler) => {
|
|
210
209
|
const listener = () => {
|
|
211
|
-
handler();
|
|
210
|
+
handler("crashed");
|
|
212
211
|
};
|
|
213
212
|
worker.addEventListener("error", listener);
|
|
214
213
|
worker.addEventListener("messageerror", listener);
|
|
@@ -223,6 +222,7 @@ async function createWebWallet(options) {
|
|
|
223
222
|
};
|
|
224
223
|
};
|
|
225
224
|
const wallet = await connectKernel({
|
|
225
|
+
factors: "software",
|
|
226
226
|
spawn,
|
|
227
227
|
passkey: options.passkey,
|
|
228
228
|
ceremony,
|
package/dist/browser.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.mjs","names":[],"sources":["../src/browser/passkey.ts","../src/browser/index.ts"],"sourcesContent":["/**\n * The WebAuthn passkey ceremony (main thread only): `navigator.credentials` with the PRF extension.\n * A wallet passkey derives the wallet seed from `PRF(walletSalt)`; the vault factor derives the\n * vault KEK from `PRF(vaultSalt)` on the same credential. Every ceremony output is checked for\n * `prf.enabled`, PRF results and the user-verification flag — a credential that cannot prove UV\n * never reaches the kernel.\n */\nimport { WalletError } from \"@qntx/wallet\";\nimport { PASSKEY_PURPOSE_VAULT, PASSKEY_PURPOSE_WALLET } from \"@qntx/wallet/host\";\nimport type { PasskeyCeremony, PrfResult, PrfSalts } from \"@qntx/wallet/host\";\n\nexport type WebAuthnRp = {\n /** RP id — the origin's effective domain, e.g. `location.hostname`. */\n readonly id: string;\n readonly name: string;\n};\n\nconst UV_FLAG = 0x04; // authenticator-data flags byte (offset 32), bit 2 = user verified\nconst BE_FLAG = 0x08; // bit 3 = credential is backup-eligible (multi-device capable)\nconst BS_FLAG = 0x10; // bit 4 = credential is currently backed up (synced)\n\nfunction factorUnavailable(message: string): WalletError {\n return new WalletError(\"factor-unavailable\", message);\n}\n\nfunction cancelled(message: string): WalletError {\n return new WalletError(\"cancelled\", message);\n}\n\nfunction mapCeremonyError(error: unknown): never {\n if (\n error instanceof DOMException &&\n (error.name === \"NotAllowedError\" || error.name === \"AbortError\")\n ) {\n throw cancelled(\"the passkey ceremony was cancelled\");\n }\n if (error instanceof WalletError) {\n throw error;\n }\n throw factorUnavailable(\"the passkey ceremony failed\");\n}\n\n/** The UV bit of an authenticator-data blob (flags byte at offset 32, bit 0x04). */\nfunction userVerified(authenticatorData: ArrayBuffer): boolean {\n return (\n authenticatorData.byteLength > 32 &&\n // oxlint-disable-next-line eslint/no-bitwise -- flags bit test\n (new DataView(authenticatorData).getUint8(32) & UV_FLAG) !== 0\n );\n}\n\n/**\n * Whether the credential is backed up off-device (flags byte at offset 32): synced requires both BE\n * (the credential is eligible for backup) and BS (the authenticator actually backs it up).\n */\nfunction credentialSynced(authenticatorData: ArrayBuffer): boolean {\n return (\n authenticatorData.byteLength > 32 &&\n // oxlint-disable-next-line eslint/no-bitwise -- flags bit test\n (new DataView(authenticatorData).getUint8(32) & (BE_FLAG | BS_FLAG)) === (BE_FLAG | BS_FLAG)\n );\n}\n\nfunction prfEval(salts: PrfSalts): AuthenticationExtensionsPRFValues {\n // The constructor copy narrows Uint8Array<ArrayBufferLike> to Uint8Array<ArrayBuffer> —\n // the DOM BufferSource does not admit shared-array-buffer views.\n return salts.second === undefined\n ? { first: new Uint8Array(salts.first) }\n : { first: new Uint8Array(salts.first), second: new Uint8Array(salts.second) };\n}\n\n/** A detached copy of a BufferSource — the returned Uint8Array owns its own fresh buffer. */\nfunction copyBufferSource(source: BufferSource): Uint8Array {\n const view =\n source instanceof ArrayBuffer\n ? new Uint8Array(source)\n : new Uint8Array(source.buffer, source.byteOffset, source.byteLength);\n return new Uint8Array(view);\n}\n\nfunction readPrfResult(\n credential: PublicKeyCredential,\n results: AuthenticationExtensionsPRFValues | undefined,\n wantSecond: boolean,\n synced: boolean,\n userHandle?: ArrayBuffer,\n): PrfResult {\n const first = results?.first;\n const second = results?.second;\n if (first === undefined || (wantSecond && second === undefined)) {\n throw factorUnavailable(\"the passkey produced no PRF output\");\n }\n // Copy into fresh Uint8Arrays and drop every reference to the extension's ArrayBuffers.\n return {\n credentialId: copyBufferSource(credential.rawId),\n first: copyBufferSource(first),\n ...(second === undefined ? {} : { second: copyBufferSource(second) }),\n ...(userHandle === undefined ? {} : { userHandle: copyBufferSource(userHandle) }),\n synced,\n };\n}\n\nfunction checkCredential(credential: unknown): PublicKeyCredential {\n if (!(credential instanceof PublicKeyCredential)) {\n throw factorUnavailable(\"the authenticator returned no public-key credential\");\n }\n return credential;\n}\n\n/**\n * The WebAuthn ceremony. `create` registers a resident credential with `prf.eval` over the salts;\n * when the extension returns no results a single assertion over the new credential produces them.\n * `assert` runs a discoverable (or allowCredentials-scoped) get with the same eval shape.\n */\nexport function createWebAuthnPasskey(rp: WebAuthnRp): PasskeyCeremony {\n async function assert(input: {\n readonly credentialIds?: ReadonlyArray<Uint8Array>;\n readonly salts: PrfSalts;\n }): Promise<PrfResult> {\n let credential: PublicKeyCredential;\n try {\n credential = checkCredential(\n await navigator.credentials.get({\n publicKey: {\n rpId: rp.id,\n challenge: crypto.getRandomValues(new Uint8Array(32)),\n userVerification: \"required\",\n ...(input.credentialIds === undefined\n ? {}\n : {\n allowCredentials: input.credentialIds.map((id) => ({\n type: \"public-key\" as const,\n id: new Uint8Array(id),\n })),\n }),\n extensions: { prf: { eval: prfEval(input.salts) } },\n },\n }),\n );\n } catch (error) {\n mapCeremonyError(error);\n }\n const { response } = credential;\n if (!(response instanceof AuthenticatorAssertionResponse)) {\n throw factorUnavailable(\"the authenticator returned no assertion response\");\n }\n if (!userVerified(response.authenticatorData)) {\n throw factorUnavailable(\"the passkey assertion was not user-verified\");\n }\n return readPrfResult(\n credential,\n credential.getClientExtensionResults().prf?.results,\n input.salts.second !== undefined,\n credentialSynced(response.authenticatorData),\n response.userHandle ?? undefined,\n );\n }\n\n return {\n create: async (input) => {\n const userId = new Uint8Array(16);\n userId[0] = input.purpose === \"wallet\" ? PASSKEY_PURPOSE_WALLET : PASSKEY_PURPOSE_VAULT;\n crypto.getRandomValues(userId.subarray(1));\n let credential: PublicKeyCredential;\n try {\n credential = checkCredential(\n await navigator.credentials.create({\n publicKey: {\n rp: { id: rp.id, name: rp.name },\n user: {\n id: userId,\n name: input.name,\n displayName: input.name,\n },\n challenge: crypto.getRandomValues(new Uint8Array(32)),\n pubKeyCredParams: [\n { type: \"public-key\", alg: -7 },\n { type: \"public-key\", alg: -257 },\n ],\n authenticatorSelection: {\n residentKey: \"required\",\n userVerification: \"required\",\n },\n extensions: { prf: { eval: prfEval(input.salts) } },\n },\n }),\n );\n } catch (error) {\n mapCeremonyError(error);\n }\n const { response } = credential;\n if (!(response instanceof AuthenticatorAttestationResponse)) {\n throw factorUnavailable(\"the authenticator returned no attestation response\");\n }\n const extensions = credential.getClientExtensionResults();\n if (extensions.prf?.enabled !== true) {\n throw factorUnavailable(\"the authenticator does not support the PRF extension\");\n }\n if (extensions.prf.results === undefined) {\n // Some authenticators omit creation-time PRF outputs: one assertion over the new\n // credential produces them (it is that response whose UV and BE/BS flags get read).\n return assert({\n credentialIds: [copyBufferSource(credential.rawId)],\n salts: input.salts,\n });\n }\n const authenticatorData = response.getAuthenticatorData();\n if (!userVerified(authenticatorData)) {\n throw factorUnavailable(\"the passkey creation was not user-verified\");\n }\n return readPrfResult(\n credential,\n extensions.prf.results,\n input.salts.second !== undefined,\n credentialSynced(authenticatorData),\n );\n },\n assert,\n };\n}\n","/**\n * The Web host: `createWebWallet` spawns the bundled kernel worker (dist/kernel.worker.js) whose\n * vault lives in the worker's own IndexedDB. Lock destroys the worker; the vault survives in\n * IndexedDB, so the respawned worker reloads it. `pagehide` locks immediately; the idle timer is\n * refreshed by user-presence signals, not request traffic. The WebAuthn ceremony runs on the main\n * thread.\n */\nimport { WalletError } from \"@qntx/wallet\";\nimport type { Wallet, WalletOptions } from \"@qntx/wallet\";\nimport { connectKernel } from \"@qntx/wallet/host\";\nimport type { MainPort } from \"@qntx/wallet/host\";\nimport type { ProtocolValue } from \"@qntx/wallet/protocol\";\n\nimport type { BrowserWorkerInit } from \"../shared.ts\";\nimport { createWebAuthnPasskey } from \"./passkey.ts\";\n\nconst LISTENER_OPTIONS = { capture: true, passive: true };\nconst ACTIVITY_EVENTS = [\"pointerdown\", \"pointermove\", \"keydown\", \"wheel\", \"touchstart\"];\n\n/**\n * The browser's user-presence source: direct input on the page, or returning to a visible tab.\n * Capture-phase passive listeners do a single boolean check per event — high-frequency signals\n * (scroll, pointermove) cost nothing. Returns the unsubscribe the wallet owns.\n */\nfunction subscribePresence(onActivity: () => void): () => void {\n for (const type of ACTIVITY_EVENTS) {\n globalThis.addEventListener(type, onActivity, LISTENER_OPTIONS);\n }\n const onVisible = (): void => {\n if (document.visibilityState === \"visible\") {\n onActivity();\n }\n };\n document.addEventListener(\"visibilitychange\", onVisible);\n return () => {\n for (const type of ACTIVITY_EVENTS) {\n globalThis.removeEventListener(type, onActivity, LISTENER_OPTIONS);\n }\n document.removeEventListener(\"visibilitychange\", onVisible);\n };\n}\n\nasync function bestEffort(task: () => Promise<unknown>): Promise<void> {\n try {\n await task();\n } catch {\n // Lifecycle hooks and persistence hints must never surface failures.\n }\n}\n\nexport async function createWebWallet(options: WalletOptions): Promise<Wallet> {\n const { autoLock } = options;\n if (autoLock.platform !== \"web\") {\n throw new WalletError(\"invalid-input\", \"createWebWallet accepts only the web autoLock policy\");\n }\n // The page sends only the idle choice; the worker owns the maximum session duration.\n const init: BrowserWorkerInit = { t: \"init\", idleMinutes: autoLock.idleMinutes };\n const kernelUrl = new URL(\"kernel.worker.js\", import.meta.url);\n const ceremony = createWebAuthnPasskey({\n id: options.passkey.rpId,\n name: options.passkey.rpName,\n });\n\n const spawn = (): MainPort => {\n const worker = new Worker(kernelUrl, { type: \"module\", name: \"qntx-wallet\" });\n // oxlint-disable-next-line unicorn/require-post-message-target-origin -- Worker.postMessage takes a transfer list, not an origin\n worker.postMessage(init); // the kernel validates it strictly before startKernel\n return {\n post: (message: ProtocolValue, transfer: ArrayBuffer[]) => {\n worker.postMessage(message, transfer);\n },\n listen: (handler) => {\n const listener = (event: MessageEvent): void => {\n handler(event.data);\n };\n worker.addEventListener(\"message\", listener);\n return () => {\n worker.removeEventListener(\"message\", listener);\n };\n },\n onExit: (handler) => {\n // Web Workers have no exit event; error/messageerror are the only death signals.\n const listener = (): void => {\n handler();\n };\n worker.addEventListener(\"error\", listener);\n worker.addEventListener(\"messageerror\", listener);\n return () => {\n worker.removeEventListener(\"error\", listener);\n worker.removeEventListener(\"messageerror\", listener);\n };\n },\n terminate: () => {\n worker.terminate();\n },\n };\n };\n\n const wallet = await connectKernel({\n spawn,\n passkey: options.passkey,\n ceremony,\n setTimer: (delayMs, fn) => {\n const timer = globalThis.setTimeout(fn, delayMs);\n return () => {\n globalThis.clearTimeout(timer);\n };\n },\n presence: subscribePresence,\n onDestroy: () => {\n globalThis.removeEventListener(\"pagehide\", onPageHide);\n },\n });\n\n // Page lifecycle: lock on pagehide. A BFCache-frozen page still runs the queued locked-event\n // handler on restore, so the wallet respawns then — terminating the worker directly would\n // leave the wallet pointing at a dead port.\n const onPageHide = (): void => {\n void bestEffort(async () => wallet.vault.lock());\n };\n globalThis.addEventListener(\"pagehide\", onPageHide);\n\n // Ask the browser to persist the vault once it exists — a denied request is fine.\n let persisted = false;\n const unsubscribe = wallet.subscribe(() => {\n if (!persisted && wallet.getSnapshot().vault !== \"uninitialized\") {\n persisted = true;\n unsubscribe();\n void bestEffort(async () => navigator.storage.persist());\n }\n });\n\n return wallet;\n}\n\nexport { createWebAuthnPasskey } from \"./passkey.ts\";\nexport type { WebAuthnRp } from \"./passkey.ts\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"browser.mjs","names":[],"sources":["../src/browser/passkey.ts","../src/browser/index.ts"],"sourcesContent":["/**\n * The WebAuthn passkey ceremony (main thread only): `navigator.credentials` with the PRF extension.\n * A wallet passkey derives the wallet seed from `PRF(walletSalt)`; the vault factor derives the\n * vault KEK from `PRF(vaultSalt)` on the same credential. Every ceremony output is checked for\n * `prf.enabled`, PRF results and the user-verification flag — a credential that cannot prove UV\n * never reaches the kernel.\n */\nimport { WalletError } from \"@qntx/wallet\";\nimport { PASSKEY_PURPOSE_VAULT, PASSKEY_PURPOSE_WALLET } from \"@qntx/wallet/host\";\nimport type { PasskeyCeremony, PrfResult, PrfSalts } from \"@qntx/wallet/host\";\n\nexport type WebAuthnRp = {\n /** RP id — the origin's effective domain, e.g. `location.hostname`. */\n readonly id: string;\n readonly name: string;\n};\n\nconst UV_FLAG = 0x04; // authenticator-data flags byte (offset 32), bit 2 = user verified\nconst BE_FLAG = 0x08; // bit 3 = credential is backup-eligible (multi-device capable)\nconst BS_FLAG = 0x10; // bit 4 = credential is currently backed up (synced)\n\nfunction factorUnavailable(message: string): WalletError {\n return new WalletError(\"factor-unavailable\", message);\n}\n\nfunction cancelled(message: string): WalletError {\n return new WalletError(\"cancelled\", message);\n}\n\nfunction mapCeremonyError(error: unknown): never {\n if (\n error instanceof DOMException &&\n (error.name === \"NotAllowedError\" || error.name === \"AbortError\")\n ) {\n throw cancelled(\"the passkey ceremony was cancelled\");\n }\n if (error instanceof WalletError) {\n throw error;\n }\n throw factorUnavailable(\"the passkey ceremony failed\");\n}\n\n/** The UV bit of an authenticator-data blob (flags byte at offset 32, bit 0x04). */\nfunction userVerified(authenticatorData: ArrayBuffer): boolean {\n return (\n authenticatorData.byteLength > 32 &&\n // oxlint-disable-next-line eslint/no-bitwise -- flags bit test\n (new DataView(authenticatorData).getUint8(32) & UV_FLAG) !== 0\n );\n}\n\n/**\n * Whether the credential is backed up off-device (flags byte at offset 32): synced requires both BE\n * (the credential is eligible for backup) and BS (the authenticator actually backs it up).\n */\nfunction credentialSynced(authenticatorData: ArrayBuffer): boolean {\n return (\n authenticatorData.byteLength > 32 &&\n // oxlint-disable-next-line eslint/no-bitwise -- flags bit test\n (new DataView(authenticatorData).getUint8(32) & (BE_FLAG | BS_FLAG)) === (BE_FLAG | BS_FLAG)\n );\n}\n\nfunction prfEval(salts: PrfSalts): AuthenticationExtensionsPRFValues {\n // The constructor copy narrows Uint8Array<ArrayBufferLike> to Uint8Array<ArrayBuffer> —\n // the DOM BufferSource does not admit shared-array-buffer views.\n return salts.second === undefined\n ? { first: new Uint8Array(salts.first) }\n : { first: new Uint8Array(salts.first), second: new Uint8Array(salts.second) };\n}\n\n/** A detached copy of a BufferSource — the returned Uint8Array owns its own fresh buffer. */\nfunction copyBufferSource(source: BufferSource): Uint8Array {\n const view =\n source instanceof ArrayBuffer\n ? new Uint8Array(source)\n : new Uint8Array(source.buffer, source.byteOffset, source.byteLength);\n return new Uint8Array(view);\n}\n\nfunction readPrfResult(\n credential: PublicKeyCredential,\n results: AuthenticationExtensionsPRFValues | undefined,\n wantSecond: boolean,\n synced: boolean,\n userHandle?: ArrayBuffer,\n): PrfResult {\n const first = results?.first;\n const second = results?.second;\n if (first === undefined || (wantSecond && second === undefined)) {\n throw factorUnavailable(\"the passkey produced no PRF output\");\n }\n // Copy into fresh Uint8Arrays and drop every reference to the extension's ArrayBuffers.\n return {\n credentialId: copyBufferSource(credential.rawId),\n first: copyBufferSource(first),\n ...(second === undefined ? {} : { second: copyBufferSource(second) }),\n ...(userHandle === undefined ? {} : { userHandle: copyBufferSource(userHandle) }),\n synced,\n };\n}\n\nfunction checkCredential(credential: unknown): PublicKeyCredential {\n if (!(credential instanceof PublicKeyCredential)) {\n throw factorUnavailable(\"the authenticator returned no public-key credential\");\n }\n return credential;\n}\n\n/**\n * The WebAuthn ceremony. `create` registers a resident credential with `prf.eval` over the salts;\n * when the extension returns no results a single assertion over the new credential produces them.\n * `assert` runs a discoverable (or allowCredentials-scoped) get with the same eval shape.\n */\nexport function createWebAuthnPasskey(rp: WebAuthnRp): PasskeyCeremony {\n async function assert(input: {\n readonly credentialIds?: ReadonlyArray<Uint8Array>;\n readonly salts: PrfSalts;\n }): Promise<PrfResult> {\n let credential: PublicKeyCredential;\n try {\n credential = checkCredential(\n await navigator.credentials.get({\n publicKey: {\n rpId: rp.id,\n challenge: crypto.getRandomValues(new Uint8Array(32)),\n userVerification: \"required\",\n ...(input.credentialIds === undefined\n ? {}\n : {\n allowCredentials: input.credentialIds.map((id) => ({\n type: \"public-key\" as const,\n id: new Uint8Array(id),\n })),\n }),\n extensions: { prf: { eval: prfEval(input.salts) } },\n },\n }),\n );\n } catch (error) {\n mapCeremonyError(error);\n }\n const { response } = credential;\n if (!(response instanceof AuthenticatorAssertionResponse)) {\n throw factorUnavailable(\"the authenticator returned no assertion response\");\n }\n if (!userVerified(response.authenticatorData)) {\n throw factorUnavailable(\"the passkey assertion was not user-verified\");\n }\n return readPrfResult(\n credential,\n credential.getClientExtensionResults().prf?.results,\n input.salts.second !== undefined,\n credentialSynced(response.authenticatorData),\n response.userHandle ?? undefined,\n );\n }\n\n return {\n create: async (input) => {\n const userId = new Uint8Array(16);\n userId[0] = input.purpose === \"wallet\" ? PASSKEY_PURPOSE_WALLET : PASSKEY_PURPOSE_VAULT;\n crypto.getRandomValues(userId.subarray(1));\n let credential: PublicKeyCredential;\n try {\n credential = checkCredential(\n await navigator.credentials.create({\n publicKey: {\n rp: { id: rp.id, name: rp.name },\n user: {\n id: userId,\n name: input.name,\n displayName: input.name,\n },\n challenge: crypto.getRandomValues(new Uint8Array(32)),\n pubKeyCredParams: [\n { type: \"public-key\", alg: -7 },\n { type: \"public-key\", alg: -257 },\n ],\n authenticatorSelection: {\n residentKey: \"required\",\n userVerification: \"required\",\n },\n extensions: { prf: { eval: prfEval(input.salts) } },\n },\n }),\n );\n } catch (error) {\n mapCeremonyError(error);\n }\n const { response } = credential;\n if (!(response instanceof AuthenticatorAttestationResponse)) {\n throw factorUnavailable(\"the authenticator returned no attestation response\");\n }\n const extensions = credential.getClientExtensionResults();\n if (extensions.prf?.enabled !== true) {\n throw factorUnavailable(\"the authenticator does not support the PRF extension\");\n }\n if (extensions.prf.results === undefined) {\n // Some authenticators omit creation-time PRF outputs: one assertion over the new\n // credential produces them (it is that response whose UV and BE/BS flags get read).\n return assert({\n credentialIds: [copyBufferSource(credential.rawId)],\n salts: input.salts,\n });\n }\n const authenticatorData = response.getAuthenticatorData();\n if (!userVerified(authenticatorData)) {\n throw factorUnavailable(\"the passkey creation was not user-verified\");\n }\n return readPrfResult(\n credential,\n extensions.prf.results,\n input.salts.second !== undefined,\n credentialSynced(authenticatorData),\n );\n },\n assert,\n };\n}\n","/**\n * The Web host: `createWebWallet` spawns the bundled kernel worker (dist/kernel.worker.js) whose\n * vault lives in the worker's own IndexedDB. Lock destroys the worker; the vault survives in\n * IndexedDB, so the respawned worker reloads it. `pagehide` locks immediately; the idle timer is\n * refreshed by user-presence signals, not request traffic. The WebAuthn ceremony runs on the main\n * thread.\n */\nimport { WalletError } from \"@qntx/wallet\";\nimport type { Wallet, WalletOptions } from \"@qntx/wallet\";\nimport { connectKernel } from \"@qntx/wallet/host\";\nimport type { MainPort } from \"@qntx/wallet/host\";\nimport type { ProtocolValue } from \"@qntx/wallet/protocol\";\n\nimport type { BrowserWorkerInit } from \"../shared.ts\";\nimport { createWebAuthnPasskey } from \"./passkey.ts\";\n\nconst LISTENER_OPTIONS = { capture: true, passive: true };\nconst ACTIVITY_EVENTS = [\"pointerdown\", \"pointermove\", \"keydown\", \"wheel\", \"touchstart\"];\n\n/**\n * The browser's user-presence source: direct input on the page, or returning to a visible tab.\n * Capture-phase passive listeners do a single boolean check per event — high-frequency signals\n * (scroll, pointermove) cost nothing. Returns the unsubscribe the wallet owns.\n */\nfunction subscribePresence(onActivity: () => void): () => void {\n for (const type of ACTIVITY_EVENTS) {\n globalThis.addEventListener(type, onActivity, LISTENER_OPTIONS);\n }\n const onVisible = (): void => {\n if (document.visibilityState === \"visible\") {\n onActivity();\n }\n };\n document.addEventListener(\"visibilitychange\", onVisible);\n return () => {\n for (const type of ACTIVITY_EVENTS) {\n globalThis.removeEventListener(type, onActivity, LISTENER_OPTIONS);\n }\n document.removeEventListener(\"visibilitychange\", onVisible);\n };\n}\n\nasync function bestEffort(task: () => Promise<unknown>): Promise<void> {\n try {\n await task();\n } catch {\n // Lifecycle hooks and persistence hints must never surface failures.\n }\n}\n\nexport async function createWebWallet(options: WalletOptions): Promise<Wallet> {\n const { autoLock } = options;\n if (autoLock.platform !== \"web\") {\n throw new WalletError(\"invalid-input\", \"createWebWallet accepts only the web autoLock policy\");\n }\n // The page sends only the idle choice; the worker owns the maximum session duration.\n const init: BrowserWorkerInit = { t: \"init\", idleMinutes: autoLock.idleMinutes };\n const ceremony = createWebAuthnPasskey({\n id: options.passkey.rpId,\n name: options.passkey.rpName,\n });\n\n const spawn = (): MainPort => {\n // Keep this exact `new Worker(new URL(\"…\", import.meta.url))` shape: bundlers (Vite,\n // webpack, Parcel, esbuild-based tools) recognize only this literal form as a worker entry and\n // bundle the kernel with its chunks and spark.wasm. A precomputed URL is copied as one opaque\n // asset, and the worker then dies loading its first chunk.\n const worker = new Worker(new URL(\"kernel.worker.js\", import.meta.url), {\n type: \"module\",\n name: \"qntx-wallet\",\n });\n // oxlint-disable-next-line unicorn/require-post-message-target-origin -- Worker.postMessage takes a transfer list, not an origin\n worker.postMessage(init); // the kernel validates it strictly before startKernel\n return {\n post: (message: ProtocolValue, transfer: ArrayBuffer[]) => {\n worker.postMessage(message, transfer);\n },\n listen: (handler) => {\n const listener = (event: MessageEvent): void => {\n handler(event.data);\n };\n worker.addEventListener(\"message\", listener);\n return () => {\n worker.removeEventListener(\"message\", listener);\n };\n },\n onExit: (handler) => {\n // Web Workers have no exit event; error/messageerror are the only death signals.\n const listener = (): void => {\n handler(\"crashed\");\n };\n worker.addEventListener(\"error\", listener);\n worker.addEventListener(\"messageerror\", listener);\n return () => {\n worker.removeEventListener(\"error\", listener);\n worker.removeEventListener(\"messageerror\", listener);\n };\n },\n terminate: () => {\n worker.terminate();\n },\n };\n };\n\n const wallet = await connectKernel({\n factors: \"software\",\n spawn,\n passkey: options.passkey,\n ceremony,\n setTimer: (delayMs, fn) => {\n const timer = globalThis.setTimeout(fn, delayMs);\n return () => {\n globalThis.clearTimeout(timer);\n };\n },\n presence: subscribePresence,\n onDestroy: () => {\n globalThis.removeEventListener(\"pagehide\", onPageHide);\n },\n });\n\n // Page lifecycle: lock on pagehide. A BFCache-frozen page still runs the queued locked-event\n // handler on restore, so the wallet respawns then — terminating the worker directly would\n // leave the wallet pointing at a dead port.\n const onPageHide = (): void => {\n void bestEffort(async () => wallet.vault.lock());\n };\n globalThis.addEventListener(\"pagehide\", onPageHide);\n\n // Ask the browser to persist the vault once it exists — a denied request is fine.\n let persisted = false;\n const unsubscribe = wallet.subscribe(() => {\n if (!persisted && wallet.getSnapshot().vault !== \"uninitialized\") {\n persisted = true;\n unsubscribe();\n void bestEffort(async () => navigator.storage.persist());\n }\n });\n\n return wallet;\n}\n\nexport { createWebAuthnPasskey } from \"./passkey.ts\";\nexport type { WebAuthnRp } from \"./passkey.ts\";\n"],"mappings":";;;;;;;;;;AAiBA,MAAM,UAAU;AAIhB,SAAS,kBAAkB,SAA8B;CACvD,OAAO,IAAI,YAAY,sBAAsB,OAAO;AACtD;AAEA,SAAS,UAAU,SAA8B;CAC/C,OAAO,IAAI,YAAY,aAAa,OAAO;AAC7C;AAEA,SAAS,iBAAiB,OAAuB;CAC/C,IACE,iBAAiB,iBAChB,MAAM,SAAS,qBAAqB,MAAM,SAAS,eAEpD,MAAM,UAAU,oCAAoC;CAEtD,IAAI,iBAAiB,aACnB,MAAM;CAER,MAAM,kBAAkB,6BAA6B;AACvD;;AAGA,SAAS,aAAa,mBAAyC;CAC7D,OACE,kBAAkB,aAAa,OAE9B,IAAI,SAAS,iBAAiB,CAAC,CAAC,SAAS,EAAE,IAAI,aAAa;AAEjE;;;;;AAMA,SAAS,iBAAiB,mBAAyC;CACjE,OACE,kBAAkB,aAAa,OAE9B,IAAI,SAAS,iBAAiB,CAAC,CAAC,SAAS,EAAE,IAAK,QAAyB;AAE9E;AAEA,SAAS,QAAQ,OAAoD;CAGnE,OAAO,MAAM,WAAW,KAAA,IACpB,EAAE,OAAO,IAAI,WAAW,MAAM,KAAK,EAAE,IACrC;EAAE,OAAO,IAAI,WAAW,MAAM,KAAK;EAAG,QAAQ,IAAI,WAAW,MAAM,MAAM;CAAE;AACjF;;AAGA,SAAS,iBAAiB,QAAkC;CAC1D,MAAM,OACJ,kBAAkB,cACd,IAAI,WAAW,MAAM,IACrB,IAAI,WAAW,OAAO,QAAQ,OAAO,YAAY,OAAO,UAAU;CACxE,OAAO,IAAI,WAAW,IAAI;AAC5B;AAEA,SAAS,cACP,YACA,SACA,YACA,QACA,YACW;CACX,MAAM,QAAQ,SAAS;CACvB,MAAM,SAAS,SAAS;CACxB,IAAI,UAAU,KAAA,KAAc,cAAc,WAAW,KAAA,GACnD,MAAM,kBAAkB,oCAAoC;CAG9D,OAAO;EACL,cAAc,iBAAiB,WAAW,KAAK;EAC/C,OAAO,iBAAiB,KAAK;EAC7B,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,iBAAiB,MAAM,EAAE;EACnE,GAAI,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,iBAAiB,UAAU,EAAE;EAC/E;CACF;AACF;AAEA,SAAS,gBAAgB,YAA0C;CACjE,IAAI,EAAE,sBAAsB,sBAC1B,MAAM,kBAAkB,qDAAqD;CAE/E,OAAO;AACT;;;;;;AAOA,SAAgB,sBAAsB,IAAiC;CACrE,eAAe,OAAO,OAGC;EACrB,IAAI;EACJ,IAAI;GACF,aAAa,gBACX,MAAM,UAAU,YAAY,IAAI,EAC9B,WAAW;IACT,MAAM,GAAG;IACT,WAAW,OAAO,gCAAgB,IAAI,WAAW,EAAE,CAAC;IACpD,kBAAkB;IAClB,GAAI,MAAM,kBAAkB,KAAA,IACxB,CAAC,IACD,EACE,kBAAkB,MAAM,cAAc,KAAK,QAAQ;KACjD,MAAM;KACN,IAAI,IAAI,WAAW,EAAE;IACvB,EAAE,EACJ;IACJ,YAAY,EAAE,KAAK,EAAE,MAAM,QAAQ,MAAM,KAAK,EAAE,EAAE;GACpD,EACF,CAAC,CACH;EACF,SAAS,OAAO;GACd,iBAAiB,KAAK;EACxB;EACA,MAAM,EAAE,aAAa;EACrB,IAAI,EAAE,oBAAoB,iCACxB,MAAM,kBAAkB,kDAAkD;EAE5E,IAAI,CAAC,aAAa,SAAS,iBAAiB,GAC1C,MAAM,kBAAkB,6CAA6C;EAEvE,OAAO,cACL,YACA,WAAW,0BAA0B,CAAC,CAAC,KAAK,SAC5C,MAAM,MAAM,WAAW,KAAA,GACvB,iBAAiB,SAAS,iBAAiB,GAC3C,SAAS,cAAc,KAAA,CACzB;CACF;CAEA,OAAO;EACL,QAAQ,OAAO,UAAU;GACvB,MAAM,yBAAS,IAAI,WAAW,EAAE;GAChC,OAAO,KAAK,MAAM,YAAY,WAAW,yBAAyB;GAClE,OAAO,gBAAgB,OAAO,SAAS,CAAC,CAAC;GACzC,IAAI;GACJ,IAAI;IACF,aAAa,gBACX,MAAM,UAAU,YAAY,OAAO,EACjC,WAAW;KACT,IAAI;MAAE,IAAI,GAAG;MAAI,MAAM,GAAG;KAAK;KAC/B,MAAM;MACJ,IAAI;MACJ,MAAM,MAAM;MACZ,aAAa,MAAM;KACrB;KACA,WAAW,OAAO,gCAAgB,IAAI,WAAW,EAAE,CAAC;KACpD,kBAAkB,CAChB;MAAE,MAAM;MAAc,KAAK;KAAG,GAC9B;MAAE,MAAM;MAAc,KAAK;KAAK,CAClC;KACA,wBAAwB;MACtB,aAAa;MACb,kBAAkB;KACpB;KACA,YAAY,EAAE,KAAK,EAAE,MAAM,QAAQ,MAAM,KAAK,EAAE,EAAE;IACpD,EACF,CAAC,CACH;GACF,SAAS,OAAO;IACd,iBAAiB,KAAK;GACxB;GACA,MAAM,EAAE,aAAa;GACrB,IAAI,EAAE,oBAAoB,mCACxB,MAAM,kBAAkB,oDAAoD;GAE9E,MAAM,aAAa,WAAW,0BAA0B;GACxD,IAAI,WAAW,KAAK,YAAY,MAC9B,MAAM,kBAAkB,sDAAsD;GAEhF,IAAI,WAAW,IAAI,YAAY,KAAA,GAG7B,OAAO,OAAO;IACZ,eAAe,CAAC,iBAAiB,WAAW,KAAK,CAAC;IAClD,OAAO,MAAM;GACf,CAAC;GAEH,MAAM,oBAAoB,SAAS,qBAAqB;GACxD,IAAI,CAAC,aAAa,iBAAiB,GACjC,MAAM,kBAAkB,4CAA4C;GAEtE,OAAO,cACL,YACA,WAAW,IAAI,SACf,MAAM,MAAM,WAAW,KAAA,GACvB,iBAAiB,iBAAiB,CACpC;EACF;EACA;CACF;AACF;;;;;;;;;;AC3MA,MAAM,mBAAmB;CAAE,SAAS;CAAM,SAAS;AAAK;AACxD,MAAM,kBAAkB;CAAC;CAAe;CAAe;CAAW;CAAS;AAAY;;;;;;AAOvF,SAAS,kBAAkB,YAAoC;CAC7D,KAAK,MAAM,QAAQ,iBACjB,WAAW,iBAAiB,MAAM,YAAY,gBAAgB;CAEhE,MAAM,kBAAwB;EAC5B,IAAI,SAAS,oBAAoB,WAC/B,WAAW;CAEf;CACA,SAAS,iBAAiB,oBAAoB,SAAS;CACvD,aAAa;EACX,KAAK,MAAM,QAAQ,iBACjB,WAAW,oBAAoB,MAAM,YAAY,gBAAgB;EAEnE,SAAS,oBAAoB,oBAAoB,SAAS;CAC5D;AACF;AAEA,eAAe,WAAW,MAA6C;CACrE,IAAI;EACF,MAAM,KAAK;CACb,QAAQ,CAER;AACF;AAEA,eAAsB,gBAAgB,SAAyC;CAC7E,MAAM,EAAE,aAAa;CACrB,IAAI,SAAS,aAAa,OACxB,MAAM,IAAI,YAAY,iBAAiB,sDAAsD;CAG/F,MAAM,OAA0B;EAAE,GAAG;EAAQ,aAAa,SAAS;CAAY;CAC/E,MAAM,WAAW,sBAAsB;EACrC,IAAI,QAAQ,QAAQ;EACpB,MAAM,QAAQ,QAAQ;CACxB,CAAC;CAED,MAAM,cAAwB;EAK5B,MAAM,SAAS,IAAI,OAAO,IAAI,IAAI,oBAAoB,YAAY,GAAG,GAAG;GACtE,MAAM;GACN,MAAM;EACR,CAAC;EAED,OAAO,YAAY,IAAI;EACvB,OAAO;GACL,OAAO,SAAwB,aAA4B;IACzD,OAAO,YAAY,SAAS,QAAQ;GACtC;GACA,SAAS,YAAY;IACnB,MAAM,YAAY,UAA8B;KAC9C,QAAQ,MAAM,IAAI;IACpB;IACA,OAAO,iBAAiB,WAAW,QAAQ;IAC3C,aAAa;KACX,OAAO,oBAAoB,WAAW,QAAQ;IAChD;GACF;GACA,SAAS,YAAY;IAEnB,MAAM,iBAAuB;KAC3B,QAAQ,SAAS;IACnB;IACA,OAAO,iBAAiB,SAAS,QAAQ;IACzC,OAAO,iBAAiB,gBAAgB,QAAQ;IAChD,aAAa;KACX,OAAO,oBAAoB,SAAS,QAAQ;KAC5C,OAAO,oBAAoB,gBAAgB,QAAQ;IACrD;GACF;GACA,iBAAiB;IACf,OAAO,UAAU;GACnB;EACF;CACF;CAEA,MAAM,SAAS,MAAM,cAAc;EACjC,SAAS;EACT;EACA,SAAS,QAAQ;EACjB;EACA,WAAW,SAAS,OAAO;GACzB,MAAM,QAAQ,WAAW,WAAW,IAAI,OAAO;GAC/C,aAAa;IACX,WAAW,aAAa,KAAK;GAC/B;EACF;EACA,UAAU;EACV,iBAAiB;GACf,WAAW,oBAAoB,YAAY,UAAU;EACvD;CACF,CAAC;CAKD,MAAM,mBAAyB;EAC7B,WAAgB,YAAY,OAAO,MAAM,KAAK,CAAC;CACjD;CACA,WAAW,iBAAiB,YAAY,UAAU;CAGlD,IAAI,YAAY;CAChB,MAAM,cAAc,OAAO,gBAAgB;EACzC,IAAI,CAAC,aAAa,OAAO,YAAY,CAAC,CAAC,UAAU,iBAAiB;GAChE,YAAY;GACZ,YAAY;GACZ,WAAgB,YAAY,UAAU,QAAQ,QAAQ,CAAC;EACzD;CACF,CAAC;CAED,OAAO;AACT"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"backends": {
|
|
5
5
|
"spark": {
|
|
6
6
|
"abiVersion": 1,
|
|
7
|
-
"wasmIntegrity": "sha256-
|
|
7
|
+
"wasmIntegrity": "sha256-OBAVP0QjuY/JyeQh6Ay3XnczAKsMYrWoHdCJ73RSPeQ="
|
|
8
8
|
}
|
|
9
9
|
},
|
|
10
10
|
"libraries": [
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"name": "@qntx/nostr",
|
|
28
|
-
"version": "0.15.
|
|
29
|
-
"integrity": "sha512-
|
|
28
|
+
"version": "0.15.3",
|
|
29
|
+
"integrity": "sha512-AefcRXe/3basIgDA0gBGzzQ2Y9k131SfrU0hZ+eJqfLzCSzsVJyum5XKzffJ74VU6FroQPbS/fSS7W5mGd22ow=="
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"name": "@qntx/spark",
|
|
33
|
-
"version": "0.
|
|
34
|
-
"integrity": "sha512-
|
|
33
|
+
"version": "0.5.2",
|
|
34
|
+
"integrity": "sha512-x5J1vJCuUdW5ANldVuRJRu5qczxkENhnZqEWH5PQt7dKKGqJbuLvsV6EyphKbf0MoYYM2kxWalW0NCzoXojamQ=="
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"name": "@qntx/spark-wasm",
|
|
38
|
-
"version": "0.
|
|
39
|
-
"integrity": "sha512
|
|
38
|
+
"version": "0.5.2",
|
|
39
|
+
"integrity": "sha512-0ZgjgOeIAMvWNmFq/StiG6ymQKvFRJkTBTjkfda04ooeBv5/32p+u39CFeN1G0hxnCBiDYe5FOAkrrGXVZ5VxQ=="
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
"name": "@scure/base",
|
|
@@ -57,52 +57,45 @@
|
|
|
57
57
|
"name": "@scure/btc-signer",
|
|
58
58
|
"version": "2.4.1",
|
|
59
59
|
"integrity": "sha512-ch6h66o9nPhEoe/KciMMUJeH2MBPB8UAJI1if35Fxl6885lKTsc9YoCRqTW0/RjI0Vv9GSg1tmKtHHODyAw81g=="
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "micro-key-producer",
|
|
63
|
+
"version": "0.10.2",
|
|
64
|
+
"integrity": "sha512-ApVEYeOhHwC4fsf54Jdt3Cf21eYfib/9GANrZCjlMCl1Sa71cXncS4J6wQ3IlbchBCvulOjQa2UoWf6/aE9XgA=="
|
|
60
65
|
}
|
|
61
66
|
],
|
|
62
67
|
"bundles": [
|
|
63
|
-
{
|
|
64
|
-
"file": "worker-30nrftxx.js",
|
|
65
|
-
"sha256": "d550424185659b7a49f324b4e5327620892a1318947202216a042f627346338c"
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
"file": "signer-tgawen1v.js",
|
|
69
|
-
"sha256": "fabee6d12dda04cbd9ccb4ec95ae69c15cd17ac5a5dc3a684098a4b7394a97af"
|
|
70
|
-
},
|
|
71
68
|
{
|
|
72
69
|
"file": "kernel.worker.js",
|
|
73
|
-
"sha256": "
|
|
70
|
+
"sha256": "50ca92701265a22ae4206c83aa4f1885fd4124b9e35d94983ed5ce2b387f58b9"
|
|
74
71
|
},
|
|
75
72
|
{
|
|
76
|
-
"file": "browser-
|
|
77
|
-
"sha256": "
|
|
73
|
+
"file": "browser-1vpxmqwn.js",
|
|
74
|
+
"sha256": "e5afdeec6f071a87cb6a1040ddeb1b9995d53ed2cea239f8f9424dc1d3df601b"
|
|
78
75
|
},
|
|
79
76
|
{
|
|
80
77
|
"file": "kernel.node.js",
|
|
81
|
-
"sha256": "
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"file": "index-cgstn3zw.js",
|
|
85
|
-
"sha256": "ba08ed6a721c04343273ada887bdb946aa65e9d543d0f7af87ede2605a2d246e"
|
|
78
|
+
"sha256": "e2f99d5c064f2627925d679d4d3a4420749d2c0a70b5f49e3916e17125683a35"
|
|
86
79
|
},
|
|
87
80
|
{
|
|
88
|
-
"file": "node-
|
|
89
|
-
"sha256": "
|
|
81
|
+
"file": "node-gsare3mw.js",
|
|
82
|
+
"sha256": "163a8aeb01c8d699a7567eecb90c6d5c859151d1e7322c3be0487eb13940b9c7"
|
|
90
83
|
},
|
|
91
84
|
{
|
|
92
|
-
"file": "worker-
|
|
93
|
-
"sha256": "
|
|
85
|
+
"file": "worker-366p4fej.js",
|
|
86
|
+
"sha256": "449410efad818a8f1dcfd02840375c1748c61294ad172e4c6c57e9d1d7c81283"
|
|
94
87
|
},
|
|
95
88
|
{
|
|
96
|
-
"file": "
|
|
97
|
-
"sha256": "
|
|
89
|
+
"file": "signer-gs57sjgx.js",
|
|
90
|
+
"sha256": "e83ab645d9efc41a821e572768e4b28a7902336ccfa692c2101755284b2a7d42"
|
|
98
91
|
},
|
|
99
92
|
{
|
|
100
|
-
"file": "node-
|
|
101
|
-
"sha256": "
|
|
93
|
+
"file": "node-366p4fej.js",
|
|
94
|
+
"sha256": "449410efad818a8f1dcfd02840375c1748c61294ad172e4c6c57e9d1d7c81283"
|
|
102
95
|
},
|
|
103
96
|
{
|
|
104
|
-
"file": "worker-
|
|
105
|
-
"sha256": "
|
|
97
|
+
"file": "worker-gsare3mw.js",
|
|
98
|
+
"sha256": "163a8aeb01c8d699a7567eecb90c6d5c859151d1e7322c3be0487eb13940b9c7"
|
|
106
99
|
}
|
|
107
100
|
]
|
|
108
101
|
}
|