feat: initialize markr nostr bookmark client

- Add project structure with TypeScript, React, and Vite
- Implement nostr authentication using browser extension (NIP-07)
- Add NIP-51 compliant bookmark fetching and display
- Create minimal UI with login and bookmark components
- Integrate applesauce-core and applesauce-react libraries
- Add responsive styling with dark/light mode support
- Include comprehensive README with setup instructions

This is a minimal MVP for a nostr bookmark client that allows users to
view their bookmarks according to NIP-51 specification.
This commit is contained in:
Gigi
2025-10-02 07:17:07 +02:00
commit 5d53a827e0
11194 changed files with 1827829 additions and 0 deletions

21
node_modules/applesauce-content/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2024 hzrd149
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

32
node_modules/applesauce-content/README.md generated vendored Normal file
View File

@@ -0,0 +1,32 @@
# applesauce-content
applesauce package for parsing text note content
## Example
```ts
import { getParsedContent } from "applesauce-content/text";
const stringContent = `
hello nostr!
nostr:npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6
`;
const ats = getParsedContent(stringContent);
console.log(ats);
/*
{
type: 'root',
event: undefined,
children: [
{ type: 'text', value: 'hello nostr!' },
{ type: 'text', value: '\n' },
{
type: 'mention',
decoded: [Object],
encoded: 'npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6'
}
]
}
*/
```

View File

@@ -0,0 +1,2 @@
export * from "./regexp.js";
export * from "./media.js";

View File

@@ -0,0 +1,2 @@
export * from "./regexp.js";
export * from "./media.js";

View File

@@ -0,0 +1,3 @@
import { MediaAttachment } from "applesauce-core/helpers/file-metadata";
/** Returns all URLs in a content string that contain a sha256 hash */
export declare function getMediaAttachmentURLsFromContent(content: string): MediaAttachment[];

15
node_modules/applesauce-content/dist/helpers/media.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
import { getSha256FromURL } from "applesauce-core/helpers/file-metadata";
import { Tokens } from "./regexp.js";
/** Returns all URLs in a content string that contain a sha256 hash */
export function getMediaAttachmentURLsFromContent(content) {
return (Array.from(content.matchAll(Tokens.link))
.map((match) => match[0])
// filter out invalid URLs
.filter((str) => URL.canParse(str))
// convert to URLs
.map((url) => new URL(url))
// only keep urls with sha256 hashes in the
.filter((url) => !!getSha256FromURL(url))
// convert to media attachments
.map((url) => ({ url: url.toString(), sha256: getSha256FromURL(url) })));
}

View File

@@ -0,0 +1,19 @@
export declare const Expressions: {
readonly url: RegExp;
readonly link: RegExp;
readonly cashu: RegExp;
readonly nostrLink: RegExp;
readonly emoji: RegExp;
readonly hashtag: RegExp;
readonly lightning: RegExp;
};
/** A list of Regular Expressions that match tokens surrounded by whitespace to avoid matching in URLs */
export declare const Tokens: {
readonly url: RegExp;
readonly link: RegExp;
readonly cashu: RegExp;
readonly nostrLink: RegExp;
readonly emoji: RegExp;
readonly hashtag: RegExp;
readonly lightning: RegExp;
};

48
node_modules/applesauce-content/dist/helpers/regexp.js generated vendored Normal file
View File

