@yaebal/rich 0.0.1 → 0.0.2
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.
- package/README.md +104 -30
- package/lib/blocks.d.ts +93 -32
- package/lib/blocks.d.ts.map +1 -1
- package/lib/blocks.js +227 -71
- package/lib/blocks.js.map +1 -1
- package/lib/blocks.test.d.ts +2 -0
- package/lib/blocks.test.d.ts.map +1 -0
- package/lib/blocks.test.js +142 -0
- package/lib/blocks.test.js.map +1 -0
- package/lib/document.d.ts +28 -0
- package/lib/document.d.ts.map +1 -0
- package/lib/document.js +46 -0
- package/lib/document.js.map +1 -0
- package/lib/draft.d.ts +46 -9
- package/lib/draft.d.ts.map +1 -1
- package/lib/draft.js +115 -19
- package/lib/draft.js.map +1 -1
- package/lib/draft.test.d.ts +2 -0
- package/lib/draft.test.d.ts.map +1 -0
- package/lib/draft.test.js +122 -0
- package/lib/draft.test.js.map +1 -0
- package/lib/escape.d.ts +8 -0
- package/lib/escape.d.ts.map +1 -1
- package/lib/escape.js +17 -0
- package/lib/escape.js.map +1 -1
- package/lib/escape.test.d.ts +2 -0
- package/lib/escape.test.d.ts.map +1 -0
- package/lib/escape.test.js +28 -0
- package/lib/escape.test.js.map +1 -0
- package/lib/guards.test.d.ts +2 -0
- package/lib/guards.test.d.ts.map +1 -0
- package/lib/guards.test.js +85 -0
- package/lib/guards.test.js.map +1 -0
- package/lib/index.d.ts +18 -9
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +17 -8
- package/lib/index.js.map +1 -1
- package/lib/index.test.js +73 -103
- package/lib/index.test.js.map +1 -1
- package/lib/inline.d.ts +48 -59
- package/lib/inline.d.ts.map +1 -1
- package/lib/inline.js +87 -82
- package/lib/inline.js.map +1 -1
- package/lib/inline.test.d.ts +2 -0
- package/lib/inline.test.d.ts.map +1 -0
- package/lib/inline.test.js +84 -0
- package/lib/inline.test.js.map +1 -0
- package/lib/node.d.ts +25 -0
- package/lib/node.d.ts.map +1 -0
- package/lib/node.js +21 -0
- package/lib/node.js.map +1 -0
- package/lib/plaintext.test.d.ts +2 -0
- package/lib/plaintext.test.d.ts.map +1 -0
- package/lib/plaintext.test.js +100 -0
- package/lib/plaintext.test.js.map +1 -0
- package/lib/render.d.ts +17 -0
- package/lib/render.d.ts.map +1 -0
- package/lib/render.js +30 -0
- package/lib/render.js.map +1 -0
- package/lib/send.d.ts +6 -3
- package/lib/send.d.ts.map +1 -1
- package/lib/send.js +12 -3
- package/lib/send.js.map +1 -1
- package/lib/template.d.ts +46 -0
- package/lib/template.d.ts.map +1 -0
- package/lib/template.js +82 -0
- package/lib/template.js.map +1 -0
- package/lib/template.test.d.ts +2 -0
- package/lib/template.test.d.ts.map +1 -0
- package/lib/template.test.js +78 -0
- package/lib/template.test.js.map +1 -0
- package/package.json +6 -5
- package/src/blocks.test.ts +233 -0
- package/src/blocks.ts +304 -86
- package/src/document.ts +51 -0
- package/src/draft.test.ts +167 -0
- package/src/draft.ts +148 -21
- package/src/escape.test.ts +38 -0
- package/src/escape.ts +20 -0
- package/src/guards.test.ts +138 -0
- package/src/index.test.ts +79 -140
- package/src/index.ts +26 -11
- package/src/inline.test.ts +125 -0
- package/src/inline.ts +131 -97
- package/src/node.ts +43 -0
- package/src/plaintext.test.ts +141 -0
- package/src/render.ts +54 -0
- package/src/send.ts +17 -10
- package/src/template.test.ts +100 -0
- package/src/template.ts +115 -0
- package/lib/message.d.ts +0 -26
- package/lib/message.d.ts.map +0 -1
- package/lib/message.js +0 -31
- package/lib/message.js.map +0 -1
- package/src/message.ts +0 -45
package/lib/send.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE5E,mHAAmH;AACnH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,gBAAgB,GAAG,MAAM,CAAC;AAQlE;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC9B,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,KAAK,EAAE,UAAU,EACjB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACjC,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,UAAU,EACjB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACjC,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,MAAM,WAAW,WAAW;IAC3B,wEAAwE;IACxE,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF,6EAA6E;IAC7E,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,gBAAgB,CAAC;CACvF;AAED,mFAAmF;AACnF,wBAAgB,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,CAwBnD"}
|
package/lib/send.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { RichDocument } from "./document.js";
|
|
1
2
|
import { RichMessageDraft } from "./draft.js";
|
|
2
3
|
function toInput(input) {
|
|
3
|
-
|
|
4
|
+
if (typeof input === "string")
|
|
5
|
+
return { html: input };
|
|
6
|
+
if (input instanceof RichDocument)
|
|
7
|
+
return input.toInputRichMessage();
|
|
8
|
+
return input;
|
|
4
9
|
}
|
|
5
10
|
/**
|
|
6
11
|
* `sendRichMessage` — standalone, no plugin install required (the `@yaebal/fmt` /
|
|
@@ -38,14 +43,18 @@ export function rich() {
|
|
|
38
43
|
if (chatId === undefined) {
|
|
39
44
|
return Promise.reject(new Error("sendRichMessage(): no chat in this update"));
|
|
40
45
|
}
|
|
41
|
-
return sendRichMessage(this.api, chatId, input, extra);
|
|
46
|
+
return sendRichMessage(this.api, chatId, input, { ...this.routing(), ...extra });
|
|
42
47
|
},
|
|
43
48
|
richMessageDraft(draftId, options) {
|
|
44
49
|
const chatId = this.chat?.id;
|
|
45
50
|
if (chatId === undefined) {
|
|
46
51
|
throw new Error("richMessageDraft(): no chat in this update");
|
|
47
52
|
}
|
|
48
|
-
return new RichMessageDraft(this.api, chatId, draftId,
|
|
53
|
+
return new RichMessageDraft(this.api, chatId, draftId, {
|
|
54
|
+
messageThreadId: this.messageThreadId,
|
|
55
|
+
businessConnectionId: this.businessConnectionId,
|
|
56
|
+
...options,
|
|
57
|
+
});
|
|
49
58
|
},
|
|
50
59
|
});
|
|
51
60
|
}
|
package/lib/send.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send.js","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAgC,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAgC,MAAM,YAAY,CAAC;AAK5E,SAAS,OAAO,CAAC,KAAiB;IACjC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACtD,IAAI,KAAK,YAAY,YAAY;QAAE,OAAO,KAAK,CAAC,kBAAkB,EAAE,CAAC;IACrE,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC9B,GAAQ,EACR,MAAuB,EACvB,KAAiB,EACjB,QAAiC,EAAE;IAEnC,OAAO,GAAG,CAAC,IAAI,CAAU,iBAAiB,EAAE;QAC3C,OAAO,EAAE,MAAM;QACf,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC;QAC5B,GAAG,KAAK;KACR,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CACnC,GAAQ,EACR,MAAc,EACd,OAAe,EACf,KAAiB,EACjB,QAAiC,EAAE;IAEnC,OAAO,GAAG,CAAC,IAAI,CAAU,sBAAsB,EAAE;QAChD,OAAO,EAAE,MAAM;QACf,QAAQ,EAAE,OAAO;QACjB,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC;QAC5B,GAAG,KAAK;KACR,CAAC,CAAC;AACJ,CAAC;AASD,mFAAmF;AACnF,MAAM,UAAU,IAAI;IACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CACnB,QAAQ,CAAC,QAAQ,CAAc;QAC9B,eAAe,CAAgB,KAAK,EAAE,KAAK,GAAG,EAAE;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC1B,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC,CAAC;YAC/E,CAAC;YAED,OAAO,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,gBAAgB,CAAgB,OAAO,EAAE,OAAO;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAC/D,CAAC;YAED,OAAO,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;gBACtD,eAAe,EAAE,IAAI,CAAC,eAAe;gBACrC,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;gBAC/C,GAAG,OAAO;aACV,CAAC,CAAC;QACJ,CAAC;KACD,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { RichDocument } from "./document.js";
|
|
2
|
+
import type { Dialect } from "./node.js";
|
|
3
|
+
import { type Insertable } from "./render.js";
|
|
4
|
+
export interface RichTemplate {
|
|
5
|
+
/** plain-function form: passed through as-is, no escaping/dedent (already-formatted content). */
|
|
6
|
+
(source: string): RichDocument;
|
|
7
|
+
/** block-array form: each entry rendered; blocks blank-line-joined in markdown. */
|
|
8
|
+
(blocks: readonly Insertable[]): RichDocument;
|
|
9
|
+
/** tagged-template form: dedented, `${}` interpolation escaped/rendered per-item. */
|
|
10
|
+
(strings: TemplateStringsArray, ...subs: Insertable[]): RichDocument;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* the html authoring entry point — a tagged template whose interpolations are
|
|
14
|
+
* auto-escaped (never re-parsed as markup); builder nodes render themselves,
|
|
15
|
+
* a nested `RichDocument` is spliced in raw (dialect-checked). also accepts a
|
|
16
|
+
* block array (`html([heading(1, t), table(rows)])`) or a plain pre-formatted
|
|
17
|
+
* string. returns a `RichDocument` — pass it straight to `sendRichMessage`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const html: RichTemplate;
|
|
20
|
+
/**
|
|
21
|
+
* the markdown authoring entry point — same three forms as `html`, emitting
|
|
22
|
+
* `InputRichMessage.markdown` instead. the *same* builders work under both tags:
|
|
23
|
+
* every `RichNode` renders itself into whichever dialect the template asks for.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* md`
|
|
27
|
+
* # ${title}
|
|
28
|
+
*
|
|
29
|
+
* ${bold("status:")} ${status}
|
|
30
|
+
* `
|
|
31
|
+
*/
|
|
32
|
+
export declare const md: RichTemplate;
|
|
33
|
+
export interface DocumentOptions {
|
|
34
|
+
/** target dialect. defaults to `"html"` (the fully documented one). */
|
|
35
|
+
dialect?: Dialect;
|
|
36
|
+
/** `InputRichMessage.is_rtl` — show the message right-to-left. */
|
|
37
|
+
rtl?: boolean;
|
|
38
|
+
/** `InputRichMessage.skip_entity_detection` — see `RichDocument.noEntityDetection`. */
|
|
39
|
+
skipEntityDetection?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* assemble top-level blocks into a `RichDocument` in one call — the options-object
|
|
43
|
+
* counterpart of `html([...])`/`md([...])` + the fluent flag setters.
|
|
44
|
+
*/
|
|
45
|
+
export declare function document(blocks: readonly Insertable[] | Insertable, options?: DocumentOptions): RichDocument;
|
|
46
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,KAAK,UAAU,EAAU,MAAM,aAAa,CAAC;AAsBtD,MAAM,WAAW,YAAY;IAC5B,iGAAiG;IACjG,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC;IAC/B,mFAAmF;IACnF,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,YAAY,CAAC;IAC9C,qFAAqF;IACrF,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,GAAG,YAAY,CAAC;CACrE;AAmCD;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,EAAE,YAAmC,CAAC;AAEvD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,EAAE,EAAE,YAAuC,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC/B,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,uFAAuF;IACvF,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACvB,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,EAC1C,OAAO,GAAE,eAAoB,GAC3B,YAAY,CASd"}
|
package/lib/template.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { RichDocument } from "./document.js";
|
|
2
|
+
import { render } from "./render.js";
|
|
3
|
+
// strip the common leading indentation shared by the literal skeleton, then trim blank edges.
|
|
4
|
+
function dedent(skeleton) {
|
|
5
|
+
const rawLines = skeleton.split("\n");
|
|
6
|
+
let min = Number.POSITIVE_INFINITY;
|
|
7
|
+
for (const line of rawLines) {
|
|
8
|
+
if (line.trim() === "")
|
|
9
|
+
continue;
|
|
10
|
+
const indent = /^[ \t]*/.exec(line)?.[0].length ?? 0;
|
|
11
|
+
min = Math.min(min, indent);
|
|
12
|
+
}
|
|
13
|
+
if (!Number.isFinite(min))
|
|
14
|
+
min = 0;
|
|
15
|
+
return rawLines
|
|
16
|
+
.map((line) => line.slice(min))
|
|
17
|
+
.join("\n")
|
|
18
|
+
.replace(/^\n+/, "")
|
|
19
|
+
.trimEnd();
|
|
20
|
+
}
|
|
21
|
+
// top-level blocks need a blank line between them in markdown; in html the tags
|
|
22
|
+
// carry the structure and separators are just cosmetic whitespace.
|
|
23
|
+
function joinBlocks(blocks, dialect) {
|
|
24
|
+
const separator = dialect === "markdown" ? "\n\n" : "";
|
|
25
|
+
return blocks.map((block) => render(block, dialect)).join(separator);
|
|
26
|
+
}
|
|
27
|
+
function makeTemplate(dialect) {
|
|
28
|
+
return ((first, ...subs) => {
|
|
29
|
+
if (typeof first === "string")
|
|
30
|
+
return new RichDocument(dialect, first);
|
|
31
|
+
// a plain array (no `.raw` template marker) composes top-level blocks.
|
|
32
|
+
if (!("raw" in first)) {
|
|
33
|
+
return new RichDocument(dialect, joinBlocks(first, dialect));
|
|
34
|
+
}
|
|
35
|
+
// dedent the literal skeleton only (\x00 marks interpolation seams so values aren't dedented).
|
|
36
|
+
const seam = String.fromCharCode(0);
|
|
37
|
+
const dedented = dedent(first.join(seam)).split(seam);
|
|
38
|
+
let out = "";
|
|
39
|
+
for (let i = 0; i < dedented.length; i++) {
|
|
40
|
+
out += dedented[i] ?? "";
|
|
41
|
+
if (i < subs.length)
|
|
42
|
+
out += render(subs[i], dialect);
|
|
43
|
+
}
|
|
44
|
+
return new RichDocument(dialect, out);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* the html authoring entry point — a tagged template whose interpolations are
|
|
49
|
+
* auto-escaped (never re-parsed as markup); builder nodes render themselves,
|
|
50
|
+
* a nested `RichDocument` is spliced in raw (dialect-checked). also accepts a
|
|
51
|
+
* block array (`html([heading(1, t), table(rows)])`) or a plain pre-formatted
|
|
52
|
+
* string. returns a `RichDocument` — pass it straight to `sendRichMessage`.
|
|
53
|
+
*/
|
|
54
|
+
export const html = makeTemplate("html");
|
|
55
|
+
/**
|
|
56
|
+
* the markdown authoring entry point — same three forms as `html`, emitting
|
|
57
|
+
* `InputRichMessage.markdown` instead. the *same* builders work under both tags:
|
|
58
|
+
* every `RichNode` renders itself into whichever dialect the template asks for.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* md`
|
|
62
|
+
* # ${title}
|
|
63
|
+
*
|
|
64
|
+
* ${bold("status:")} ${status}
|
|
65
|
+
* `
|
|
66
|
+
*/
|
|
67
|
+
export const md = makeTemplate("markdown");
|
|
68
|
+
/**
|
|
69
|
+
* assemble top-level blocks into a `RichDocument` in one call — the options-object
|
|
70
|
+
* counterpart of `html([...])`/`md([...])` + the fluent flag setters.
|
|
71
|
+
*/
|
|
72
|
+
export function document(blocks, options = {}) {
|
|
73
|
+
const dialect = options.dialect ?? "html";
|
|
74
|
+
const entries = Array.isArray(blocks) ? blocks : [blocks];
|
|
75
|
+
const doc = new RichDocument(dialect, joinBlocks(entries, dialect));
|
|
76
|
+
if (options.rtl !== undefined)
|
|
77
|
+
doc.rtl(options.rtl);
|
|
78
|
+
if (options.skipEntityDetection !== undefined)
|
|
79
|
+
doc.noEntityDetection(options.skipEntityDetection);
|
|
80
|
+
return doc;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.test.d.ts","sourceRoot":"","sources":["../src/template.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { heading, list, paragraph } from "./blocks.js";
|
|
4
|
+
import { RichDocument } from "./document.js";
|
|
5
|
+
import { bold } from "./inline.js";
|
|
6
|
+
import { RichError } from "./node.js";
|
|
7
|
+
import { document, html, md } from "./template.js";
|
|
8
|
+
test("tagged form escapes interpolated text per dialect and keeps the literal skeleton raw", () => {
|
|
9
|
+
const user = "<script>alert(1)</script>";
|
|
10
|
+
assert.equal(html `<p>${user}</p>`.content, "<p><script>alert(1)</script></p>");
|
|
11
|
+
assert.equal(md `# ${"a*b"}`.content, "# a\\*b");
|
|
12
|
+
});
|
|
13
|
+
test("the same builder node renders into whichever dialect the template asks for", () => {
|
|
14
|
+
const status = bold("ok");
|
|
15
|
+
assert.equal(html `state: ${status}`.content, "state: <b>ok</b>");
|
|
16
|
+
assert.equal(md `state: ${status}`.content, "state: **ok**");
|
|
17
|
+
});
|
|
18
|
+
test("tagged form dedents the literal skeleton but not interpolated values", () => {
|
|
19
|
+
const doc = md `
|
|
20
|
+
# title
|
|
21
|
+
|
|
22
|
+
${" indented value"}
|
|
23
|
+
`;
|
|
24
|
+
assert.equal(doc.content, "# title\n\n indented value");
|
|
25
|
+
});
|
|
26
|
+
test("array and falsy interpolations — arrays concatenate, null/undefined/false vanish", () => {
|
|
27
|
+
const cond = false;
|
|
28
|
+
assert.equal(html `${["a", bold("b")]}${cond && "no"}${null}`.content, "a<b>b</b>");
|
|
29
|
+
});
|
|
30
|
+
test("a RichDocument interpolates raw when the dialect matches, throws RichError when it doesn't", () => {
|
|
31
|
+
const inner = md `**pre-rendered**`;
|
|
32
|
+
assert.equal(md `${inner}!`.content, "**pre-rendered**!");
|
|
33
|
+
assert.throws(() => html `${inner}`, RichError);
|
|
34
|
+
});
|
|
35
|
+
test("plain-function form passes a pre-formatted string through untouched", () => {
|
|
36
|
+
assert.equal(html("<p>as-is</p>").content, "<p>as-is</p>");
|
|
37
|
+
assert.equal(md("# as-is").content, "# as-is");
|
|
38
|
+
});
|
|
39
|
+
test("block-array form joins blocks — blank lines in markdown, adjacent tags in html", () => {
|
|
40
|
+
const blocks = [heading(1, "t"), paragraph("p"), list(["a"])];
|
|
41
|
+
assert.equal(html(blocks).content, "<h1>t</h1><p>p</p><ul><li>a</li></ul>");
|
|
42
|
+
assert.equal(md(blocks).content, "# t\n\np\n\n- a");
|
|
43
|
+
});
|
|
44
|
+
test("templates return a RichDocument that serializes to the right InputRichMessage shape", () => {
|
|
45
|
+
assert.deepEqual(html `x`.toInputRichMessage(), { html: "x" });
|
|
46
|
+
assert.deepEqual(md `x`.toInputRichMessage(), { markdown: "x" });
|
|
47
|
+
});
|
|
48
|
+
test("RichDocument fluent flags land in the payload, and toJSON() mirrors toInputRichMessage()", () => {
|
|
49
|
+
const doc = html `x`.rtl().noEntityDetection();
|
|
50
|
+
assert.deepEqual(doc.toInputRichMessage(), {
|
|
51
|
+
html: "x",
|
|
52
|
+
is_rtl: true,
|
|
53
|
+
skip_entity_detection: true,
|
|
54
|
+
});
|
|
55
|
+
assert.deepEqual(JSON.parse(JSON.stringify({ rich_message: doc })), {
|
|
56
|
+
rich_message: { html: "x", is_rtl: true, skip_entity_detection: true },
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
test("RichDocument flags are settable to false explicitly", () => {
|
|
60
|
+
assert.deepEqual(html `x`.rtl(false).toInputRichMessage(), { html: "x", is_rtl: false });
|
|
61
|
+
});
|
|
62
|
+
test("document() assembles blocks with options — html by default, markdown on request", () => {
|
|
63
|
+
assert.deepEqual(document([paragraph("hi")]).toInputRichMessage(), { html: "<p>hi</p>" });
|
|
64
|
+
assert.deepEqual(document([paragraph("hi")], { dialect: "markdown", rtl: true }).toInputRichMessage(), {
|
|
65
|
+
markdown: "hi",
|
|
66
|
+
is_rtl: true,
|
|
67
|
+
});
|
|
68
|
+
assert.deepEqual(document(paragraph("hi"), { skipEntityDetection: true }).toInputRichMessage(), {
|
|
69
|
+
html: "<p>hi</p>",
|
|
70
|
+
skip_entity_detection: true,
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
test("document() result is a RichDocument, so the fluent setters chain onto it", () => {
|
|
74
|
+
const doc = document([paragraph("hi")]);
|
|
75
|
+
assert.ok(doc instanceof RichDocument);
|
|
76
|
+
assert.deepEqual(doc.rtl().toInputRichMessage(), { html: "<p>hi</p>", is_rtl: true });
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=template.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.test.js","sourceRoot":"","sources":["../src/template.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaebal/rich",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "yaebal rich — sendRichMessage / sendRichMessageDraft: a typed block builder, a 30s-ttl draft/streaming session, and full read-side coverage of telegram's rich message model.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
"src"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@yaebal/
|
|
20
|
-
"@yaebal/
|
|
19
|
+
"@yaebal/types": "10.1.0",
|
|
20
|
+
"@yaebal/core": "0.0.8"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@types/node": "latest"
|
|
23
|
+
"@types/node": "latest",
|
|
24
|
+
"@yaebal/test": "0.2.0"
|
|
24
25
|
},
|
|
25
26
|
"engines": {
|
|
26
27
|
"node": ">=20"
|
|
@@ -46,6 +47,6 @@
|
|
|
46
47
|
"scripts": {
|
|
47
48
|
"build": "tsc -p tsconfig.json",
|
|
48
49
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
49
|
-
"test": "node --test lib"
|
|
50
|
+
"test": "node --test lib/*.test.js"
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import {
|
|
4
|
+
anchorBlock,
|
|
5
|
+
audio,
|
|
6
|
+
blockquote,
|
|
7
|
+
cell,
|
|
8
|
+
collage,
|
|
9
|
+
details,
|
|
10
|
+
divider,
|
|
11
|
+
footer,
|
|
12
|
+
h2,
|
|
13
|
+
heading,
|
|
14
|
+
image,
|
|
15
|
+
item,
|
|
16
|
+
join,
|
|
17
|
+
list,
|
|
18
|
+
map,
|
|
19
|
+
mathBlock,
|
|
20
|
+
paragraph,
|
|
21
|
+
preformatted,
|
|
22
|
+
pullquote,
|
|
23
|
+
slideshow,
|
|
24
|
+
table,
|
|
25
|
+
thinking,
|
|
26
|
+
video,
|
|
27
|
+
} from "./blocks.js";
|
|
28
|
+
import { bold, italic } from "./inline.js";
|
|
29
|
+
import type { RichNode } from "./node.js";
|
|
30
|
+
|
|
31
|
+
function both(node: RichNode): [html: string, md: string] {
|
|
32
|
+
return [node.render("html"), node.render("markdown")];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test("paragraph() — <p> in html, bare text in markdown", () => {
|
|
36
|
+
assert.deepEqual(both(paragraph("hi ", bold("there"))), [
|
|
37
|
+
"<p>hi <b>there</b></p>",
|
|
38
|
+
"hi **there**",
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("heading()/h1–h6 — <hN> / #-prefixed", () => {
|
|
43
|
+
assert.deepEqual(both(heading(3, "title")), ["<h3>title</h3>", "### title"]);
|
|
44
|
+
assert.deepEqual(both(h2("t")), ["<h2>t</h2>", "## t"]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("preformatted() — <pre><code> with language class / fenced block, code kept raw in the fence", () => {
|
|
48
|
+
assert.deepEqual(both(preformatted("if (a < b) {}", "ts")), [
|
|
49
|
+
'<pre><code class="language-ts">if (a < b) {}</code></pre>',
|
|
50
|
+
"```ts\nif (a < b) {}\n```",
|
|
51
|
+
]);
|
|
52
|
+
assert.equal(preformatted("x").render("html"), "<pre><code>x</code></pre>");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("divider()/footer()/mathBlock()/anchorBlock()", () => {
|
|
56
|
+
assert.deepEqual(both(divider()), ["<hr/>", "---"]);
|
|
57
|
+
assert.deepEqual(both(footer("fin")), ["<footer>fin</footer>", "<footer>fin</footer>"]);
|
|
58
|
+
assert.deepEqual(both(mathBlock("E=mc^2")), [
|
|
59
|
+
"<tg-math-block>E=mc^2</tg-math-block>",
|
|
60
|
+
"$$E=mc^2$$",
|
|
61
|
+
]);
|
|
62
|
+
assert.deepEqual(both(anchorBlock("s1")), ['<a name="s1"></a>', '<a name="s1"></a>']);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("blockquote() — <blockquote> with <cite> / >-prefixed lines with a credit line", () => {
|
|
66
|
+
assert.deepEqual(both(blockquote([paragraph("a"), paragraph("b")], "someone")), [
|
|
67
|
+
"<blockquote><p>a</p><p>b</p><cite>someone</cite></blockquote>",
|
|
68
|
+
">a\n>b\n> — someone",
|
|
69
|
+
]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("blockquote() in markdown >-prefixes every line of multi-line content", () => {
|
|
73
|
+
assert.equal(
|
|
74
|
+
blockquote([join([paragraph("a"), paragraph("b")])]).render("markdown"),
|
|
75
|
+
">a\n>\n>b",
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("pullquote() — <aside> with optional <cite> in both dialects", () => {
|
|
80
|
+
assert.deepEqual(both(pullquote("wow", "them")), [
|
|
81
|
+
"<aside>wow<cite>them</cite></aside>",
|
|
82
|
+
"<aside>wow<cite>them</cite></aside>",
|
|
83
|
+
]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("table() — full html table; gfm with a structural header row in markdown", () => {
|
|
87
|
+
const node = table(
|
|
88
|
+
[
|
|
89
|
+
[cell("day", { header: true }), cell("count", { header: true, align: "right" })],
|
|
90
|
+
[cell("mon"), cell(128, { align: "right" })],
|
|
91
|
+
],
|
|
92
|
+
{ bordered: true, caption: "week" },
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
assert.equal(
|
|
96
|
+
node.render("html"),
|
|
97
|
+
"<table border><caption>week</caption>" +
|
|
98
|
+
'<tr><th>day</th><th align="right">count</th></tr>' +
|
|
99
|
+
'<tr><td>mon</td><td align="right">128</td></tr></table>',
|
|
100
|
+
);
|
|
101
|
+
assert.equal(node.render("markdown"), "week\n\n| day | count |\n| :-- | --: |\n| mon | 128 |");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("table() wraps bare values in a plain cell()", () => {
|
|
105
|
+
assert.deepEqual(both(table([["a", bold("b")]])), [
|
|
106
|
+
"<table><tr><td>a</td><td><b>b</b></td></tr></table>",
|
|
107
|
+
"| a | **b** |\n| :-- | :-- |",
|
|
108
|
+
]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("cell() supports colspan/rowspan/valign and invisible cells in html; markdown keeps the text", () => {
|
|
112
|
+
const spanned = cell("x", { colspan: 2, rowspan: 3, valign: "middle" });
|
|
113
|
+
|
|
114
|
+
assert.equal(spanned.render("html"), '<td colspan="2" rowspan="3" valign="middle">x</td>');
|
|
115
|
+
assert.equal(spanned.render("markdown"), "x");
|
|
116
|
+
assert.equal(cell().render("html"), "<td></td>");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("cell content is escaped, so a `|` cannot break the gfm row", () => {
|
|
120
|
+
assert.equal(table([[cell("a|b")]]).render("markdown"), "| a\\|b |\n| :-- |");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("list() wraps bare values in <li> automatically", () => {
|
|
124
|
+
assert.deepEqual(both(list(["a", bold("b")])), [
|
|
125
|
+
"<ul><li>a</li><li><b>b</b></li></ul>",
|
|
126
|
+
"- a\n- **b**",
|
|
127
|
+
]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("list({ ordered, start }) numbers items; item value restarts the numbering", () => {
|
|
131
|
+
const node = list(["a", "b", item(["c"], { value: 10 }), "d"], { ordered: true, start: 3 });
|
|
132
|
+
|
|
133
|
+
assert.equal(
|
|
134
|
+
node.render("html"),
|
|
135
|
+
'<ol start="3"><li>a</li><li>b</li><li value="10">c</li><li>d</li></ol>',
|
|
136
|
+
);
|
|
137
|
+
assert.equal(node.render("markdown"), "3. a\n4. b\n10. c\n11. d");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("item() checkbox — <input type=checkbox> / gfm task marker", () => {
|
|
141
|
+
const node = list([
|
|
142
|
+
item(["done"], { checkbox: true, checked: true }),
|
|
143
|
+
item(["todo"], { checkbox: true }),
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
assert.equal(
|
|
147
|
+
node.render("html"),
|
|
148
|
+
'<ul><li><input type="checkbox" checked/> done</li><li><input type="checkbox"/> todo</li></ul>',
|
|
149
|
+
);
|
|
150
|
+
assert.equal(node.render("markdown"), "- [x] done\n- [ ] todo");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("item() type override is html-only", () => {
|
|
154
|
+
assert.equal(
|
|
155
|
+
list([item(["x"], { type: "i" })], { ordered: true }).render("html"),
|
|
156
|
+
'<ol><li type="i">x</li></ol>',
|
|
157
|
+
);
|
|
158
|
+
assert.equal(list([item(["x"], { type: "i" })], { ordered: true }).render("markdown"), "1. x");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("details() — html compact; markdown blank-line-separates the body", () => {
|
|
162
|
+
const node = details("more", [paragraph("hidden")], { open: true });
|
|
163
|
+
|
|
164
|
+
assert.equal(node.render("html"), "<details open><summary>more</summary><p>hidden</p></details>");
|
|
165
|
+
assert.equal(
|
|
166
|
+
node.render("markdown"),
|
|
167
|
+
"<details open><summary>more</summary>\n\nhidden\n\n</details>",
|
|
168
|
+
);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("media builders — <img>/<video>/<audio> with figure+caption / markdown image with title", () => {
|
|
172
|
+
assert.deepEqual(both(image("https://x.dev/a.png")), [
|
|
173
|
+
'<img src="https://x.dev/a.png"></img>',
|
|
174
|
+
"",
|
|
175
|
+
]);
|
|
176
|
+
assert.deepEqual(both(video("https://x.dev/a.mp4", { caption: "clip", credit: "me" })), [
|
|
177
|
+
'<figure><video src="https://x.dev/a.mp4"></video><figcaption>clip<cite>me</cite></figcaption></figure>',
|
|
178
|
+
'',
|
|
179
|
+
]);
|
|
180
|
+
assert.equal(
|
|
181
|
+
audio("https://x.dev/a.mp3", { spoiler: true }).render("html"),
|
|
182
|
+
'<audio src="https://x.dev/a.mp3" data-media-spoiler></audio>',
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("collage()/slideshow() — compact html; blank-line-separated media in markdown", () => {
|
|
187
|
+
const node = collage([image("https://x.dev/1.png"), image("https://x.dev/2.png")], {
|
|
188
|
+
caption: "pair",
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
assert.equal(
|
|
192
|
+
node.render("html"),
|
|
193
|
+
'<tg-collage><img src="https://x.dev/1.png"></img><img src="https://x.dev/2.png"></img>' +
|
|
194
|
+
"<figcaption>pair</figcaption></tg-collage>",
|
|
195
|
+
);
|
|
196
|
+
assert.equal(
|
|
197
|
+
node.render("markdown"),
|
|
198
|
+
"<tg-collage>\n\n\n\n\n\n" +
|
|
199
|
+
"<figcaption>pair</figcaption></tg-collage>",
|
|
200
|
+
);
|
|
201
|
+
assert.equal(
|
|
202
|
+
slideshow([image("https://x.dev/1.png")]).render("html"),
|
|
203
|
+
'<tg-slideshow><img src="https://x.dev/1.png"></img></tg-slideshow>',
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("map() — <tg-map> with telegram's field names as attributes, in both dialects", () => {
|
|
208
|
+
const node = map(
|
|
209
|
+
{ latitude: 55.75, longitude: 37.61 },
|
|
210
|
+
{ zoom: 14, width: 400, height: 300 },
|
|
211
|
+
{ caption: "moscow" },
|
|
212
|
+
);
|
|
213
|
+
const expected =
|
|
214
|
+
'<tg-map latitude="55.75" longitude="37.61" zoom="14" width="400" height="300">' +
|
|
215
|
+
"<figcaption>moscow</figcaption></tg-map>";
|
|
216
|
+
|
|
217
|
+
assert.deepEqual(both(node), [expected, expected]);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("thinking() — draft-only <tg-thinking> in both dialects", () => {
|
|
221
|
+
assert.deepEqual(both(thinking("hmm ", italic("…"))), [
|
|
222
|
+
"<tg-thinking>hmm <i>…</i></tg-thinking>",
|
|
223
|
+
"<tg-thinking>hmm *…*</tg-thinking>",
|
|
224
|
+
]);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("join() — inline entries join with the separator, block entries blank-line-join in markdown", () => {
|
|
228
|
+
assert.equal(join(["a", "b"], " · ").render("html"), "a · b");
|
|
229
|
+
assert.equal(join([paragraph("a"), paragraph("b")]).render("markdown"), "a\n\nb");
|
|
230
|
+
assert.equal(join([paragraph("a"), paragraph("b")]).render("html"), "<p>a</p><p>b</p>");
|
|
231
|
+
assert.equal(join([paragraph("a"), "b"]).level, "block");
|
|
232
|
+
assert.equal(join(["a", "b"]).level, "inline");
|
|
233
|
+
});
|