Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nostr-tools

Package Overview
Dependencies
Maintainers
1
Versions
151
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nostr-tools - npm Package Compare versions

Comparing version 2.7.1 to 2.7.2

9

lib/cjs/abstract-pool.js

@@ -620,2 +620,11 @@ "use strict";

}
listConnectionStatus() {
const map = /* @__PURE__ */ new Map();
this.relays.forEach((relay, url) => map.set(url, relay.connected));
return map;
}
destroy() {
this.relays.forEach((conn) => conn.close());
this.relays = /* @__PURE__ */ new Map();
}
};

6

lib/cjs/filter.js

@@ -34,2 +34,5 @@ "use strict";

}
function isParameterizedReplaceableKind(kind) {
return 3e4 <= kind && kind < 4e4;
}

@@ -106,4 +109,5 @@ // filter.ts

filter.ids?.length ?? Infinity,
filter.authors?.length && filter.kinds?.every((kind) => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity
filter.authors?.length && filter.kinds?.every((kind) => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity,
filter.authors?.length && filter.kinds?.every((kind) => isParameterizedReplaceableKind(kind)) && filter["#d"]?.length ? filter.authors.length * filter.kinds.length * filter["#d"].length : Infinity
);
}

416

lib/cjs/nip29.js

@@ -23,12 +23,37 @@ "use strict";

__export(nip29_exports, {
GroupAdminPermission: () => GroupAdminPermission,
encodeGroupReference: () => encodeGroupReference,
fetchGroupAdminsEvent: () => fetchGroupAdminsEvent,
fetchGroupMembersEvent: () => fetchGroupMembersEvent,
fetchGroupMetadataEvent: () => fetchGroupMetadataEvent,
fetchRelayInformationByGroupReference: () => fetchRelayInformationByGroupReference,
generateGroupAdminsEventTemplate: () => generateGroupAdminsEventTemplate,
generateGroupMembersEventTemplate: () => generateGroupMembersEventTemplate,
generateGroupMetadataEventTemplate: () => generateGroupMetadataEventTemplate,
getNormalizedRelayURLByGroupReference: () => getNormalizedRelayURLByGroupReference,
loadGroup: () => loadGroup,
loadGroupFromCode: () => loadGroupFromCode,
parseGroup: () => parseGroup,
parseGroupAdminsEvent: () => parseGroupAdminsEvent,
parseGroupCode: () => parseGroupCode,
parseMembers: () => parseMembers,
subscribeRelayGroups: () => subscribeRelayGroups
parseGroupMembersEvent: () => parseGroupMembersEvent,
parseGroupMetadataEvent: () => parseGroupMetadataEvent,
subscribeRelayGroupsMetadataEvents: () => subscribeRelayGroupsMetadataEvents,
validateGroupAdminsEvent: () => validateGroupAdminsEvent,
validateGroupMembersEvent: () => validateGroupMembersEvent,
validateGroupMetadataEvent: () => validateGroupMetadataEvent
});
module.exports = __toCommonJS(nip29_exports);
// nip11.ts
var _fetch;
try {
_fetch = fetch;
} catch {
}
async function fetchRelayInformation(url) {
return await (await fetch(url.replace("ws://", "http://").replace("wss://", "https://"), {
headers: { Accept: "application/nostr+json" }
})).json();
}
// nip19.ts

@@ -151,64 +176,255 @@ var import_utils = require("@noble/hashes/utils");

