Added page for Experimental features, with NIP-26 support.
This commit is contained in:
@@ -77,6 +77,18 @@
|
|||||||
944A6DD72988BD230032C2E3 /* permission.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DD52988BD230032C2E3 /* permission.js */; };
|
944A6DD72988BD230032C2E3 /* permission.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DD52988BD230032C2E3 /* permission.js */; };
|
||||||
944A6DD92988D7900032C2E3 /* permission.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DD82988D7900032C2E3 /* permission.build.js */; };
|
944A6DD92988D7900032C2E3 /* permission.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DD82988D7900032C2E3 /* permission.build.js */; };
|
||||||
944A6DDA2988D7900032C2E3 /* permission.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DD82988D7900032C2E3 /* permission.build.js */; };
|
944A6DDA2988D7900032C2E3 /* permission.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DD82988D7900032C2E3 /* permission.build.js */; };
|
||||||
|
944A6DE02991DFC60032C2E3 /* delegation_wizard.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DDC2991DFC60032C2E3 /* delegation_wizard.build.js */; };
|
||||||
|
944A6DE12991DFC60032C2E3 /* delegation_wizard.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DDC2991DFC60032C2E3 /* delegation_wizard.build.js */; };
|
||||||
|
944A6DE22991DFC60032C2E3 /* delegation_wizard.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DDD2991DFC60032C2E3 /* delegation_wizard.js */; };
|
||||||
|
944A6DE32991DFC60032C2E3 /* delegation_wizard.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DDD2991DFC60032C2E3 /* delegation_wizard.js */; };
|
||||||
|
944A6DE52991E4550032C2E3 /* delegation_wizard.html in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE42991E4550032C2E3 /* delegation_wizard.html */; };
|
||||||
|
944A6DE62991E4550032C2E3 /* delegation_wizard.html in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE42991E4550032C2E3 /* delegation_wizard.html */; };
|
||||||
|
944A6DEA299682EF0032C2E3 /* experimental.html in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE7299682EF0032C2E3 /* experimental.html */; };
|
||||||
|
944A6DEB299682EF0032C2E3 /* experimental.html in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE7299682EF0032C2E3 /* experimental.html */; };
|
||||||
|
944A6DEC299682EF0032C2E3 /* experimental.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE8299682EF0032C2E3 /* experimental.js */; };
|
||||||
|
944A6DED299682EF0032C2E3 /* experimental.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE8299682EF0032C2E3 /* experimental.js */; };
|
||||||
|
944A6DEE299682EF0032C2E3 /* experimental.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE9299682EF0032C2E3 /* experimental.build.js */; };
|
||||||
|
944A6DEF299682EF0032C2E3 /* experimental.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE9299682EF0032C2E3 /* experimental.build.js */; };
|
||||||
948C69D9297F887600FB3574 /* options.html in Resources */ = {isa = PBXBuildFile; fileRef = 948C69D8297F887600FB3574 /* options.html */; };
|
948C69D9297F887600FB3574 /* options.html in Resources */ = {isa = PBXBuildFile; fileRef = 948C69D8297F887600FB3574 /* options.html */; };
|
||||||
948C69DA297F887600FB3574 /* options.html in Resources */ = {isa = PBXBuildFile; fileRef = 948C69D8297F887600FB3574 /* options.html */; };
|
948C69DA297F887600FB3574 /* options.html in Resources */ = {isa = PBXBuildFile; fileRef = 948C69D8297F887600FB3574 /* options.html */; };
|
||||||
948C69DD297F88A200FB3574 /* options.css in Resources */ = {isa = PBXBuildFile; fileRef = 948C69DB297F88A200FB3574 /* options.css */; };
|
948C69DD297F88A200FB3574 /* options.css in Resources */ = {isa = PBXBuildFile; fileRef = 948C69DB297F88A200FB3574 /* options.css */; };
|
||||||
@@ -180,6 +192,12 @@
|
|||||||
944A6DD22988BA200032C2E3 /* permission.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = permission.html; sourceTree = "<group>"; };
|
944A6DD22988BA200032C2E3 /* permission.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = permission.html; sourceTree = "<group>"; };
|
||||||
944A6DD52988BD230032C2E3 /* permission.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = permission.js; sourceTree = "<group>"; };
|
944A6DD52988BD230032C2E3 /* permission.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = permission.js; sourceTree = "<group>"; };
|
||||||
944A6DD82988D7900032C2E3 /* permission.build.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = permission.build.js; sourceTree = "<group>"; };
|
944A6DD82988D7900032C2E3 /* permission.build.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = permission.build.js; sourceTree = "<group>"; };
|
||||||
|
944A6DDC2991DFC60032C2E3 /* delegation_wizard.build.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = delegation_wizard.build.js; sourceTree = "<group>"; };
|
||||||
|
944A6DDD2991DFC60032C2E3 /* delegation_wizard.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = delegation_wizard.js; sourceTree = "<group>"; };
|
||||||
|
944A6DE42991E4550032C2E3 /* delegation_wizard.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = delegation_wizard.html; sourceTree = "<group>"; };
|
||||||
|
944A6DE7299682EF0032C2E3 /* experimental.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = experimental.html; sourceTree = "<group>"; };
|
||||||
|
944A6DE8299682EF0032C2E3 /* experimental.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = experimental.js; sourceTree = "<group>"; };
|
||||||
|
944A6DE9299682EF0032C2E3 /* experimental.build.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = experimental.build.js; sourceTree = "<group>"; };
|
||||||
948C69D8297F887600FB3574 /* options.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = options.html; sourceTree = "<group>"; };
|
948C69D8297F887600FB3574 /* options.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = options.html; sourceTree = "<group>"; };
|
||||||
948C69DB297F88A200FB3574 /* options.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = options.css; sourceTree = "<group>"; };
|
948C69DB297F88A200FB3574 /* options.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = options.css; sourceTree = "<group>"; };
|
||||||
948C69DC297F88A200FB3574 /* options.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = options.js; sourceTree = "<group>"; };
|
948C69DC297F88A200FB3574 /* options.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = options.js; sourceTree = "<group>"; };
|
||||||
@@ -267,6 +285,12 @@
|
|||||||
941B03A2296FA90400CA291E /* Resources */ = {
|
941B03A2296FA90400CA291E /* Resources */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
944A6DE9299682EF0032C2E3 /* experimental.build.js */,
|
||||||
|
944A6DE7299682EF0032C2E3 /* experimental.html */,
|
||||||
|
944A6DE8299682EF0032C2E3 /* experimental.js */,
|
||||||
|
944A6DE42991E4550032C2E3 /* delegation_wizard.html */,
|
||||||
|
944A6DDC2991DFC60032C2E3 /* delegation_wizard.build.js */,
|
||||||
|
944A6DDD2991DFC60032C2E3 /* delegation_wizard.js */,
|
||||||
944A6DD82988D7900032C2E3 /* permission.build.js */,
|
944A6DD82988D7900032C2E3 /* permission.build.js */,
|
||||||
944A6DD52988BD230032C2E3 /* permission.js */,
|
944A6DD52988BD230032C2E3 /* permission.js */,
|
||||||
944A6DD22988BA200032C2E3 /* permission.html */,
|
944A6DD22988BA200032C2E3 /* permission.html */,
|
||||||
@@ -510,8 +534,12 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
944A6DEE299682EF0032C2E3 /* experimental.build.js in Resources */,
|
||||||
941B0413297110F100CA291E /* background.build.js in Resources */,
|
941B0413297110F100CA291E /* background.build.js in Resources */,
|
||||||
|
944A6DE22991DFC60032C2E3 /* delegation_wizard.js in Resources */,
|
||||||
|
944A6DE52991E4550032C2E3 /* delegation_wizard.html in Resources */,
|
||||||
948C69E82982DFE900FB3574 /* background.html in Resources */,
|
948C69E82982DFE900FB3574 /* background.html in Resources */,
|
||||||
|
944A6DEA299682EF0032C2E3 /* experimental.html in Resources */,
|
||||||
948C69DF297F88A200FB3574 /* options.js in Resources */,
|
948C69DF297F88A200FB3574 /* options.js in Resources */,
|
||||||
944A6DD32988BA200032C2E3 /* permission.html in Resources */,
|
944A6DD32988BA200032C2E3 /* permission.html in Resources */,
|
||||||
948C69DD297F88A200FB3574 /* options.css in Resources */,
|
948C69DD297F88A200FB3574 /* options.css in Resources */,
|
||||||
@@ -524,12 +552,14 @@
|
|||||||
941B042C2978CD8E00CA291E /* iOS-Icon-1024.png in Resources */,
|
941B042C2978CD8E00CA291E /* iOS-Icon-1024.png in Resources */,
|
||||||
941B03F6296FA90400CA291E /* popup.html in Resources */,
|
941B03F6296FA90400CA291E /* popup.html in Resources */,
|
||||||
941B040D296FAD6900CA291E /* nostr.js in Resources */,
|
941B040D296FAD6900CA291E /* nostr.js in Resources */,
|
||||||
|
944A6DE02991DFC60032C2E3 /* delegation_wizard.build.js in Resources */,
|
||||||
941B03EE296FA90400CA291E /* images in Resources */,
|
941B03EE296FA90400CA291E /* images in Resources */,
|
||||||
941B03F0296FA90400CA291E /* manifest.json in Resources */,
|
941B03F0296FA90400CA291E /* manifest.json in Resources */,
|
||||||
944A6DD62988BD230032C2E3 /* permission.js in Resources */,
|
944A6DD62988BD230032C2E3 /* permission.js in Resources */,
|
||||||
941B04312978CDF900CA291E /* Icon-32.png in Resources */,
|
941B04312978CDF900CA291E /* Icon-32.png in Resources */,
|
||||||
941B041A2971139000CA291E /* content.build.js in Resources */,
|
941B041A2971139000CA291E /* content.build.js in Resources */,
|
||||||
941B041C2971139000CA291E /* popup.build.js in Resources */,
|
941B041C2971139000CA291E /* popup.build.js in Resources */,
|
||||||
|
944A6DEC299682EF0032C2E3 /* experimental.js in Resources */,
|
||||||
941B03EC296FA90400CA291E /* _locales in Resources */,
|
941B03EC296FA90400CA291E /* _locales in Resources */,
|
||||||
941B04222977A25700CA291E /* Icon-512.png in Resources */,
|
941B04222977A25700CA291E /* Icon-512.png in Resources */,
|
||||||
948C69E5297F8BA600FB3574 /* options.build.css in Resources */,
|
948C69E5297F8BA600FB3574 /* options.build.css in Resources */,
|
||||||
@@ -547,8 +577,12 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
944A6DEF299682EF0032C2E3 /* experimental.build.js in Resources */,
|
||||||
941B0414297110F100CA291E /* background.build.js in Resources */,
|
941B0414297110F100CA291E /* background.build.js in Resources */,
|
||||||
|
944A6DE32991DFC60032C2E3 /* delegation_wizard.js in Resources */,
|
||||||
|
944A6DE62991E4550032C2E3 /* delegation_wizard.html in Resources */,
|
||||||
948C69E92982DFE900FB3574 /* background.html in Resources */,
|
948C69E92982DFE900FB3574 /* background.html in Resources */,
|
||||||
|
944A6DEB299682EF0032C2E3 /* experimental.html in Resources */,
|
||||||
948C69E0297F88A200FB3574 /* options.js in Resources */,
|
948C69E0297F88A200FB3574 /* options.js in Resources */,
|
||||||
944A6DD42988BA200032C2E3 /* permission.html in Resources */,
|
944A6DD42988BA200032C2E3 /* permission.html in Resources */,
|
||||||
948C69DE297F88A200FB3574 /* options.css in Resources */,
|
948C69DE297F88A200FB3574 /* options.css in Resources */,
|
||||||
@@ -561,12 +595,14 @@
|
|||||||
941B042D2978CD8E00CA291E /* iOS-Icon-1024.png in Resources */,
|
941B042D2978CD8E00CA291E /* iOS-Icon-1024.png in Resources */,
|
||||||
941B03F7296FA90400CA291E /* popup.html in Resources */,
|
941B03F7296FA90400CA291E /* popup.html in Resources */,
|
||||||
941B040E296FAD6900CA291E /* nostr.js in Resources */,
|
941B040E296FAD6900CA291E /* nostr.js in Resources */,
|
||||||
|
944A6DE12991DFC60032C2E3 /* delegation_wizard.build.js in Resources */,
|
||||||
941B03EF296FA90400CA291E /* images in Resources */,
|
941B03EF296FA90400CA291E /* images in Resources */,
|
||||||
941B03F1296FA90400CA291E /* manifest.json in Resources */,
|
941B03F1296FA90400CA291E /* manifest.json in Resources */,
|
||||||
944A6DD72988BD230032C2E3 /* permission.js in Resources */,
|
944A6DD72988BD230032C2E3 /* permission.js in Resources */,
|
||||||
941B04322978CDF900CA291E /* Icon-32.png in Resources */,
|
941B04322978CDF900CA291E /* Icon-32.png in Resources */,
|
||||||
941B041B2971139000CA291E /* content.build.js in Resources */,
|
941B041B2971139000CA291E /* content.build.js in Resources */,
|
||||||
941B041D2971139000CA291E /* popup.build.js in Resources */,
|
941B041D2971139000CA291E /* popup.build.js in Resources */,
|
||||||
|
944A6DED299682EF0032C2E3 /* experimental.js in Resources */,
|
||||||
941B03ED296FA90400CA291E /* _locales in Resources */,
|
941B03ED296FA90400CA291E /* _locales in Resources */,
|
||||||
941B04232977A25700CA291E /* Icon-512.png in Resources */,
|
941B04232977A25700CA291E /* Icon-512.png in Resources */,
|
||||||
948C69E6297F8BA600FB3574 /* options.build.css in Resources */,
|
948C69E6297F8BA600FB3574 /* options.build.css in Resources */,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import {
|
|||||||
signEvent,
|
signEvent,
|
||||||
nip04,
|
nip04,
|
||||||
nip19,
|
nip19,
|
||||||
|
nip26,
|
||||||
|
getEventHash,
|
||||||
} from 'nostr-tools';
|
} from 'nostr-tools';
|
||||||
import { Mutex } from 'async-mutex';
|
import { Mutex } from 'async-mutex';
|
||||||
import {
|
import {
|
||||||
@@ -12,6 +14,7 @@ import {
|
|||||||
getProfile,
|
getProfile,
|
||||||
getPermission,
|
getPermission,
|
||||||
setPermission,
|
setPermission,
|
||||||
|
feature,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
const storage = browser.storage.local;
|
const storage = browser.storage.local;
|
||||||
@@ -19,20 +22,6 @@ const log = msg => console.log('Background: ', msg);
|
|||||||
const validations = {};
|
const validations = {};
|
||||||
let prompt = { mutex: new Mutex(), release: null, tabId: null };
|
let prompt = { mutex: new Mutex(), release: null, tabId: null };
|
||||||
|
|
||||||
browser.runtime.onInstalled.addListener(async ({ reason }) => {
|
|
||||||
// I would like to be able to skip this for development purposes
|
|
||||||
// let ignoreHook = (await storage.get({ ignoreInstallHook: false }))
|
|
||||||
// .ignoreInstallHook;
|
|
||||||
// if (ignoreHook === true) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (['install'].includes(reason)) {
|
|
||||||
// browser.tabs.create({
|
|
||||||
// url: 'https://ursus.camp/nostore',
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
|
|
||||||
browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||||
log(message);
|
log(message);
|
||||||
let uuid = crypto.randomUUID();
|
let uuid = crypto.randomUUID();
|
||||||
@@ -57,6 +46,10 @@ browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|||||||
return getNpub(message.payload);
|
return getNpub(message.payload);
|
||||||
case 'getNsec':
|
case 'getNsec':
|
||||||
return getNsec(message.payload);
|
return getNsec(message.payload);
|
||||||
|
case 'createDelegation':
|
||||||
|
return createDelegation(message.payload);
|
||||||
|
case 'calcPubKey':
|
||||||
|
return Promise.resolve(getPublicKey(message.payload));
|
||||||
|
|
||||||
// window.nostr
|
// window.nostr
|
||||||
case 'getPubKey':
|
case 'getPubKey':
|
||||||
@@ -64,11 +57,9 @@ browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|||||||
case 'nip04.encrypt':
|
case 'nip04.encrypt':
|
||||||
case 'nip04.decrypt':
|
case 'nip04.decrypt':
|
||||||
case 'getRelays':
|
case 'getRelays':
|
||||||
console.log('asking');
|
|
||||||
validations[uuid] = sendResponse;
|
validations[uuid] = sendResponse;
|
||||||
ask(uuid, message);
|
setDelegation(message).then(() => ask(uuid, message));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('timeout release');
|
|
||||||
prompt.release?.();
|
prompt.release?.();
|
||||||
}, 10_000);
|
}, 10_000);
|
||||||
return true;
|
return true;
|
||||||
@@ -98,9 +89,7 @@ async function ask(uuid, { kind, host, payload }) {
|
|||||||
|
|
||||||
let mKind = kind === 'signEvent' ? `signEvent:${payload.kind}` : kind;
|
let mKind = kind === 'signEvent' ? `signEvent:${payload.kind}` : kind;
|
||||||
let permission = await getPermission(host, mKind);
|
let permission = await getPermission(host, mKind);
|
||||||
console.log('existing permission: ', permission);
|
|
||||||
if (permission === 'allow') {
|
if (permission === 'allow') {
|
||||||
console.log('already allowed');
|
|
||||||
complete({
|
complete({
|
||||||
payload: uuid,
|
payload: uuid,
|
||||||
origKind: kind,
|
origKind: kind,
|
||||||
@@ -113,13 +102,11 @@ async function ask(uuid, { kind, host, payload }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (permission === 'deny') {
|
if (permission === 'deny') {
|
||||||
console.log('already denied');
|
|
||||||
deny({ payload: uuid, origKind: kind, host });
|
deny({ payload: uuid, origKind: kind, host });
|
||||||
prompt.release();
|
prompt.release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('creating asking popup');
|
|
||||||
let qs = new URLSearchParams({
|
let qs = new URLSearchParams({
|
||||||
uuid,
|
uuid,
|
||||||
kind,
|
kind,
|
||||||
@@ -136,22 +123,18 @@ async function ask(uuid, { kind, host, payload }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function complete({ payload, origKind, event, remember, host }) {
|
function complete({ payload, origKind, event, remember, host }) {
|
||||||
console.log('complete');
|
|
||||||
sendResponse = validations[payload];
|
sendResponse = validations[payload];
|
||||||
|
|
||||||
if (remember) {
|
if (remember) {
|
||||||
console.log('saving permission');
|
|
||||||
let mKind =
|
let mKind =
|
||||||
origKind === 'signEvent' ? `signEvent:${event.kind}` : origKind;
|
origKind === 'signEvent' ? `signEvent:${event.kind}` : origKind;
|
||||||
setPermission(host, mKind, 'allow');
|
setPermission(host, mKind, 'allow');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sendResponse) {
|
if (sendResponse) {
|
||||||
console.log('sendResponse found');
|
|
||||||
switch (origKind) {
|
switch (origKind) {
|
||||||
case 'getPubKey':
|
case 'getPubKey':
|
||||||
getPubKey().then(pk => {
|
getPubKey().then(pk => {
|
||||||
console.log(pk);
|
|
||||||
sendResponse(pk);
|
sendResponse(pk);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@@ -172,11 +155,9 @@ function complete({ payload, origKind, event, remember, host }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deny({ origKind, host, payload, remember, event }) {
|
function deny({ origKind, host, payload, remember, event }) {
|
||||||
console.log('denied');
|
|
||||||
sendResponse = validations[payload];
|
sendResponse = validations[payload];
|
||||||
|
|
||||||
if (remember) {
|
if (remember) {
|
||||||
console.log('saving permission');
|
|
||||||
let mKind =
|
let mKind =
|
||||||
origKind === 'signEvent' ? `signEvent:${event.kind}` : origKind;
|
origKind === 'signEvent' ? `signEvent:${event.kind}` : origKind;
|
||||||
setPermission(host, mKind, 'deny');
|
setPermission(host, mKind, 'deny');
|
||||||
@@ -186,17 +167,6 @@ function deny({ origKind, host, payload, remember, event }) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyDeleter(key) {
|
|
||||||
return new Promise(resolver => {
|
|
||||||
setTimeout(() => {
|
|
||||||
console.log('Validations: ', validations);
|
|
||||||
console.log('Deleting key validations: ', key);
|
|
||||||
resolver();
|
|
||||||
delete validations[key];
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
async function savePrivateKey([index, privKey]) {
|
async function savePrivateKey([index, privKey]) {
|
||||||
if (privKey.startsWith('nsec')) {
|
if (privKey.startsWith('nsec')) {
|
||||||
@@ -227,6 +197,11 @@ async function getPrivKey() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getPubKey() {
|
async function getPubKey() {
|
||||||
|
let pi = await getProfileIndex();
|
||||||
|
let profile = await getProfile(pi);
|
||||||
|
if (profile.delegate) {
|
||||||
|
return profile.delegator;
|
||||||
|
}
|
||||||
let privKey = await getPrivKey();
|
let privKey = await getPrivKey();
|
||||||
let pubKey = getPublicKey(privKey);
|
let pubKey = getPublicKey(privKey);
|
||||||
return pubKey;
|
return pubKey;
|
||||||
@@ -239,9 +214,13 @@ async function currentProfile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function signEvent_(event) {
|
async function signEvent_(event) {
|
||||||
event = { ...event };
|
event = JSON.parse(JSON.stringify(event));
|
||||||
let privKey = await getPrivKey();
|
let privKey = await getPrivKey();
|
||||||
|
let pubKey = getPublicKey(privKey);
|
||||||
|
event.pubkey = pubKey;
|
||||||
|
event.id = getEventHash(event);
|
||||||
event.sig = signEvent(event, privKey);
|
event.sig = signEvent(event, privKey);
|
||||||
|
console.log(JSON.stringify(event));
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,3 +245,42 @@ async function getRelays() {
|
|||||||
});
|
});
|
||||||
return relayObj;
|
return relayObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createDelegation({
|
||||||
|
kind,
|
||||||
|
delegatorPrivKey,
|
||||||
|
delegateePubKey,
|
||||||
|
until,
|
||||||
|
since,
|
||||||
|
}) {
|
||||||
|
delegatorPrivKey = nip19.decode(delegatorPrivKey).data;
|
||||||
|
let delegation = nip26.createDelegation(delegatorPrivKey, {
|
||||||
|
pubkey: delegateePubKey,
|
||||||
|
until,
|
||||||
|
// kind,
|
||||||
|
// since,
|
||||||
|
});
|
||||||
|
return Promise.resolve(delegation);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setDelegation({ payload }) {
|
||||||
|
if (!payload) return;
|
||||||
|
let active = await feature('delegation');
|
||||||
|
if (!active) return;
|
||||||
|
|
||||||
|
let { delegate, delegation } = await currentProfile();
|
||||||
|
|
||||||
|
// Nothing to do if this is not a delegate
|
||||||
|
if (!delegate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.tags = payload.tags || [];
|
||||||
|
|
||||||
|
payload.tags.push([
|
||||||
|
'delegation',
|
||||||
|
delegation.from,
|
||||||
|
delegation.cond,
|
||||||
|
delegation.sig,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|||||||
59
Shared (Extension)/Resources/delegation_wizard.html
Normal file
59
Shared (Extension)/Resources/delegation_wizard.html
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script defer src="delegation_wizard.build.js"></script>
|
||||||
|
<link rel="stylesheet" href="options.build.css">
|
||||||
|
|
||||||
|
<title>Delegation Wizard</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body x-data="delegated" class="text-fuchsia-900 p-3.5 lg:p-32">
|
||||||
|
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">New Delegated Profile</h1>
|
||||||
|
|
||||||
|
<p class="mt-6">
|
||||||
|
A delegated user, as laid out in the
|
||||||
|
<a href="https://github.com/nostr-protocol/nips/blob/master/26.md" @click.prevent="openNip($event)">NIP-26
|
||||||
|
specification</a>,
|
||||||
|
is a user that is authorized to sign events for a different user. Additional limits can be
|
||||||
|
put on the delegated account, such as time limits.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="mt-3">
|
||||||
|
This may be useful if you wish to keep your main key stored safely offline, but still post as that user
|
||||||
|
with a throwaway key.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2 class="section-header">Delegator</h2>
|
||||||
|
<p class="text-small italic">The secure account to be delegated.</p>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<label for="delegator">Delegator Private Key</label>
|
||||||
|
<br>
|
||||||
|
<input type="text" class="input" x-model="privKey" :class="validKeyClass" autocapitalize="off" autocomplete="off"
|
||||||
|
spellcheck="off">
|
||||||
|
<p class="text-xs italic">This is not stored, but only used to sign a token which is stored instead.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<label for="duration">Delegated Duration</label>
|
||||||
|
<br>
|
||||||
|
<select x-model.number="duration" class="input">
|
||||||
|
<option value="1">1 day</option>
|
||||||
|
<option value="7">7 days</option>
|
||||||
|
<option value="30">30 days</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<button class="button" @click.prevent="goBack">Back</button>
|
||||||
|
<button class="button" @click.prevent="save" :disabled="!isKeyValid">Save</button>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
82
Shared (Extension)/Resources/delegation_wizard.js
Normal file
82
Shared (Extension)/Resources/delegation_wizard.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import Alpine from 'alpinejs';
|
||||||
|
import { generateProfile, getProfiles, validateKey } from './utils';
|
||||||
|
import { getPublicKey, nip26, nip19 } from 'nostr-tools';
|
||||||
|
|
||||||
|
const storage = browser.storage.local;
|
||||||
|
|
||||||
|
Alpine.data('delegated', () => ({
|
||||||
|
privKey: '',
|
||||||
|
duration: 7,
|
||||||
|
profile: {},
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
this.profile = await generateProfile('New Delegate');
|
||||||
|
this.profile.delegate = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
openNip(event) {
|
||||||
|
browser.tabs.create({ url: event.target.href, active: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
window.location = browser.runtime.getURL('options.html');
|
||||||
|
},
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
let profiles = await getProfiles();
|
||||||
|
|
||||||
|
// We need to jankify this Alpine proxy object so it's in the right format
|
||||||
|
// when we save it to storage
|
||||||
|
let profile = JSON.parse(JSON.stringify(this.profile));
|
||||||
|
profile.delegator = getPublicKey(this.decodedPrivKey);
|
||||||
|
profile.delegation = this.getDelegation();
|
||||||
|
|
||||||
|
profiles.push(profile);
|
||||||
|
let profileIndex = profiles.length - 1;
|
||||||
|
await storage.set({ profiles, profileIndex });
|
||||||
|
|
||||||
|
window.location = `${browser.runtime.getURL(
|
||||||
|
'options.html'
|
||||||
|
)}?index=${profileIndex}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
getDelegation() {
|
||||||
|
let pubkey = getPublicKey(this.profile.privKey);
|
||||||
|
|
||||||
|
let delegation = nip26.createDelegation(this.decodedPrivKey, {
|
||||||
|
pubkey,
|
||||||
|
until: this.until,
|
||||||
|
since: Math.round(Date.now() / 1000) - 1,
|
||||||
|
});
|
||||||
|
console.log(delegation);
|
||||||
|
return delegation;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
|
||||||
|
get isKeyValid() {
|
||||||
|
return validateKey(this.privKey);
|
||||||
|
},
|
||||||
|
|
||||||
|
get validKeyClass() {
|
||||||
|
return this.isKeyValid
|
||||||
|
? ''
|
||||||
|
: 'ring-2 ring-rose-500 focus:ring-2 focus:ring-rose-500 border-transparent focus:border-transparent';
|
||||||
|
},
|
||||||
|
|
||||||
|
get until() {
|
||||||
|
return Math.round(Date.now() / 1000) + 60 * 60 * 24 * this.duration;
|
||||||
|
},
|
||||||
|
|
||||||
|
get decodedPrivKey() {
|
||||||
|
if (!this.isKeyValid) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (this.privKey.startsWith('nsec')) {
|
||||||
|
return nip19.decode(this.privKey).data;
|
||||||
|
}
|
||||||
|
return this.privKey;
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
Alpine.start();
|
||||||
34
Shared (Extension)/Resources/experimental.html
Normal file
34
Shared (Extension)/Resources/experimental.html
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="options.build.css">
|
||||||
|
<script src="experimental.build.js" defer></script>
|
||||||
|
<title>Experimental Features</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="experimental">
|
||||||
|
<p>
|
||||||
|
<a href="options.html" class="border-none hover:underline">← Back</a>
|
||||||
|
</p>
|
||||||
|
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">Experimental Features</h1>
|
||||||
|
<p class="mt-3 text-center font-bold italic">
|
||||||
|
These things may only work partially, or not
|
||||||
|
work at all. Caveat
|
||||||
|
emptor!
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<template x-for="feature in features" :key="feature[0]">
|
||||||
|
<div class="mt-4">
|
||||||
|
<input class="checkbox" type="checkbox" :id="feature[0]" x-model="feature[1]"
|
||||||
|
@change="change(feature[0], feature[1])">
|
||||||
|
<label :for="feature[0]" x-text="feature[2]" class="font-bold"></label>
|
||||||
|
<p x-text="feature[3]" class="italic"></p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
40
Shared (Extension)/Resources/experimental.js
Normal file
40
Shared (Extension)/Resources/experimental.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import Alpine from 'alpinejs';
|
||||||
|
|
||||||
|
const FEATURES = [
|
||||||
|
[
|
||||||
|
'delegation',
|
||||||
|
'NIP-26 Delegation Profiles',
|
||||||
|
'Allow user to create delegated profiles that obey the NIP-26 standard. Requires client support.',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
Alpine.data('experimental', () => ({
|
||||||
|
features: [],
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
await this.reloadFeatures();
|
||||||
|
|
||||||
|
console.log(this.features);
|
||||||
|
},
|
||||||
|
|
||||||
|
async reloadFeatures() {
|
||||||
|
this.features = await Promise.all(
|
||||||
|
FEATURES.map(async ([name, shortDesc, longDesc]) => {
|
||||||
|
name = `feature:${name}`;
|
||||||
|
let active = await browser.storage.local.get({
|
||||||
|
[name]: false,
|
||||||
|
});
|
||||||
|
active = active[name];
|
||||||
|
return [name, active, shortDesc, longDesc];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
async change(feature, active) {
|
||||||
|
console.log(feature, active);
|
||||||
|
await browser.storage.local.set({ [feature]: active });
|
||||||
|
await this.reloadFeatures();
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
Alpine.start();
|
||||||
@@ -49,7 +49,10 @@
|
|||||||
"popup.build.js",
|
"popup.build.js",
|
||||||
"options.build.js",
|
"options.build.js",
|
||||||
"options.build.css",
|
"options.build.css",
|
||||||
"options.html"
|
"options.html",
|
||||||
|
"delegation_wizard.html",
|
||||||
|
"delegation_wizard.build.js",
|
||||||
|
"delegation_wizard.build.css"
|
||||||
],
|
],
|
||||||
"matches": [
|
"matches": [
|
||||||
"<all_urls>"
|
"<all_urls>"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body x-data="options" class="text-fuchsia-900 p-3.5 lg:p-32">
|
<body x-data="options" class="text-fuchsia-900 p-3.5 lg:p-32">
|
||||||
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">Settings</h1>
|
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">Settings</h1>
|
||||||
|
|
||||||
<!-- PROFILES -->
|
<!-- PROFILES -->
|
||||||
@@ -22,7 +22,8 @@
|
|||||||
</template>
|
</template>
|
||||||
</select>
|
</select>
|
||||||
<div class="block md:inline p-3 pl-0 md:p-0">
|
<div class="block md:inline p-3 pl-0 md:p-0">
|
||||||
<button class="button" @click.prevent="await newProfile()">New</button>
|
<button class="button" @click.prevent="newProfile">New</button>
|
||||||
|
<button class="button" @click.prevent="newDelegated" x-show="delegationActive">New Delegate</button>
|
||||||
<button class="button" @click.prevent="deleteProfile">Delete</button>
|
<button class="button" @click.prevent="deleteProfile">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -158,6 +159,10 @@
|
|||||||
<button class="button" @click.prevent="window.close()">Close</button>
|
<button class="button" @click.prevent="window.close()">Close</button>
|
||||||
<button class="button" @click.prevent="clearData">Clear Data</button>
|
<button class="button" @click.prevent="clearData">Clear Data</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<a href="experimental.html" class="border-none hover:underline">Experimental features →</a>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
KINDS,
|
KINDS,
|
||||||
humanPermission,
|
humanPermission,
|
||||||
validateKey,
|
validateKey,
|
||||||
|
feature,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
const log = console.log;
|
const log = console.log;
|
||||||
@@ -36,6 +37,7 @@ Alpine.data('options', () => ({
|
|||||||
host: '',
|
host: '',
|
||||||
permHosts: [],
|
permHosts: [],
|
||||||
hostPerms: [],
|
hostPerms: [],
|
||||||
|
delegationActive: false,
|
||||||
setPermission,
|
setPermission,
|
||||||
|
|
||||||
async init(watch = true) {
|
async init(watch = true) {
|
||||||
@@ -63,7 +65,10 @@ Alpine.data('options', () => ({
|
|||||||
// on init to set the correct profile.
|
// on init to set the correct profile.
|
||||||
await this.getProfileNames();
|
await this.getProfileNames();
|
||||||
await this.getProfileIndex();
|
await this.getProfileIndex();
|
||||||
|
this.setProfileIndexFromSearch();
|
||||||
await this.refreshProfile();
|
await this.refreshProfile();
|
||||||
|
|
||||||
|
this.delegationActive = await feature('delegation');
|
||||||
},
|
},
|
||||||
|
|
||||||
async refreshProfile() {
|
async refreshProfile() {
|
||||||
@@ -77,6 +82,15 @@ Alpine.data('options', () => ({
|
|||||||
|
|
||||||
// Profile functions
|
// Profile functions
|
||||||
|
|
||||||
|
setProfileIndexFromSearch() {
|
||||||
|
let p = new URLSearchParams(window.location.search);
|
||||||
|
let index = p.get('index');
|
||||||
|
if (!index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.profileIndex = parseInt(index);
|
||||||
|
},
|
||||||
|
|
||||||
async getProfileNames() {
|
async getProfileNames() {
|
||||||
this.profileNames = await getProfileNames();
|
this.profileNames = await getProfileNames();
|
||||||
},
|
},
|
||||||
@@ -98,6 +112,10 @@ Alpine.data('options', () => ({
|
|||||||
this.profileIndex = newIndex;
|
this.profileIndex = newIndex;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
newDelegated() {
|
||||||
|
window.location = browser.runtime.getURL('delegation_wizard.html');
|
||||||
|
},
|
||||||
|
|
||||||
async deleteProfile() {
|
async deleteProfile() {
|
||||||
if (
|
if (
|
||||||
confirm(
|
confirm(
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
relays now?
|
relays now?
|
||||||
</span>
|
</span>
|
||||||
<br>
|
<br>
|
||||||
<button @click="await addRelays()">Add Relays</button>
|
<button class="button" @click="await addRelays()">Add Relays</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="help">
|
<div class="help">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const DB_VERSION = 1;
|
const DB_VERSION = 2;
|
||||||
const storage = browser.storage.local;
|
const storage = browser.storage.local;
|
||||||
export const RECOMMENDED_RELAYS = [
|
export const RECOMMENDED_RELAYS = [
|
||||||
new URL('wss://relay.damus.io'),
|
new URL('wss://relay.damus.io'),
|
||||||
@@ -41,6 +41,14 @@ async function migrate(version, goal) {
|
|||||||
await storage.set({ profiles });
|
await storage.set({ profiles });
|
||||||
return version + 1;
|
return version + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version === 1) {
|
||||||
|
console.log('migrating to version 2.');
|
||||||
|
let profiles = await getProfiles();
|
||||||
|
profiles.forEach(profile => (profile.delegate = false));
|
||||||
|
await storage.set({ profiles });
|
||||||
|
return version + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProfiles() {
|
export async function getProfiles() {
|
||||||
@@ -98,6 +106,7 @@ export async function generateProfile(name = 'Default') {
|
|||||||
privKey: await generatePrivateKey(),
|
privKey: await generatePrivateKey(),
|
||||||
hosts: {},
|
hosts: {},
|
||||||
relays: [],
|
relays: [],
|
||||||
|
delegate: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,3 +222,9 @@ export function validateKey(key) {
|
|||||||
|
|
||||||
return hexMatch || b32Match;
|
return hexMatch || b32Match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function feature(name) {
|
||||||
|
let fname = `feature:${name}`;
|
||||||
|
let f = await browser.storage.local.get({ [fname]: false });
|
||||||
|
return f[fname];
|
||||||
|
}
|
||||||
|
|||||||
4
build.js
4
build.js
@@ -19,6 +19,10 @@ require('esbuild')
|
|||||||
'popup.build': './Shared (Extension)/Resources/popup.js',
|
'popup.build': './Shared (Extension)/Resources/popup.js',
|
||||||
'options.build': './Shared (Extension)/Resources/options.js',
|
'options.build': './Shared (Extension)/Resources/options.js',
|
||||||
'permission.build': './Shared (Extension)/Resources/permission.js',
|
'permission.build': './Shared (Extension)/Resources/permission.js',
|
||||||
|
'experimental.build':
|
||||||
|
'./Shared (Extension)/Resources/experimental.js',
|
||||||
|
'delegation_wizard.build':
|
||||||
|
'./Shared (Extension)/Resources/delegation_wizard.js',
|
||||||
},
|
},
|
||||||
outdir: './Shared (Extension)/Resources',
|
outdir: './Shared (Extension)/Resources',
|
||||||
sourcemap: 'inline',
|
sourcemap: 'inline',
|
||||||
|
|||||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -12,7 +12,7 @@
|
|||||||
"alpinejs": "^3.10.5",
|
"alpinejs": "^3.10.5",
|
||||||
"async-mutex": "^0.4.0",
|
"async-mutex": "^0.4.0",
|
||||||
"json-format-highlight": "^1.0.4",
|
"json-format-highlight": "^1.0.4",
|
||||||
"nostr-tools": "^1.2.1"
|
"nostr-tools": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
@@ -965,9 +965,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nostr-tools": {
|
"node_modules/nostr-tools": {
|
||||||
"version": "1.2.1",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.3.0.tgz",
|
||||||
"integrity": "sha512-SL0sst29mrQ7oUPGQn+NMH4yuTe69a8S4bliNpYB2IG0fDl3Cx+xSLnuCTb4nZiNalatYsA5l+751wQiDGA3+A==",
|
"integrity": "sha512-AJosFolIhMFfs34sAX1V9kXV2/kDDGzfIVVEFK3u92Q96zTn9dekED6PybZXmKGNniUKfuy1h76oBPeK1MRVQw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "^0.5.7",
|
"@noble/hashes": "^0.5.7",
|
||||||
"@noble/secp256k1": "^1.7.0",
|
"@noble/secp256k1": "^1.7.0",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
"alpinejs": "^3.10.5",
|
"alpinejs": "^3.10.5",
|
||||||
"async-mutex": "^0.4.0",
|
"async-mutex": "^0.4.0",
|
||||||
"json-format-highlight": "^1.0.4",
|
"json-format-highlight": "^1.0.4",
|
||||||
"nostr-tools": "^1.2.1"
|
"nostr-tools": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
|
|||||||
Reference in New Issue
Block a user