Files
2025-09-29 07:21:46 -04:00

185 lines
5.3 KiB
JavaScript

"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nipb7.ts
var nipb7_exports = {};
__export(nipb7_exports, {
BlossomClient: () => BlossomClient
});
module.exports = __toCommonJS(nipb7_exports);
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// nipb7.ts
var BlossomClient = class {
mediaserver;
signer;
constructor(mediaserver, signer) {
if (!mediaserver.startsWith("http")) {
mediaserver = "https://" + mediaserver;
}
this.mediaserver = mediaserver.replace(/\/$/, "") + "/";
this.signer = signer;
}
async httpCall(method, url, contentType, addAuthorization, body, result) {
const headers = {};
if (contentType) {
headers["Content-Type"] = contentType;
}
if (addAuthorization) {
const auth = await addAuthorization();
if (auth) {
headers["Authorization"] = auth;
}
}
const response = await fetch(this.mediaserver + url, {
method,
headers,
body
});
if (response.status >= 300) {
const reason = response.headers.get("X-Reason") || response.statusText;
throw new Error(`${url} returned an error (${response.status}): ${reason}`);
}
if (result !== null && response.headers.get("content-type")?.includes("application/json")) {
return await response.json();
}
return response;
}
async authorizationHeader(modify) {
const now = Math.floor(Date.now() / 1e3);
const event = {
created_at: now,
kind: 24242,
content: "blossom stuff",
tags: [["expiration", String(now + 60)]]
};
if (modify) {
modify(event);
}
try {
const signedEvent = await this.signer.signEvent(event);
const eventJson = JSON.stringify(signedEvent);
return "Nostr " + btoa(eventJson);
} catch (error) {
return "";
}
}
isValid32ByteHex(hash) {
return /^[a-f0-9]{64}$/i.test(hash);
}
async check(hash) {
if (!this.isValid32ByteHex(hash)) {
throw new Error(`${hash} is not a valid 32-byte hex string`);
}
try {
await this.httpCall("HEAD", hash);
} catch (error) {
throw new Error(`failed to check for ${hash}: ${error}`);
}
}
async uploadBlob(file, contentType) {
const hash = (0, import_utils.bytesToHex)((0, import_sha256.sha256)(new Uint8Array(await file.arrayBuffer())));
const actualContentType = contentType || file.type || "application/octet-stream";
const bd = await this.httpCall(
"PUT",
"upload",
actualContentType,
() => this.authorizationHeader((evt) => {
evt.tags.push(["t", "upload"]);
evt.tags.push(["x", hash]);
}),
file,
{}
);
return bd;
}
async uploadFile(file) {
return this.uploadBlob(file, file.type);
}
async download(hash) {
if (!this.isValid32ByteHex(hash)) {
throw new Error(`${hash} is not a valid 32-byte hex string`);
}
const authHeader = await this.authorizationHeader((evt) => {
evt.tags.push(["t", "get"]);
evt.tags.push(["x", hash]);
});
const response = await fetch(this.mediaserver + hash, {
method: "GET",
headers: {
Authorization: authHeader
}
});
if (response.status >= 300) {
throw new Error(`${hash} is not present in ${this.mediaserver}: ${response.status}`);
}
return await response.arrayBuffer();
}
async downloadAsBlob(hash) {
const arrayBuffer = await this.download(hash);
return new Blob([arrayBuffer]);
}
async list() {
const pubkey = await this.signer.getPublicKey();
if (!this.isValid32ByteHex(pubkey)) {
throw new Error(`pubkey ${pubkey} is not valid`);
}
try {
const bds = await this.httpCall(
"GET",
`list/${pubkey}`,
void 0,
() => this.authorizationHeader((evt) => {
evt.tags.push(["t", "list"]);
}),
void 0,
[]
);
return bds;
} catch (error) {
throw new Error(`failed to list blobs: ${error}`);
}
}
async delete(hash) {
if (!this.isValid32ByteHex(hash)) {
throw new Error(`${hash} is not a valid 32-byte hex string`);
}
try {
await this.httpCall(
"DELETE",
hash,
void 0,
() => this.authorizationHeader((evt) => {
evt.tags.push(["t", "delete"]);
evt.tags.push(["x", hash]);
}),
void 0,
null
);
} catch (error) {
throw new Error(`failed to delete ${hash}: ${error}`);
}
}
};