init
This commit is contained in:
parent
9244831423
commit
4dcf10b04b
@ -10,6 +10,10 @@
|
|||||||
"@types/node": "^16.7.13",
|
"@types/node": "^16.7.13",
|
||||||
"@types/react": "^18.0.0",
|
"@types/react": "^18.0.0",
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.0.0",
|
||||||
|
"dexie": "^3.2.3",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"nostr-relaypool": "^0.6.27",
|
||||||
|
"parse-diff": "^0.11.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
46
src/App.css
46
src/App.css
@ -1,38 +1,26 @@
|
|||||||
.App {
|
.app {
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-logo {
|
section.side {
|
||||||
height: 40vmin;
|
width: 200px;
|
||||||
pointer-events: none;
|
position: fixed;
|
||||||
}
|
height: 100vh;
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.App-logo {
|
|
||||||
animation: App-logo-spin infinite 20s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-header {
|
|
||||||
background-color: #282c34;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-link {
|
section.side>div {
|
||||||
color: #61dafb;
|
padding: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes App-logo-spin {
|
.patch-list {
|
||||||
from {
|
display: flex;
|
||||||
transform: rotate(0deg);
|
flex-direction: column;
|
||||||
}
|
margin-left: 200px;
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,9 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { render, screen } from '@testing-library/react';
|
|
||||||
import App from './App';
|
|
||||||
|
|
||||||
test('renders learn react link', () => {
|
|
||||||
render(<App />);
|
|
||||||
const linkElement = screen.getByText(/learn react/i);
|
|
||||||
expect(linkElement).toBeInTheDocument();
|
|
||||||
});
|
|
70
src/App.tsx
70
src/App.tsx
@ -1,26 +1,56 @@
|
|||||||
import React from 'react';
|
import { useMemo, useState, useSyncExternalStore } from 'react';
|
||||||
import logo from './logo.svg';
|
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
import { RelayPool } from "nostr-relaypool";
|
||||||
|
import { PatchCache } from './Cache/PatchCache';
|
||||||
|
import { PatchstrDb } from './Db';
|
||||||
|
import { PatchRow } from './PatchRow';
|
||||||
|
import { parseDiffEvent } from './Diff';
|
||||||
|
|
||||||
function App() {
|
const relays = [
|
||||||
|
"wss://relay.damus.io",
|
||||||
|
"wss://nos.lol",
|
||||||
|
"wss://relay.snort.social"
|
||||||
|
];
|
||||||
|
|
||||||
|
const Store = new PatchCache("Patches", PatchstrDb.events);
|
||||||
|
const Nostr = new RelayPool(relays);
|
||||||
|
const sub = Nostr.subscribe([
|
||||||
|
{
|
||||||
|
kinds: [19691228],
|
||||||
|
limit: 200
|
||||||
|
}
|
||||||
|
], relays,
|
||||||
|
async (e) => {
|
||||||
|
console.debug(e);
|
||||||
|
await Store.set(parseDiffEvent(e));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function usePatchStore() {
|
||||||
|
return useSyncExternalStore(a => Store.hook(a, "*"), () => Store.snapshot());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
const store = usePatchStore();
|
||||||
|
const tags = [...new Set(store.map(a => a.tag))];
|
||||||
|
const [tag, setTag] = useState<string>();
|
||||||
|
|
||||||
|
const patches = useMemo(() => {
|
||||||
|
return store.filter(a => tag === undefined || a.tag === tag);
|
||||||
|
}, [tag]);
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="app">
|
||||||
<header className="App-header">
|
<section className="side">
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
<div onClick={() => setTag(undefined)}>
|
||||||
<p>
|
All
|
||||||
Edit <code>src/App.tsx</code> and save to reload.
|
</div>
|
||||||
</p>
|
{tags.map(a => <div key={a} onClick={() => setTag(a)}>
|
||||||
<a
|
{a}
|
||||||
className="App-link"
|
</div>)}
|
||||||
href="https://reactjs.org"
|
</section>
|
||||||
target="_blank"
|
<section className="patch-list">
|
||||||
rel="noopener noreferrer"
|
{patches.map(a => <PatchRow ev={a} key={a.id} />)}
|
||||||
>
|
</section>
|
||||||
Learn React
|
|
||||||
</a>
|
|
||||||
</header>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
197
src/Cache/BaseFeedCache.ts
Normal file
197
src/Cache/BaseFeedCache.ts
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import { Table } from "dexie";
|
||||||
|
import { unixNowMs, unwrap } from "../Util";
|
||||||
|
|
||||||
|
type HookFn = () => void;
|
||||||
|
|
||||||
|
interface HookFilter {
|
||||||
|
key: string;
|
||||||
|
fn: HookFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = {
|
||||||
|
ready: true
|
||||||
|
}
|
||||||
|
|
||||||
|
export default abstract class BaseFeedCache<TCached> {
|
||||||
|
#name: string;
|
||||||
|
#table: Table<TCached>;
|
||||||
|
#hooks: Array<HookFilter> = [];
|
||||||
|
#snapshot: Readonly<Array<TCached>> = [];
|
||||||
|
#changed = true;
|
||||||
|
protected onTable: Set<string> = new Set();
|
||||||
|
protected cache: Map<string, TCached> = new Map();
|
||||||
|
|
||||||
|
constructor(name: string, table: Table<TCached>) {
|
||||||
|
this.#name = name;
|
||||||
|
this.#table = table;
|
||||||
|
setInterval(() => {
|
||||||
|
console.debug(
|
||||||
|
`[${this.#name}] ${this.cache.size} loaded, ${this.onTable.size} on-disk, ${this.#hooks.length} hooks`
|
||||||
|
);
|
||||||
|
}, 5_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async preload() {
|
||||||
|
if (db.ready) {
|
||||||
|
const keys = await this.#table.toCollection().primaryKeys();
|
||||||
|
this.onTable = new Set<string>(keys.map(a => a as string));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hook(fn: HookFn, key: string | undefined) {
|
||||||
|
if (!key) {
|
||||||
|
return () => {
|
||||||
|
//noop
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#hooks.push({
|
||||||
|
key,
|
||||||
|
fn,
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
const idx = this.#hooks.findIndex(a => a.fn === fn);
|
||||||
|
if (idx >= 0) {
|
||||||
|
this.#hooks.splice(idx, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getFromCache(key?: string) {
|
||||||
|
if (key) {
|
||||||
|
return this.cache.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(key?: string) {
|
||||||
|
if (key && !this.cache.has(key) && db.ready) {
|
||||||
|
const cached = await this.#table.get(key);
|
||||||
|
if (cached) {
|
||||||
|
this.cache.set(this.key(cached), cached);
|
||||||
|
this.notifyChange([key]);
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return key ? this.cache.get(key) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async bulkGet(keys: Array<string>) {
|
||||||
|
const missing = keys.filter(a => !this.cache.has(a));
|
||||||
|
if (missing.length > 0 && db.ready) {
|
||||||
|
const cached = await this.#table.bulkGet(missing);
|
||||||
|
cached.forEach(a => {
|
||||||
|
if (a) {
|
||||||
|
this.cache.set(this.key(a), a);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
.map(a => this.cache.get(a))
|
||||||
|
.filter(a => a)
|
||||||
|
.map(a => unwrap(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
async set(obj: TCached) {
|
||||||
|
const k = this.key(obj);
|
||||||
|
this.cache.set(k, obj);
|
||||||
|
if (db.ready) {
|
||||||
|
await this.#table.put(obj);
|
||||||
|
this.onTable.add(k);
|
||||||
|
}
|
||||||
|
this.notifyChange([k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async bulkSet(obj: Array<TCached>) {
|
||||||
|
if (db.ready) {
|
||||||
|
await this.#table.bulkPut(obj);
|
||||||
|
obj.forEach(a => this.onTable.add(this.key(a)));
|
||||||
|
}
|
||||||
|
obj.forEach(v => this.cache.set(this.key(v), v));
|
||||||
|
this.notifyChange(obj.map(a => this.key(a)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to update an entry where created values exists
|
||||||
|
* @param m Profile metadata
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async update<TCachedWithCreated extends TCached & { created: number, loaded: number }>(m: TCachedWithCreated) {
|
||||||
|
const k = this.key(m);
|
||||||
|
const existing = this.getFromCache(k) as TCachedWithCreated;
|
||||||
|
const updateType = (() => {
|
||||||
|
if (!existing) {
|
||||||
|
return "new";
|
||||||
|
}
|
||||||
|
if (existing.created < m.created) {
|
||||||
|
return "updated";
|
||||||
|
}
|
||||||
|
if (existing && existing.loaded < m.loaded) {
|
||||||
|
return "refresh";
|
||||||
|
}
|
||||||
|
return "no_change";
|
||||||
|
})();
|
||||||
|
console.debug(`Updating ${k} ${updateType}`, m);
|
||||||
|
if (updateType !== "no_change") {
|
||||||
|
const updated = {
|
||||||
|
...existing,
|
||||||
|
...m,
|
||||||
|
};
|
||||||
|
await this.set(updated);
|
||||||
|
}
|
||||||
|
return updateType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a list of rows from disk cache
|
||||||
|
* @param keys List of ids to load
|
||||||
|
* @returns Keys that do not exist on disk cache
|
||||||
|
*/
|
||||||
|
async buffer(keys: Array<string>): Promise<Array<string>> {
|
||||||
|
const needsBuffer = keys.filter(a => !this.cache.has(a));
|
||||||
|
if (db.ready && needsBuffer.length > 0) {
|
||||||
|
const mapped = needsBuffer.map(a => ({
|
||||||
|
has: this.onTable.has(a),
|
||||||
|
key: a,
|
||||||
|
}));
|
||||||
|
const start = unixNowMs();
|
||||||
|
const fromCache = await this.#table.bulkGet(mapped.filter(a => a.has).map(a => a.key));
|
||||||
|
const fromCacheFiltered = fromCache.filter(a => a !== undefined).map(a => unwrap(a));
|
||||||
|
fromCacheFiltered.forEach(a => {
|
||||||
|
this.cache.set(this.key(a), a);
|
||||||
|
});
|
||||||
|
this.notifyChange(fromCacheFiltered.map(a => this.key(a)));
|
||||||
|
console.debug(
|
||||||
|
`[${this.#name}] Loaded ${fromCacheFiltered.length}/${keys.length} in ${(
|
||||||
|
unixNowMs() - start
|
||||||
|
).toLocaleString()} ms`
|
||||||
|
);
|
||||||
|
return mapped.filter(a => !a.has).map(a => a.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// no IndexdDB always return all keys
|
||||||
|
return needsBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
await this.#table.clear();
|
||||||
|
this.cache.clear();
|
||||||
|
this.onTable.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot() {
|
||||||
|
if (this.#changed) {
|
||||||
|
this.#snapshot = this.takeSnapshot();
|
||||||
|
this.#changed = false;
|
||||||
|
}
|
||||||
|
return this.#snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected notifyChange(keys: Array<string>) {
|
||||||
|
this.#changed = true;
|
||||||
|
this.#hooks.filter(a => keys.includes(a.key) || a.key === "*").forEach(h => h.fn());
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract key(of: TCached): string;
|
||||||
|
abstract takeSnapshot(): Array<TCached>;
|
||||||
|
}
|
13
src/Cache/PatchCache.ts
Normal file
13
src/Cache/PatchCache.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import BaseFeedCache from "./BaseFeedCache";
|
||||||
|
import { ParsedPatch } from "../Diff";
|
||||||
|
|
||||||
|
export class PatchCache extends BaseFeedCache<ParsedPatch> {
|
||||||
|
key(of: ParsedPatch): string {
|
||||||
|
return of.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
takeSnapshot(): ParsedPatch[] {
|
||||||
|
return [...this.cache.values()];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
src/Db.ts
Normal file
15
src/Db.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import Dexie, { Table } from "dexie";
|
||||||
|
import { ParsedPatch } from "./Diff";
|
||||||
|
|
||||||
|
export class Db extends Dexie {
|
||||||
|
events!: Table<ParsedPatch>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super("patchstr");
|
||||||
|
this.version(1).stores({
|
||||||
|
events: "++id"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PatchstrDb = new Db();
|
38
src/Diff.ts
Normal file
38
src/Diff.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import * as parseDiff from "parse-diff";
|
||||||
|
import { Event } from 'nostr-tools';
|
||||||
|
|
||||||
|
export interface ParsedPatch {
|
||||||
|
id: string
|
||||||
|
created: number
|
||||||
|
pubkey: string
|
||||||
|
tag: string
|
||||||
|
author: AuthorName
|
||||||
|
subject: string
|
||||||
|
diff: parseDiff.File[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthorName {
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseDiffEvent(ev: Event) {
|
||||||
|
const tag = ev.tags.find(a => a[0] === "t")?.[1] ?? "";
|
||||||
|
const author = ev.tags.find(a => a[0] === "author")?.[1] ?? "";
|
||||||
|
const subject = ev.tags.find(a => a[0] === "subject")?.[1] ?? "";
|
||||||
|
|
||||||
|
const EmailRegex = /^([\w ]+)(<?\S+>)?$/i;
|
||||||
|
const matches = author.match(EmailRegex);
|
||||||
|
return {
|
||||||
|
id: ev.id,
|
||||||
|
created: ev.created_at,
|
||||||
|
pubkey: ev.pubkey,
|
||||||
|
tag,
|
||||||
|
author: {
|
||||||
|
name: matches?.[1],
|
||||||
|
email: matches?.[2]
|
||||||
|
},
|
||||||
|
subject,
|
||||||
|
diff: parseDiff.default(ev.content)
|
||||||
|
} as ParsedPatch;
|
||||||
|
}
|
35
src/PatchRow.css
Normal file
35
src/PatchRow.css
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
.patch-header {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-columns: 150px auto 100px 50px;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
width: fill;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 2em;
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.patch-header>div {
|
||||||
|
}
|
||||||
|
|
||||||
|
.patch-header>diV:nth-child(1) {
|
||||||
|
background-color: grey;
|
||||||
|
}
|
||||||
|
.patch-header>div:nth-child(2) {
|
||||||
|
}
|
||||||
|
.patch-header>div:nth-child(3) {
|
||||||
|
background-color: grey;
|
||||||
|
}
|
||||||
|
.patch-header>div:nth-child(4) {
|
||||||
|
background-color: grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add {
|
||||||
|
color: lightgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove {
|
||||||
|
color: darkred;
|
||||||
|
}
|
31
src/PatchRow.tsx
Normal file
31
src/PatchRow.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import "./PatchRow.css"
|
||||||
|
import moment from "moment";
|
||||||
|
import { ParsedPatch } from "./Diff";
|
||||||
|
|
||||||
|
export function PatchRow({ ev }: { ev: ParsedPatch }) {
|
||||||
|
const ts = new Date(ev.created * 1000);
|
||||||
|
return <div className="patch-row">
|
||||||
|
<div className="patch-header">
|
||||||
|
<div>
|
||||||
|
{ev.author.name}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{ev.subject}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<time dateTime={ts.toISOString()}>
|
||||||
|
{moment(ts).fromNow()}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="add">
|
||||||
|
+{ev.diff.reduce((acc, v) => acc + v.additions, 0)}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="remove">
|
||||||
|
-{ev.diff.reduce((acc, v) => acc + v.deletions, 0)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
10
src/Util.ts
Normal file
10
src/Util.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export function unixNowMs() {
|
||||||
|
return new Date().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unwrap<T>(value?: T) {
|
||||||
|
if (!value) {
|
||||||
|
throw new Error("Missing value");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
@ -1,10 +1,13 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
sans-serif;
|
sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App';
|
import {App} from './App';
|
||||||
import reportWebVitals from './reportWebVitals';
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById('root') as HTMLElement
|
document.getElementById('root') as HTMLElement
|
||||||
@ -12,8 +12,3 @@ root.render(
|
|||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
|
||||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
|
||||||
reportWebVitals();
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es2020",
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
|
89
yarn.lock
89
yarn.lock
@ -1417,6 +1417,15 @@
|
|||||||
graceful-fs "^4.2.9"
|
graceful-fs "^4.2.9"
|
||||||
source-map "^0.6.0"
|
source-map "^0.6.0"
|
||||||
|
|
||||||
|
"@jest/source-map@^29.4.3":
|
||||||
|
version "29.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20"
|
||||||
|
integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.15"
|
||||||
|
callsites "^3.0.0"
|
||||||
|
graceful-fs "^4.2.9"
|
||||||
|
|
||||||
"@jest/test-result@^27.5.1":
|
"@jest/test-result@^27.5.1":
|
||||||
version "27.5.1"
|
version "27.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb"
|
resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb"
|
||||||
@ -1540,7 +1549,7 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
|
||||||
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
|
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
|
||||||
|
|
||||||
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
|
"@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
|
||||||
version "0.3.18"
|
version "0.3.18"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6"
|
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6"
|
||||||
integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==
|
integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==
|
||||||
@ -1560,6 +1569,16 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
eslint-scope "5.1.1"
|
eslint-scope "5.1.1"
|
||||||
|
|
||||||
|
"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0":
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12"
|
||||||
|
integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==
|
||||||
|
|
||||||
|
"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0":
|
||||||
|
version "1.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c"
|
||||||
|
integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==
|
||||||
|
|
||||||
"@nodelib/fs.scandir@2.1.5":
|
"@nodelib/fs.scandir@2.1.5":
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||||
@ -1638,6 +1657,28 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728"
|
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728"
|
||||||
integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==
|
integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==
|
||||||
|
|
||||||
|
"@scure/base@1.1.1", "@scure/base@~1.1.0":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938"
|
||||||
|
integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==
|
||||||
|
|
||||||
|
"@scure/bip32@1.1.4":
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.4.tgz#2c91a7be0156b15f26dd0c843a06a1917f129efd"
|
||||||
|
integrity sha512-m925ACYK0wPELsF7Z/VdLGmKj1StIeHraPMYB9xiAFiq/PnvqWd/99I0TQ2OZhjjlMDsDJeZlyXMWi0beaA7NA==
|
||||||
|
dependencies:
|
||||||
|
"@noble/hashes" "~1.2.0"
|
||||||
|
"@noble/secp256k1" "~1.7.0"
|
||||||
|
"@scure/base" "~1.1.0"
|
||||||
|
|
||||||
|
"@scure/bip39@1.1.1":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5"
|
||||||
|
integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==
|
||||||
|
dependencies:
|
||||||
|
"@noble/hashes" "~1.2.0"
|
||||||
|
"@scure/base" "~1.1.0"
|
||||||
|
|
||||||
"@sinclair/typebox@^0.24.1":
|
"@sinclair/typebox@^0.24.1":
|
||||||
version "0.24.51"
|
version "0.24.51"
|
||||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f"
|
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f"
|
||||||
@ -3661,6 +3702,11 @@ detect-port-alt@^1.1.6:
|
|||||||
address "^1.0.1"
|
address "^1.0.1"
|
||||||
debug "^2.6.0"
|
debug "^2.6.0"
|
||||||
|
|
||||||
|
dexie@^3.2.3:
|
||||||
|
version "3.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/dexie/-/dexie-3.2.3.tgz#f35c91ca797599df8e771b998e9ae9669c877f8c"
|
||||||
|
integrity sha512-iHayBd4UYryDCVUNa3PMsJMEnd8yjyh5p7a+RFeC8i8n476BC9wMhVvqiImq5zJZJf5Tuer+s4SSj+AA3x+ZbQ==
|
||||||
|
|
||||||
didyoumean@^1.2.2:
|
didyoumean@^1.2.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
|
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
|
||||||
@ -5316,6 +5362,11 @@ isexe@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||||
|
|
||||||
|
isomorphic-ws@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf"
|
||||||
|
integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==
|
||||||
|
|
||||||
istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
|
istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3"
|
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3"
|
||||||
@ -6340,6 +6391,11 @@ mkdirp@~0.5.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.6"
|
minimist "^1.2.6"
|
||||||
|
|
||||||
|
moment@^2.29.4:
|
||||||
|
version "2.29.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
|
||||||
|
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
@ -6435,6 +6491,27 @@ normalize-url@^6.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
|
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
|
||||||
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
|
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
|
||||||
|
|
||||||
|
nostr-relaypool@^0.6.27:
|
||||||
|
version "0.6.27"
|
||||||
|
resolved "https://registry.yarnpkg.com/nostr-relaypool/-/nostr-relaypool-0.6.27.tgz#d2a3f046043964ad3b2b82db2b3467a64a9da863"
|
||||||
|
integrity sha512-YtQxb8z9VHsPEQfC4rkxztqyGvWM1kcwiLhp/N8PpZX1+9mJhoIFctgpGxWB1LXhZgRiyJfY5Ml4EklvtWELuw==
|
||||||
|
dependencies:
|
||||||
|
"@jest/source-map" "^29.4.3"
|
||||||
|
isomorphic-ws "^5.0.0"
|
||||||
|
nostr-tools "^1.10.0"
|
||||||
|
safe-stable-stringify "^2.4.2"
|
||||||
|
|
||||||
|
nostr-tools@^1.10.0:
|
||||||
|
version "1.10.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.10.1.tgz#b52043b3031f4314478d0a3bfaa8ffb9cc4f98a0"
|
||||||
|
integrity sha512-zgTYJeuZQ3CDASsmBEcB5i6V6l0IaA6cjnll6OVik3FoZcvbCaL7yP8I40hYnOIi3KlJykV7jEF9fn8h1NzMnA==
|
||||||
|
dependencies:
|
||||||
|
"@noble/hashes" "1.2.0"
|
||||||
|
"@noble/secp256k1" "1.7.1"
|
||||||
|
"@scure/base" "1.1.1"
|
||||||
|
"@scure/bip32" "1.1.4"
|
||||||
|
"@scure/bip39" "1.1.1"
|
||||||
|
|
||||||
npm-run-path@^4.0.1:
|
npm-run-path@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
|
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
|
||||||
@ -6672,6 +6749,11 @@ parent-module@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
callsites "^3.0.0"
|
callsites "^3.0.0"
|
||||||
|
|
||||||
|
parse-diff@^0.11.1:
|
||||||
|
version "0.11.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/parse-diff/-/parse-diff-0.11.1.tgz#d93ca2d225abed280782bccb1476711ca9dd84f0"
|
||||||
|
integrity sha512-Oq4j8LAOPOcssanQkIjxosjATBIEJhCxMCxPhMu+Ci4wdNmAEdx0O+a7gzbR2PyKXgKPvRLIN5g224+dJAsKHA==
|
||||||
|
|
||||||
parse-json@^5.0.0, parse-json@^5.2.0:
|
parse-json@^5.0.0, parse-json@^5.2.0:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
|
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
|
||||||
@ -7877,6 +7959,11 @@ safe-regex-test@^1.0.0:
|
|||||||
get-intrinsic "^1.1.3"
|
get-intrinsic "^1.1.3"
|
||||||
is-regex "^1.1.4"
|
is-regex "^1.1.4"
|
||||||
|
|
||||||
|
safe-stable-stringify@^2.4.2:
|
||||||
|
version "2.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886"
|
||||||
|
integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==
|
||||||
|
|
||||||
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
|
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
|
Loading…
Reference in New Issue
Block a user