applesauce-content 4.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1 @@
1
- export * from "./regexp.js";
2
1
  export * from "./media.js";
@@ -1,2 +1 @@
1
- export * from "./regexp.js";
2
1
  export * from "./media.js";
@@ -1,3 +1,3 @@
1
- import { MediaAttachment } from "applesauce-core/helpers/file-metadata";
1
+ import { MediaAttachment } from "applesauce-common/helpers/file-metadata";
2
2
  /** Returns all URLs in a content string that contain a sha256 hash */
3
3
  export declare function getMediaAttachmentURLsFromContent(content: string): MediaAttachment[];
@@ -1,5 +1,5 @@
1
- import { getSha256FromURL } from "applesauce-core/helpers/file-metadata";
2
- import { Tokens } from "./regexp.js";
1
+ import { getSha256FromURL } from "applesauce-common/helpers/file-metadata";
2
+ import { Tokens } from "applesauce-core/helpers/regexp";
3
3
  /** Returns all URLs in a content string that contain a sha256 hash */
4
4
  export function getMediaAttachmentURLsFromContent(content) {
5
5
  return (Array.from(content.matchAll(Tokens.link))
@@ -0,0 +1,9 @@
1
+ import { ParsedBlossomURI } from "applesauce-common/helpers/blossom";
2
+ import { Link, Nodes } from "mdast";
3
+ import { Transformer } from "unified";
4
+ export interface BlossomMdastLink extends Link {
5
+ type: "link";
6
+ data: ParsedBlossomURI;
7
+ }
8
+ /** Finds and creates BUD-10 `blossom:` URI links in a mdast tree */
9
+ export declare function remarkBlossomURIs(): Transformer<Nodes>;
@@ -0,0 +1,22 @@
1
+ import { parseBlossomURI } from "applesauce-common/helpers/blossom";
2
+ import { Tokens } from "applesauce-core/helpers/regexp";
3
+ import { findAndReplace } from "mdast-util-find-and-replace";
4
+ /** Finds and creates BUD-10 `blossom:` URI links in a mdast tree */
5
+ export function remarkBlossomURIs() {
6
+ return (tree) => {
7
+ findAndReplace(tree, [
8
+ Tokens.blossom,
9
+ (raw) => {
10
+ const parsed = parseBlossomURI(raw);
11
+ if (!parsed)
12
+ return false;
13
+ return {
14
+ type: "link",
15
+ data: parsed,
16
+ url: raw,
17
+ children: [{ type: "text", value: raw }],
18
+ };
19
+ },
20
+ ]);
21
+ };
22
+ }
@@ -1 +1,2 @@
1
1
  export * from "./mentions.js";
2
+ export * from "./blossom.js";
@@ -1 +1,2 @@
1
1
  export * from "./mentions.js";
2
+ export * from "./blossom.js";
@@ -1,6 +1,6 @@
1
+ import { decodePointer } from "applesauce-core/helpers/pointers";
2
+ import { Tokens } from "applesauce-core/helpers/regexp";
1
3
  import { findAndReplace } from "mdast-util-find-and-replace";
2
- import { decode } from "nostr-tools/nip19";
3
- import { Tokens } from "../helpers/regexp.js";
4
4
  export function remarkNostrMentions() {
5
5
  return (tree) => {
6
6
  findAndReplace(tree, [
@@ -9,7 +9,7 @@ export function remarkNostrMentions() {
9
9
  try {
10
10
  return {
11
11
  type: "link",
12
- data: decode($1),
12
+ data: decodePointer($1),
13
13
  children: [],
14
14
  url: "nostr:" + $1,
15
15
  };
@@ -13,6 +13,9 @@ export function truncateContent(tree, maxLength = 256) {
13
13
  case "cashu":
14
14
  length += node.raw.length;
15
15
  break;
16
+ case "blossom":
17
+ length += node.raw.length;
18
+ break;
16
19
  case "gallery":
17
20
  length += node.links.reduce((t, l) => t + l.length, 0);
18
21
  break;
@@ -1,7 +1,7 @@
1
1
  import { type Token } from "@cashu/cashu-ts";
2
- import { type DecodeResult } from "applesauce-core/helpers";
3
- import { type ParsedInvoice } from "applesauce-core/helpers/bolt11";
4
- import { type EventTemplate, type NostrEvent } from "nostr-tools";
2
+ import { type ParsedInvoice } from "applesauce-common/helpers/bolt11";
3
+ import { type EventTemplate, type NostrEvent } from "applesauce-core/helpers/event";
4
+ import { type DecodeResult } from "applesauce-core/helpers/pointers";
5
5
  import { type Parent, type Node as UnistNode } from "unist";
6
6
  export interface CommonData {
7
7
  eol?: boolean;
@@ -53,6 +53,21 @@ export interface Emoji extends Node {
53
53
  url: string;
54
54
  tag: ["emoji", ...string[]];
55
55
  }
56
+ export interface BlossomURI extends Node {
57
+ type: "blossom";
58
+ /** The original matched text, e.g. `blossom:<hash>.pdf?xs=...` */
59
+ raw: string;
60
+ /** 64 character lowercase hex sha256 hash of the blob */
61
+ sha256: string;
62
+ /** File extension without the leading dot */
63
+ ext: string;
64
+ /** Optional exact blob size in bytes */
65
+ size?: number;
66
+ /** Server hints from repeated `xs` query parameters */
67
+ servers: string[];
68
+ /** Author hex pubkeys from repeated `as` query parameters */
69
+ authors: string[];
70
+ }
56
71
  export interface ContentMap {
57
72
  text: Text;
58
73
  link: Link;
@@ -62,6 +77,7 @@ export interface ContentMap {
62
77
  hashtag: Hashtag;
63
78
  emoji: Emoji;
64
79
  gallery: Gallery;
80
+ blossom: BlossomURI;
65
81
  }
66
82
  export type Content = ContentMap[keyof ContentMap];
67
83
  export interface Root extends Parent {
@@ -0,0 +1,4 @@
1
+ import { Transformer } from "unified";
2
+ import { Root } from "../nast/types.js";
3
+ /** Finds and creates BUD-10 blossom URI nodes in the tree */
4
+ export declare function blossomURIs(): Transformer<Root>;
@@ -0,0 +1,23 @@
1
+ import { parseBlossomURI } from "applesauce-common/helpers/blossom";
2
+ import { Tokens } from "applesauce-core/helpers/regexp";
3
+ import { findAndReplace } from "../nast/find-and-replace.js";
4
+ /** Finds and creates BUD-10 blossom URI nodes in the tree */
5
+ export function blossomURIs() {
6
+ return (tree) => {
7
+ findAndReplace(tree, [
8
+ [
9
+ Tokens.blossom,
10
+ (raw) => {
11
+ const parsed = parseBlossomURI(raw);
12
+ if (!parsed)
13
+ return false;
14
+ return {
15
+ type: "blossom",
16
+ raw,
17
+ ...parsed,
18
+ };
19
+ },
20
+ ],
21
+ ]);
22
+ };
23
+ }
@@ -1,5 +1,5 @@
1
1
  import { getDecodedToken } from "@cashu/cashu-ts";
2
- import { Tokens } from "../helpers/regexp.js";
2
+ import { Tokens } from "applesauce-core/helpers/regexp";
3
3
  import { findAndReplace } from "../nast/find-and-replace.js";
4
4
  /** Parse cashu tokens from an ATS tree */
5
5
  export function cashuTokens() {
@@ -1,5 +1,5 @@
1
1
  import { Transformer } from "unified";
2
- import { EventTemplate, NostrEvent } from "nostr-tools";
2
+ import { EventTemplate, NostrEvent } from "applesauce-core/helpers/event";
3
3
  import { Root } from "../nast/types.js";
4
4
  import { galleries } from "./gallery.js";
5
5
  export declare const TextNoteContentSymbol: unique symbol;
@@ -9,9 +9,11 @@ import { galleries } from "./gallery.js";
9
9
  import { lightningInvoices } from "./lightning.js";
10
10
  import { eolMetadata } from "../nast/eol-metadata.js";
11
11
  import { links } from "./links.js";
12
+ import { blossomURIs } from "./blossom.js";
12
13
  export const TextNoteContentSymbol = Symbol.for("text-note-content");
13
14
  // default kind 1 transformers
14
15
  export const textNoteTransformers = [
16
+ blossomURIs,
15
17
  links,
16
18
  nostrMentions,
17
19
  galleries,
@@ -48,6 +50,5 @@ export function getParsedContent(event, content, transformers = textNoteTransfor
48
50
  });
49
51
  }
50
52
  export function removeParsedTextContent(event) {
51
- // @ts-expect-error
52
- delete event[TextNoteContentSymbol];
53
+ Reflect.deleteProperty(event, TextNoteContentSymbol);
53
54
  }
@@ -1,5 +1,5 @@
1
- import { getEmojiTag } from "applesauce-core/helpers/emoji";
2
- import { Tokens } from "../helpers/regexp.js";
1
+ import { getEmojiTag } from "applesauce-common/helpers/emoji";
2
+ import { Tokens } from "applesauce-core/helpers/regexp";
3
3
  import { findAndReplace } from "../nast/find-and-replace.js";
4
4
  /** Finds and creates emoji nodes in the tree */
5
5
  export function emojis() {
@@ -1,4 +1,8 @@
1
1
  import { Transformer } from "unified";
2
2
  import { Root } from "../nast/types.js";
3
+ export interface GalleriesOptions {
4
+ /** When true, adjacent `blossom:` image URIs are clustered alongside HTTP image links. Defaults to false. */
5
+ includeBlossom?: boolean;
6
+ }
3
7
  /** Group images into galleries in an ATS tree */
4
- export declare function galleries(types?: string[]): Transformer<Root>;
8
+ export declare function galleries(types?: string[], options?: GalleriesOptions): Transformer<Root>;
@@ -1,21 +1,23 @@
1
1
  import { convertToUrl, getURLFilename, IMAGE_EXT } from "applesauce-core/helpers/url";
2
2
  /** Group images into galleries in an ATS tree */
3
- export function galleries(types = IMAGE_EXT) {
3
+ export function galleries(types = IMAGE_EXT, options = {}) {
4
+ const { includeBlossom = false } = options;
4
5
  return (tree) => {
5
- let links = [];
6
+ let items = [];
7
+ const getItemHref = (item) => (item.type === "link" ? item.href : item.raw);
6
8
  const commit = (index) => {
7
9
  // only create a gallery if there are more than a single image
8
- if (links.length > 1) {
9
- const start = tree.children.indexOf(links[0]);
10
- const end = tree.children.indexOf(links[links.length - 1]);
10
+ if (items.length > 1) {
11
+ const start = tree.children.indexOf(items[0]);
12
+ const end = tree.children.indexOf(items[items.length - 1]);
11
13
  // replace all nodes with a gallery
12
- tree.children.splice(start, 1 + end - start, { type: "gallery", links: links.map((l) => l.href) });
13
- links = [];
14
+ tree.children.splice(start, 1 + end - start, { type: "gallery", links: items.map(getItemHref) });
15
+ items = [];
14
16
  // return new cursor
15
17
  return end - 1;
16
18
  }
17
19
  else {
18
- links = [];
20
+ items = [];
19
21
  return index;
20
22
  }
21
23
  };
@@ -26,13 +28,21 @@ export function galleries(types = IMAGE_EXT) {
26
28
  const url = convertToUrl(node.href);
27
29
  const filename = getURLFilename(url);
28
30
  if (filename && types.some((ext) => filename.endsWith(ext))) {
29
- links.push(node);
31
+ items.push(node);
30
32
  }
31
33
  else {
32
34
  i = commit(i);
33
35
  }
34
36
  }
35
- else if (node.type === "text" && links.length > 0) {
37
+ else if (node.type === "blossom" && includeBlossom) {
38
+ if (types.some((ext) => ext === `.${node.ext.toLowerCase()}`)) {
39
+ items.push(node);
40
+ }
41
+ else {
42
+ i = commit(i);
43
+ }
44
+ }
45
+ else if (node.type === "text" && items.length > 0) {
36
46
  const isEmpty = node.value === "\n" || !node.value.match(/[^\s]/g);
37
47
  if (!isEmpty)
38
48
  i = commit(i);
@@ -1,5 +1,5 @@
1
- import { getHashtagTag } from "applesauce-core/helpers/hashtag";
2
- import { Tokens } from "../helpers/regexp.js";
1
+ import { getHashtagTag } from "applesauce-common/helpers/hashtag";
2
+ import { Tokens } from "applesauce-core/helpers/regexp";
3
3
  import { findAndReplace } from "../nast/find-and-replace.js";
4
4
  /** Find and create hashtag notes in provided tree */
5
5
  export function hashtags() {
@@ -7,3 +7,4 @@ export * from "./parser.js";
7
7
  export * from "./hashtag.js";
8
8
  export * from "./gallery.js";
9
9
  export * from "./lightning.js";
10
+ export * from "./blossom.js";
@@ -7,3 +7,4 @@ export * from "./parser.js";
7
7
  export * from "./hashtag.js";
8
8
  export * from "./gallery.js";
9
9
  export * from "./lightning.js";
10
+ export * from "./blossom.js";
@@ -1,5 +1,5 @@
1
- import { parseBolt11 } from "applesauce-core/helpers/bolt11";
2
- import { Tokens } from "../helpers/regexp.js";
1
+ import { parseBolt11 } from "applesauce-common/helpers/bolt11";
2
+ import { Tokens } from "applesauce-core/helpers/regexp";
3
3
  import { findAndReplace } from "../nast/find-and-replace.js";
4
4
  /** Finds and creates lightning invoice nodes in the tree */
5
5
  export function lightningInvoices() {
@@ -1,4 +1,4 @@
1
- import { Tokens } from "../helpers/regexp.js";
1
+ import { Tokens } from "applesauce-core/helpers";
2
2
  import { findAndReplace } from "../nast/find-and-replace.js";
3
3
  /** Finds and creates web links in the tree */
4
4
  export function links() {
@@ -1,5 +1,5 @@
1
- import { decode } from "nostr-tools/nip19";
2
- import { Tokens } from "../helpers/regexp.js";
1
+ import { decodePointer } from "applesauce-core/helpers/pointers";
2
+ import { Tokens } from "applesauce-core/helpers/regexp";
3
3
  import { findAndReplace } from "../nast/find-and-replace.js";
4
4
  /** Finds and creates NIP-19 nostr mentions in the tree */
5
5
  export function nostrMentions() {
@@ -11,7 +11,7 @@ export function nostrMentions() {
11
11
  try {
12
12
  return {
13
13
  type: "mention",
14
- decoded: decode($1),
14
+ decoded: decodePointer($1),
15
15
  encoded: $1,
16
16
  };
17
17
  }
@@ -1,4 +1,4 @@
1
- import { EventTemplate, NostrEvent } from "nostr-tools";
1
+ import { EventTemplate, NostrEvent } from "applesauce-core/helpers/event";
2
2
  import { Root } from "../nast/types.js";
3
3
  /** Creates a {@link Root} ATS node for a text note */
4
4
  export declare function createEventContentTree(event: NostrEvent | EventTemplate | string, content?: string): Root;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-content",
3
- "version": "4.0.0",
3
+ "version": "6.0.0",
4
4
  "description": "Unified plugins for processing event content",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -46,23 +46,23 @@
46
46
  }
47
47
  },
48
48
  "dependencies": {
49
- "@cashu/cashu-ts": "^2.7.2",
49
+ "@cashu/cashu-ts": "^3.1.1",
50
50
  "@types/hast": "^3.0.4",
51
51
  "@types/mdast": "^4.0.4",
52
52
  "@types/unist": "^3.0.3",
53
- "applesauce-core": "^4.0.0",
53
+ "applesauce-common": "^6.0.0",
54
+ "applesauce-core": "^6.0.0",
54
55
  "mdast-util-find-and-replace": "^3.0.2",
55
- "nostr-tools": "~2.17",
56
56
  "remark": "^15.0.1",
57
57
  "remark-parse": "^11.0.0",
58
58
  "unified": "^11.0.5",
59
59
  "unist-util-visit-parents": "^6.0.1"
60
60
  },
61
61
  "devDependencies": {
62
- "applesauce-signers": "^4.0.0",
62
+ "applesauce-signers": "^6.0.0",
63
63
  "rimraf": "^6.0.1",
64
64
  "typescript": "^5.8.3",
65
- "vitest": "^3.2.4"
65
+ "vitest": "^4.0.15"
66
66
  },
67
67
  "funding": {
68
68
  "type": "lightning",
@@ -1,19 +0,0 @@
1
- export declare const Expressions: {
2
- readonly url: RegExp;
3
- readonly link: RegExp;
4
- readonly cashu: RegExp;
5
- readonly nostrLink: RegExp;
6
- readonly emoji: RegExp;
7
- readonly hashtag: RegExp;
8
- readonly lightning: RegExp;
9
- };
10
- /** A list of Regular Expressions that match tokens surrounded by whitespace to avoid matching in URLs */
11
- export declare const Tokens: {
12
- readonly url: RegExp;
13
- readonly link: RegExp;
14
- readonly cashu: RegExp;
15
- readonly nostrLink: RegExp;
16
- readonly emoji: RegExp;
17
- readonly hashtag: RegExp;
18
- readonly lightning: RegExp;
19
- };
@@ -1,48 +0,0 @@
1
- export const Expressions = {
2
- get url() {
3
- return /(?:https?|wss?|ircs?|s?ftp):\/\/([a-zA-Z0-9\.\-]+\.[a-zA-Z]+(?::\d+)?)([\/\?#][\p{L}\p{N}\p{M}&\.-\/\?=#\-@%\+_,:!~*]*)?/gu;
4
- },
5
- get link() {
6
- return /https?:\/\/([a-zA-Z0-9\.\-]+\.[a-zA-Z]+(?::\d+)?)([\/\?#][\p{L}\p{N}\p{M}&\.-\/\?=#\-@%\+_,:!~*]*)?/gu;
7
- },
8
- get cashu() {
9
- return /(?:cashu:\/{0,2})?(cashu(?:A|B)[A-Za-z0-9_-]{100,}={0,3})/gi;
10
- },
11
- get nostrLink() {
12
- return /(?:nostr:)?((npub|note|nprofile|nevent|naddr)1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{58,})/gi;
13
- },
14
- get emoji() {
15
- return /:([a-zA-Z0-9_-]+):/gi;
16
- },
17
- get hashtag() {
18
- // NOTE: cant use \b here because it uses \w which only matches latin letters
19
- return /(?<=^|[^\p{L}#\/])#([\p{L}\p{N}\p{M}]+)(?=\p{Z}|$|\s)/gu;
20
- },
21
- get lightning() {
22
- return /(?:lightning:)?(LNBC[A-Za-z0-9]+)/gim;
23
- },
24
- };
25
- /** A list of Regular Expressions that match tokens surrounded by whitespace to avoid matching in URLs */
26
- export const Tokens = {
27
- get url() {
28
- return Expressions.url;
29
- },
30
- get link() {
31
- return Expressions.link;
32
- },
33
- get cashu() {
34
- return new RegExp(`(?<=^|\\s)${Expressions.cashu.source}`, "gi");
35
- },
36
- get nostrLink() {
37
- return new RegExp(`(?<=^|\\s)${Expressions.nostrLink.source}`, "gi");
38
- },
39
- get emoji() {
40
- return Expressions.emoji;
41
- },
42
- get hashtag() {
43
- return Expressions.hashtag;
44
- },
45
- get lightning() {
46
- return new RegExp(`(?<=^|\\s)${Expressions.lightning.source}`, "gim");
47
- },
48
- };