@gangtiser/md2wechat 0.1.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.
Files changed (55) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +105 -0
  3. package/dist/src/cli/index.d.ts +2 -0
  4. package/dist/src/cli/index.js +251 -0
  5. package/dist/src/cli/index.js.map +1 -0
  6. package/dist/src/config/config.d.ts +9 -0
  7. package/dist/src/config/config.js +11 -0
  8. package/dist/src/config/config.js.map +1 -0
  9. package/dist/src/core/article.d.ts +20 -0
  10. package/dist/src/core/article.js +81 -0
  11. package/dist/src/core/article.js.map +1 -0
  12. package/dist/src/core/envelope.d.ts +14 -0
  13. package/dist/src/core/envelope.js +34 -0
  14. package/dist/src/core/envelope.js.map +1 -0
  15. package/dist/src/core/errors.d.ts +7 -0
  16. package/dist/src/core/errors.js +22 -0
  17. package/dist/src/core/errors.js.map +1 -0
  18. package/dist/src/core/html.d.ts +2 -0
  19. package/dist/src/core/html.js +18 -0
  20. package/dist/src/core/html.js.map +1 -0
  21. package/dist/src/core/io.d.ts +11 -0
  22. package/dist/src/core/io.js +28 -0
  23. package/dist/src/core/io.js.map +1 -0
  24. package/dist/src/images/generate.d.ts +17 -0
  25. package/dist/src/images/generate.js +28 -0
  26. package/dist/src/images/generate.js.map +1 -0
  27. package/dist/src/index.d.ts +1 -0
  28. package/dist/src/index.js +2 -0
  29. package/dist/src/index.js.map +1 -0
  30. package/dist/src/inspect/inspect.d.ts +18 -0
  31. package/dist/src/inspect/inspect.js +31 -0
  32. package/dist/src/inspect/inspect.js.map +1 -0
  33. package/dist/src/mcp/server.d.ts +1 -0
  34. package/dist/src/mcp/server.js +25 -0
  35. package/dist/src/mcp/server.js.map +1 -0
  36. package/dist/src/mcp/tools.d.ts +19 -0
  37. package/dist/src/mcp/tools.js +190 -0
  38. package/dist/src/mcp/tools.js.map +1 -0
  39. package/dist/src/openai/client.d.ts +34 -0
  40. package/dist/src/openai/client.js +101 -0
  41. package/dist/src/openai/client.js.map +1 -0
  42. package/dist/src/preview/preview.d.ts +2 -0
  43. package/dist/src/preview/preview.js +21 -0
  44. package/dist/src/preview/preview.js.map +1 -0
  45. package/dist/src/renderer/render.d.ts +19 -0
  46. package/dist/src/renderer/render.js +36 -0
  47. package/dist/src/renderer/render.js.map +1 -0
  48. package/dist/src/themes/builtin.d.ts +2 -0
  49. package/dist/src/themes/builtin.js +21 -0
  50. package/dist/src/themes/builtin.js.map +1 -0
  51. package/dist/src/themes/registry.d.ts +18 -0
  52. package/dist/src/themes/registry.js +130 -0
  53. package/dist/src/themes/registry.js.map +1 -0
  54. package/package.json +55 -0
  55. package/skills/md2wechat/SKILL.md +52 -0
