Fix parser bugs, fix repository delete
This commit is contained in:
Generated
+4
-4
@@ -3092,7 +3092,7 @@
|
|||||||
},
|
},
|
||||||
"packages/content": {
|
"packages/content": {
|
||||||
"name": "@welshman/content",
|
"name": "@welshman/content",
|
||||||
"version": "0.0.1",
|
"version": "0.0.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"insane": "^2.6.2",
|
"insane": "^2.6.2",
|
||||||
@@ -3110,7 +3110,7 @@
|
|||||||
"version": "0.0.9",
|
"version": "0.0.9",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@welshman/util": "0.0.11"
|
"@welshman/util": "0.0.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gts": "^5.0.1",
|
"gts": "^5.0.1",
|
||||||
@@ -3148,7 +3148,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@welshman/lib": "0.0.8",
|
"@welshman/lib": "0.0.8",
|
||||||
"@welshman/util": "0.0.11",
|
"@welshman/util": "0.0.12",
|
||||||
"isomorphic-ws": "^5.0.0",
|
"isomorphic-ws": "^5.0.0",
|
||||||
"ws": "^8.16.0"
|
"ws": "^8.16.0"
|
||||||
},
|
},
|
||||||
@@ -3160,7 +3160,7 @@
|
|||||||
},
|
},
|
||||||
"packages/util": {
|
"packages/util": {
|
||||||
"name": "@welshman/util",
|
"name": "@welshman/util",
|
||||||
"version": "0.0.11",
|
"version": "0.0.12",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@welshman/lib": "0.0.8",
|
"@welshman/lib": "0.0.8",
|
||||||
|
|||||||
+35
-46
@@ -5,6 +5,9 @@ const last = <T>(xs: T[], ...args: unknown[]) => xs[xs.length - 1]
|
|||||||
|
|
||||||
const fromNostrURI = (s: string) => s.replace(/^nostr:\/?\/?/, "")
|
const fromNostrURI = (s: string) => s.replace(/^nostr:\/?\/?/, "")
|
||||||
|
|
||||||
|
export const urlIsMedia = (url: string) =>
|
||||||
|
Boolean(url.match(/\.(jpe?g|png|wav|mp3|mp4|mov|avi|webm|webp|gif|bmp|svg)$/))
|
||||||
|
|
||||||
// Copy some types from nostr-tools because I can't import them
|
// Copy some types from nostr-tools because I can't import them
|
||||||
|
|
||||||
type AddressPointer = {
|
type AddressPointer = {
|
||||||
@@ -37,8 +40,7 @@ export type ParseContext = {
|
|||||||
export enum ParsedType {
|
export enum ParsedType {
|
||||||
Address = "address",
|
Address = "address",
|
||||||
Cashu = "cashu",
|
Cashu = "cashu",
|
||||||
CodeBlock = "code_block",
|
Code = "code",
|
||||||
CodeInline = "code_inline",
|
|
||||||
Ellipsis = "ellipsis",
|
Ellipsis = "ellipsis",
|
||||||
Event = "event",
|
Event = "event",
|
||||||
Invoice = "invoice",
|
Invoice = "invoice",
|
||||||
@@ -55,14 +57,8 @@ export type ParsedCashu = {
|
|||||||
raw: string
|
raw: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ParsedCodeBlock = {
|
export type ParsedCode = {
|
||||||
type: ParsedType.CodeBlock
|
type: ParsedType.Code
|
||||||
value: string
|
|
||||||
raw: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ParsedCodeInline = {
|
|
||||||
type: ParsedType.CodeInline
|
|
||||||
value: string
|
value: string
|
||||||
raw: string
|
raw: string
|
||||||
}
|
}
|
||||||
@@ -130,8 +126,7 @@ export type ParsedAddress = {
|
|||||||
export type Parsed =
|
export type Parsed =
|
||||||
ParsedAddress |
|
ParsedAddress |
|
||||||
ParsedCashu |
|
ParsedCashu |
|
||||||
ParsedCodeBlock |
|
ParsedCode |
|
||||||
ParsedCodeInline |
|
|
||||||
ParsedEllipsis |
|
ParsedEllipsis |
|
||||||
ParsedEvent |
|
ParsedEvent |
|
||||||
ParsedInvoice |
|
ParsedInvoice |
|
||||||
@@ -141,6 +136,20 @@ export type Parsed =
|
|||||||
ParsedText |
|
ParsedText |
|
||||||
ParsedTopic
|
ParsedTopic
|
||||||
|
|
||||||
|
// Matchers
|
||||||
|
|
||||||
|
export const isAddress = (parsed: Parsed): parsed is ParsedAddress => parsed.type === ParsedType.Address
|
||||||
|
export const isCashu = (parsed: Parsed): parsed is ParsedCashu => parsed.type === ParsedType.Cashu
|
||||||
|
export const isCode = (parsed: Parsed): parsed is ParsedCode => parsed.type === ParsedType.Code
|
||||||
|
export const isEllipsis = (parsed: Parsed): parsed is ParsedEllipsis => parsed.type === ParsedType.Ellipsis
|
||||||
|
export const isEvent = (parsed: Parsed): parsed is ParsedEvent => parsed.type === ParsedType.Event
|
||||||
|
export const isInvoice = (parsed: Parsed): parsed is ParsedInvoice => parsed.type === ParsedType.Invoice
|
||||||
|
export const isLink = (parsed: Parsed): parsed is ParsedLink => parsed.type === ParsedType.Link
|
||||||
|
export const isNewline = (parsed: Parsed): parsed is ParsedNewline => parsed.type === ParsedType.Newline
|
||||||
|
export const isProfile = (parsed: Parsed): parsed is ParsedProfile => parsed.type === ParsedType.Profile
|
||||||
|
export const isText = (parsed: Parsed): parsed is ParsedText => parsed.type === ParsedType.Text
|
||||||
|
export const isTopic = (parsed: Parsed): parsed is ParsedTopic => parsed.type === ParsedType.Topic
|
||||||
|
|
||||||
// Parsers for known formats
|
// Parsers for known formats
|
||||||
|
|
||||||
export const parseAddress = (text: string, context: ParseContext): ParsedAddress | void => {
|
export const parseAddress = (text: string, context: ParseContext): ParsedAddress | void => {
|
||||||
@@ -165,19 +174,19 @@ export const parseCashu = (text: string, context: ParseContext): ParsedCashu | v
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parseCodeBlock = (text: string, context: ParseContext): ParsedCodeBlock | void => {
|
export const parseCodeBlock = (text: string, context: ParseContext): ParsedCode | void => {
|
||||||
const [code, value] = text.match(/^```([^]*?)```/i) || []
|
const [code, value] = text.match(/^```([^]*?)```/i) || []
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
return {type: ParsedType.CodeBlock, value, raw: code}
|
return {type: ParsedType.Code, value, raw: code}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parseCodeInline = (text: string, context: ParseContext): ParsedCodeInline | void => {
|
export const parseCodeInline = (text: string, context: ParseContext): ParsedCode | void => {
|
||||||
const [code, value] = text.match(/^`(.*?)`/i) || []
|
const [code, value] = text.match(/^`(.*?)`/i) || []
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
return {type: ParsedType.CodeInline, value, raw: code}
|
return {type: ParsedType.Code, value, raw: code}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,28 +216,18 @@ export const parseInvoice = (text: string, context: ParseContext): ParsedInvoice
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const parseLink = (text: string, context: ParseContext): ParsedLink | void => {
|
export const parseLink = (text: string, context: ParseContext): ParsedLink | void => {
|
||||||
let [link] = text.match(/^([a-z\+:]{2,30}:\/\/)?[^<>\(\)\s]+\.[a-z]{2,6}[^\s]*[^<>"'\.!?,:\s\)\(]/gi) || []
|
|
||||||
|
|
||||||
if (!link) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const prev = last(context.results)
|
const prev = last(context.results)
|
||||||
|
const [link] = text.match(/^([a-z\+:]{2,30}:\/\/)?[^<>\(\)\s]+\.[a-z]{2,6}[^\s]*[^<>"'\.!?,:\s\)\(]/gi) || []
|
||||||
|
|
||||||
// Skip url if it's just the end of a filepath or an ellipse
|
// Skip url if it's just the end of a filepath or an ellipse
|
||||||
if (prev?.type === ParsedType.Text && prev.value.endsWith("/") || link.match(/\.\./)) {
|
if (!link || prev?.type === ParsedType.Text && prev.value.endsWith("/") || link.match(/\.\./)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure there's a protocol
|
// Parse using URL, make sure there's a protocol
|
||||||
if (!link.match(/^\w+:\/\//)) {
|
|
||||||
link = "https://" + link
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse using URL
|
|
||||||
let url
|
let url
|
||||||
try {
|
try {
|
||||||
url = new URL(link)
|
url = new URL(link.match(/^\w+:\/\//) ? link : "https://" + link)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -241,13 +240,7 @@ export const parseLink = (text: string, context: ParseContext): ParsedLink | voi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isMedia = Boolean(
|
return {type: ParsedType.Link, value: {url, meta, isMedia: urlIsMedia(url.pathname)}, raw: link}
|
||||||
url.pathname.match(/\.(jpe?g|png|wav|mp3|mp4|mov|avi|webm|webp|gif|bmp|svg)$/)
|
|
||||||
)
|
|
||||||
|
|
||||||
const value = {url, meta, isMedia}
|
|
||||||
|
|
||||||
return {type: ParsedType.Link, value, raw: link}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parseNewline = (text: string, context: ParseContext): ParsedNewline | void => {
|
export const parseNewline = (text: string, context: ParseContext): ParsedNewline | void => {
|
||||||
@@ -280,7 +273,7 @@ export const parseTopic = (text: string, context: ParseContext): ParsedTopic | v
|
|||||||
|
|
||||||
// Skip numeric topics
|
// Skip numeric topics
|
||||||
if (value && !value.match(/^#\d+$/)) {
|
if (value && !value.match(/^#\d+$/)) {
|
||||||
return {type: ParsedType.Topic, value, raw: value}
|
return {type: ParsedType.Topic, value: value.slice(1), raw: value}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,19 +441,16 @@ export class HTML {
|
|||||||
HTML.useSafely(`<a href=${href} target="_blank">${display}</a>`)
|
HTML.useSafely(`<a href=${href} target="_blank">${display}</a>`)
|
||||||
|
|
||||||
static buildEntityLink = (entity: string, options: RenderOptions) =>
|
static buildEntityLink = (entity: string, options: RenderOptions) =>
|
||||||
HTML.buildLink(options.entityBaseUrl + entity, entity.slice(0, 16))
|
HTML.buildLink(options.entityBaseUrl + entity, entity.slice(0, 16) + '…')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const renderCashu = (parsed: ParsedCashu, options: RenderOptions) =>
|
export const renderCashu = (parsed: ParsedCashu, options: RenderOptions) =>
|
||||||
HTML.useSafely(parsed.value)
|
HTML.useSafely(parsed.value)
|
||||||
|
|
||||||
export const renderCodeBlock = (parsed: ParsedCodeBlock, options: RenderOptions) =>
|
export const renderCode = (parsed: ParsedCode, options: RenderOptions) =>
|
||||||
HTML.useSafely(parsed.value)
|
HTML.useSafely(parsed.value)
|
||||||
|
|
||||||
export const renderCodeInline = (parsed: ParsedCodeInline, options: RenderOptions) =>
|
export const renderEllipsis = (parsed: ParsedEllipsis, options: RenderOptions) => "…"
|
||||||
HTML.useSafely(parsed.value)
|
|
||||||
|
|
||||||
export const renderEllipsis = (parsed: ParsedEllipsis, options: RenderOptions) => "..."
|
|
||||||
|
|
||||||
export const renderInvoice = (parsed: ParsedInvoice, options: RenderOptions) =>
|
export const renderInvoice = (parsed: ParsedInvoice, options: RenderOptions) =>
|
||||||
HTML.useSafely(parsed.value)
|
HTML.useSafely(parsed.value)
|
||||||
@@ -496,8 +486,7 @@ export const render = (parsed: Parsed, options: RenderOptions = {}) => {
|
|||||||
switch (parsed.type) {
|
switch (parsed.type) {
|
||||||
case ParsedType.Address: return renderAddress(parsed as ParsedAddress, options)
|
case ParsedType.Address: return renderAddress(parsed as ParsedAddress, options)
|
||||||
case ParsedType.Cashu: return renderCashu(parsed as ParsedCashu, options)
|
case ParsedType.Cashu: return renderCashu(parsed as ParsedCashu, options)
|
||||||
case ParsedType.CodeBlock: return renderCodeBlock(parsed as ParsedCodeBlock, options)
|
case ParsedType.Code: return renderCode(parsed as ParsedCode, options)
|
||||||
case ParsedType.CodeInline: return renderCodeInline(parsed as ParsedCodeInline, options)
|
|
||||||
case ParsedType.Ellipsis: return renderEllipsis(parsed as ParsedEllipsis, options)
|
case ParsedType.Ellipsis: return renderEllipsis(parsed as ParsedEllipsis, options)
|
||||||
case ParsedType.Event: return renderEvent(parsed as ParsedEvent, options)
|
case ParsedType.Event: return renderEvent(parsed as ParsedEvent, options)
|
||||||
case ParsedType.Invoice: return renderInvoice(parsed as ParsedInvoice, options)
|
case ParsedType.Invoice: return renderInvoice(parsed as ParsedInvoice, options)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@welshman/content",
|
"name": "@welshman/content",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"author": "hodlbod",
|
"author": "hodlbod",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "A collection of utilities for parsing nostr note content.",
|
"description": "A collection of utilities for parsing nostr note content.",
|
||||||
|
|||||||
@@ -31,6 +31,6 @@
|
|||||||
"typescript": "~5.1.6"
|
"typescript": "~5.1.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@welshman/util": "0.0.11"
|
"@welshman/util": "0.0.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@welshman/lib": "0.0.8",
|
"@welshman/lib": "0.0.8",
|
||||||
"@welshman/util": "0.0.11",
|
"@welshman/util": "0.0.12",
|
||||||
"isomorphic-ws": "^5.0.0",
|
"isomorphic-ws": "^5.0.0",
|
||||||
"ws": "^8.16.0"
|
"ws": "^8.16.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {throttle} from 'throttle-debounce'
|
import {throttle} from 'throttle-debounce'
|
||||||
import {flatten, Emitter, sortBy, inc, chunk, sleep, uniq, omit, now, range, identity} from '@welshman/lib'
|
import {flatten, nth, Emitter, sortBy, inc, chunk, sleep, uniq, omit, now, range, identity} from '@welshman/lib'
|
||||||
import {DELETE} from './Kinds'
|
import {DELETE} from './Kinds'
|
||||||
import {EPOCH, matchFilter} from './Filters'
|
import {EPOCH, matchFilter} from './Filters'
|
||||||
import {isReplaceable, isTrustedEvent} from './Events'
|
import {getIdAndAddress, isReplaceable, isTrustedEvent} from './Events'
|
||||||
import {getAddress} from './Address'
|
import {getAddress} from './Address'
|
||||||
import type {Filter} from './Filters'
|
import type {Filter} from './Filters'
|
||||||
import type {TrustedEvent} from './Events'
|
import type {TrustedEvent} from './Events'
|
||||||
@@ -68,8 +68,8 @@ export class Repository extends Emitter {
|
|||||||
this.emit('event', event)
|
this.emit('event', event)
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyDelete = (event: TrustedEvent) => {
|
notifyDelete = (values: string[]) => {
|
||||||
this.emit('delete', event)
|
this.emit('delete', new Set(values))
|
||||||
}
|
}
|
||||||
|
|
||||||
// API
|
// API
|
||||||
@@ -162,7 +162,7 @@ export class Repository extends Emitter {
|
|||||||
|
|
||||||
// Delete our duplicate first
|
// Delete our duplicate first
|
||||||
if (duplicate) {
|
if (duplicate) {
|
||||||
this.notifyDelete(duplicate)
|
this.notifyDelete(getIdAndAddress(duplicate))
|
||||||
this.eventsById.delete(duplicate.id)
|
this.eventsById.delete(duplicate.id)
|
||||||
|
|
||||||
if (isReplaceable(duplicate)) {
|
if (isReplaceable(duplicate)) {
|
||||||
@@ -200,7 +200,7 @@ export class Repository extends Emitter {
|
|||||||
|
|
||||||
// Deletes are tricky, re-evaluate all subscriptions if that's what we're dealing with
|
// Deletes are tricky, re-evaluate all subscriptions if that's what we're dealing with
|
||||||
if (event.kind === DELETE) {
|
if (event.kind === DELETE) {
|
||||||
this.notifyDelete(event)
|
this.notifyDelete(event.tags.map(nth(1)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@welshman/util",
|
"name": "@welshman/util",
|
||||||
"version": "0.0.11",
|
"version": "0.0.12",
|
||||||
"author": "hodlbod",
|
"author": "hodlbod",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "A collection of nostr-related utilities.",
|
"description": "A collection of nostr-related utilities.",
|
||||||
|
|||||||
Reference in New Issue
Block a user