@@ -89,6 +89,12 @@
|
||||
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 */; };
|
||||
944A6DF3299975A70032C2E3 /* event_log.html in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DF0299975A70032C2E3 /* event_log.html */; };
|
||||
944A6DF4299975A70032C2E3 /* event_log.html in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DF0299975A70032C2E3 /* event_log.html */; };
|
||||
944A6DF5299975A70032C2E3 /* event_log.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DF1299975A70032C2E3 /* event_log.build.js */; };
|
||||
944A6DF6299975A70032C2E3 /* event_log.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DF1299975A70032C2E3 /* event_log.build.js */; };
|
||||
944A6DF7299975A70032C2E3 /* event_log.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DF2299975A70032C2E3 /* event_log.js */; };
|
||||
944A6DF8299975A70032C2E3 /* event_log.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DF2299975A70032C2E3 /* event_log.js */; };
|
||||
948C69D9297F887600FB3574 /* 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 */; };
|
||||
@@ -198,6 +204,9 @@
|
||||
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>"; };
|
||||
944A6DF0299975A70032C2E3 /* event_log.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = event_log.html; sourceTree = "<group>"; };
|
||||
944A6DF1299975A70032C2E3 /* event_log.build.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = event_log.build.js; sourceTree = "<group>"; };
|
||||
944A6DF2299975A70032C2E3 /* event_log.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = event_log.js; 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>"; };
|
||||
948C69DC297F88A200FB3574 /* options.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = options.js; sourceTree = "<group>"; };
|
||||
@@ -285,6 +294,9 @@
|
||||
941B03A2296FA90400CA291E /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
944A6DF1299975A70032C2E3 /* event_log.build.js */,
|
||||
944A6DF0299975A70032C2E3 /* event_log.html */,
|
||||
944A6DF2299975A70032C2E3 /* event_log.js */,
|
||||
944A6DE9299682EF0032C2E3 /* experimental.build.js */,
|
||||
944A6DE7299682EF0032C2E3 /* experimental.html */,
|
||||
944A6DE8299682EF0032C2E3 /* experimental.js */,
|
||||
@@ -548,17 +560,20 @@
|
||||
948C69E2297F891F00FB3574 /* options.build.js in Resources */,
|
||||
941B03F8296FA90400CA291E /* popup.css in Resources */,
|
||||
941B04292977A28700CA291E /* Icon-128.png in Resources */,
|
||||
944A6DF3299975A70032C2E3 /* event_log.html in Resources */,
|
||||
941B04182971138F00CA291E /* nostr.build.js in Resources */,
|
||||
941B042C2978CD8E00CA291E /* iOS-Icon-1024.png in Resources */,
|
||||
941B03F6296FA90400CA291E /* popup.html in Resources */,
|
||||
941B040D296FAD6900CA291E /* nostr.js in Resources */,
|
||||
944A6DE02991DFC60032C2E3 /* delegation_wizard.build.js in Resources */,
|
||||
944A6DF5299975A70032C2E3 /* event_log.build.js in Resources */,
|
||||
941B03EE296FA90400CA291E /* images in Resources */,
|
||||
941B03F0296FA90400CA291E /* manifest.json in Resources */,
|
||||
944A6DD62988BD230032C2E3 /* permission.js in Resources */,
|
||||
941B04312978CDF900CA291E /* Icon-32.png in Resources */,
|
||||
941B041A2971139000CA291E /* content.build.js in Resources */,
|
||||
941B041C2971139000CA291E /* popup.build.js in Resources */,
|
||||
944A6DF7299975A70032C2E3 /* event_log.js in Resources */,
|
||||
944A6DEC299682EF0032C2E3 /* experimental.js in Resources */,
|
||||
941B03EC296FA90400CA291E /* _locales in Resources */,
|
||||
941B04222977A25700CA291E /* Icon-512.png in Resources */,
|
||||
@@ -591,17 +606,20 @@
|
||||
948C69E3297F891F00FB3574 /* options.build.js in Resources */,
|
||||
941B03F9296FA90400CA291E /* popup.css in Resources */,
|
||||
941B042A2977A28700CA291E /* Icon-128.png in Resources */,
|
||||
944A6DF4299975A70032C2E3 /* event_log.html in Resources */,
|
||||
941B04192971138F00CA291E /* nostr.build.js in Resources */,
|
||||
941B042D2978CD8E00CA291E /* iOS-Icon-1024.png in Resources */,
|
||||
941B03F7296FA90400CA291E /* popup.html in Resources */,
|
||||
941B040E296FAD6900CA291E /* nostr.js in Resources */,
|
||||
944A6DE12991DFC60032C2E3 /* delegation_wizard.build.js in Resources */,
|
||||
944A6DF6299975A70032C2E3 /* event_log.build.js in Resources */,
|
||||
941B03EF296FA90400CA291E /* images in Resources */,
|
||||
941B03F1296FA90400CA291E /* manifest.json in Resources */,
|
||||
944A6DD72988BD230032C2E3 /* permission.js in Resources */,
|
||||
941B04322978CDF900CA291E /* Icon-32.png in Resources */,
|
||||
941B041B2971139000CA291E /* content.build.js in Resources */,
|
||||
941B041D2971139000CA291E /* popup.build.js in Resources */,
|
||||
944A6DF8299975A70032C2E3 /* event_log.js in Resources */,
|
||||
944A6DED299682EF0032C2E3 /* experimental.js in Resources */,
|
||||
941B03ED296FA90400CA291E /* _locales in Resources */,
|
||||
941B04232977A25700CA291E /* Icon-512.png in Resources */,
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<key>Nostore (iOS).xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>Nostore (macOS).xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>nostore (iOS).xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
setPermission,
|
||||
feature,
|
||||
} from './utils';
|
||||
import { saveEvent } from './db';
|
||||
|
||||
const storage = browser.storage.local;
|
||||
const log = msg => console.log('Background: ', msg);
|
||||
@@ -139,7 +140,7 @@ function complete({ payload, origKind, event, remember, host }) {
|
||||
});
|
||||
break;
|
||||
case 'signEvent':
|
||||
signEvent_(event).then(e => sendResponse(e));
|
||||
signEvent_(event, host).then(e => sendResponse(e));
|
||||
break;
|
||||
case 'nip04.encrypt':
|
||||
nip04Encrypt(event).then(e => sendResponse(e));
|
||||
@@ -213,14 +214,17 @@ async function currentProfile() {
|
||||
return profiles[index];
|
||||
}
|
||||
|
||||
async function signEvent_(event) {
|
||||
async function signEvent_(event, host) {
|
||||
event = JSON.parse(JSON.stringify(event));
|
||||
let privKey = await getPrivKey();
|
||||
let pubKey = getPublicKey(privKey);
|
||||
event.pubkey = pubKey;
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, privKey);
|
||||
console.log(JSON.stringify(event));
|
||||
saveEvent({
|
||||
event,
|
||||
metadata: { host, signed_at: Math.round(Date.now() / 1000) },
|
||||
});
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
69
Shared (Extension)/Resources/db.js
Normal file
69
Shared (Extension)/Resources/db.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import { openDB } from 'idb';
|
||||
|
||||
async function openEventsDb() {
|
||||
return await openDB('events', 1, {
|
||||
upgrade(db) {
|
||||
const events = db.createObjectStore('events', {
|
||||
keyPath: 'event.id',
|
||||
});
|
||||
events.createIndex('pubkey', 'event.pubkey');
|
||||
events.createIndex('created_at', 'event.created_at');
|
||||
events.createIndex('kind', 'event.kind');
|
||||
events.createIndex('host', 'metadata.host');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function saveEvent(event) {
|
||||
let db = await openEventsDb();
|
||||
return db.put('events', event);
|
||||
}
|
||||
|
||||
export async function sortByIndex(index, query, asc, max) {
|
||||
let db = await openEventsDb();
|
||||
let events = [];
|
||||
let cursor = await db
|
||||
.transaction('events')
|
||||
.store.index(index)
|
||||
.openCursor(query, asc ? 'next' : 'prev');
|
||||
while (cursor) {
|
||||
events.push(cursor.value);
|
||||
if (events.length >= max) {
|
||||
break;
|
||||
}
|
||||
cursor = await cursor.continue();
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
export async function getHosts() {
|
||||
let db = await openEventsDb();
|
||||
let hosts = new Set();
|
||||
let cursor = await db.transaction('events').store.openCursor();
|
||||
while (cursor) {
|
||||
hosts.add(cursor.value.metadata.host);
|
||||
cursor = await cursor.continue();
|
||||
}
|
||||
return [...hosts];
|
||||
}
|
||||
|
||||
export async function downloadAllContents() {
|
||||
let db = await openEventsDb();
|
||||
let events = [];
|
||||
let cursor = await db.transaction('events').store.openCursor();
|
||||
while (cursor) {
|
||||
events.push(cursor.value.event);
|
||||
cursor = await cursor.continue();
|
||||
}
|
||||
events = events.map(e => JSON.stringify(e));
|
||||
events = events.join('\n');
|
||||
console.log(events);
|
||||
|
||||
const file = new File([events], 'events.jsonl', {
|
||||
type: 'application/octet-stream',
|
||||
});
|
||||
|
||||
const blob = new Blob([events], { type: 'plain/text' });
|
||||
|
||||
return blob;
|
||||
}
|
||||
135
Shared (Extension)/Resources/event_log.html
Normal file
135
Shared (Extension)/Resources/event_log.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<!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="event_log.build.js"></script>
|
||||
<link rel="stylesheet" href="options.build.css">
|
||||
<title>Event Log</title>
|
||||
|
||||
<style>
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="eventLog">
|
||||
<p>
|
||||
<a href="options.html" class="border-none hover:underline">← Back</a>
|
||||
</p>
|
||||
|
||||
<h1 class="section-header">Event Log</h1>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">Filters</div>
|
||||
|
||||
<div class="grid grid-cols-2 xl:grid-cols-4 gap-4">
|
||||
<div>
|
||||
<label for="view">View</label>
|
||||
<select id="view" class="input" x-model="view" @change="reload">
|
||||
<option value="created_at">created_at</option>
|
||||
<option value="kind">kind</option>
|
||||
<option value="host">host</option>
|
||||
<option value="pubkey">pubkey</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="sort">Order</label>
|
||||
<select id="sort" x-model="sort" class="input" @change="reload">
|
||||
<option value="asc">Ascending</option>
|
||||
<option value="desc">Descending</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="max">Max</label>
|
||||
<input type="number" x-model.number="max" @input.debounce.750ms="reload" class="input" min="10">
|
||||
</div>
|
||||
|
||||
<div></div>
|
||||
|
||||
<div x-show="view === 'created_at'">
|
||||
<label for="fromCreatedAt">From</label>
|
||||
<input type="date" id="fromCreatedAt" x-model="fromCreatedAt" class="input" @change="reload">
|
||||
</div>
|
||||
|
||||
<div x-show="view === 'created_at'">
|
||||
<label for="toCreatedAt">To</label>
|
||||
<input type="date" id="toCreatedAt" x-model="toCreatedAt" class="input" @change="reload">
|
||||
</div>
|
||||
|
||||
<div x-show="view === 'kind'">
|
||||
<label for="kindShortcut">Quick Select</label>
|
||||
<select id="kindShortcut" class="input" @change="quickKindSelect" x-model="quickKind">
|
||||
<option></option>
|
||||
<template x-for="k in kinds">
|
||||
<option :value="k[0]" x-text="k[1]"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div x-show="view === 'kind'">
|
||||
<label for="fromKind">From</label>
|
||||
<input type="number" id="fromKind" x-model.number="fromKind" class="input" @change="reload">
|
||||
</div>
|
||||
|
||||
<div x-show="view === 'kind'">
|
||||
<label for="toKind">To</label>
|
||||
<input type="number" id="toKind" x-model.number="toKind" class="input" @change="reload">
|
||||
</div>
|
||||
|
||||
<div x-show="view === 'host'">
|
||||
<label for="host">Host</label>
|
||||
<select id="host" class="input" x-model="host" @change="reload">
|
||||
<option value=""></option>
|
||||
<template x-for="h in allHosts">
|
||||
<option :value="h" x-text="h"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div x-show="view === 'pubkey'">
|
||||
<label for="profiles">Profiles</label>
|
||||
<select id="profiles" class="input" x-model="profile" @change="pkFromProfile">
|
||||
<option value=""></option>
|
||||
<template x-for="p in profileNames">
|
||||
<option :value="p" x-text="p"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div x-show="view === 'pubkey'">
|
||||
<label for="pubkey">Pubkey</label>
|
||||
<input type="text" class="input" x-model="pubkey" @input.debounce="reload">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button class="button mt-3" @click="saveAll">Save all</button>
|
||||
<button class="button mt-3" @click="deleteAll">Delete all</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<template x-for="(event, index) in events">
|
||||
<div class="mt-3 border-solid border border-fuchsia-700 rounded-lg">
|
||||
<div class="select-none flex cursor-pointer text-sm md:text-xl"
|
||||
@click="selected = selected === index ? null : index">
|
||||
<div class="flex-none w-14 p-4 font-extrabold" x-text="selected === index ? '-' : '+'"></div>
|
||||
<div class="flex-1 w-64 p-4" x-text="formatDate(event.metadata.signed_at)"></div>
|
||||
<div class="flex-1 w-64 p-4" x-text="event.metadata.host"></div>
|
||||
<div class="flex-1 w-64 p-4" x-text="formatKind(event.event.kind)"></div>
|
||||
</div>
|
||||
|
||||
<pre class="rounded-b-lg bg-slate-200 text-sm md:text-xl" x-html="highlight(event)" x-show="selected === index"
|
||||
x-transition:enter.opacity.delay.75ms x-transition:leave.opacity>
|
||||
</pre>
|
||||
</div>
|
||||
</template>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
135
Shared (Extension)/Resources/event_log.js
Normal file
135
Shared (Extension)/Resources/event_log.js
Normal file
@@ -0,0 +1,135 @@
|
||||
import Alpine from 'alpinejs';
|
||||
import { deleteDB } from 'idb';
|
||||
import jsonFormatHighlight from 'json-format-highlight';
|
||||
import { getPublicKey } from 'nostr-tools';
|
||||
import { downloadAllContents, getHosts, sortByIndex } from './db';
|
||||
import { getProfiles, KINDS } from './utils';
|
||||
|
||||
const TOMORROW = new Date();
|
||||
TOMORROW.setDate(TOMORROW.getDate() + 1);
|
||||
|
||||
Alpine.data('eventLog', () => ({
|
||||
kinds: KINDS,
|
||||
events: [],
|
||||
view: 'created_at',
|
||||
max: 100,
|
||||
sort: 'asc',
|
||||
allHosts: [],
|
||||
host: '',
|
||||
allProfiles: [],
|
||||
profile: '',
|
||||
pubkey: '',
|
||||
selected: null,
|
||||
|
||||
// date view
|
||||
fromCreatedAt: '2008-10-31',
|
||||
toCreatedAt: TOMORROW.toISOString().split('T')[0],
|
||||
|
||||
// kind view
|
||||
quickKind: '',
|
||||
fromKind: 0,
|
||||
toKind: 50000,
|
||||
|
||||
async init() {
|
||||
await this.reload();
|
||||
},
|
||||
|
||||
async reload() {
|
||||
let events = await sortByIndex(
|
||||
this.view,
|
||||
this.keyRange,
|
||||
this.sort === 'asc',
|
||||
this.max
|
||||
);
|
||||
this.events = events;
|
||||
getHosts().then(hosts => (this.allHosts = hosts));
|
||||
const profiles = await getProfiles();
|
||||
console.log(profiles);
|
||||
this.allProfiles = profiles.map(profile => ({
|
||||
name: profile.name,
|
||||
pubkey: getPublicKey(profile.privKey),
|
||||
}));
|
||||
},
|
||||
|
||||
async saveAll() {
|
||||
const file = await downloadAllContents();
|
||||
browser.tabs.create({
|
||||
url: URL.createObjectURL(file),
|
||||
active: true,
|
||||
});
|
||||
},
|
||||
|
||||
async deleteAll() {
|
||||
if (confirm('Are you sure you want to delete ALL events?')) {
|
||||
await deleteDB('events');
|
||||
await this.reload();
|
||||
}
|
||||
},
|
||||
|
||||
quickKindSelect() {
|
||||
if (this.quickKind === '') return;
|
||||
const i = parseInt(this.quickKind);
|
||||
this.fromKind = i;
|
||||
this.toKind = i;
|
||||
this.reload();
|
||||
},
|
||||
|
||||
pkFromProfile() {
|
||||
this.pubkey = this.allProfiles.find(
|
||||
({ name }) => name === this.profile
|
||||
).pubkey;
|
||||
this.reload();
|
||||
},
|
||||
|
||||
highlight(event) {
|
||||
return jsonFormatHighlight(event);
|
||||
},
|
||||
|
||||
formatDate(epochSeconds) {
|
||||
return new Date(epochSeconds * 1000).toUTCString();
|
||||
},
|
||||
|
||||
formatKind(kind) {
|
||||
const k = KINDS.find(([kNum, _]) => kNum === kind);
|
||||
return k ? `${k[1]} (${kind})` : `Unknown (${kind})`;
|
||||
},
|
||||
|
||||
// Properties
|
||||
|
||||
get fromTime() {
|
||||
let dt = new Date(this.fromCreatedAt);
|
||||
return Math.floor(dt.getTime() / 1000);
|
||||
},
|
||||
|
||||
get toTime() {
|
||||
let dt = new Date(this.toCreatedAt);
|
||||
return Math.floor(dt.getTime() / 1000);
|
||||
},
|
||||
|
||||
get profileNames() {
|
||||
return this.allProfiles.map(p => p.name);
|
||||
},
|
||||
|
||||
get keyRange() {
|
||||
switch (this.view) {
|
||||
case 'created_at':
|
||||
return IDBKeyRange.bound(this.fromTime, this.toTime);
|
||||
|
||||
case 'kind':
|
||||
return IDBKeyRange.bound(this.fromKind, this.toKind);
|
||||
|
||||
case 'host':
|
||||
if (this.host.length === 0) return null;
|
||||
return IDBKeyRange.only(this.host);
|
||||
|
||||
case 'pubkey':
|
||||
if (this.pubkey.length === 0) return null;
|
||||
return IDBKeyRange.only(this.pubkey);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
Alpine.start();
|
||||
@@ -162,6 +162,8 @@
|
||||
|
||||
<div class="mt-6">
|
||||
<a href="experimental.html" class="border-none hover:underline">Experimental features →</a>
|
||||
<br>
|
||||
<a href="event_log.html" class="border-none hover:underline">Event log →</a>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
||||
1
build.js
1
build.js
@@ -23,6 +23,7 @@ require('esbuild')
|
||||
'./Shared (Extension)/Resources/experimental.js',
|
||||
'delegation_wizard.build':
|
||||
'./Shared (Extension)/Resources/delegation_wizard.js',
|
||||
'event_log.build': './Shared (Extension)/Resources/event_log.js',
|
||||
},
|
||||
outdir: './Shared (Extension)/Resources',
|
||||
sourcemap: 'inline',
|
||||
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"alpinejs": "^3.10.5",
|
||||
"async-mutex": "^0.4.0",
|
||||
"idb": "^7.1.1",
|
||||
"json-format-highlight": "^1.0.4",
|
||||
"nostr-tools": "^1.3.0"
|
||||
},
|
||||
@@ -835,6 +836,11 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/idb": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
|
||||
"integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"dependencies": {
|
||||
"alpinejs": "^3.10.5",
|
||||
"async-mutex": "^0.4.0",
|
||||
"idb": "^7.1.1",
|
||||
"json-format-highlight": "^1.0.4",
|
||||
"nostr-tools": "^1.3.0"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user