package/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ Copyright (c) 2026 Gangtiser. All rights reserved.
2
+
3
+ This software is provided for use by Gangtiser customers and authorized users.
4
+ Redistribution, sublicensing, or resale is not permitted without prior written
5
+ permission from Gangtiser.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # md2wechat
2
+
3
+ `md2wechat` is a TypeScript CLI, Codex Skill, and MCP server for turning Markdown into WeChat Official Account HTML. It runs locally, uses OpenAI for rendering and image generation, and includes a local theme registry.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @gangtiser/md2wechat
9
+ ```
10
+
11
+ Node.js 20 or newer is required. Set your OpenAI key before conversion or image generation:
12
+
13
+ ```bash
14
+ export OPENAI_API_KEY="your_openai_key"
15
+ ```
16
+
17
+ ## Defaults
18
+
19
+ - Text rendering model: `gpt-5.5`
20
+ - Image generation model: `gpt-image-2`
21
+ - Runtime: Node.js 20+
22
+ - No hosted md2wechat conversion service is used.
23
+
24
+ ## CLI Usage
25
+
26
+ ```bash
27
+ md2wechat inspect article.md --json
28
+ md2wechat themes list --json
29
+ md2wechat convert article.md --theme default --output article.html --json
30
+ md2wechat preview article.md --theme default --output preview.html --json
31
+ md2wechat generate-image "Editorial cover for an AI article" --output cover.png --json
32
+ ```
33
+
34
+ Theme management:
35
+
36
+ ```bash
37
+ md2wechat themes register custom ./themes/custom.json --json
38
+ md2wechat themes remove custom --json
39
+ ```
40
+
41
+ ## MCP Usage
42
+
43
+ Start the stdio MCP server:
44
+
45
+ ```bash
46
+ md2wechat mcp
47
+ ```
48
+
49
+ Example MCP client configuration:
50
+
51
+ ```json
52
+ {
53
+ "mcpServers": {
54
+ "md2wechat": {
55
+ "command": "md2wechat",
56
+ "args": ["mcp"],
57
+ "env": {
58
+ "OPENAI_API_KEY": "your_openai_key"
59
+ }
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ Available tools:
66
+
67
+ - `convert_article`
68
+ - `preview_article`
69
+ - `generate_image`
70
+ - `list_themes`
71
+ - `register_theme`
72
+ - `remove_theme`
73
+
74
+ ## Configuration
75
+
76
+ - `OPENAI_API_KEY`: required for conversion and image generation
77
+ - `OPENAI_BASE_URL`: optional OpenAI-compatible base URL
78
+ - `OPENAI_TEXT_MODEL`: optional override, defaults to `gpt-5.5`
79
+ - `OPENAI_IMAGE_MODEL`: optional override, defaults to `gpt-image-2`
80
+ - `MD2WECHAT_THEME_REGISTRY`: optional custom theme registry file
81
+ - `MD2WECHAT_THEMES_DIR`: optional custom theme directory
82
+
83
+ ## Theme File Format
84
+
85
+ Custom themes are JSON files:
86
+
87
+ ```json
88
+ {
89
+ "id": "custom",
90
+ "name": "Custom",
91
+ "description": "Optional description",
92
+ "prompt": "Style guidance for the renderer."
93
+ }
94
+ ```
95
+
96
+ ## Development
97
+
98
+ ```bash
99
+ npm install
100
+ npm run typecheck
101
+ npm test
102
+ npm run verify
103
+ ```
104
+
105
+ Generated build output lives in `dist/` and is packaged for npm, but not committed to git.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { parseArticle } from "../core/article.js";
5
+ import { successEnvelope, failureEnvelope } from "../core/envelope.js";
6
+ import { AppError, toAppError } from "../core/errors.js";
7
+ import { loadInput } from "../core/io.js";
8
+ import { loadConfig } from "../config/config.js";
9
+ import { generateImage } from "../images/generate.js";
10
+ import { createOpenAIClient } from "../openai/client.js";
11
+ import { buildPreviewDocument } from "../preview/preview.js";
12
+ import { renderArticle } from "../renderer/render.js";
13
+ import { inspectArticle } from "../inspect/inspect.js";
14
+ import { listThemes, registerTheme, removeTheme, resolveTheme } from "../themes/registry.js";
15
+ async function main(argv = process.argv.slice(2)) {
16
+ const args = parseArgs(argv);
17
+ try {
18
+ rejectModeFlag(args);
19
+ await route(args);
20
+ }
21
+ catch (error) {
22
+ const appError = toAppError(error);
23
+ if (args.flags.has("json")) {
24
+ printJson(failureEnvelope(appError.code, appError.message, appError.retryable, appError.message));
25
+ }
26
+ else {
27
+ console.error(appError.message);
28
+ }
29
+ process.exitCode = 1;
30
+ }
31
+ }
32
+ function parseArgs(argv) {
33
+ const command = [];
34
+ const values = [];
35
+ const flags = new Map();
36
+ for (let index = 0; index < argv.length; index += 1) {
37
+ const item = argv[index];
38
+ if (item.startsWith("--")) {
39
+ const name = item.slice(2);
40
+ const next = argv[index + 1];
41
+ if (next && !next.startsWith("--")) {
42
+ flags.set(name, next);
43
+ index += 1;
44
+ }
45
+ else {
46
+ flags.set(name, true);
47
+ }
48
+ continue;
49
+ }
50
+ if (command.length === 0 || (command[0] === "themes" && command.length === 1)) {
51
+ command.push(item);
52
+ }
53
+ else {
54
+ values.push(item);
55
+ }
56
+ }
57
+ return { command, values, flags };
58
+ }
59
+ function rejectModeFlag(args) {
60
+ if (args.flags.has("mode")) {
61
+ throw new AppError("MODE_NOT_SUPPORTED", "mode option is not supported");
62
+ }
63
+ }
64
+ async function route(args) {
65
+ const [primary, secondary] = args.command;
66
+ if (primary === "inspect") {
67
+ await inspectCommand(args);
68
+ return;
69
+ }
70
+ if (primary === "convert") {
71
+ await convertCommand(args);
72
+ return;
73
+ }
74
+ if (primary === "preview") {
75
+ await previewCommand(args);
76
+ return;
77
+ }
78
+ if (primary === "generate-image") {
79
+ await generateImageCommand(args);
80
+ return;
81
+ }
82
+ if (primary === "themes" && secondary === "list") {
83
+ await themesListCommand(args);
84
+ return;
85
+ }
86
+ if (primary === "themes" && secondary === "register") {
87
+ await themesRegisterCommand(args);
88
+ return;
89
+ }
90
+ if (primary === "themes" && secondary === "remove") {
91
+ await themesRemoveCommand(args);
92
+ return;
93
+ }
94
+ if (primary === "mcp") {
95
+ const { startMcpServer } = await import("../mcp/server.js");
96
+ await startMcpServer();
97
+ return;
98
+ }
99
+ throw new AppError("COMMAND_INVALID", `unknown command: ${args.command.join(" ") || "(empty)"}`);
100
+ }
101
+ async function inspectCommand(args) {
102
+ const file = requiredValue(args, 0, "markdown file is required");
103
+ const input = await loadInput({ file });
104
+ const result = inspectArticle(input.content, { baseDir: input.baseDir });
105
+ output(args, "INSPECT_COMPLETED", "Inspection completed", result, formatInspect(result));
106
+ }
107
+ async function convertCommand(args) {
108
+ const file = requiredValue(args, 0, "markdown file is required");
109
+ const outputPath = stringFlag(args, "output") || stringFlag(args, "o");
110
+ const themeId = stringFlag(args, "theme") || "default";
111
+ const config = loadConfig();
112
+ const apiKey = requireOpenAIKey(config.openaiApiKey);
113
+ const input = await loadInput({ file });
114
+ const article = parseArticle(input.content);
115
+ const theme = await resolveTheme(themeId, themeOptions(config));
116
+ const client = createOpenAIClient({
117
+ apiKey,
118
+ baseUrl: config.openaiBaseUrl,
119
+ textModel: config.openaiTextModel,
120
+ imageModel: config.openaiImageModel
121
+ });
122
+ const rendered = await renderArticle({
123
+ markdown: article.body,
124
+ metadata: article.metadata,
125
+ theme,
126
+ client
127
+ });
128
+ if (outputPath) {
129
+ await writeOutputFile(outputPath, rendered.html);
130
+ }
131
+ output(args, "CONVERT_COMPLETED", "Conversion completed", {
132
+ ...rendered,
133
+ metadata: article.metadata,
134
+ output_file: outputPath ? path.resolve(outputPath) : undefined
135
+ }, outputPath ? `HTML written to ${outputPath}` : rendered.html);
136
+ }
137
+ async function previewCommand(args) {
138
+ const file = requiredValue(args, 0, "markdown file is required");
139
+ const outputPath = stringFlag(args, "output") || stringFlag(args, "o") || "preview.html";
140
+ const themeId = stringFlag(args, "theme") || "default";
141
+ const config = loadConfig();
142
+ const apiKey = requireOpenAIKey(config.openaiApiKey);
143
+ const input = await loadInput({ file });
144
+ const article = parseArticle(input.content);
145
+ const theme = await resolveTheme(themeId, themeOptions(config));
146
+ const client = createOpenAIClient({
147
+ apiKey,
148
+ baseUrl: config.openaiBaseUrl,
149
+ textModel: config.openaiTextModel,
150
+ imageModel: config.openaiImageModel
151
+ });
152
+ const rendered = await renderArticle({
153
+ markdown: article.body,
154
+ metadata: article.metadata,
155
+ theme,
156
+ client
157
+ });
158
+ const preview = buildPreviewDocument(rendered.html, article.metadata);
159
+ await writeOutputFile(outputPath, preview);
160
+ output(args, "PREVIEW_READY", "Preview ready", {
161
+ output_file: path.resolve(outputPath),
162
+ metadata: article.metadata,
163
+ theme_id: theme.id
164
+ }, `Preview written to ${outputPath}`);
165
+ }
166
+ async function generateImageCommand(args) {
167
+ const config = loadConfig();
168
+ const apiKey = requireOpenAIKey(config.openaiApiKey);
169
+ const prompt = args.values[0] || "WeChat article cover image";
170
+ const articleFile = stringFlag(args, "article");
171
+ const articleContext = articleFile ? (await loadInput({ file: articleFile })).content : undefined;
172
+ const outputPath = stringFlag(args, "output") || stringFlag(args, "o") || "image.png";
173
+ const client = createOpenAIClient({
174
+ apiKey,
175
+ baseUrl: config.openaiBaseUrl,
176
+ textModel: config.openaiTextModel,
177
+ imageModel: config.openaiImageModel
178
+ });
179
+ const image = await generateImage({
180
+ prompt,
181
+ articleContext,
182
+ output: outputPath,
183
+ size: stringFlag(args, "size"),
184
+ quality: stringFlag(args, "quality"),
185
+ client
186
+ });
187
+ output(args, "IMAGE_GENERATED", "Image generated", image, `Image written to ${image.outputPath}`);
188
+ }
189
+ async function themesListCommand(args) {
190
+ const themes = await listThemes(themeOptions(loadConfig()));
191
+ output(args, "THEMES_LISTED", "Themes listed", { themes }, themes.map((theme) => `${theme.id}\t${theme.name}`).join("\n"));
192
+ }
193
+ async function themesRegisterCommand(args) {
194
+ const name = requiredValue(args, 0, "theme name is required");
195
+ const themePath = requiredValue(args, 1, "theme path is required");
196
+ const theme = await registerTheme(name, themePath, themeOptions(loadConfig()));
197
+ output(args, "THEME_REGISTERED", "Theme registered", { theme }, `Registered theme ${theme.id}`);
198
+ }
199
+ async function themesRemoveCommand(args) {
200
+ const name = requiredValue(args, 0, "theme name is required");
201
+ await removeTheme(name, themeOptions(loadConfig()));
202
+ output(args, "THEME_REMOVED", "Theme removed", { id: name }, `Removed theme ${name}`);
203
+ }
204
+ function output(args, code, message, data, text) {
205
+ if (args.flags.has("json")) {
206
+ printJson(successEnvelope(code, message, data));
207
+ }
208
+ else {
209
+ console.log(text);
210
+ }
211
+ }
212
+ function printJson(value) {
213
+ console.log(JSON.stringify(value, null, 2));
214
+ }
215
+ function requiredValue(args, index, message) {
216
+ const value = args.values[index];
217
+ if (!value) {
218
+ throw new AppError("INPUT_INVALID", message);
219
+ }
220
+ return value;
221
+ }
222
+ function stringFlag(args, name) {
223
+ const value = args.flags.get(name);
224
+ return typeof value === "string" ? value : undefined;
225
+ }
226
+ function requireOpenAIKey(value) {
227
+ if (!value) {
228
+ throw new AppError("OPENAI_KEY_MISSING", "OPENAI_API_KEY is required");
229
+ }
230
+ return value;
231
+ }
232
+ function themeOptions(config) {
233
+ return {
234
+ registryPath: config.themeRegistryPath,
235
+ projectThemesDir: config.themesDir
236
+ };
237
+ }
238
+ async function writeOutputFile(outputPath, content) {
239
+ const absolute = path.resolve(outputPath);
240
+ await mkdir(path.dirname(absolute), { recursive: true });
241
+ await writeFile(absolute, content);
242
+ }
243
+ function formatInspect(result) {
244
+ return [
245
+ `Title: ${result.metadata.title || "(missing)"}`,
246
+ `Images: ${result.images.length}`,
247
+ ...result.checks.map((check) => `${check.status.toUpperCase()} ${check.code}: ${check.message}`)
248
+ ].join("\n");
249
+ }
250
+ void main();
251
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface RuntimeConfig {
2
+ openaiApiKey?: string;
3
+ openaiBaseUrl: string;
4
+ openaiTextModel: string;
5
+ openaiImageModel: string;
6
+ themeRegistryPath?: string;
7
+ themesDir?: string;
8
+ }
9
+ export declare function loadConfig(env?: NodeJS.ProcessEnv): RuntimeConfig;
@@ -0,0 +1,11 @@
1
+ export function loadConfig(env = process.env) {
2
+ return {
3
+ openaiApiKey: env.OPENAI_API_KEY,
4
+ openaiBaseUrl: env.OPENAI_BASE_URL || "https://api.openai.com/v1",
5
+ openaiTextModel: env.OPENAI_TEXT_MODEL || "gpt-5.5",
6
+ openaiImageModel: env.OPENAI_IMAGE_MODEL || "gpt-image-2",
7
+ themeRegistryPath: env.MD2WECHAT_THEME_REGISTRY,
8
+ themesDir: env.MD2WECHAT_THEMES_DIR
9
+ };
10
+ }
11
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/config/config.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,OAAO;QACL,YAAY,EAAE,GAAG,CAAC,cAAc;QAChC,aAAa,EAAE,GAAG,CAAC,eAAe,IAAI,2BAA2B;QACjE,eAAe,EAAE,GAAG,CAAC,iBAAiB,IAAI,SAAS;QACnD,gBAAgB,EAAE,GAAG,CAAC,kBAAkB,IAAI,aAAa;QACzD,iBAAiB,EAAE,GAAG,CAAC,wBAAwB;QAC/C,SAAS,EAAE,GAAG,CAAC,oBAAoB;KACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ export type ImageReferenceType = "local" | "remote" | "ai";
2
+ export interface ArticleMetadata {
3
+ title?: string;
4
+ author?: string;
5
+ digest?: string;
6
+ }
7
+ export interface ImageReference {
8
+ index: number;
9
+ alt: string;
10
+ src: string;
11
+ type: ImageReferenceType;
12
+ }
13
+ export interface ParsedArticle {
14
+ originalMarkdown: string;
15
+ body: string;
16
+ metadata: ArticleMetadata;
17
+ headingTitle?: string;
18
+ images: ImageReference[];
19
+ }
20
+ export declare function parseArticle(markdown: string): ParsedArticle;
@@ -0,0 +1,81 @@
1
+ export function parseArticle(markdown) {
2
+ const { metadata, body } = parseFrontmatter(markdown);
3
+ const headingTitle = firstHeading(body);
4
+ const resolvedMetadata = {
5
+ ...metadata,
6
+ title: metadata.title || headingTitle
7
+ };
8
+ return {
9
+ originalMarkdown: markdown,
10
+ body,
11
+ metadata: resolvedMetadata,
12
+ headingTitle,
13
+ images: extractImages(body)
14
+ };
15
+ }
16
+ function parseFrontmatter(markdown) {
17
+ if (!markdown.startsWith("---\n")) {
18
+ return { metadata: {}, body: markdown };
19
+ }
20
+ const end = markdown.indexOf("\n---", 4);
21
+ if (end === -1) {
22
+ return { metadata: {}, body: markdown };
23
+ }
24
+ const frontmatter = markdown.slice(4, end);
25
+ const body = markdown.slice(end + 4).replace(/^\r?\n/, "");
26
+ const fields = {};
27
+ for (const line of frontmatter.split(/\r?\n/)) {
28
+ const match = /^([A-Za-z_][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
29
+ if (!match) {
30
+ continue;
31
+ }
32
+ fields[match[1].toLowerCase()] = stripQuotes(match[2].trim());
33
+ }
34
+ return {
35
+ metadata: {
36
+ title: emptyToUndefined(fields.title),
37
+ author: emptyToUndefined(fields.author),
38
+ digest: emptyToUndefined(fields.digest || fields.summary || fields.description)
39
+ },
40
+ body
41
+ };
42
+ }
43
+ function stripQuotes(value) {
44
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
45
+ return value.slice(1, -1);
46
+ }
47
+ return value;
48
+ }
49
+ function emptyToUndefined(value) {
50
+ const trimmed = value?.trim();
51
+ return trimmed ? trimmed : undefined;
52
+ }
53
+ function firstHeading(markdown) {
54
+ const match = /^#\s+(.+?)\s*$/m.exec(markdown);
55
+ return match?.[1]?.trim();
56
+ }
57
+ function extractImages(markdown) {
58
+ const images = [];
59
+ const pattern = /!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
60
+ let match;
61
+ while ((match = pattern.exec(markdown)) !== null) {
62
+ const src = match[2];
63
+ images.push({
64
+ index: images.length,
65
+ alt: match[1],
66
+ src,
67
+ type: imageType(src)
68
+ });
69
+ }
70
+ return images;
71
+ }
72
+ function imageType(src) {
73
+ if (src.startsWith("ai:")) {
74
+ return "ai";
75
+ }
76
+ if (/^https?:\/\//i.test(src)) {
77
+ return "remote";
78
+ }
79
+ return "local";
80
+ }
81
+ //# sourceMappingURL=article.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article.js","sourceRoot":"","sources":["../../../src/core/article.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export type EnvelopeStatus = "completed" | "action_required" | "failed";
2
+ export interface JsonEnvelope<T = unknown> {
3
+ success: boolean;
4
+ code: string;
5
+ message: string;
6
+ schema_version: "1.0";
7
+ status: EnvelopeStatus;
8
+ retryable: boolean;
9
+ data?: T;
10
+ error?: string;
11
+ }
12
+ export declare function successEnvelope<T>(code: string, message: string, data: T): JsonEnvelope<T>;
13
+ export declare function actionEnvelope<T>(code: string, message: string, data: T): JsonEnvelope<T>;
14
+ export declare function failureEnvelope(code: string, message: string, retryable: boolean, error?: string): JsonEnvelope;
@@ -0,0 +1,34 @@
1
+ export function successEnvelope(code, message, data) {
2
+ return {
3
+ success: true,
4
+ code,
5
+ message,
6
+ schema_version: "1.0",
7
+ status: "completed",
8
+ retryable: false,
9
+ data
10
+ };
11
+ }
12
+ export function actionEnvelope(code, message, data) {
13
+ return {
14
+ success: true,
15
+ code,
16
+ message,
17
+ schema_version: "1.0",
18
+ status: "action_required",
19
+ retryable: false,
20
+ data
21
+ };
22
+ }
23
+ export function failureEnvelope(code, message, retryable, error) {
24
+ return {
25
+ success: false,
26
+ code,
27
+ message,
28
+ schema_version: "1.0",
29
+ status: "failed",
30
+ retryable,
31
+ error
32
+ };
33
+ }
34
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../../src/core/envelope.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,eAAe,CAAI,IAAY,EAAE,OAAe,EAAE,IAAO;IACvE,OAAO;QACL,OAAO,EAAE,IAAI;QACb,IAAI;QACJ,OAAO;QACP,cAAc,EAAE,KAAK;QACrB,MAAM,EAAE,WAAW;QACnB,SAAS,EAAE,KAAK;QAChB,IAAI;KACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAI,IAAY,EAAE,OAAe,EAAE,IAAO;IACtE,OAAO;QACL,OAAO,EAAE,IAAI;QACb,IAAI;QACJ,OAAO;QACP,cAAc,EAAE,KAAK;QACrB,MAAM,EAAE,iBAAiB;QACzB,SAAS,EAAE,KAAK;QAChB,IAAI;KACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe,EAAE,SAAkB,EAAE,KAAc;IAC/F,OAAO;QACL,OAAO,EAAE,KAAK;QACd,IAAI;QACJ,OAAO;QACP,cAAc,EAAE,KAAK;QACrB,MAAM,EAAE,QAAQ;QAChB,SAAS;QACT,KAAK;KACN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare class AppError extends Error {
2
+ readonly code: string;
3
+ readonly retryable: boolean;
4
+ readonly original?: unknown | undefined;
5
+ constructor(code: string, message: string, retryable?: boolean, original?: unknown | undefined);
6
+ }
7
+ export declare function toAppError(error: unknown, fallbackCode?: string): AppError;
@@ -0,0 +1,22 @@
1
+ export class AppError extends Error {
2
+ code;
3
+ retryable;
4
+ original;
5
+ constructor(code, message, retryable = false, original) {
6
+ super(message);
7
+ this.code = code;
8
+ this.retryable = retryable;
9
+ this.original = original;
10
+ this.name = "AppError";
11
+ }
12
+ }
13
+ export function toAppError(error, fallbackCode = "ERROR") {
14
+ if (error instanceof AppError) {
15
+ return error;
16
+ }
17
+ if (error instanceof Error) {
18
+ return new AppError(fallbackCode, error.message, false, error);
19
+ }
20
+ return new AppError(fallbackCode, String(error), false, error);
21
+ }
22
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEf;IAEA;IACA;IAJlB,YACkB,IAAY,EAC5B,OAAe,EACC,YAAY,KAAK,EACjB,QAAkB;QAElC,KAAK,CAAC,OAAO,CAAC,CAAC;QALC,SAAI,GAAJ,IAAI,CAAQ;QAEZ,cAAS,GAAT,SAAS,CAAQ;QACjB,aAAQ,GAAR,QAAQ,CAAU;QAGlC,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,KAAc,EAAE,YAAY,GAAG,OAAO;IAC/D,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,IAAI,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function sanitizeWechatHtml(html: string): string;
2
+ export declare function escapeHtml(value: string): string;
@@ -0,0 +1,18 @@
1
+ export function sanitizeWechatHtml(html) {
2
+ return html
3
+ .replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "")
4
+ .replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "")
5
+ .replace(/\s+on[a-z]+\s*=\s*"[^"]*"/gi, "")
6
+ .replace(/\s+on[a-z]+\s*=\s*'[^']*'/gi, "")
7
+ .replace(/\s+on[a-z]+\s*=\s*[^\s>]+/gi, "")
8
+ .replace(/javascript:/gi, "");
9
+ }
10
+ export function escapeHtml(value) {
11
+ return value
12
+ .replace(/&/g, "&amp;")
13
+ .replace(/</g, "&lt;")
14
+ .replace(/>/g, "&gt;")
15
+ .replace(/"/g, "&quot;")
16
+ .replace(/'/g, "&#39;");
17
+ }
18
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../../src/core/html.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI;SACR,OAAO,CAAC,qCAAqC,EAAE,EAAE,CAAC;SAClD,OAAO,CAAC,mCAAmC,EAAE,EAAE,CAAC;SAChD,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC;SAC1C,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC;SAC1C,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC;SAC1C,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface LoadInputOptions {
2
+ content?: string;
3
+ file?: string;
4
+ contentUrl?: string;
5
+ }
6
+ export interface LoadedInput {
7
+ content: string;
8
+ baseDir?: string;
9
+ source: "content" | "file" | "url";
10
+ }
11
+ export declare function loadInput(options: LoadInputOptions): Promise<LoadedInput>;