mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-03 11:22:25 +00:00
feat(storage): Blossom Phase 0 — flag + vendored SDK + service skeleton (#30)
Phase 0 of docs/blossom-plan.md. No UX; off-by-default ("off = no trace").
- Vendored: pinned [email protected] bundled by scripts/build-blossom-sdk.mjs
→ vendor/blossom-client-sdk.bundle.min.js (esbuild, browser ESM, ~27KB). The
optional @cashu/cashu-ts peer dep (BUD-07 payments, only reached on a lazy 402
branch bitvid never hits) is aliased to a throwing stub. `npm run build:blossom-sdk`.
- Flag: FEATURE_BLOSSOM_STORAGE wired like FEATURE_AUDIO_INGEST / _BITCOIN_CONNECT
(instance-config → config → constants), default FALSE.
- Service: js/services/blossomService.js implements the same
uploadFile({…}) → {url,key} contract as s3UploadService (so a later
isBlossomProvider branch drops into mediaUploader). Lazy-loads the SDK; core
path = createUploadAuth(signer, sha256) + multiServerUpload (BUD-11 auth via
bitvid's existing signer, BUD-04 mirror). Torrent parity + result-shape mapping +
mediaUploader wiring + Storage-pane config land in Phase 1.
Tests: tests/blossom-service.test.mjs (5 — availability gate + input guards).
Lint + build clean. Nothing wired into the app yet.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
b7c6be830f
commit
02e60f1842
12 files changed
+363
No files matched your search
@@ -276,6 +276,19 @@ export const FEATURE_AUDIO_INGEST = false;
|
||||
*/
|
||||
export const FEATURE_BITCOIN_CONNECT = true;
|
||||
|
||||
/**
|
||||
* Whether this deployment offers Blossom (nostr-native blob storage) as a
|
||||
* first-class upload provider alongside Cloudflare R2 / generic S3 / Backblaze B2
|
||||
* (docs/blossom-plan.md, TODO #30). Blossom uploads authorize with the user's
|
||||
* existing nostr signer (kind-24242 auth events) instead of S3 access keys, and
|
||||
* mirror to multiple servers for resilience.
|
||||
*
|
||||
* Defaults to `false` ("off = no trace"): with it off the Storage pane shows only
|
||||
* R2 / S3 / B2 as today and the vendored Blossom SDK is never imported. Flip to
|
||||
* `true` to expose the Blossom provider.
|
||||
*/
|
||||
export const FEATURE_BLOSSOM_STORAGE = false;
|
||||
|
||||
/**
|
||||
* Card liveness visibility policy — what a non-owner video card does while its
|
||||
* CDN/WebTorrent liveness probes are still running:
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
FEATURE_NIP71_INGEST,
|
||||
FEATURE_AUDIO_INGEST,
|
||||
FEATURE_BITCOIN_CONNECT,
|
||||
FEATURE_BLOSSOM_STORAGE,
|
||||
CARD_LIVENESS_POLICY,
|
||||
LIVENESS_PROBE_PREFETCH_MARGIN,
|
||||
DEFAULT_TRUST_SEED_NPUBS,
|
||||
@@ -125,6 +126,7 @@ export { ALLOW_NSFW_CONTENT };
|
||||
export { FEATURE_NIP71_INGEST };
|
||||
export { FEATURE_AUDIO_INGEST };
|
||||
export { FEATURE_BITCOIN_CONNECT };
|
||||
export { FEATURE_BLOSSOM_STORAGE };
|
||||
export { CARD_LIVENESS_POLICY };
|
||||
export { LIVENESS_PROBE_PREFETCH_MARGIN };
|
||||
export { DEFAULT_TRUST_SEED_NPUBS };
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
FEATURE_NIP71_INGEST as CONFIG_FEATURE_NIP71_INGEST,
|
||||
FEATURE_AUDIO_INGEST as CONFIG_FEATURE_AUDIO_INGEST,
|
||||
FEATURE_BITCOIN_CONNECT as CONFIG_FEATURE_BITCOIN_CONNECT,
|
||||
FEATURE_BLOSSOM_STORAGE as CONFIG_FEATURE_BLOSSOM_STORAGE,
|
||||
CARD_LIVENESS_POLICY as CONFIG_CARD_LIVENESS_POLICY,
|
||||
LIVENESS_PROBE_PREFETCH_MARGIN as CONFIG_LIVENESS_PROBE_PREFETCH_MARGIN,
|
||||
IS_DEV_MODE,
|
||||
@@ -203,6 +204,9 @@ const DEFAULT_FLAGS = Object.freeze({
|
||||
// Bitcoin Connect NWC "Connect wallet" flow (docs/bitcoin-connect-plan.md, #61).
|
||||
// Default OFF; only an explicit `true` in the instance config enables it.
|
||||
FEATURE_BITCOIN_CONNECT: CONFIG_FEATURE_BITCOIN_CONNECT === true,
|
||||
// Blossom (nostr-native blob storage) provider (docs/blossom-plan.md, #30).
|
||||
// Default OFF; only an explicit `true` in the instance config enables it.
|
||||
FEATURE_BLOSSOM_STORAGE: CONFIG_FEATURE_BLOSSOM_STORAGE === true,
|
||||
FEATURE_HASHTAG_PREFERENCES: false,
|
||||
FEATURE_TRUST_SEEDS: true, // Rollback: disable to drop baseline trust seeds without code changes.
|
||||
FEATURE_TRUSTED_HIDE_CONTROLS: true,
|
||||
@@ -231,6 +235,7 @@ const runtimeFlags = (() => {
|
||||
FEATURE_NIP71_INGEST: DEFAULT_FLAGS.FEATURE_NIP71_INGEST,
|
||||
FEATURE_AUDIO_INGEST: DEFAULT_FLAGS.FEATURE_AUDIO_INGEST,
|
||||
FEATURE_BITCOIN_CONNECT: DEFAULT_FLAGS.FEATURE_BITCOIN_CONNECT,
|
||||
FEATURE_BLOSSOM_STORAGE: DEFAULT_FLAGS.FEATURE_BLOSSOM_STORAGE,
|
||||
FEATURE_HASHTAG_PREFERENCES: DEFAULT_FLAGS.FEATURE_HASHTAG_PREFERENCES,
|
||||
FEATURE_TRUST_SEEDS: DEFAULT_FLAGS.FEATURE_TRUST_SEEDS,
|
||||
FEATURE_TRUSTED_HIDE_CONTROLS: DEFAULT_FLAGS.FEATURE_TRUSTED_HIDE_CONTROLS,
|
||||
@@ -286,6 +291,11 @@ export let FEATURE_BITCOIN_CONNECT = coerceBoolean(
|
||||
DEFAULT_FLAGS.FEATURE_BITCOIN_CONNECT
|
||||
);
|
||||
|
||||
export let FEATURE_BLOSSOM_STORAGE = coerceBoolean(
|
||||
runtimeFlags.FEATURE_BLOSSOM_STORAGE,
|
||||
DEFAULT_FLAGS.FEATURE_BLOSSOM_STORAGE
|
||||
);
|
||||
|
||||
export let FEATURE_HASHTAG_PREFERENCES = coerceBoolean(
|
||||
runtimeFlags.FEATURE_HASHTAG_PREFERENCES,
|
||||
DEFAULT_FLAGS.FEATURE_HASHTAG_PREFERENCES
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
// Blossom (nostr-native blob storage) upload service — Phase 0 skeleton.
|
||||
//
|
||||
// Implements the same `uploadFile({…}) → { url, key, … }` contract as
|
||||
// s3UploadService / r2Service (see js/ui/components/mediaUploader.js), so a later
|
||||
// `isBlossomProvider` branch can route to it with no change downstream. Auth is a
|
||||
// signed kind-24242 nostr event (BUD-11) built from bitvid's existing signer — no
|
||||
// access keys. Uploads mirror to multiple servers for resilience (BUD-04).
|
||||
//
|
||||
// Phase 0: the vendored SDK loader, availability gate, and the core upload path
|
||||
// exist but nothing is wired into mediaUploader yet, and the flag is off by
|
||||
// default. Phase 1 completes torrent parity (magnet/infoHash), the result-shape
|
||||
// mapping, and the mediaUploader routing + Storage-pane config. See
|
||||
// docs/blossom-plan.md.
|
||||
import { FEATURE_BLOSSOM_STORAGE } from "../constants.js";
|
||||
import { userLogger } from "../utils/logger.js";
|
||||
|
||||
// Vendored, pinned blossom-client-sdk bundle (scripts/build-blossom-sdk.mjs).
|
||||
// Lazy-imported so its (small) weight never touches the main bundle.
|
||||
const BLOSSOM_SDK_BUNDLE_URL = "../../vendor/blossom-client-sdk.bundle.min.js";
|
||||
|
||||
function normalizeServerList(servers) {
|
||||
const out = [];
|
||||
const seen = new Set();
|
||||
for (const entry of Array.isArray(servers) ? servers : []) {
|
||||
const url = typeof entry === "string" ? entry.trim() : "";
|
||||
if (!url || seen.has(url)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(url);
|
||||
out.push(url);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export class BlossomService {
|
||||
constructor(deps = {}) {
|
||||
this.deps = deps;
|
||||
this._sdkPromise = null;
|
||||
}
|
||||
|
||||
// On/off gate — off ⇒ the SDK is never imported and callers should not route
|
||||
// uploads here.
|
||||
isAvailable() {
|
||||
return FEATURE_BLOSSOM_STORAGE === true;
|
||||
}
|
||||
|
||||
// Lazy-load the vendored SDK once.
|
||||
async loadSdk() {
|
||||
if (this._sdkPromise) {
|
||||
return this._sdkPromise;
|
||||
}
|
||||
this._sdkPromise = import(BLOSSOM_SDK_BUNDLE_URL).catch((error) => {
|
||||
this._sdkPromise = null;
|
||||
throw error;
|
||||
});
|
||||
return this._sdkPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a file to one or more Blossom servers, mirroring for resilience.
|
||||
*
|
||||
* @param {object} params
|
||||
* @param {File|Blob} params.file - the blob to upload.
|
||||
* @param {string[]} params.servers - Blossom server base URLs (first = primary).
|
||||
* @param {(draft: object) => Promise<object>} params.signer - bitvid's active
|
||||
* signer (BUD-11 kind-24242 auth). Same shape the app already uses.
|
||||
* @param {string} [params.type] - the auth `t` verb (default "upload").
|
||||
* @param {(pct:number)=>void} [params.onProgress]
|
||||
* @returns {Promise<{ url: string, key: string, servers: string[], descriptors: object[] }>}
|
||||
*
|
||||
* NOTE (Phase 1): torrent parity (magnet/infoHash), storagePointer, and the full
|
||||
* mediaUploader result shape are added when this is wired into the upload flow.
|
||||
*/
|
||||
async uploadFile({ file, servers, signer, type = "upload", onProgress } = {}) {
|
||||
if (!file) {
|
||||
throw new Error("Blossom upload requires a file.");
|
||||
}
|
||||
const serverList = normalizeServerList(servers);
|
||||
if (serverList.length === 0) {
|
||||
throw new Error("Blossom upload requires at least one server URL.");
|
||||
}
|
||||
if (typeof signer !== "function") {
|
||||
throw new Error("Blossom upload requires a signer (a signed-event function).");
|
||||
}
|
||||
|
||||
const sdk = await this.loadSdk();
|
||||
const { multiServerUpload, createUploadAuth } = sdk;
|
||||
if (
|
||||
typeof multiServerUpload !== "function" ||
|
||||
typeof createUploadAuth !== "function"
|
||||
) {
|
||||
throw new Error("Blossom SDK is missing expected exports.");
|
||||
}
|
||||
|
||||
// One auth per action, scoped to the blob's sha256; the SDK reuses it across
|
||||
// the mirror set so a multi-server upload isn't N signer prompts.
|
||||
const results = await multiServerUpload(serverList, file, {
|
||||
onAuth: (server, sha256) =>
|
||||
createUploadAuth(signer, sha256, { type }),
|
||||
onProgress:
|
||||
typeof onProgress === "function" ? onProgress : undefined,
|
||||
});
|
||||
|
||||
// results: Map<serverUrl, BlobDescriptor{ url, sha256, size, type, … }>
|
||||
const descriptors = Array.from(results?.values?.() || []);
|
||||
const primary =
|
||||
descriptors.find((d) => typeof d?.url === "string" && d.url) || null;
|
||||
if (!primary) {
|
||||
throw new Error("Blossom upload did not return a usable blob URL.");
|
||||
}
|
||||
|
||||
return {
|
||||
url: primary.url,
|
||||
key: primary.sha256 || "",
|
||||
servers: serverList,
|
||||
descriptors,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const blossomService = new BlossomService();
|
||||
|
||||
export default blossomService;
|
||||
Generated
+39
@@ -22,6 +22,7 @@
|
||||
"devDependencies": {
|
||||
"@getalby/bitcoin-connect": "3.12.3",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"blossom-client-sdk": "5.0.0",
|
||||
"cssnano": "^7.1.2",
|
||||
"fake-indexeddb": "^6.2.5",
|
||||
"jsdom": "^28.1.0",
|
||||
@@ -1862,6 +1863,44 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/blossom-client-sdk": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/blossom-client-sdk/-/blossom-client-sdk-5.0.0.tgz",
|
||||
"integrity": "sha512-xz7w6eBQv6uiNK9ogh5AymnzCAohiU029XySpKxJyG7RsDHkoru+xoZhY55tiIOyWFX/5s8pip4hVh5hO1PN/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^1.8.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cashu/cashu-ts": "^2.4.3",
|
||||
"hls.js": "^1.6.16"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cashu/cashu-ts": {
|
||||
"optional": true
|
||||
},
|
||||
"hls.js": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/blossom-client-sdk/node_modules/@noble/hashes": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"build:beacon:bundle": "node scripts/build-beacon.mjs",
|
||||
"build:beacon": "npm run build:beacon:bundle && npm run lint:inline-styles",
|
||||
"build:bitcoin-connect": "node scripts/build-bitcoin-connect.mjs",
|
||||
"build:blossom-sdk": "node scripts/build-blossom-sdk.mjs",
|
||||
"ci:beacon": "npm run build:beacon",
|
||||
"format": "prettier --cache --write css/tailwind.source.css css/tokens.css css/docs.css \"docs/**/*.html\" tailwind.config.cjs README.md CONTRIBUTING.md",
|
||||
"audit": "node scripts/daily-design-system-audit.mjs",
|
||||
@@ -60,6 +61,7 @@
|
||||
"devDependencies": {
|
||||
"@getalby/bitcoin-connect": "3.12.3",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"blossom-client-sdk": "5.0.0",
|
||||
"cssnano": "^7.1.2",
|
||||
"fake-indexeddb": "^6.2.5",
|
||||
"jsdom": "^28.1.0",
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Bundles blossom-client-sdk into a single self-contained ESM file that bitvid
|
||||
// lazy-imports behind FEATURE_BLOSSOM_STORAGE (docs/blossom-plan.md, TODO #30).
|
||||
// Mirrors the bitcoin-connect / floating-ui vendor steps. Pinned via the exact
|
||||
// devDependency version in package.json.
|
||||
import { build } from "esbuild";
|
||||
import { mkdir, copyFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const entryPoint = path.join(repoRoot, "scripts/vendor/blossom-sdk-entry.mjs");
|
||||
const vendorDir = path.join(repoRoot, "vendor");
|
||||
const outFile = path.join(vendorDir, "blossom-client-sdk.bundle.min.js");
|
||||
const licenseSrc = path.join(
|
||||
repoRoot,
|
||||
"node_modules/blossom-client-sdk/LICENSE.txt",
|
||||
);
|
||||
const licenseOut = path.join(vendorDir, "blossom-client-sdk.LICENSE");
|
||||
|
||||
await mkdir(vendorDir, { recursive: true });
|
||||
|
||||
await build({
|
||||
bundle: true,
|
||||
entryPoints: [entryPoint],
|
||||
outfile: outFile,
|
||||
format: "esm",
|
||||
splitting: false,
|
||||
platform: "browser",
|
||||
target: ["es2020"],
|
||||
minify: true,
|
||||
sourcemap: false,
|
||||
define: {
|
||||
"process.env.NODE_ENV": '"production"',
|
||||
global: "globalThis",
|
||||
},
|
||||
// @cashu/cashu-ts is an OPTIONAL peer dep the SDK only lazy-imports on the
|
||||
// BUD-07 payment branch (HTTP 402). bitvid uses free Blossom servers, so alias
|
||||
// it to a throwing stub instead of pulling the dep.
|
||||
alias: {
|
||||
"@cashu/cashu-ts": path.join(repoRoot, "scripts/vendor/cashu-stub.mjs"),
|
||||
},
|
||||
logLevel: "info",
|
||||
});
|
||||
|
||||
try {
|
||||
await copyFile(licenseSrc, licenseOut);
|
||||
} catch (error) {
|
||||
console.warn("[build-blossom-sdk] could not copy LICENSE:", error.message);
|
||||
}
|
||||
|
||||
console.log("[build-blossom-sdk] wrote", path.relative(repoRoot, outFile));
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
// Vendor entry for blossom-client-sdk (hzrd149), bundled by
|
||||
// scripts/build-blossom-sdk.mjs into vendor/blossom-client-sdk.bundle.min.js so
|
||||
// bitvid can lazy-import it behind FEATURE_BLOSSOM_STORAGE without a runtime CDN.
|
||||
// Re-exports only the surface bitvid needs. See docs/blossom-plan.md.
|
||||
//
|
||||
// The action helpers live under the `./actions/*` subpaths (the root entry
|
||||
// namespaces them as `Actions`); auth + discovery are top-level modules.
|
||||
export {
|
||||
createUploadAuth,
|
||||
createMirrorAuth,
|
||||
createListAuth,
|
||||
encodeAuthorizationHeader,
|
||||
} from "blossom-client-sdk/auth";
|
||||
export {
|
||||
getServersFromServerListEvent,
|
||||
USER_BLOSSOM_SERVER_LIST_KIND,
|
||||
} from "blossom-client-sdk/nostr";
|
||||
export {
|
||||
multiServerUpload,
|
||||
multiServerMediaUpload,
|
||||
} from "blossom-client-sdk/actions/multi-server";
|
||||
export { uploadBlob } from "blossom-client-sdk/actions/upload";
|
||||
export { mirrorBlob } from "blossom-client-sdk/actions/mirror";
|
||||
export { listBlobs, iterateBlobs } from "blossom-client-sdk/actions/list";
|
||||
export { deleteBlob } from "blossom-client-sdk/actions/delete";
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
// Stub for the optional @cashu/cashu-ts peer dep, aliased in when bundling
|
||||
// blossom-client-sdk (scripts/build-blossom-sdk.mjs). The SDK only reaches for it
|
||||
// on the BUD-07 cashu-payment branch (a lazy `await import()` triggered by an HTTP
|
||||
// 402). bitvid targets free Blossom servers, so this never runs; the stubs throw
|
||||
// loudly if a payment-gated server is ever hit rather than silently misbehaving.
|
||||
function unsupported() {
|
||||
throw new Error(
|
||||
"Cashu payments (BUD-07) are not supported in the bitvid Blossom bundle",
|
||||
);
|
||||
}
|
||||
|
||||
export const getEncodedToken = unsupported;
|
||||
export const decodePaymentRequest = unsupported;
|
||||
export const getDecodedToken = unsupported;
|
||||
|
||||
export default {};
|
||||
@@ -0,0 +1,55 @@
|
||||
// Phase 0 skeleton tests for blossomService: the availability gate and the
|
||||
// input guards run without loading the vendored SDK (they throw first). Upload
|
||||
// round-trips against a real Blossom server are exercised in Phase 1. See
|
||||
// docs/blossom-plan.md / TODO #30.
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import blossomService, { BlossomService } from "../js/services/blossomService.js";
|
||||
|
||||
test("isAvailable reflects the FEATURE_BLOSSOM_STORAGE flag (off by default)", () => {
|
||||
assert.equal(blossomService.isAvailable(), false);
|
||||
});
|
||||
|
||||
test("uploadFile rejects a missing file before touching the SDK", async () => {
|
||||
const svc = new BlossomService();
|
||||
await assert.rejects(
|
||||
() => svc.uploadFile({ servers: ["https://blossom.example"], signer: async () => ({}) }),
|
||||
/requires a file/,
|
||||
);
|
||||
});
|
||||
|
||||
test("uploadFile rejects an empty server list", async () => {
|
||||
const svc = new BlossomService();
|
||||
await assert.rejects(
|
||||
() => svc.uploadFile({ file: new Blob(["x"]), servers: [], signer: async () => ({}) }),
|
||||
/at least one server/,
|
||||
);
|
||||
});
|
||||
|
||||
test("uploadFile rejects a non-function signer", async () => {
|
||||
const svc = new BlossomService();
|
||||
await assert.rejects(
|
||||
() =>
|
||||
svc.uploadFile({
|
||||
file: new Blob(["x"]),
|
||||
servers: ["https://blossom.example"],
|
||||
signer: null,
|
||||
}),
|
||||
/requires a signer/,
|
||||
);
|
||||
});
|
||||
|
||||
test("uploadFile de-dupes and trims the server list before requiring one", async () => {
|
||||
const svc = new BlossomService();
|
||||
// All-blank/dup servers collapse to empty → the "at least one server" guard.
|
||||
await assert.rejects(
|
||||
() =>
|
||||
svc.uploadFile({
|
||||
file: new Blob(["x"]),
|
||||
servers: [" ", "", " "],
|
||||
signer: async () => ({}),
|
||||
}),
|
||||
/at least one server/,
|
||||
);
|
||||
});
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 hzrd149
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
var _t=Object.defineProperty;var ue=(n,e,t)=>e in n?_t(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var $=(n,e)=>()=>(n&&(e=n(n=0)),e);var Et=(n,e)=>{for(var t in e)_t(n,t,{get:e[t],enumerable:!0})};var Q=(n,e,t)=>ue(n,typeof e!="symbol"?e+"":e,t);function he(n){return n instanceof Uint8Array||ArrayBuffer.isView(n)&&n.constructor.name==="Uint8Array"}function Y(n,...e){if(!he(n))throw new Error("Uint8Array expected");if(e.length>0&&!e.includes(n.length))throw new Error("Uint8Array expected of length "+e+", got length="+n.length)}function St(n,e=!0){if(n.destroyed)throw new Error("Hash instance has been destroyed");if(e&&n.finished)throw new Error("Hash#digest() has already been called")}function Mt(n,e){Y(n);let t=e.outputLen;if(n.length<t)throw new Error("digestInto() expects output buffer of length at least "+t)}function V(...n){for(let e=0;e<n.length;e++)n[e].fill(0)}function st(n){return new DataView(n.buffer,n.byteOffset,n.byteLength)}function R(n,e){return n<<32-e|n>>>e}function Ft(n){if(Y(n),fe)return n.toHex();let e="";for(let t=0;t<n.length;t++)e+=le[n[t]];return e}function de(n){if(typeof n!="string")throw new Error("string expected");return new Uint8Array(new TextEncoder().encode(n))}function Bt(n){return typeof n=="string"&&(n=de(n)),Y(n),n}function N(n){let e=r=>n().update(Bt(r)).digest(),t=n();return e.outputLen=t.outputLen,e.blockLen=t.blockLen,e.create=()=>n(),e}var fe,le,it,ct=$(()=>{fe=typeof Uint8Array.from([]).toHex=="function"&&typeof Uint8Array.fromHex=="function",le=Array.from({length:256},(n,e)=>e.toString(16).padStart(2,"0"));it=class{}});function xe(n,e,t,r){if(typeof n.setBigUint64=="function")return n.setBigUint64(e,t,r);let o=BigInt(32),i=BigInt(4294967295),a=Number(t>>o&i),s=Number(t&i),u=r?4:0,h=r?0:4;n.setUint32(e+u,a,r),n.setUint32(e+h,s,r)}function kt(n,e,t){return n&e^~n&t}function Dt(n,e,t){return n&e^n&t^e&t}var Z,_,M,E,S,jt=$(()=>{ct();Z=class extends it{constructor(e,t,r,o){super(),this.finished=!1,this.length=0,this.pos=0,this.destroyed=!1,this.blockLen=e,this.outputLen=t,this.padOffset=r,this.isLE=o,this.buffer=new Uint8Array(e),this.view=st(this.buffer)}update(e){St(this),e=Bt(e),Y(e);let{view:t,buffer:r,blockLen:o}=this,i=e.length;for(let a=0;a<i;){let s=Math.min(o-this.pos,i-a);if(s===o){let u=st(e);for(;o<=i-a;a+=o)this.process(u,a);continue}r.set(e.subarray(a,a+s),this.pos),this.pos+=s,a+=s,this.pos===o&&(this.process(t,0),this.pos=0)}return this.length+=e.length,this.roundClean(),this}digestInto(e){St(this),Mt(e,this),this.finished=!0;let{buffer:t,view:r,blockLen:o,isLE:i}=this,{pos:a}=this;t[a++]=128,V(this.buffer.subarray(a)),this.padOffset>o-a&&(this.process(r,0),a=0);for(let c=a;c<o;c++)t[c]=0;xe(r,o-8,BigInt(this.length*8),i),this.process(r,0);let s=st(e),u=this.outputLen;if(u%4)throw new Error("_sha2: outputLen should be aligned to 32bit");let h=u/4,d=this.get();if(h>d.length)throw new Error("_sha2: outputLen bigger than state");for(let c=0;c<h;c++)s.setUint32(4*c,d[c],i)}digest(){let{buffer:e,outputLen:t}=this;this.digestInto(e);let r=e.slice(0,t);return this.destroy(),r}_cloneInto(e){e||(e=new this.constructor),e.set(...this.get());let{blockLen:t,buffer:r,length:o,finished:i,destroyed:a,pos:s}=this;return e.destroyed=a,e.finished=i,e.length=o,e.pos=s,o%t&&e.buffer.set(r),e}clone(){return this._cloneInto()}},_=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),M=Uint32Array.from([3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428]),E=Uint32Array.from([3418070365,3238371032,1654270250,914150663,2438529370,812702999,355462360,4144912697,1731405415,4290775857,2394180231,1750603025,3675008525,1694076839,1203062813,3204075428]),S=Uint32Array.from([1779033703,4089235720,3144134277,2227873595,1013904242,4271175723,2773480762,1595750129,1359893119,2917565137,2600822924,725511199,528734635,4215389547,1541459225,327033209])});function me(n,e=!1){return e?{h:Number(n&ut),l:Number(n>>Vt&ut)}:{h:Number(n>>Vt&ut)|0,l:Number(n&ut)|0}}function Nt(n,e=!1){let t=n.length,r=new Uint32Array(t),o=new Uint32Array(t);for(let i=0;i<t;i++){let{h:a,l:s}=me(n[i],e);[r[i],o[i]]=[a,s]}return[r,o]}function I(n,e,t,r){let o=(e>>>0)+(r>>>0);return{h:n+t+(o/2**32|0)|0,l:o|0}}var ut,Vt,Ut,Ht,G,W,tt,et,Gt,Wt,Ot,qt,Xt,Kt,$t=$(()=>{ut=BigInt(4294967295),Vt=BigInt(32);Ut=(n,e,t)=>n>>>t,Ht=(n,e,t)=>n<<32-t|e>>>t,G=(n,e,t)=>n>>>t|e<<32-t,W=(n,e,t)=>n<<32-t|e>>>t,tt=(n,e,t)=>n<<64-t|e>>>t-32,et=(n,e,t)=>n>>>t-32|e<<64-t;Gt=(n,e,t)=>(n>>>0)+(e>>>0)+(t>>>0),Wt=(n,e,t,r)=>e+t+r+(n/2**32|0)|0,Ot=(n,e,t,r)=>(n>>>0)+(e>>>0)+(t>>>0)+(r>>>0),qt=(n,e,t,r,o)=>e+t+r+o+(n/2**32|0)|0,Xt=(n,e,t,r,o)=>(n>>>0)+(e>>>0)+(t>>>0)+(r>>>0)+(o>>>0),Kt=(n,e,t,r,o,i)=>e+t+r+o+i+(n/2**32|0)|0});var Qt={};Et(Qt,{SHA224:()=>ht,SHA256:()=>nt,SHA384:()=>ft,SHA512:()=>O,SHA512_224:()=>lt,SHA512_256:()=>dt,sha224:()=>Ae,sha256:()=>ye,sha384:()=>Se,sha512:()=>Ee,sha512_224:()=>Ue,sha512_256:()=>Be});var ge,k,nt,ht,Jt,wLine truncated
|
||||
/*! Bundled license information:
|
||||
|
||||
@noble/hashes/esm/utils.js:
|
||||
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
||||
*/
|
||||
Reference in new issue
Block a user