@@ -0,0 +1,48 @@
export const Expressions = {
get url() {
return /(?:https?|wss?|ircs?|s?ftp):\/\/([a-zA-Z0-9\.\-]+\.[a-zA-Z]+(?::\d+)?)([\/\?#][\p{L}\p{N}\p{M}&\.-\/\?=#\-@%\+_,:!~*]*)?/gu;
},
get link() {
return /https?:\/\/([a-zA-Z0-9\.\-]+\.[a-zA-Z]+(?::\d+)?)([\/\?#][\p{L}\p{N}\p{M}&\.-\/\?=#\-@%\+_,:!~*]*)?/gu;
},
get cashu() {
return /(?:cashu:\/{0,2})?(cashu(?:A|B)[A-Za-z0-9_-]{100,}={0,3})/gi;
},
get nostrLink() {
return /(?:nostr:)?((npub|note|nprofile|nevent|naddr)1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{58,})/gi;
},
get emoji() {
return /:([a-zA-Z0-9_-]+):/gi;
},
get hashtag() {
// NOTE: cant use \b here because it uses \w which only matches latin letters
return /(?<=^|[^\p{L}#\/])#([\p{L}\p{N}\p{M}]+)(?=\p{Z}|$|\s)/gu;
},
get lightning() {
return /(?:lightning:)?(LNBC[A-Za-z0-9]+)/gim;
},
};
/** A list of Regular Expressions that match tokens surrounded by whitespace to avoid matching in URLs */
export const Tokens = {
get url() {
return Expressions.url;
},
get link() {
return Expressions.link;
},
get cashu() {
return new RegExp(`(?<=^|\\s)${Expressions.cashu.source}`, "gi");
},
get nostrLink() {
return new RegExp(`(?<=^|\\s)${Expressions.nostrLink.source}`, "gi");
},
get emoji() {
return Expressions.emoji;
},
get hashtag() {
return Expressions.hashtag;
},
get lightning() {
return new RegExp(`(?<=^|\\s)${Expressions.lightning.source}`, "gim");
},
};

4
node_modules/applesauce-content/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export * as Text from "./text/index.js";
export * as Nast from "./nast/index.js";
export * as Markdown from "./markdown/index.js";
export * as Helpers from "./helpers/index.js";

4
node_modules/applesauce-content/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export * as Text from "./text/index.js";
export * as Nast from "./nast/index.js";
export * as Markdown from "./markdown/index.js";
export * as Helpers from "./helpers/index.js";

View File

@@ -0,0 +1 @@
export * from "./mentions.js";

View File

@@ -0,0 +1 @@
export * from "./mentions.js";

View File

@@ -0,0 +1,8 @@
import { DecodeResult } from "applesauce-core/helpers";
import { Link, Nodes } from "mdast";
import { Transformer } from "unified";
export interface NostrMention extends Link {
type: "link";
data: DecodeResult;
}
export declare function remarkNostrMentions(): Transformer<Nodes>;

View File

@@ -0,0 +1,22 @@
import { findAndReplace } from "mdast-util-find-and-replace";
import { decode } from "nostr-tools/nip19";
import { Tokens } from "../helpers/regexp.js";
export function remarkNostrMentions() {
return (tree) => {
findAndReplace(tree, [
Tokens.nostrLink,
(_, $1) => {
try {
return {
type: "link",
data: decode($1),
children: [],
url: "nostr:" + $1,
};
}
catch (error) { }
return false;
},
]);
};
}

View File

@@ -0,0 +1,3 @@
import { Transformer } from "unified";
import { Root } from "../nast/types.js";
export declare function eolMetadata(): Transformer<Root>;

View File

@@ -0,0 +1,14 @@
export function eolMetadata() {
return (tree) => {
for (let i = 0; i < tree.children.length; i++) {
const node = tree.children[i];
const next = tree.children[i + 1];
if ((node.type === "text" && node.value.endsWith("\n")) ||
!next ||
(next.type === "text" && next.value.startsWith("\n"))) {
node.data = node.data || {};
node.data.eol = true;
}
}
};
}

View File

@@ -0,0 +1,6 @@
import { Content, Root } from "./types.js";
type Replace = (...groups: string[]) => null | undefined | false | string | Content | Content[];
type FindAndReplaceTuple = [RegExp, Replace];
type FindAndReplaceList = FindAndReplaceTuple[];
export declare function findAndReplace(tree: Root, list: FindAndReplaceList): void;
export {};

View File

@@ -0,0 +1,95 @@
import { visitParents } from "unist-util-visit-parents";
export function findAndReplace(tree, list) {
const pairs = list;
let pairIndex = -1;
const visitor = (node, parents) => {
let index = -1;
/** @type {Parents | undefined} */
let grandparent;
while (++index < parents.length) {
const parent = parents[index];
// const siblings = grandparent ? grandparent.children : undefined;
grandparent = parent;
}
if (grandparent) {
return handler(node, parents);
}
return undefined;
};
while (++pairIndex < pairs.length) {
visitParents(tree, "text", visitor);
}
/**
* Handle a text node which is not in an ignored parent.
*
* @param {Text} node
* Text node.
* @param {Array<Parents>} parents
* Parents.
* @returns {VisitorResult}
* Result.
*/
function handler(node, parents) {
const parent = parents[parents.length - 1];
const find = pairs[pairIndex][0];
const replace = pairs[pairIndex][1];
let start = 0;
const siblings = parent.children;
const index = siblings.indexOf(node);
let change = false;
let nodes = [];
find.lastIndex = 0;
let match = find.exec(node.value);
while (match) {
const position = match.index;
/** @type {RegExpMatchObject} */
// const matchObject = {
// index: match.index,
// input: match.input,
// stack: [...parents, node],
// };
// let value = replace(...match, matchObject);
let value = replace(...match);
if (typeof value === "string") {
value = value.length > 0 ? { type: "text", value } : undefined;
}
// It wasnt a match after all.
if (value === false) {
// False acts as if there was no match.
// So we need to reset `lastIndex`, which currently being at the end of
// the current match, to the beginning.
find.lastIndex = position + 1;
}
else {
if (start !== position) {
nodes.push({
type: "text",
value: node.value.slice(start, position),
});
}
if (Array.isArray(value)) {
nodes.push(...value);
}
else if (value) {
nodes.push(value);
}
start = position + match[0].length;
change = true;
}
if (!find.global) {
break;
}
match = find.exec(node.value);
}
if (change) {
if (start < node.value.length) {
nodes.push({ type: "text", value: node.value.slice(start) });
}
parent.children.splice(index, 1, ...nodes);
}
else {
nodes = [node];
}
return index + nodes.length;
}
}

4
node_modules/applesauce-content/dist/nast/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export * from "./types.js";
export * from "./find-and-replace.js";
export * from "./eol-metadata.js";
export * from "./truncate.js";

4
node_modules/applesauce-content/dist/nast/index.js generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export * from "./types.js";
export * from "./find-and-replace.js";
export * from "./eol-metadata.js";
export * from "./truncate.js";

View File

@@ -0,0 +1,2 @@
import { Root } from "./types.js";
export declare function truncateContent(tree: Root, maxLength?: number): Root;

48
node_modules/applesauce-content/dist/nast/truncate.js generated vendored Normal file
View File

@@ -0,0 +1,48 @@
export function truncateContent(tree, maxLength = 256) {
let length = 0;
for (let i = 0; i < tree.children.length; i++) {
const node = tree.children[i];
switch (node.type) {
case "hashtag":
length += 1 + node.hashtag.length;
break;
case "mention":
// guess user names are about 10 long
length += 10;
break;
case "cashu":
length += node.raw.length;
break;
case "gallery":
length += node.links.reduce((t, l) => t + l.length, 0);
break;
case "link":
case "text":
length += node.value.length;
break;
case "emoji":
length += 1;
break;
}
if (length > maxLength) {
if (node.type === "text") {
const children = i > 0 ? tree.children.slice(0, i) : [];
const chunkLength = node.value.length - (length - maxLength);
// find the nearest newline
const newLines = node.value.matchAll(/\n/g);
for (const match of newLines) {
if (match.index && match.index > chunkLength) {
children.push({ type: "text", value: node.value.slice(0, match.index) });
return { ...tree, children, truncated: true };
}
}
// just cut the string
children.push({ type: "text", value: node.value.slice(0, maxLength - length) });
return { ...tree, children, truncated: true };
}
else
return { ...tree, children: tree.children.slice(0, i), truncated: true };
}
}
return tree;
}

72
node_modules/applesauce-content/dist/nast/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,72 @@
import { type Token } from "@cashu/cashu-ts";
import { type DecodeResult } from "applesauce-core/helpers";
import { type ParsedInvoice } from "applesauce-core/helpers/bolt11";
import { type EventTemplate, type NostrEvent } from "nostr-tools";
import { type Parent, type Node as UnistNode } from "unist";
export interface CommonData {
eol?: boolean;
}
export interface Node extends Omit<UnistNode, "data"> {
data?: CommonData;
}
export interface Text extends Node {
type: "text";
value: string;
}
export interface Link extends Node {
type: "link";
value: string;
href: string;
}
export interface Gallery extends Node {
type: "gallery";
links: string[];
}
export interface Mention extends Node {
type: "mention";
decoded: DecodeResult;
encoded: string;
}
export interface CashuToken extends Node {
type: "cashu";
token: Token;
raw: string;
}
export interface LightningInvoice extends Node {
type: "lightning";
invoice: string;
parsed: ParsedInvoice;
}
export interface Hashtag extends Node {
type: "hashtag";
/** The name as it was written in the event */
name: string;
/** The lowercase canonical name */
hashtag: string;
/** The indexable tag for the hashtag. will be undefined if none was found */
tag?: ["t", ...string[]];
}
export interface Emoji extends Node {
type: "emoji";
code: string;
raw: string;
url: string;
tag: ["emoji", ...string[]];
}
export interface ContentMap {
text: Text;
link: Link;
mention: Mention;
cashu: CashuToken;
lightning: LightningInvoice;
hashtag: Hashtag;
emoji: Emoji;
gallery: Gallery;
}
export type Content = ContentMap[keyof ContentMap];
export interface Root extends Parent {
type: "root";
children: Content[];
event?: NostrEvent | EventTemplate;
truncated?: boolean;
}

1
node_modules/applesauce-content/dist/nast/types.js generated vendored Normal file
View File

@@ -0,0 +1 @@
export {};

4
node_modules/applesauce-content/dist/text/cashu.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
import { Transformer } from "unified";
import { Root } from "../nast/types.js";
/** Parse cashu tokens from an ATS tree */
export declare function cashuTokens(): Transformer<Root>;

25
node_modules/applesauce-content/dist/text/cashu.js generated vendored Normal file
View File

@@ -0,0 +1,25 @@
import { getDecodedToken } from "@cashu/cashu-ts";
import { Tokens } from "../helpers/regexp.js";
import { findAndReplace } from "../nast/find-and-replace.js";
/** Parse cashu tokens from an ATS tree */
export function cashuTokens() {
return (tree) => {
findAndReplace(tree, [
[
Tokens.cashu,
(_, $1) => {
try {
const token = getDecodedToken($1);
return {
type: "cashu",
token,
raw: $1,
};
}
catch (error) { }
return false;
},
],
]);
};
}

View File

@@ -0,0 +1,9 @@
import { Transformer } from "unified";
import { EventTemplate, NostrEvent } from "nostr-tools";
import { Root } from "../nast/types.js";
import { galleries } from "./gallery.js";
export declare const TextNoteContentSymbol: unique symbol;
export declare const textNoteTransformers: (typeof galleries)[];
/** Parsed and process a note with custom transformers */
export declare function getParsedContent(event: NostrEvent | EventTemplate | string, content?: string, transformers?: (() => Transformer<Root>)[], cacheKey?: symbol | null | undefined): Root;
export declare function removeParsedTextContent(event: NostrEvent | EventTemplate): void;

53
node_modules/applesauce-content/dist/text/content.js generated vendored Normal file
View File

@@ -0,0 +1,53 @@
import { unified } from "unified";
import { getOrComputeCachedValue } from "applesauce-core/helpers/cache";
import { nostrMentions } from "./mentions.js";
import { cashuTokens } from "./cashu.js";
import { emojis } from "./emoji.js";
import { createEventContentTree } from "./parser.js";
import { hashtags } from "./hashtag.js";
import { galleries } from "./gallery.js";
import { lightningInvoices } from "./lightning.js";
import { eolMetadata } from "../nast/eol-metadata.js";
import { links } from "./links.js";
export const TextNoteContentSymbol = Symbol.for("text-note-content");
// default kind 1 transformers
export const textNoteTransformers = [
links,
nostrMentions,
galleries,
emojis,
hashtags,
lightningInvoices,
cashuTokens,
eolMetadata,
];
/** Parsed and process a note with custom transformers */
export function getParsedContent(event, content, transformers = textNoteTransformers, cacheKey = TextNoteContentSymbol) {
// process strings
if (typeof event === "string") {
const processor = unified();
for (const transformer of transformers) {
processor.use(transformer);
}
return processor.runSync(createEventContentTree(event, content));
}
// no caching
if (!cacheKey) {
const processor = unified();
for (const transformer of transformers) {
processor.use(transformer);
}
return processor.runSync(createEventContentTree(event, content));
}
return getOrComputeCachedValue(event, cacheKey, () => {
const processor = unified();
for (const transformer of transformers) {
processor.use(transformer);
}
return processor.runSync(createEventContentTree(event, content));
});
}
export function removeParsedTextContent(event) {
// @ts-expect-error
delete event[TextNoteContentSymbol];
}

4
node_modules/applesauce-content/dist/text/emoji.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
import { type Transformer } from "unified";
import { Root } from "../nast/types.js";
/** Finds and creates emoji nodes in the tree */
export declare function emojis(): Transformer<Root>;

32
node_modules/applesauce-content/dist/text/emoji.js generated vendored Normal file
View File

@@ -0,0 +1,32 @@
import { getEmojiTag } from "applesauce-core/helpers/emoji";
import { Tokens } from "../helpers/regexp.js";
import { findAndReplace } from "../nast/find-and-replace.js";
/** Finds and creates emoji nodes in the tree */
export function emojis() {
return (tree) => {
const event = tree.event;
if (!event)
return;
findAndReplace(tree, [
[
Tokens.emoji,
(full, $1) => {
try {
const tag = getEmojiTag(event, $1);
if (!tag)
return false;
return {
type: "emoji",
tag,
raw: full,
code: tag[1].toLowerCase(),
url: tag[2],
};
}
catch (error) { }
return false;
},
],
]);
};
}

View File

@@ -0,0 +1,4 @@
import { Transformer } from "unified";
import { Root } from "../nast/types.js";
/** Group images into galleries in an ATS tree */
export declare function galleries(types?: string[]): Transformer<Root>;

48
node_modules/applesauce-content/dist/text/gallery.js generated vendored Normal file
View File

@@ -0,0 +1,48 @@
import { convertToUrl, getURLFilename, IMAGE_EXT } from "applesauce-core/helpers/url";
/** Group images into galleries in an ATS tree */
export function galleries(types = IMAGE_EXT) {
return (tree) => {
let links = [];
const commit = (index) => {
// only create a gallery if there are more than a single image
if (links.length > 1) {
const start = tree.children.indexOf(links[0]);
const end = tree.children.indexOf(links[links.length - 1]);
// replace all nodes with a gallery
tree.children.splice(start, 1 + end - start, { type: "gallery", links: links.map((l) => l.href) });
links = [];
// return new cursor
return end - 1;
}
else {
links = [];
return index;
}
};
for (let i = 0; i < tree.children.length; i++) {
const node = tree.children[i];
try {
if (node.type === "link") {
const url = convertToUrl(node.href);
const filename = getURLFilename(url);
if (filename && types.some((ext) => filename.endsWith(ext))) {
links.push(node);
}
else {
i = commit(i);
}
}
else if (node.type === "text" && links.length > 0) {
const isEmpty = node.value === "\n" || !node.value.match(/[^\s]/g);
if (!isEmpty)
i = commit(i);
}
}
catch (error) {
i = commit(i);
}
}
// Do one finally commit, just in case a link is the last element in the list
commit(tree.children.length);
};
}

View File

@@ -0,0 +1,4 @@
import { Transformer } from "unified";
import { Root } from "../nast/types.js";
/** Find and create hashtag notes in provided tree */
export declare function hashtags(): Transformer<Root>;

30
node_modules/applesauce-content/dist/text/hashtag.js generated vendored Normal file
View File

@@ -0,0 +1,30 @@
import { getHashtagTag } from "applesauce-core/helpers/hashtag";
import { Tokens } from "../helpers/regexp.js";
import { findAndReplace } from "../nast/find-and-replace.js";
/** Find and create hashtag notes in provided tree */
export function hashtags() {
return (tree) => {
const event = tree.event;
findAndReplace(tree, [
[
Tokens.hashtag,
(_, $1) => {
try {
const tag = event && getHashtagTag(event, $1);
// Skip if the match if no tag was found in the event
if (!tag && event)
return false;
return {
type: "hashtag",
tag,
name: $1,
hashtag: tag?.[1].toLowerCase() || $1.toLowerCase(),
};
}
catch (error) { }
return false;
},
],
]);
};
}

9
node_modules/applesauce-content/dist/text/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
export * from "./content.js";
export * from "./links.js";
export * from "./mentions.js";
export * from "./cashu.js";
export * from "./emoji.js";
export * from "./parser.js";
export * from "./hashtag.js";
export * from "./gallery.js";
export * from "./lightning.js";

9
node_modules/applesauce-content/dist/text/index.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
export * from "./content.js";
export * from "./links.js";
export * from "./mentions.js";
export * from "./cashu.js";
export * from "./emoji.js";
export * from "./parser.js";
export * from "./hashtag.js";
export * from "./gallery.js";
export * from "./lightning.js";

View File

@@ -0,0 +1,4 @@
import { type Transformer } from "unified";
import { Root } from "../nast/types.js";
/** Finds and creates lightning invoice nodes in the tree */
export declare function lightningInvoices(): Transformer<Root>;

26
node_modules/applesauce-content/dist/text/lightning.js generated vendored Normal file
View File

@@ -0,0 +1,26 @@
import { parseBolt11 } from "applesauce-core/helpers/bolt11";
import { Tokens } from "../helpers/regexp.js";
import { findAndReplace } from "../nast/find-and-replace.js";
/** Finds and creates lightning invoice nodes in the tree */
export function lightningInvoices() {
return (tree) => {
findAndReplace(tree, [
[
Tokens.lightning,
(_, $1) => {
try {
const invoice = $1;
const parsed = parseBolt11(invoice);
return {
type: "lightning",
invoice,
parsed,
};
}
catch (error) { }
return false;
},
],
]);
};
}

4
node_modules/applesauce-content/dist/text/links.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
import { Transformer } from "unified";
import { Root } from "../nast/types.js";
/** Finds and creates web links in the tree */
export declare function links(): Transformer<Root>;

23
node_modules/applesauce-content/dist/text/links.js generated vendored Normal file
View File

@@ -0,0 +1,23 @@
import { Tokens } from "../helpers/regexp.js";
import { findAndReplace } from "../nast/find-and-replace.js";
/** Finds and creates web links in the tree */
export function links() {
return (tree) => {
findAndReplace(tree, [
[
Tokens.link,
(_) => {
try {
return {
type: "link",
href: new URL(_).toString(),
value: _,
};
}
catch (error) { }
return false;
},
],
]);
};
}

View File

@@ -0,0 +1,4 @@
import { Transformer } from "unified";
import { Root } from "../nast/types.js";
/** Finds and creates NIP-19 nostr mentions in the tree */
export declare function nostrMentions(): Transformer<Root>;

24
node_modules/applesauce-content/dist/text/mentions.js generated vendored Normal file
View File

@@ -0,0 +1,24 @@
import { decode } from "nostr-tools/nip19";
import { Tokens } from "../helpers/regexp.js";
import { findAndReplace } from "../nast/find-and-replace.js";
/** Finds and creates NIP-19 nostr mentions in the tree */
export function nostrMentions() {
return (tree) => {
findAndReplace(tree, [
[
Tokens.nostrLink,
(_, $1) => {
try {
return {
type: "mention",
decoded: decode($1),
encoded: $1,
};
}
catch (error) { }
return false;
},
],
]);
};
}

View File

@@ -0,0 +1,6 @@
import { EventTemplate, NostrEvent } from "nostr-tools";
import { Root } from "../nast/types.js";
/** Creates a {@link Root} ATS node for a text note */
export declare function createEventContentTree(event: NostrEvent | EventTemplate | string, content?: string): Root;
/** @deprecated use createEventContentTree instead */
export declare const createTextNoteATS: typeof createEventContentTree;

15
node_modules/applesauce-content/dist/text/parser.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
/** Creates a {@link Root} ATS node for a text note */
export function createEventContentTree(event, content) {
return {
type: "root",
event: typeof event !== "string" ? event : undefined,
children: [
{
type: "text",
value: content || (typeof event === "string" ? event : event.content),
},
],
};
}
/** @deprecated use createEventContentTree instead */
export const createTextNoteATS = createEventContentTree;

78
node_modules/applesauce-content/package.json generated vendored Normal file
View File

@@ -0,0 +1,78 @@
{
"name": "applesauce-content",
"version": "3.1.0",
"description": "Unified plugins for processing event content",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"keywords": [
"nostr",
"applesauce"
],
"author": "hzrd149",
"license": "MIT",
"files": [
"dist",
"applesauce"
],
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./helpers": {
"import": "./dist/helpers/index.js",
"require": "./dist/helpers/index.js",
"types": "./dist/helpers/index.d.ts"
},
"./helpers/*": {
"import": "./dist/helpers/*.js",
"require": "./dist/helpers/*.js",
"types": "./dist/helpers/*.d.ts"
},
"./nast": {
"import": "./dist/nast/index.js",
"require": "./dist/nast/index.js",
"types": "./dist/nast/index.d.ts"
},
"./markdown": {
"import": "./dist/markdown/index.js",
"require": "./dist/markdown/index.js",
"types": "./dist/markdown/index.d.ts"
},
"./text": {
"import": "./dist/text/index.js",
"require": "./dist/text/index.js",
"types": "./dist/text/index.d.ts"
}
},
"dependencies": {
"@cashu/cashu-ts": "2.0.0-rc1",
"@types/hast": "^3.0.4",
"@types/mdast": "^4.0.4",
"@types/unist": "^3.0.3",
"applesauce-core": "^3.1.0",
"mdast-util-find-and-replace": "^3.0.2",
"nostr-tools": "~2.15",
"remark": "^15.0.1",
"remark-parse": "^11.0.0",
"unified": "^11.0.5",
"unist-util-visit-parents": "^6.0.1"
},
"devDependencies": {
"typescript": "^5.8.3",
"applesauce-signers": "^3.1.0",
"vitest": "^3.2.3"
},
"funding": {
"type": "lightning",
"url": "lightning:nostrudel@geyser.fund"
},
"scripts": {
"build": "tsc",
"watch:build": "tsc --watch > /dev/null",
"test": "vitest run --passWithNoTests",
"watch:test": "vitest"
}
}