// nip11.ts
var _fetch;
try {
_fetch = fetch;
} catch {
// nip29.ts
var GroupAdminPermission = /* @__PURE__ */ ((GroupAdminPermission2) => {
GroupAdminPermission2["AddUser"] = "add-user";
GroupAdminPermission2["EditMetadata"] = "edit-metadata";
GroupAdminPermission2["DeleteEvent"] = "delete-event";
GroupAdminPermission2["RemoveUser"] = "remove-user";
GroupAdminPermission2["AddPermission"] = "add-permission";
GroupAdminPermission2["RemovePermission"] = "remove-permission";
GroupAdminPermission2["EditGroupStatus"] = "edit-group-status";
return GroupAdminPermission2;
})(GroupAdminPermission || {});
function generateGroupMetadataEventTemplate(group) {
const tags = [["d", group.metadata.id]];
group.metadata.name && tags.push(["name", group.metadata.name]);
group.metadata.picture && tags.push(["picture", group.metadata.picture]);
group.metadata.about && tags.push(["about", group.metadata.about]);
group.metadata.isPublic && tags.push(["public"]);
group.metadata.isOpen && tags.push(["open"]);
return {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: 39e3,
tags
};
}
async function fetchRelayInformation(url) {
return await (await fetch(url.replace("ws://", "http://").replace("wss://", "https://"), {
headers: { Accept: "application/nostr+json" }
})).json();
function validateGroupMetadataEvent(event) {
if (event.kind !== 39e3)
return false;
if (!event.pubkey)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
return true;
}
// nip29.ts
function subscribeRelayGroups(pool, url, params) {
let normalized = normalizeURL(url);
let sub;
let groups = [];
fetchRelayInformation(normalized).then(async (info) => {
let rl = await pool.ensureRelay(normalized);
params.onconnect?.();
sub = rl.prepareSubscription(
[
{
kinds: [39e3],
limit: 50,
authors: [info.pubkey]
}
],
{
onevent(event) {
groups.push(parseGroup(event, normalized));
},
oneose() {
params.ongroups(groups);
sub.onevent = (event) => {
groups.push(parseGroup(event, normalized));
params.ongroups(groups);
};
}
}
);
sub.fire();
}).catch(params.onerror);
return () => sub.close();
function generateGroupAdminsEventTemplate(group, admins) {
const tags = [["d", group.metadata.id]];
for (const admin of admins) {
tags.push(["p", admin.pubkey, admin.label || "", ...admin.permissions]);
}
return {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: 39001,
tags
};
}
async function loadGroup(pool, gr) {
let normalized = normalizeURL(gr.host);
let info = await fetchRelayInformation(normalized);
let event = await pool.get([normalized], {
function validateGroupAdminsEvent(event) {
if (event.kind !== 39001)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
for (const [tag, value, label, ...permissions] of event.tags) {
if (tag !== "p")
continue;
for (let i = 0; i < permissions.length; i += 1) {
if (typeof permissions[i] !== "string")
return false;
if (!Object.values(GroupAdminPermission).includes(permissions[i]))
return false;
}
}
return true;
}
function generateGroupMembersEventTemplate(group, members) {
const tags = [["d", group.metadata.id]];
for (const member of members) {
tags.push(["p", member.pubkey, member.label || ""]);
}
return {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: 39002,
tags
};
}
function validateGroupMembersEvent(event) {
if (event.kind !== 39002)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
return true;
}
function getNormalizedRelayURLByGroupReference(groupReference) {
return normalizeURL(groupReference.host);
}
async function fetchRelayInformationByGroupReference(groupReference) {
const normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
return fetchRelayInformation(normalizedRelayURL);
}
async function fetchGroupMetadataEvent({
pool,
groupReference,
relayInformation,
normalizedRelayURL
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const groupMetadataEvent = await pool.get([normalizedRelayURL], {
kinds: [39e3],
authors: [info.pubkey],
"#d": [gr.id]
authors: [relayInformation.pubkey],
"#d": [groupReference.id]
});
if (!event)
throw new Error(`group '${gr.id}' not found on ${gr.host}`);
return parseGroup(event, normalized);
if (!groupMetadataEvent)
throw new Error(`group '${groupReference.id}' not found on ${normalizedRelayURL}`);
return groupMetadataEvent;
}
function parseGroupMetadataEvent(event) {
if (!validateGroupMetadataEvent(event))
throw new Error("invalid group metadata event");
const metadata = {
id: "",
pubkey: event.pubkey
};
for (const [tag, value] of event.tags) {
switch (tag) {
case "d":
metadata.id = value;
break;
case "name":
metadata.name = value;
break;
case "picture":
metadata.picture = value;
break;
case "about":
metadata.about = value;
break;
case "public":
metadata.isPublic = true;
break;
case "open":
metadata.isOpen = true;
break;
}
}
return metadata;
}
async function fetchGroupAdminsEvent({
pool,
groupReference,
relayInformation,
normalizedRelayURL
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const groupAdminsEvent = await pool.get([normalizedRelayURL], {
kinds: [39001],
authors: [relayInformation.pubkey],
"#d": [groupReference.id]
});
if (!groupAdminsEvent)
throw new Error(`admins for group '${groupReference.id}' not found on ${normalizedRelayURL}`);
return groupAdminsEvent;
}
function parseGroupAdminsEvent(event) {
if (!validateGroupAdminsEvent(event))
throw new Error("invalid group admins event");
const admins = [];
for (const [tag, value, label, ...permissions] of event.tags) {
if (tag !== "p")
continue;
admins.push({
pubkey: value,
label,
permissions
});
}
return admins;
}
async function fetchGroupMembersEvent({
pool,
groupReference,
relayInformation,
normalizedRelayURL
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const groupMembersEvent = await pool.get([normalizedRelayURL], {
kinds: [39002],
authors: [relayInformation.pubkey],
"#d": [groupReference.id]
});
if (!groupMembersEvent)
throw new Error(`members for group '${groupReference.id}' not found on ${normalizedRelayURL}`);
return groupMembersEvent;
}
function parseGroupMembersEvent(event) {
if (!validateGroupMembersEvent(event))
throw new Error("invalid group members event");
const members = [];
for (const [tag, value, label] of event.tags) {
if (tag !== "p")
continue;
members.push({
pubkey: value,
label
});
}
return members;
}
async function loadGroup({
pool,
groupReference,
normalizedRelayURL,
relayInformation
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const metadataEvent = await fetchGroupMetadataEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
const metadata = parseGroupMetadataEvent(metadataEvent);
const adminsEvent = await fetchGroupAdminsEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
const admins = parseGroupAdminsEvent(adminsEvent);
const membersEvent = await fetchGroupMembersEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
const members = parseGroupMembersEvent(membersEvent);
const group = {
relay: normalizedRelayURL,
metadata,
admins,
members,
reference: groupReference
};
return group;
}
async function loadGroupFromCode(pool, code) {
let gr = parseGroupCode(code);
if (!gr)
throw new Error(`code "${code}" does not identify a group`);
return loadGroup(pool, gr);
const groupReference = parseGroupCode(code);
if (!groupReference)
throw new Error("invalid group code");
return loadGroup({ pool, groupReference });
}

@@ -237,53 +453,37 @@ function parseGroupCode(code) {

function encodeGroupReference(gr) {
if (gr.host.startsWith("https://"))
gr.host = gr.host.slice(8);
if (gr.host.startsWith("wss://"))
gr.host = gr.host.slice(6);
return `${gr.host}'${gr.id}`;
const { host, id } = gr;
const normalizedHost = host.replace(/^(https?:\/\/|wss?:\/\/)/, "");
return `${normalizedHost}'${id}`;
}
function parseGroup(event, relay) {
const group = { relay, pubkey: event.pubkey };
for (let i = 0; i < event.tags.length; i++) {
const tag = event.tags[i];
switch (tag[0]) {
case "d":
group.id = tag[1] || "";
break;
case "name":
group.name = tag[1] || "";
break;
case "about":
group.about = tag[1] || "";
break;
case "picture":
group.picture = tag[1] || "";
break;
case "open":
group.open = true;
break;
case "public":
group.public = true;
break;
}
}
return group;
function subscribeRelayGroupsMetadataEvents({
pool,
relayURL,
onError,
onEvent,
onConnect
}) {
let sub;
const normalizedRelayURL = normalizeURL(relayURL);
fetchRelayInformation(normalizedRelayURL).then(async (info) => {
const abstractedRelay = await pool.ensureRelay(normalizedRelayURL);
onConnect?.();
sub = abstractedRelay.prepareSubscription(
[
{
kinds: [39e3],
limit: 50,
authors: [info.pubkey]
}
],
{
onevent(event) {
onEvent(event);
}
}
);
}).catch((err) => {
sub.close();
onError(err);
});
return () => sub.close();
}
function parseMembers(event) {
const members = [];
for (let i = 0; i < event.tags.length; i++) {
const tag = event.tags[i];
if (tag.length < 2)
continue;
if (tag[0] !== "p")
continue;
if (!tag[1].match(/^[0-9a-f]{64}$/))
continue;
const member = { pubkey: tag[1], permissions: [] };
if (tag.length > 2)
member.label = tag[2];
if (tag.length > 3)
member.permissions = tag.slice(3);
members.push(member);
}
return members;
}

@@ -831,2 +831,11 @@ "use strict";

}
listConnectionStatus() {
const map = /* @__PURE__ */ new Map();
this.relays.forEach((relay, url) => map.set(url, relay.connected));
return map;
}
destroy() {
this.relays.forEach((conn) => conn.close());
this.relays = /* @__PURE__ */ new Map();
}
};

@@ -833,0 +842,0 @@

@@ -121,3 +121,2 @@ "use strict";

const formData = new FormData();
formData.append("Authorization", nip98AuthorizationHeader);
optionalFormDataFields && Object.entries(optionalFormDataFields).forEach(([key, value]) => {

@@ -132,4 +131,3 @@ if (value) {

headers: {
Authorization: nip98AuthorizationHeader,
"Content-Type": "multipart/form-data"
Authorization: nip98AuthorizationHeader
},

@@ -136,0 +134,0 @@ body: formData

@@ -705,2 +705,11 @@ "use strict";

}
listConnectionStatus() {
const map = /* @__PURE__ */ new Map();
this.relays.forEach((relay, url) => map.set(url, relay.connected));
return map;
}
destroy() {
this.relays.forEach((conn) => conn.close());
this.relays = /* @__PURE__ */ new Map();
}
};

@@ -707,0 +716,0 @@

@@ -594,2 +594,11 @@ // kinds.ts

}
listConnectionStatus() {
const map = /* @__PURE__ */ new Map();
this.relays.forEach((relay, url) => map.set(url, relay.connected));
return map;
}
destroy() {
this.relays.forEach((conn) => conn.close());
this.relays = /* @__PURE__ */ new Map();
}
};

@@ -596,0 +605,0 @@ export {

@@ -5,2 +5,5 @@ // kinds.ts

}
function isParameterizedReplaceableKind(kind) {
return 3e4 <= kind && kind < 4e4;
}

@@ -77,3 +80,4 @@ // filter.ts

filter.ids?.length ?? Infinity,
filter.authors?.length && filter.kinds?.every((kind) => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity
filter.authors?.length && filter.kinds?.every((kind) => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity,
filter.authors?.length && filter.kinds?.every((kind) => isParameterizedReplaceableKind(kind)) && filter["#d"]?.length ? filter.authors.length * filter.kinds.length * filter["#d"].length : Infinity
);

@@ -80,0 +84,0 @@ }

@@ -468,3 +468,4 @@ var __defProp = Object.defineProperty;

filter.ids?.length ?? Infinity,
filter.authors?.length && filter.kinds?.every((kind) => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity
filter.authors?.length && filter.kinds?.every((kind) => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity,
filter.authors?.length && filter.kinds?.every((kind) => isParameterizedReplaceableKind(kind)) && filter["#d"]?.length ? filter.authors.length * filter.kinds.length * filter["#d"].length : Infinity
);

@@ -1010,2 +1011,11 @@ }

}
listConnectionStatus() {
const map = /* @__PURE__ */ new Map();
this.relays.forEach((relay, url) => map.set(url, relay.connected));
return map;
}
destroy() {
this.relays.forEach((conn) => conn.close());
this.relays = /* @__PURE__ */ new Map();
}
};

@@ -1012,0 +1022,0 @@

@@ -0,1 +1,13 @@

// nip11.ts
var _fetch;
try {
_fetch = fetch;
} catch {
}
async function fetchRelayInformation(url) {
return await (await fetch(url.replace("ws://", "http://").replace("wss://", "https://"), {
headers: { Accept: "application/nostr+json" }
})).json();
}
// nip19.ts

@@ -118,64 +130,255 @@ import { bytesToHex, concatBytes, hexToBytes } from "@noble/hashes/utils";

// nip11.ts
var _fetch;
try {
_fetch = fetch;
} catch {
// nip29.ts
var GroupAdminPermission = /* @__PURE__ */ ((GroupAdminPermission2) => {
GroupAdminPermission2["AddUser"] = "add-user";
GroupAdminPermission2["EditMetadata"] = "edit-metadata";
GroupAdminPermission2["DeleteEvent"] = "delete-event";
GroupAdminPermission2["RemoveUser"] = "remove-user";
GroupAdminPermission2["AddPermission"] = "add-permission";
GroupAdminPermission2["RemovePermission"] = "remove-permission";
GroupAdminPermission2["EditGroupStatus"] = "edit-group-status";
return GroupAdminPermission2;
})(GroupAdminPermission || {});
function generateGroupMetadataEventTemplate(group) {
const tags = [["d", group.metadata.id]];
group.metadata.name && tags.push(["name", group.metadata.name]);
group.metadata.picture && tags.push(["picture", group.metadata.picture]);
group.metadata.about && tags.push(["about", group.metadata.about]);
group.metadata.isPublic && tags.push(["public"]);
group.metadata.isOpen && tags.push(["open"]);
return {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: 39e3,
tags
};
}
async function fetchRelayInformation(url) {
return await (await fetch(url.replace("ws://", "http://").replace("wss://", "https://"), {
headers: { Accept: "application/nostr+json" }
})).json();
function validateGroupMetadataEvent(event) {
if (event.kind !== 39e3)
return false;
if (!event.pubkey)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
return true;
}
// nip29.ts
function subscribeRelayGroups(pool, url, params) {
let normalized = normalizeURL(url);
let sub;
let groups = [];
fetchRelayInformation(normalized).then(async (info) => {
let rl = await pool.ensureRelay(normalized);
params.onconnect?.();
sub = rl.prepareSubscription(
[
{
kinds: [39e3],
limit: 50,
authors: [info.pubkey]
}
],
{
onevent(event) {
groups.push(parseGroup(event, normalized));
},
oneose() {
params.ongroups(groups);
sub.onevent = (event) => {
groups.push(parseGroup(event, normalized));
params.ongroups(groups);
};
}
}
);
sub.fire();
}).catch(params.onerror);
return () => sub.close();
function generateGroupAdminsEventTemplate(group, admins) {
const tags = [["d", group.metadata.id]];
for (const admin of admins) {
tags.push(["p", admin.pubkey, admin.label || "", ...admin.permissions]);
}
return {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: 39001,
tags
};
}
async function loadGroup(pool, gr) {
let normalized = normalizeURL(gr.host);
let info = await fetchRelayInformation(normalized);
let event = await pool.get([normalized], {
function validateGroupAdminsEvent(event) {
if (event.kind !== 39001)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
for (const [tag, value, label, ...permissions] of event.tags) {
if (tag !== "p")
continue;
for (let i = 0; i < permissions.length; i += 1) {
if (typeof permissions[i] !== "string")
return false;
if (!Object.values(GroupAdminPermission).includes(permissions[i]))
return false;
}
}
return true;
}
function generateGroupMembersEventTemplate(group, members) {
const tags = [["d", group.metadata.id]];
for (const member of members) {
tags.push(["p", member.pubkey, member.label || ""]);
}
return {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: 39002,
tags
};
}
function validateGroupMembersEvent(event) {
if (event.kind !== 39002)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
return true;
}
function getNormalizedRelayURLByGroupReference(groupReference) {
return normalizeURL(groupReference.host);
}
async function fetchRelayInformationByGroupReference(groupReference) {
const normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
return fetchRelayInformation(normalizedRelayURL);
}
async function fetchGroupMetadataEvent({
pool,
groupReference,
relayInformation,
normalizedRelayURL
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const groupMetadataEvent = await pool.get([normalizedRelayURL], {
kinds: [39e3],
authors: [info.pubkey],
"#d": [gr.id]
authors: [relayInformation.pubkey],
"#d": [groupReference.id]
});
if (!event)
throw new Error(`group '${gr.id}' not found on ${gr.host}`);
return parseGroup(event, normalized);
if (!groupMetadataEvent)
throw new Error(`group '${groupReference.id}' not found on ${normalizedRelayURL}`);
return groupMetadataEvent;
}
function parseGroupMetadataEvent(event) {
if (!validateGroupMetadataEvent(event))
throw new Error("invalid group metadata event");
const metadata = {
id: "",
pubkey: event.pubkey
};
for (const [tag, value] of event.tags) {
switch (tag) {
case "d":
metadata.id = value;
break;
case "name":
metadata.name = value;
break;
case "picture":
metadata.picture = value;
break;
case "about":
metadata.about = value;
break;
case "public":
metadata.isPublic = true;
break;
case "open":
metadata.isOpen = true;
break;
}
}
return metadata;
}
async function fetchGroupAdminsEvent({
pool,
groupReference,
relayInformation,
normalizedRelayURL
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const groupAdminsEvent = await pool.get([normalizedRelayURL], {
kinds: [39001],
authors: [relayInformation.pubkey],
"#d": [groupReference.id]
});
if (!groupAdminsEvent)
throw new Error(`admins for group '${groupReference.id}' not found on ${normalizedRelayURL}`);
return groupAdminsEvent;
}
function parseGroupAdminsEvent(event) {
if (!validateGroupAdminsEvent(event))
throw new Error("invalid group admins event");
const admins = [];
for (const [tag, value, label, ...permissions] of event.tags) {
if (tag !== "p")
continue;
admins.push({
pubkey: value,
label,
permissions
});
}
return admins;
}
async function fetchGroupMembersEvent({
pool,
groupReference,
relayInformation,
normalizedRelayURL
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const groupMembersEvent = await pool.get([normalizedRelayURL], {
kinds: [39002],
authors: [relayInformation.pubkey],
"#d": [groupReference.id]
});
if (!groupMembersEvent)
throw new Error(`members for group '${groupReference.id}' not found on ${normalizedRelayURL}`);
return groupMembersEvent;
}
function parseGroupMembersEvent(event) {
if (!validateGroupMembersEvent(event))
throw new Error("invalid group members event");
const members = [];
for (const [tag, value, label] of event.tags) {
if (tag !== "p")
continue;
members.push({
pubkey: value,
label
});
}
return members;
}
async function loadGroup({
pool,
groupReference,
normalizedRelayURL,
relayInformation
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const metadataEvent = await fetchGroupMetadataEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
const metadata = parseGroupMetadataEvent(metadataEvent);
const adminsEvent = await fetchGroupAdminsEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
const admins = parseGroupAdminsEvent(adminsEvent);
const membersEvent = await fetchGroupMembersEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
const members = parseGroupMembersEvent(membersEvent);
const group = {
relay: normalizedRelayURL,
metadata,
admins,
members,
reference: groupReference
};
return group;
}
async function loadGroupFromCode(pool, code) {
let gr = parseGroupCode(code);
if (!gr)
throw new Error(`code "${code}" does not identify a group`);
return loadGroup(pool, gr);
const groupReference = parseGroupCode(code);
if (!groupReference)
throw new Error("invalid group code");
return loadGroup({ pool, groupReference });
}

@@ -204,62 +407,59 @@ function parseGroupCode(code) {

function encodeGroupReference(gr) {
if (gr.host.startsWith("https://"))
gr.host = gr.host.slice(8);
if (gr.host.startsWith("wss://"))
gr.host = gr.host.slice(6);
return `${gr.host}'${gr.id}`;
const { host, id } = gr;
const normalizedHost = host.replace(/^(https?:\/\/|wss?:\/\/)/, "");
return `${normalizedHost}'${id}`;
}
function parseGroup(event, relay) {
const group = { relay, pubkey: event.pubkey };
for (let i = 0; i < event.tags.length; i++) {
const tag = event.tags[i];
switch (tag[0]) {
case "d":
group.id = tag[1] || "";
break;
case "name":
group.name = tag[1] || "";
break;
case "about":
group.about = tag[1] || "";
break;
case "picture":
group.picture = tag[1] || "";
break;
case "open":
group.open = true;
break;
case "public":
group.public = true;
break;
}
}
return group;
function subscribeRelayGroupsMetadataEvents({
pool,
relayURL,
onError,
onEvent,
onConnect
}) {
let sub;
const normalizedRelayURL = normalizeURL(relayURL);
fetchRelayInformation(normalizedRelayURL).then(async (info) => {
const abstractedRelay = await pool.ensureRelay(normalizedRelayURL);
onConnect?.();
sub = abstractedRelay.prepareSubscription(
[
{
kinds: [39e3],
limit: 50,
authors: [info.pubkey]
}
],
{
onevent(event) {
onEvent(event);
}
}
);
}).catch((err) => {
sub.close();
onError(err);
});
return () => sub.close();
}
function parseMembers(event) {
const members = [];
for (let i = 0; i < event.tags.length; i++) {
const tag = event.tags[i];
if (tag.length < 2)
continue;
if (tag[0] !== "p")
continue;
if (!tag[1].match(/^[0-9a-f]{64}$/))
continue;
const member = { pubkey: tag[1], permissions: [] };
if (tag.length > 2)
member.label = tag[2];
if (tag.length > 3)
member.permissions = tag.slice(3);
members.push(member);
}
return members;
}
export {
GroupAdminPermission,
encodeGroupReference,
fetchGroupAdminsEvent,
fetchGroupMembersEvent,
fetchGroupMetadataEvent,
fetchRelayInformationByGroupReference,
generateGroupAdminsEventTemplate,
generateGroupMembersEventTemplate,
generateGroupMetadataEventTemplate,
getNormalizedRelayURLByGroupReference,
loadGroup,
loadGroupFromCode,
parseGroup,
parseGroupAdminsEvent,
parseGroupCode,
parseMembers,
subscribeRelayGroups
parseGroupMembersEvent,
parseGroupMetadataEvent,
subscribeRelayGroupsMetadataEvents,
validateGroupAdminsEvent,
validateGroupMembersEvent,
validateGroupMetadataEvent
};

@@ -801,2 +801,11 @@ // nip46.ts

}
listConnectionStatus() {
const map = /* @__PURE__ */ new Map();
this.relays.forEach((relay, url) => map.set(url, relay.connected));
return map;
}
destroy() {
this.relays.forEach((conn) => conn.close());
this.relays = /* @__PURE__ */ new Map();
}
};

@@ -803,0 +812,0 @@

@@ -88,3 +88,2 @@ // nip96.ts

const formData = new FormData();
formData.append("Authorization", nip98AuthorizationHeader);
optionalFormDataFields && Object.entries(optionalFormDataFields).forEach(([key, value]) => {

@@ -99,4 +98,3 @@ if (value) {

headers: {
Authorization: nip98AuthorizationHeader,
"Content-Type": "multipart/form-data"
Authorization: nip98AuthorizationHeader
},

@@ -103,0 +101,0 @@ body: formData

@@ -677,2 +677,11 @@ // pure.ts

}
listConnectionStatus() {
const map = /* @__PURE__ */ new Map();
this.relays.forEach((relay, url) => map.set(url, relay.connected));
return map;
}
destroy() {
this.relays.forEach((conn) => conn.close());
this.relays = /* @__PURE__ */ new Map();
}
};

@@ -679,0 +688,0 @@

@@ -33,2 +33,4 @@ import { AbstractRelay as AbstractRelay, SubscriptionParams, type AbstractRelayConstructorOptions } from './abstract-relay.ts';

publish(relays: string[], event: Event): Promise<string>[];
listConnectionStatus(): Map<string, boolean>;
destroy(): void;
}

@@ -15,3 +15,6 @@ import { Event } from './core.ts';

export declare function mergeFilters(...filters: Filter[]): Filter;
/** Calculate the intrinsic limit of a filter. This function may return `Infinity`. */
/**
* Calculate the intrinsic limit of a filter.
* This function returns a positive integer, or `Infinity` if there is no intrinsic limit.
*/
export declare function getFilterLimit(filter: Filter): number;
import { AbstractSimplePool } from './abstract-pool.ts';
import type { Event } from './core.ts';
export declare function subscribeRelayGroups(pool: AbstractSimplePool, url: string, params: {
ongroups: (_: Group[]) => void;
onerror: (_: Error) => void;
onconnect?: () => void;
}): () => void;
export declare function loadGroup(pool: AbstractSimplePool, gr: GroupReference): Promise<Group>;
export declare function loadGroupFromCode(pool: AbstractSimplePool, code: string): Promise<Group>;
export type GroupReference = {
id: string;
host: string;
import type { Event, EventTemplate } from './core.ts';
import { RelayInformation } from './nip11.ts';
/**
* Represents a NIP29 group.
*/
export type Group = {
relay: string;
metadata: GroupMetadata;
admins?: GroupAdmin[];
members?: GroupMember[];
reference: GroupReference;
};
export declare function parseGroupCode(code: string): null | GroupReference;
export declare function encodeGroupReference(gr: GroupReference): string;
export type Group = {
/**
* Represents the metadata for a NIP29 group.
*/
export type GroupMetadata = {
id: string;
relay: string;
pubkey: string;

@@ -23,11 +23,236 @@ name?: string;

about?: string;
public?: boolean;
open?: boolean;
isPublic?: boolean;
isOpen?: boolean;
};
export declare function parseGroup(event: Event, relay: string): Group;
export type Member = {
/**
* Represents a NIP29 group reference.
*/
export type GroupReference = {
id: string;
host: string;
};
/**
* Represents a NIP29 group member.
*/
export type GroupMember = {
pubkey: string;
label?: string;
permissions: string[];
};
export declare function parseMembers(event: Event): Member[];
/**
* Represents a NIP29 group admin.
*/
export type GroupAdmin = {
pubkey: string;
label?: string;
permissions: GroupAdminPermission[];
};
/**
* Represents the permissions that a NIP29 group admin can have.
*/
export declare enum GroupAdminPermission {
AddUser = "add-user",
EditMetadata = "edit-metadata",
DeleteEvent = "delete-event",
RemoveUser = "remove-user",
AddPermission = "add-permission",
RemovePermission = "remove-permission",
EditGroupStatus = "edit-group-status"
}
/**
* Generates a group metadata event template.
*
* @param group - The group object.
* @returns An event template with the generated group metadata that can be signed later.
*/
export declare function generateGroupMetadataEventTemplate(group: Group): EventTemplate;
/**
* Validates a group metadata event.
*
* @param event - The event to validate.
* @returns A boolean indicating whether the event is valid.
*/
export declare function validateGroupMetadataEvent(event: Event): boolean;
/**
* Generates an event template for group admins.
*
* @param group - The group object.
* @param admins - An array of group admins.
* @returns The generated event template with the group admins that can be signed later.
*/
export declare function generateGroupAdminsEventTemplate(group: Group, admins: GroupAdmin[]): EventTemplate;
/**
* Validates a group admins event.
*
* @param event - The event to validate.
* @returns True if the event is valid, false otherwise.
*/
export declare function validateGroupAdminsEvent(event: Event): boolean;
/**
* Generates an event template for a group with its members.
*
* @param group - The group object.
* @param members - An array of group members.
* @returns The generated event template with the group members that can be signed later.
*/
export declare function generateGroupMembersEventTemplate(group: Group, members: GroupMember[]): EventTemplate;
/**
* Validates a group members event.
*
* @param event - The event to validate.
* @returns Returns `true` if the event is a valid group members event, `false` otherwise.
*/
export declare function validateGroupMembersEvent(event: Event): boolean;
/**
* Returns the normalized relay URL based on the provided group reference.
*
* @param groupReference - The group reference object containing the host.
* @returns The normalized relay URL.
*/
export declare function getNormalizedRelayURLByGroupReference(groupReference: GroupReference): string;
/**
* Fetches relay information by group reference.
*
* @param groupReference The group reference.
* @returns A promise that resolves to the relay information.
*/
export declare function fetchRelayInformationByGroupReference(groupReference: GroupReference): Promise<RelayInformation>;
/**
* Fetches the group metadata event from the specified pool.
* If the normalizedRelayURL is not provided, it will be obtained using the groupReference.
* If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.
*
* @param {Object} options - The options object.
* @param {AbstractSimplePool} options.pool - The pool to fetch the group metadata event from.
* @param {GroupReference} options.groupReference - The reference to the group.
* @param {string} [options.normalizedRelayURL] - The normalized URL of the relay.
* @param {RelayInformation} [options.relayInformation] - The relay information object.
* @returns {Promise<Event>} The group metadata event that can be parsed later to get the group metadata object.
* @throws {Error} If the group is not found on the specified relay.
*/
export declare function fetchGroupMetadataEvent({ pool, groupReference, relayInformation, normalizedRelayURL, }: {
pool: AbstractSimplePool;
groupReference: GroupReference;
normalizedRelayURL?: string;
relayInformation?: RelayInformation;
}): Promise<Event>;
/**
* Parses a group metadata event and returns the corresponding GroupMetadata object.
*
* @param event - The event to parse.
* @returns The parsed GroupMetadata object.
* @throws An error if the group metadata event is invalid.
*/
export declare function parseGroupMetadataEvent(event: Event): GroupMetadata;
/**
* Fetches the group admins event from the specified pool.
* If the normalizedRelayURL is not provided, it will be obtained from the groupReference.
* If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.
*
* @param {Object} options - The options object.
* @param {AbstractSimplePool} options.pool - The pool to fetch the group admins event from.
* @param {GroupReference} options.groupReference - The reference to the group.
* @param {string} [options.normalizedRelayURL] - The normalized relay URL.
* @param {RelayInformation} [options.relayInformation] - The relay information.
* @returns {Promise<Event>} The group admins event that can be parsed later to get the group admins object.
* @throws {Error} If the group admins event is not found on the specified relay.
*/
export declare function fetchGroupAdminsEvent({ pool, groupReference, relayInformation, normalizedRelayURL, }: {
pool: AbstractSimplePool;
groupReference: GroupReference;
normalizedRelayURL?: string;
relayInformation?: RelayInformation;
}): Promise<Event>;
/**
* Parses a group admins event and returns an array of GroupAdmin objects.
*
* @param event - The event to parse.
* @returns An array of GroupAdmin objects.
* @throws Throws an error if the group admins event is invalid.
*/
export declare function parseGroupAdminsEvent(event: Event): GroupAdmin[];
/**
* Fetches the group members event from the specified relay.
* If the normalizedRelayURL is not provided, it will be obtained using the groupReference.
* If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.
*
* @param {Object} options - The options object.
* @param {AbstractSimplePool} options.pool - The pool object.
* @param {GroupReference} options.groupReference - The group reference object.
* @param {string} [options.normalizedRelayURL] - The normalized relay URL.
* @param {RelayInformation} [options.relayInformation] - The relay information object.
* @returns {Promise<Event>} The group members event that can be parsed later to get the group members object.
* @throws {Error} If the group members event is not found.
*/
export declare function fetchGroupMembersEvent({ pool, groupReference, relayInformation, normalizedRelayURL, }: {
pool: AbstractSimplePool;
groupReference: GroupReference;
normalizedRelayURL?: string;
relayInformation?: RelayInformation;
}): Promise<Event>;
/**
* Parses a group members event and returns an array of GroupMember objects.
* @param event - The event to parse.
* @returns An array of GroupMember objects.
* @throws Throws an error if the group members event is invalid.
*/
export declare function parseGroupMembersEvent(event: Event): GroupMember[];
/**
* Fetches and parses the group metadata event, group admins event, and group members event from the specified pool.
* If the normalized relay URL is not provided, it will be obtained using the group reference.
* If the relay information is not provided, it will be fetched using the normalized relay URL.
*
* @param {Object} options - The options for loading the group.
* @param {AbstractSimplePool} options.pool - The pool to load the group from.
* @param {GroupReference} options.groupReference - The reference of the group to load.
* @param {string} [options.normalizedRelayURL] - The normalized URL of the relay to use.
* @param {RelayInformation} [options.relayInformation] - The relay information to use.
* @returns {Promise<Group>} A promise that resolves to the loaded group.
*/
export declare function loadGroup({ pool, groupReference, normalizedRelayURL, relayInformation, }: {
pool: AbstractSimplePool;
groupReference: GroupReference;
normalizedRelayURL?: string;
relayInformation?: RelayInformation;
}): Promise<Group>;
/**
* Loads a group from the specified pool using the provided group code.
*
* @param {AbstractSimplePool} pool - The pool to load the group from.
* @param {string} code - The code representing the group.
* @returns {Promise<Group>} - A promise that resolves to the loaded group.
* @throws {Error} - If the group code is invalid.
*/
export declare function loadGroupFromCode(pool: AbstractSimplePool, code: string): Promise<Group>;
/**
* Parses a group code and returns a GroupReference object.
*
* @param code The group code to parse.
* @returns A GroupReference object if the code is valid, otherwise null.
*/
export declare function parseGroupCode(code: string): null | GroupReference;
/**
* Encodes a group reference into a string.
*
* @param gr - The group reference to encode.
* @returns The encoded group reference as a string.
*/
export declare function encodeGroupReference(gr: GroupReference): string;
/**
* Subscribes to relay groups metadata events and calls the provided event handler function
* when an event is received.
*
* @param {Object} options - The options for subscribing to relay groups metadata events.
* @param {AbstractSimplePool} options.pool - The pool to subscribe to.
* @param {string} options.relayURL - The URL of the relay.
* @param {Function} options.onError - The error handler function.
* @param {Function} options.onEvent - The event handler function.
* @param {Function} [options.onConnect] - The connect handler function.
* @returns {Function} - A function to close the subscription
*/
export declare function subscribeRelayGroupsMetadataEvents({ pool, relayURL, onError, onEvent, onConnect, }: {
pool: AbstractSimplePool;
relayURL: string;
onError: (err: Error) => void;
onEvent: (event: Event) => void;
onConnect?: () => void;
}): () => void;
{
"type": "module",
"name": "nostr-tools",
"version": "2.7.1",
"version": "2.7.2",
"description": "Tools for making a Nostr client.",

@@ -6,0 +6,0 @@ "repository": {

@@ -7,3 +7,3 @@ # ![](https://img.shields.io/github/actions/workflow/status/nbd-wtf/nostr-tools/test.yml) nostr-tools

This package is only providing lower-level functionality. If you want an easy-to-use fully-fledged solution that abstracts the hard parts of Nostr and makes decisions on your behalf, take a look at [NDK](https://github.com/nostr-dev-kit/ndk) and [@snort/system](https://www.npmjs.com/package/@snort/system).
This package is only providing lower-level functionality. If you want more higher-level features, take a look at [Nostrify](https://nostrify.dev), or if you want an easy-to-use fully-fledged solution that abstracts the hard parts of Nostr and makes decisions on your behalf, take a look at [NDK](https://github.com/nostr-dev-kit/ndk) and [@snort/system](https://www.npmjs.com/package/@snort/system).

@@ -10,0 +10,0 @@ ## Installation

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc