applesauce-content 1.2.0 → 2.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,4 +1,5 @@
1
1
  export declare const Expressions: {
2
+ readonly url: RegExp;
2
3
  readonly link: RegExp;
3
4
  readonly cashu: RegExp;
4
5
  readonly nostrLink: RegExp;
@@ -8,6 +9,7 @@ export declare const Expressions: {
8
9
  };
9
10
  /** A list of Regular Expressions that match tokens surrounded by whitespace to avoid matching in URLs */
10
11
  export declare const Tokens: {
12
+ readonly url: RegExp;
11
13
  readonly link: RegExp;
12
14
  readonly cashu: RegExp;
13
15
  readonly nostrLink: RegExp;
@@ -1,4 +1,7 @@
1
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
+ },
2
5
  get link() {
3
6
  return /https?:\/\/([a-zA-Z0-9\.\-]+\.[a-zA-Z]+(?::\d+)?)([\/\?#][\p{L}\p{N}\p{M}&\.-\/\?=#\-@%\+_,:!~*]*)?/gu;
4
7
  },
@@ -12,7 +15,8 @@ export const Expressions = {
12
15
  return /:([a-zA-Z0-9_-]+):/gi;
13
16
  },
14
17
  get hashtag() {
15
- return /(?<=^|[^\p{L}#])#([\p{L}\p{N}\p{M}]+)/gu;
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;
16
20
  },
17
21
  get lightning() {
18
22
  return /(?:lightning:)?(LNBC[A-Za-z0-9]+)/gim;
@@ -20,14 +24,17 @@ export const Expressions = {
20
24
  };
21
25
  /** A list of Regular Expressions that match tokens surrounded by whitespace to avoid matching in URLs */
22
26
  export const Tokens = {
27
+ get url() {
28
+ return Expressions.url;
29
+ },
23
30
  get link() {
24
- return new RegExp(`(?<=\\s|^)${Expressions.link.source}(?=\\s|$)`, "gu");
31
+ return Expressions.link;
25
32
  },
26
33
  get cashu() {
27
- return new RegExp(`(?<=\\s|^)${Expressions.cashu.source}(?=\\s|$)`, "gi");
34
+ return new RegExp(`(?<=^|\\s)${Expressions.cashu.source}`, "gi");
28
35
  },
29
36
  get nostrLink() {
30
- return new RegExp(`(?<=\\s|^)${Expressions.nostrLink.source}(?=\\s|$)`, "gi");
37
+ return new RegExp(`(?<=^|\\s)${Expressions.nostrLink.source}`, "gi");
31
38
  },
32
39
  get emoji() {
33
40
  return Expressions.emoji;
@@ -36,6 +43,6 @@ export const Tokens = {
36
43
  return Expressions.hashtag;
37
44
  },
38
45
  get lightning() {
39
- return new RegExp(`(?<=\\s|^)${Expressions.lightning.source}(?=\\s|$)`, "gim");
46
+ return new RegExp(`(?<=^|\\s)${Expressions.lightning.source}`, "gim");
40
47
  },
41
48
  };
@@ -1,6 +1,6 @@
1
- import { Transformer } from "unified";
1
+ import { DecodeResult } from "applesauce-core/helpers";
2
2
  import { Link, Nodes } from "mdast";
3
- import { DecodeResult } from "nostr-tools/nip19";
3
+ import { Transformer } from "unified";
4
4
  export interface NostrMention extends Link {
5
5
  type: "link";
6
6
  data: DecodeResult;
@@ -1,8 +1,8 @@
1
- import { EventTemplate, NostrEvent } from "nostr-tools";
2
- import { DecodeResult } from "nostr-tools/nip19";
3
- import { Node as UnistNode, Parent } from "unist";
4
1
  import { type Token } from "@cashu/cashu-ts";
2
+ import { type DecodeResult } from "applesauce-core/helpers";
5
3
  import { type ParsedInvoice } from "applesauce-core/helpers/bolt11";
4
+ import { type EventTemplate, type NostrEvent } from "nostr-tools";
5
+ import { type Parent, type Node as UnistNode } from "unist";
6
6
  export interface CommonData {
7
7
  eol?: boolean;
8
8
  }
@@ -43,7 +43,8 @@ export interface Hashtag extends Node {
43
43
  name: string;
44
44
  /** The lowercase canonical name */
45
45
  hashtag: string;
46
- tag: ["t", ...string[]];
46
+ /** The indexable tag for the hashtag. will be undefined if none was found */
47
+ tag?: ["t", ...string[]];
47
48
  }
48
49
  export interface Emoji extends Node {
49
50
  type: "emoji";
@@ -3,7 +3,7 @@ import { getOrComputeCachedValue } from "applesauce-core/helpers/cache";
3
3
  import { nostrMentions } from "./mentions.js";
4
4
  import { cashuTokens } from "./cashu.js";
5
5
  import { emojis } from "./emoji.js";
6
- import { createTextNoteATS } from "./parser.js";
6
+ import { createEventContentTree } from "./parser.js";
7
7
  import { hashtags } from "./hashtag.js";
8
8
  import { galleries } from "./gallery.js";
9
9
  import { lightningInvoices } from "./lightning.js";
@@ -29,7 +29,7 @@ export function getParsedContent(event, content, transformers = textNoteTransfor
29
29
  for (const transformer of transformers) {
30
30
  processor.use(transformer);
31
31
  }
32
- return processor.runSync(createTextNoteATS(event, content));
32
+ return processor.runSync(createEventContentTree(event, content));
33
33
  }
34
34
  // no caching
35
35
  if (!cacheKey) {
@@ -37,14 +37,14 @@ export function getParsedContent(event, content, transformers = textNoteTransfor
37
37
  for (const transformer of transformers) {
38
38
  processor.use(transformer);
39
39
  }
40
- return processor.runSync(createTextNoteATS(event, content));
40
+ return processor.runSync(createEventContentTree(event, content));
41
41
  }
42
42
  return getOrComputeCachedValue(event, cacheKey, () => {
43
43
  const processor = unified();
44
44
  for (const transformer of transformers) {
45
45
  processor.use(transformer);
46
46
  }
47
- return processor.runSync(createTextNoteATS(event, content));
47
+ return processor.runSync(createEventContentTree(event, content));
48
48
  });
49
49
  }
50
50
  export function removeParsedTextContent(event) {
@@ -1,4 +1,4 @@
1
1
  import { type Transformer } from "unified";
2
2
  import { Root } from "../nast/types.js";
3
- /** Adds emoji tags to text ATS */
3
+ /** Finds and creates emoji nodes in the tree */
4
4
  export declare function emojis(): Transformer<Root>;
@@ -1,7 +1,7 @@
1
1
  import { getEmojiTag } from "applesauce-core/helpers/emoji";
2
2
  import { Tokens } from "../helpers/regexp.js";
3
3
  import { findAndReplace } from "../nast/find-and-replace.js";
4
- /** Adds emoji tags to text ATS */
4
+ /** Finds and creates emoji nodes in the tree */
5
5
  export function emojis() {
6
6
  return (tree) => {
7
7
  const event = tree.event;
@@ -1,4 +1,4 @@
1
1
  import { Transformer } from "unified";
2
2
  import { Root } from "../nast/types.js";
3
- /** Groups images into galleries in an ATS tree */
3
+ /** Group images into galleries in an ATS tree */
4
4
  export declare function galleries(types?: string[]): Transformer<Root>;
@@ -1,5 +1,5 @@
1
1
  import { convertToUrl, getURLFilename, IMAGE_EXT } from "applesauce-core/helpers/url";
2
- /** Groups images into galleries in an ATS tree */
2
+ /** Group images into galleries in an ATS tree */
3
3
  export function galleries(types = IMAGE_EXT) {
4
4
  return (tree) => {
5
5
  let links = [];
@@ -1,3 +1,4 @@
1
1
  import { Transformer } from "unified";
2
2
  import { Root } from "../nast/types.js";
3
+ /** Find and create hashtag notes in provided tree */
3
4
  export declare function hashtags(): Transformer<Root>;
@@ -1,24 +1,24 @@
1
1
  import { getHashtagTag } from "applesauce-core/helpers/hashtag";
2
2
  import { Tokens } from "../helpers/regexp.js";
3
3
  import { findAndReplace } from "../nast/find-and-replace.js";
4
+ /** Find and create hashtag notes in provided tree */
4
5
  export function hashtags() {
5
6
  return (tree) => {
6
7
  const event = tree.event;
7
- if (!event)
8
- return;
9
8
  findAndReplace(tree, [
10
9
  [
11
10
  Tokens.hashtag,
12
11
  (_, $1) => {
13
12
  try {
14
- const tag = getHashtagTag(event, $1);
15
- if (!tag)
13
+ const tag = event && getHashtagTag(event, $1);
14
+ // Skip if the match if no tag was found in the event
15
+ if (!tag && event)
16
16
  return false;
17
17
  return {
18
18
  type: "hashtag",
19
19
  tag,
20
20
  name: $1,
21
- hashtag: tag[1].toLowerCase(),
21
+ hashtag: tag?.[1].toLowerCase() || $1.toLowerCase(),
22
22
  };
23
23
  }
24
24
  catch (error) { }
@@ -1,3 +1,4 @@
1
1
  import { type Transformer } from "unified";
2
2
  import { Root } from "../nast/types.js";
3
+ /** Finds and creates lightning invoice nodes in the tree */
3
4
  export declare function lightningInvoices(): Transformer<Root>;
@@ -1,6 +1,7 @@
1
1
  import { parseBolt11 } from "applesauce-core/helpers/bolt11";
2
2
  import { Tokens } from "../helpers/regexp.js";
3
3
  import { findAndReplace } from "../nast/find-and-replace.js";
4
+ /** Finds and creates lightning invoice nodes in the tree */
4
5
  export function lightningInvoices() {
5
6
  return (tree) => {
6
7
  findAndReplace(tree, [
@@ -1,3 +1,4 @@
1
1
  import { Transformer } from "unified";
2
2
  import { Root } from "../nast/types.js";
3
+ /** Finds and creates web links in the tree */
3
4
  export declare function links(): Transformer<Root>;
@@ -1,5 +1,6 @@
1
1
  import { Tokens } from "../helpers/regexp.js";
2
2
  import { findAndReplace } from "../nast/find-and-replace.js";
3
+ /** Finds and creates web links in the tree */
3
4
  export function links() {
4
5
  return (tree) => {
5
6
  findAndReplace(tree, [
@@ -1,3 +1,4 @@
1
1
  import { Transformer } from "unified";
2
2
  import { Root } from "../nast/types.js";
3
+ /** Finds and creates NIP-19 nostr mentions in the tree */
3
4
  export declare function nostrMentions(): Transformer<Root>;
@@ -1,6 +1,7 @@
1
1
  import { decode } from "nostr-tools/nip19";
2
2
  import { Tokens } from "../helpers/regexp.js";
3
3
  import { findAndReplace } from "../nast/find-and-replace.js";
4
+ /** Finds and creates NIP-19 nostr mentions in the tree */
4
5
  export function nostrMentions() {
5
6
  return (tree) => {
6
7
  findAndReplace(tree, [
@@ -1,4 +1,6 @@
1
1
  import { EventTemplate, NostrEvent } from "nostr-tools";
2
2
  import { Root } from "../nast/types.js";
3
3
  /** Creates a {@link Root} ATS node for a text note */
4
- export declare function createTextNoteATS(event: NostrEvent | EventTemplate | string, content?: string): Root;
4
+ export declare function createEventContentTree(event: NostrEvent | EventTemplate | string, content?: string): Root;
5
+ /** @deprecated use createEventContentTree instead */
6
+ export declare const createTextNoteATS: typeof createEventContentTree;
@@ -1,5 +1,5 @@
1
1
  /** Creates a {@link Root} ATS node for a text note */
2
- export function createTextNoteATS(event, content) {
2
+ export function createEventContentTree(event, content) {
3
3
  return {
4
4
  type: "root",
5
5
  event: typeof event !== "string" ? event : undefined,
@@ -11,3 +11,5 @@ export function createTextNoteATS(event, content) {
11
11
  ],
12
12
  };
13
13
  }
14
+ /** @deprecated use createEventContentTree instead */
15
+ export const createTextNoteATS = createEventContentTree;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-content",
3
- "version": "1.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "Unified plugins for processing event content",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -52,9 +52,9 @@
52
52
  "@types/hast": "^3.0.4",
53
53
  "@types/mdast": "^4.0.4",
54
54
  "@types/unist": "^3.0.3",
55
- "applesauce-core": "^1.2.0",
55
+ "applesauce-core": "^2.0.0",
56
56
  "mdast-util-find-and-replace": "^3.0.2",
57
- "nostr-tools": "^2.10.4",
57
+ "nostr-tools": "^2.13",
58
58
  "remark": "^15.0.1",
59
59
  "remark-parse": "^11.0.0",
60
60
  "unified": "^11.0.5",
@@ -62,7 +62,8 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "typescript": "^5.8.3",
65
- "vitest": "^3.1.1"
65
+ "applesauce-signers": "^2.0.0",
66
+ "vitest": "^3.2.3"
66
67
  },
67
68
  "funding": {
68
69
  "type": "lightning",
@@ -1 +0,0 @@
1
- export {};
@@ -1,13 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import * as exports from "../index.js";
3
- describe("exports", () => {
4
- it("should export the expected functions", () => {
5
- expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
6
- [
7
- "Expressions",
8
- "Tokens",
9
- "getMediaAttachmentURLsFromContent",
10
- ]
11
- `);
12
- });
13
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,244 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { Expressions, Tokens } from "../regexp.js";
3
- describe("Regular Expressions", () => {
4
- describe("Expressions.link", () => {
5
- it("should match valid HTTP URLs", () => {
6
- const text = "Check out https://example.com";
7
- const matches = Array.from(text.matchAll(Expressions.link));
8
- expect(matches).toHaveLength(1);
9
- expect(matches[0][0]).toBe("https://example.com");
10
- });
11
- it("should match valid HTTPS URLs with paths and query parameters", () => {
12
- const text = "Visit https://example.com/path?query=value#fragment";
13
- const matches = Array.from(text.matchAll(Expressions.link));
14
- expect(matches).toHaveLength(1);
15
- expect(matches[0][0]).toBe("https://example.com/path?query=value#fragment");
16
- });
17
- it("should match URLs with ports", () => {
18
- const text = "Server at http://example.com:3000/api";
19
- const matches = Array.from(text.matchAll(Expressions.link));
20
- expect(matches).toHaveLength(1);
21
- expect(matches[0][0]).toBe("http://example.com:3000/api");
22
- });
23
- it("should match multiple URLs in text", () => {
24
- const text = "First https://example.com and second https://test.org/path";
25
- const matches = Array.from(text.matchAll(Expressions.link));
26
- expect(matches).toHaveLength(2);
27
- expect(matches[0][0]).toBe("https://example.com");
28
- expect(matches[1][0]).toBe("https://test.org/path");
29
- });
30
- });
31
- describe("Expressions.cashu", () => {
32
- const cashuA = "cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vdGVzdG51dC5jYXNodS5zcGFjZSIsInByb29mcyI6W3sic2VjcmV0IjoiMTFhYmQxZjY1OWI1MzE5MjhjYTEwMmEyYjgxYzQ2MTQxYzY3NTg1MjU2ZmZmZGNlNzRiMWY4NWFmZWRkM2M2NiIsIkMiOiIwM2FmYjZiMzE5YjAyYzkyODc3ZjkxY2VjMjM4NmNiZjcwMzVhZDRkMWFiNWUzNmRjY2VkNDdjZWY4NDRjYzNiMWUiLCJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSJ9XX1dLCJ1bml0Ijoic2F0In0";
33
- const cashuB = "cashuBo2FteBtodHRwczovL3Rlc3RudXQuY2FzaHUuc3BhY2VhdWNzYXRhdIGiYWlIAJofKTJT5B5hcIOkYWEQYXN4QDQ4ZjIwNmM5ODI2NWQ2YjlmYTU1Zjc0ZjQ5ZDU0NjI1NTE4MDBjMDc4NjE1OWQ0YzE4ZDNmMWZiYzE0Nzc3MzhhY1ghA8xqu_uJfIZ-m5UdaauOX1owIFwXeNQ0zQlnLTFsdVaOYWSjYWVYINcRIASTRc17HjOzj-s66ftGh78y4jdvYG4Hx3yh1pDIYXNYILteFXHFo9vo-NEZF2W3ofEvIY63AZxp8MgueyMJuBRYYXJYIBl_RnxlsZMsh4tmgd5K4XsIhgxDM9w5NyhD6F3UEAJSo2FhBGFzeEAxODgzYTc4MmNhMTgzMDNlMmFhMmIyMzRhYjUyMTFhY2Y1NTNjMWVlYWQ3ZWQ1YjBhMzdhYjFlZjcwZWRhMmEyYWNYIQNU8wZs14BtQPHbdOs5Rb3wSh0KXx9AdBigW1mgJwZo4KNhYQFhc3hAMDA1MDMzY2EzNDBjNjE3Njc3OGQxNWYwMTVkOTMyZDEwZjA0YWE1ZTA3ZDQyOTg0ZTUyNWYwMjk1MjM1YWIzNWFjWCECXRsBpeSyn5gUQ7_gvsljKBNCUy0VPvkrTS5HD5BCko0";
34
- it("should match valid cashu tokens", () => {
35
- const text = `Here is a token: ${cashuA}`;
36
- const matches = Array.from(text.matchAll(Expressions.cashu));
37
- expect(matches).toHaveLength(1);
38
- expect(matches[0][1]).toBe(cashuA);
39
- });
40
- it("should match cashuB tokens", () => {
41
- const text = `Here is a token: ${cashuB}`;
42
- const matches = Array.from(text.matchAll(Expressions.cashu));
43
- expect(matches).toHaveLength(1);
44
- expect(matches[0][1]).toBe(cashuB);
45
- });
46
- it("should match cashu tokens with prefix", () => {
47
- const text = `Here is a token cashu:${cashuA}`;
48
- const matches = Array.from(text.matchAll(Expressions.cashu));
49
- expect(matches).toHaveLength(1);
50
- expect(matches[0][1]).toBe(cashuA);
51
- });
52
- });
53
- describe("Expressions.nostrLink", () => {
54
- it("should match npub links", () => {
55
- const npub = "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6";
56
- const text = `Check out ${npub}`;
57
- const matches = Array.from(text.matchAll(Expressions.nostrLink));
58
- expect(matches).toHaveLength(1);
59
- expect(matches[0][1]).toBe(npub);
60
- });
61
- it("should match note links", () => {
62
- const note = "note1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsrjtey";
63
- const text = `Check out ${note}`;
64
- const matches = Array.from(text.matchAll(Expressions.nostrLink));
65
- expect(matches).toHaveLength(1);
66
- expect(matches[0][1]).toBe(note);
67
- });
68
- it("should match nprofile links", () => {
69
- const nprofile = "nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdakp7qgzwaehxw309aex2mrp0yhxummnw3ezuamfdejsygrwv3jx2mtedfhk6tcpzamhxue69uhkummnw3ezuamfdejsygrwv3jx2mtedfhk6tcpzamhxue69uhkzemp9qy";
70
- const text = `Check out ${nprofile}`;
71
- const matches = Array.from(text.matchAll(Expressions.nostrLink));
72
- expect(matches).toHaveLength(1);
73
- expect(matches[0][1]).toBe(nprofile);
74
- });
75
- it("should match nevent links", () => {
76
- const nevent = "nevent1qvzqqqqqqypzqwlsccluhy6xxsr6l9a9uhhxf75g85g8a709tprjcn4e42h053vaqyd8wumn8ghj7urewfsk66ty9enxjct5dfskvtnrdakj7qgmwaehxw309aex2mrp0yh8wetnw3jhymnzw33jucm0d5hsqgqqqrzq4vghcurgc2p3k70xka03m0wsvhwh244gh2f8tnk6dl49vgx9mgmd";
77
- const text = `Check out ${nevent}`;
78
- const matches = Array.from(text.matchAll(Expressions.nostrLink));
79
- expect(matches).toHaveLength(1);
80
- expect(matches[0][1]).toBe(nevent);
81
- });
82
- it("should match naddr links", () => {
83
- const naddr = "naddr1qqjx2wtzx93rycmz94nrqvf3956rqep3943xgvec956xxvnxxucxze33v93rvq3qeaz6dwsnvwkha5sn5puwwyxjgy26uusundrm684lg3vw4ma5c2jsxpqqqpmxw6td7rf";
84
- const text = `Check out ${naddr}`;
85
- const matches = Array.from(text.matchAll(Expressions.nostrLink));
86
- expect(matches).toHaveLength(1);
87
- expect(matches[0][1]).toBe(naddr);
88
- });
89
- it("should match nostr: prefixed links", () => {
90
- const text = "Check out nostr:npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6";
91
- const matches = Array.from(text.matchAll(Expressions.nostrLink));
92
- expect(matches).toHaveLength(1);
93
- expect(matches[0][1]).toBe("npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6");
94
- });
95
- });
96
- describe("Expressions.emoji", () => {
97
- it("should match emoji shortcodes", () => {
98
- const text = "Hello :smile: world :thumbsup:";
99
- const matches = Array.from(text.matchAll(Expressions.emoji));
100
- expect(matches).toHaveLength(2);
101
- expect(matches[0][1]).toBe("smile");
102
- expect(matches[1][1]).toBe("thumbsup");
103
- });
104
- it("should match emoji shortcodes with underscores and hyphens", () => {
105
- const text = "Hello :smiling_face: and :thumbs-up:";
106
- const matches = Array.from(text.matchAll(Expressions.emoji));
107
- expect(matches).toHaveLength(2);
108
- expect(matches[0][1]).toBe("smiling_face");
109
- expect(matches[1][1]).toBe("thumbs-up");
110
- });
111
- it("should match emoji shortcodes with numbers", () => {
112
- const text = "Hello :smile123: world :thumbs2up:";
113
- const matches = Array.from(text.matchAll(Expressions.emoji));
114
- expect(matches).toHaveLength(2);
115
- expect(matches[0][1]).toBe("smile123");
116
- expect(matches[1][1]).toBe("thumbs2up");
117
- });
118
- });
119
- describe("Expressions.hashtag", () => {
120
- it("should match simple hashtags", () => {
121
- const text = "Hello #world and #nostr";
122
- const matches = Array.from(text.matchAll(Expressions.hashtag));
123
- expect(matches).toHaveLength(2);
124
- expect(matches[0][1]).toBe("world");
125
- expect(matches[1][1]).toBe("nostr");
126
- });
127
- it("should match hashtags with numbers", () => {
128
- const text = "Hello #world2023 and #nostr42";
129
- const matches = Array.from(text.matchAll(Expressions.hashtag));
130
- expect(matches).toHaveLength(2);
131
- expect(matches[0][1]).toBe("world2023");
132
- expect(matches[1][1]).toBe("nostr42");
133
- });
134
- it("should match hashtags at the beginning of text", () => {
135
- const text = "#hello world";
136
- const matches = Array.from(text.matchAll(Expressions.hashtag));
137
- expect(matches).toHaveLength(1);
138
- expect(matches[0][1]).toBe("hello");
139
- });
140
- it("should not match hashtags within words", () => {
141
- const text = "word#hashtag is not a hashtag";
142
- const matches = Array.from(text.matchAll(Expressions.hashtag));
143
- expect(matches).toHaveLength(0);
144
- });
145
- it("should match non-Latin hashtags", () => {
146
- const text = "Hello #世界 and #привет";
147
- const matches = Array.from(text.matchAll(Expressions.hashtag));
148
- expect(matches).toHaveLength(2);
149
- expect(matches[0][1]).toBe("世界");
150
- expect(matches[1][1]).toBe("привет");
151
- });
152
- });
153
- describe("Expressions.lightning", () => {
154
- it("should match lightning invoices", () => {
155
- const invoice = "lnbc100n1p5pjxjk9qypqqqdqqxqrrsssp54ttukd5xxy2nmdzf864yjereuf9v3pyzl66hpqgxa0e8fvlzf6aspp5z6twjtwde82ec7wfcqw2m63v48r6fyw78753wxh7zjlvuru7tapsp6c9lq6m4d55u9jkxuqpepdknnzznfu05wl73swyn52z3pnkzyxrlkqf3t5jkw2hq7ukasuh5wgazvfwkkzrf0aqk4k0zluzu4rx8wqq8sut0l";
156
- const text = `Pay with ${invoice}`;
157
- const matches = Array.from(text.matchAll(Expressions.lightning));
158
- expect(matches).toHaveLength(1);
159
- expect(matches[0][1]).toBe(invoice);
160
- });
161
- it("should match lightning: prefixed invoices", () => {
162
- const invoice = "lnbc100n1p5pjxjk9qypqqqdqqxqrrsssp54ttukd5xxy2nmdzf864yjereuf9v3pyzl66hpqgxa0e8fvlzf6aspp5z6twjtwde82ec7wfcqw2m63v48r6fyw78753wxh7zjlvuru7tapsp6c9lq6m4d55u9jkxuqpepdknnzznfu05wl73swyn52z3pnkzyxrlkqf3t5jkw2hq7ukasuh5wgazvfwkkzrf0aqk4k0zluzu4rx8wqq8sut0l";
163
- const text = `Pay with lightning:${invoice}`;
164
- const matches = Array.from(text.matchAll(Expressions.lightning));
165
- expect(matches).toHaveLength(1);
166
- expect(matches[0][1]).toBe(invoice);
167
- });
168
- it("should be case insensitive", () => {
169
- const invoice = "lnbc100n1p5pjxjk9qypqqqdqqxqrrsssp54ttukd5xxy2nmdzf864yjereuf9v3pyzl66hpqgxa0e8fvlzf6aspp5z6twjtwde82ec7wfcqw2m63v48r6fyw78753wxh7zjlvuru7tapsp6c9lq6m4d55u9jkxuqpepdknnzznfu05wl73swyn52z3pnkzyxrlkqf3t5jkw2hq7ukasuh5wgazvfwkkzrf0aqk4k0zluzu4rx8wqq8sut0l".toUpperCase();
170
- const text = `Pay with ${invoice}`;
171
- const matches = Array.from(text.matchAll(Expressions.lightning));
172
- expect(matches).toHaveLength(1);
173
- expect(matches[0][1].toUpperCase()).toBe(invoice.toUpperCase());
174
- });
175
- });
176
- });
177
- describe("Token Regular Expressions", () => {
178
- describe("Tokens.link", () => {
179
- it("should match links surrounded by whitespace", () => {
180
- const text = "Check out https://example.com and https://test.org";
181
- const matches = Array.from(text.matchAll(Tokens.link));
182
- expect(matches).toHaveLength(2);
183
- expect(matches[0][0].trim()).toBe("https://example.com");
184
- expect(matches[1][0].trim()).toBe("https://test.org");
185
- });
186
- it("should match links at the beginning of text", () => {
187
- const text = "https://example.com is a website";
188
- const matches = Array.from(text.matchAll(Tokens.link));
189
- expect(matches).toHaveLength(1);
190
- expect(matches[0][0].trim()).toBe("https://example.com");
191
- });
192
- it("should match links at the end of text", () => {
193
- const text = "Visit https://example.com";
194
- const matches = Array.from(text.matchAll(Tokens.link));
195
- expect(matches).toHaveLength(1);
196
- expect(matches[0][0].trim()).toBe("https://example.com");
197
- });
198
- });
199
- describe("Tokens.cashu", () => {
200
- it("should match cashu tokens surrounded by whitespace", () => {
201
- const token = "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJpZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJzZWNyZXQiOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJDIjoiMDMwZGQwMTQxMGY2ZTIwZjk4YzRkZTlhMDhkOTM5ZDQyMjRkYjUxMzIzZWM1YWM2MThhMzA3NjRjNzJiMDFiMDg2In1dLCJtaW50IjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInZhbHVlIjoxfV19";
202
- const text = `Here is a token: ${token} end`;
203
- const matches = Array.from(text.matchAll(Tokens.cashu));
204
- expect(matches).toHaveLength(1);
205
- expect(matches[0][0].trim()).toBe(token);
206
- });
207
- });
208
- describe("Tokens.nostrLink", () => {
209
- it("should match nostr links surrounded by whitespace", () => {
210
- const npub = "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6";
211
- const text = `Check out ${npub} and more`;
212
- const matches = Array.from(text.matchAll(Tokens.nostrLink));
213
- expect(matches).toHaveLength(1);
214
- expect(matches[0][0].trim()).toBe(npub);
215
- });
216
- });
217
- describe("Tokens.emoji", () => {
218
- it("should match emoji shortcodes surrounded by whitespace", () => {
219
- const text = "Hello :smile: world :thumbsup: end";
220
- const matches = Array.from(text.matchAll(Tokens.emoji));
221
- expect(matches).toHaveLength(2);
222
- expect(matches[0][0].trim()).toBe(":smile:");
223
- expect(matches[1][0].trim()).toBe(":thumbsup:");
224
- });
225
- });
226
- describe("Tokens.hashtag", () => {
227
- it("should match hashtags surrounded by whitespace", () => {
228
- const text = "Hello #world and #nostr end";
229
- const matches = Array.from(text.matchAll(Tokens.hashtag));
230
- expect(matches).toHaveLength(2);
231
- expect(matches[0][0].trim()).toBe("#world");
232
- expect(matches[1][0].trim()).toBe("#nostr");
233
- });
234
- });
235
- describe("Tokens.lightning", () => {
236
- it("should match lightning invoices surrounded by whitespace", () => {
237
- const invoice = "LNBC1500N1PJQJJ5SPP5FSP3ZZAR0GZPJ9YHKQVSD7CQPGWG4XSULZP4TCG6VYVK63KM5USDQQCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KH0DUDVH0GWDWLWLVJRSZGQTW8QF99XWCPS9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6GQPVXHNRC";
238
- const text = `Pay with ${invoice} now`;
239
- const matches = Array.from(text.matchAll(Tokens.lightning));
240
- expect(matches).toHaveLength(1);
241
- expect(matches[0][0].trim()).toBe(invoice);
242
- });
243
- });
244
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,11 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import * as exports from "../index.js";
3
- describe("exports", () => {
4
- it("should export the expected functions", () => {
5
- expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
6
- [
7
- "remarkNostrMentions",
8
- ]
9
- `);
10
- });
11
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,13 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import * as exports from "../index.js";
3
- describe("exports", () => {
4
- it("should export the expected functions", () => {
5
- expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
6
- [
7
- "eolMetadata",
8
- "findAndReplace",
9
- "truncateContent",
10
- ]
11
- `);
12
- });
13
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,22 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import * as exports from "../index.js";
3
- describe("exports", () => {
4
- it("should export the expected functions", () => {
5
- expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
6
- [
7
- "TextNoteContentSymbol",
8
- "cashuTokens",
9
- "createTextNoteATS",
10
- "emojis",
11
- "galleries",
12
- "getParsedContent",
13
- "hashtags",
14
- "lightningInvoices",
15
- "links",
16
- "nostrMentions",
17
- "removeParsedTextContent",
18
- "textNoteTransformers",
19
- ]
20
- `);
21
- });
22
- });