@circlesac/mack 0.0.0 → 26.2.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.
@@ -0,0 +1,43 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ env:
7
+ type: choice
8
+ description: Environment
9
+ default: production
10
+ options:
11
+ - production
12
+ - dev
13
+
14
+ jobs:
15
+ release:
16
+ runs-on: ubuntu-latest
17
+ permissions:
18
+ id-token: write
19
+ contents: write
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ with:
23
+ fetch-depth: 0
24
+ - uses: actions/setup-node@v4
25
+ with:
26
+ node-version: "lts/*"
27
+ - uses: oven-sh/setup-bun@v2
28
+
29
+ - run: bun install --frozen-lockfile
30
+ - run: bun test
31
+
32
+ - run: npm install -g @circlesac/oneup
33
+ - run: |
34
+ git config user.name "github-actions[bot]"
35
+ git config user.email "github-actions[bot]@users.noreply.github.com"
36
+ oneup version
37
+
38
+ - uses: actions/setup-node@v4
39
+ with:
40
+ registry-url: "https://registry.npmjs.org"
41
+ - run: npm publish --tag ${{ inputs.env == 'production' && 'latest' || inputs.env }}
42
+
43
+ - run: git push origin master --follow-tags
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Instantish, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # Mack: Markdown to Slack Message Blocks
2
+
3
+ > Convert Markdown and GitHub Flavoured Markdown to Slack BlockKit Blocks
4
+
5
+ [![Node.js CI](https://github.com/circlesac/mack/actions/workflows/ci.yml/badge.svg)](https://github.com/circlesac/mack/actions/workflows/ci.yml)
6
+ Mack is a Markdown parser to convert any Markdown content to Slack BlockKit block objects.
7
+
8
+ Text is truncated to fit within the Slack API's limits.
9
+
10
+ ### Supported Markdown Elements
11
+
12
+ - All inline elements (italics, bold, strikethrough, inline code, hyperlinks)
13
+ - Lists (ordered, unordered, checkboxes)
14
+ - All headers
15
+ - Code blocks
16
+ - Block quotes (with some limitations)
17
+ - Images
18
+ - Thematic Breaks / Dividers
19
+ - Tables (alignment not preserved)
20
+
21
+ ### Not Yet Supported Markdown Elements
22
+
23
+ - Block quotes (limited functionality; does not support lists, headings, or images within the block quote)
24
+
25
+ ## Installation
26
+
27
+ ```
28
+ bun add @circlesac/mack
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ```ts
34
+ import { markdownToBlocks } from "@circlesac/mack"
35
+
36
+ const blocks = markdownToBlocks(`
37
+ # Hello world
38
+
39
+ * bulleted item 1
40
+ * bulleted item 2
41
+
42
+ abc _123_
43
+
44
+ ![cat](https://images.unsplash.com/photo-1574158622682-e40e69881006)
45
+ `)
46
+ ```
47
+
48
+ The `blocks` object now results in [this](https://app.slack.com/block-kit-builder/T01BFUV9UPJ#%7B%22blocks%22:%5B%7B%22text%22:%7B%22text%22:%22Hello%20world%22,%22type%22:%22plain_text%22%7D,%22type%22:%22header%22%7D,%7B%22text%22:%7B%22text%22:%22•%20bulleted%20item%201%5Cn•%20bulleted%20item%202%22,%22type%22:%22mrkdwn%22%7D,%22type%22:%22section%22%7D,%7B%22text%22:%7B%22text%22:%22abc%20_123_%22,%22type%22:%22mrkdwn%22%7D,%22type%22:%22section%22%7D,%7B%22alt_text%22:%22cat%22,%22image_url%22:%22https://images.unsplash.com/photo-1574158622682-e40e69881006?w=640%22,%22type%22:%22image%22%7D%5D%7D) payload.
49
+
50
+ ## API
51
+
52
+ `function markdownToBlocks(text: string, options: ParsingOptions): KnownBlock[]`
53
+
54
+ - `text`: the content to parse
55
+ - `options`: the options to use when parsing.
56
+
57
+ ### Parsing Options
58
+
59
+ ```ts
60
+ interface ParsingOptions {
61
+ // Configure how lists are displayed
62
+ lists?: ListOptions
63
+ }
64
+
65
+ interface ListOptions {
66
+ // Configure how checkbox list items are displayed. By default, they are prefixed with '* '
67
+ checkboxPrefix?: (checked: boolean) => string
68
+ }
69
+ ```
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Custom error types for Mack parser
3
+ */
4
+ export declare class MackError extends Error {
5
+ readonly code: string;
6
+ constructor(message: string, code: string);
7
+ }
8
+ export declare class ValidationError extends MackError {
9
+ constructor(message: string);
10
+ }
11
+ export declare class ParseError extends MackError {
12
+ readonly tokenType?: string;
13
+ constructor(message: string, tokenType?: string);
14
+ }
15
+ export declare class BlockLimitError extends MackError {
16
+ readonly blockCount: number;
17
+ readonly maxBlocks: number;
18
+ constructor(blockCount: number, maxBlocks: number);
19
+ }
20
+ export declare class SecurityError extends MackError {
21
+ constructor(message: string);
22
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ /**
3
+ * Custom error types for Mack parser
4
+ */
5
+ var __extends = (this && this.__extends) || (function () {
6
+ var extendStatics = function (d, b) {
7
+ extendStatics = Object.setPrototypeOf ||
8
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
9
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
10
+ return extendStatics(d, b);
11
+ };
12
+ return function (d, b) {
13
+ if (typeof b !== "function" && b !== null)
14
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
15
+ extendStatics(d, b);
16
+ function __() { this.constructor = d; }
17
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
18
+ };
19
+ })();
20
+ exports.__esModule = true;
21
+ exports.SecurityError = exports.BlockLimitError = exports.ParseError = exports.ValidationError = exports.MackError = void 0;
22
+ var MackError = /** @class */ (function (_super) {
23
+ __extends(MackError, _super);
24
+ function MackError(message, code) {
25
+ var _this = _super.call(this, message) || this;
26
+ _this.code = code;
27
+ _this.name = "MackError";
28
+ Object.setPrototypeOf(_this, MackError.prototype);
29
+ return _this;
30
+ }
31
+ return MackError;
32
+ }(Error));
33
+ exports.MackError = MackError;
34
+ var ValidationError = /** @class */ (function (_super) {
35
+ __extends(ValidationError, _super);
36
+ function ValidationError(message) {
37
+ var _this = _super.call(this, message, "VALIDATION_ERROR") || this;
38
+ _this.name = "ValidationError";
39
+ Object.setPrototypeOf(_this, ValidationError.prototype);
40
+ return _this;
41
+ }
42
+ return ValidationError;
43
+ }(MackError));
44
+ exports.ValidationError = ValidationError;
45
+ var ParseError = /** @class */ (function (_super) {
46
+ __extends(ParseError, _super);
47
+ function ParseError(message, tokenType) {
48
+ var _this = _super.call(this, message, "PARSE_ERROR") || this;
49
+ _this.tokenType = tokenType;
50
+ _this.name = "ParseError";
51
+ Object.setPrototypeOf(_this, ParseError.prototype);
52
+ return _this;
53
+ }
54
+ return ParseError;
55
+ }(MackError));
56
+ exports.ParseError = ParseError;
57
+ var BlockLimitError = /** @class */ (function (_super) {
58
+ __extends(BlockLimitError, _super);
59
+ function BlockLimitError(blockCount, maxBlocks) {
60
+ var _this = _super.call(this, "Block limit exceeded: ".concat(blockCount, " blocks exceeds maximum of ").concat(maxBlocks), "BLOCK_LIMIT_EXCEEDED") || this;
61
+ _this.blockCount = blockCount;
62
+ _this.maxBlocks = maxBlocks;
63
+ _this.name = "BlockLimitError";
64
+ Object.setPrototypeOf(_this, BlockLimitError.prototype);
65
+ return _this;
66
+ }
67
+ return BlockLimitError;
68
+ }(MackError));
69
+ exports.BlockLimitError = BlockLimitError;
70
+ var SecurityError = /** @class */ (function (_super) {
71
+ __extends(SecurityError, _super);
72
+ function SecurityError(message) {
73
+ var _this = _super.call(this, message, "SECURITY_ERROR") || this;
74
+ _this.name = "SecurityError";
75
+ Object.setPrototypeOf(_this, SecurityError.prototype);
76
+ return _this;
77
+ }
78
+ return SecurityError;
79
+ }(MackError));
80
+ exports.SecurityError = SecurityError;
@@ -0,0 +1,23 @@
1
+ import type { KnownBlock } from "@slack/types";
2
+ import type { RichTextBlock, TableBlock, VideoBlock } from "./slack";
3
+ import type { ParsingOptions } from "./types";
4
+ export { BlockLimitError, MackError, ParseError, SecurityError, ValidationError } from "./errors";
5
+ export type { ColumnSetting, RichTextBlock, RichTextBroadcastElement, RichTextChannelElement, RichTextDateElement, RichTextElement, RichTextEmojiElement, RichTextLinkElement, RichTextListElement, RichTextPreformattedElement, RichTextQuoteElement, RichTextSectionElement, RichTextStyle, RichTextTextElement, RichTextUserElement, RichTextUsergroupElement, TableBlock, TableCell, TableRow, VideoBlock, VideoBlockOptions } from "./slack";
6
+ export type { ListOptions, ParsingOptions } from "./types";
7
+ /**
8
+ * Parses Markdown content into Slack BlockKit Blocks.
9
+ * - Supports headings (all Markdown heading levels are treated as the single Slack header block)
10
+ * - Supports numbered lists, bulleted lists, to-do lists (as rich_text_list blocks)
11
+ * - Supports italics, bold, strikethrough, inline code, hyperlinks
12
+ * - Supports images
13
+ * - Supports thematic breaks / dividers
14
+ * - Supports code blocks (as rich_text_preformatted blocks)
15
+ * - Supports blockquotes (as rich_text_quote blocks for simple quotes)
16
+ * - Supports native table blocks with rich text formatting and column alignment
17
+ *
18
+ * Supports GitHub-flavoured Markdown.
19
+ *
20
+ * @param body any Markdown or GFM content
21
+ * @param options options to configure the parser
22
+ */
23
+ export declare function markdownToBlocks(body: string, options?: ParsingOptions): Promise<(KnownBlock | TableBlock | RichTextBlock | VideoBlock)[]>;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ exports.__esModule = true;
50
+ exports.markdownToBlocks = exports.ValidationError = exports.SecurityError = exports.ParseError = exports.MackError = exports.BlockLimitError = void 0;
51
+ var marked_1 = require("marked");
52
+ var internal_1 = require("./parser/internal");
53
+ var validation_1 = require("./validation");
54
+ var errors_1 = require("./errors");
55
+ __createBinding(exports, errors_1, "BlockLimitError");
56
+ __createBinding(exports, errors_1, "MackError");
57
+ __createBinding(exports, errors_1, "ParseError");
58
+ __createBinding(exports, errors_1, "SecurityError");
59
+ __createBinding(exports, errors_1, "ValidationError");
60
+ /**
61
+ * Parses Markdown content into Slack BlockKit Blocks.
62
+ * - Supports headings (all Markdown heading levels are treated as the single Slack header block)
63
+ * - Supports numbered lists, bulleted lists, to-do lists (as rich_text_list blocks)
64
+ * - Supports italics, bold, strikethrough, inline code, hyperlinks
65
+ * - Supports images
66
+ * - Supports thematic breaks / dividers
67
+ * - Supports code blocks (as rich_text_preformatted blocks)
68
+ * - Supports blockquotes (as rich_text_quote blocks for simple quotes)
69
+ * - Supports native table blocks with rich text formatting and column alignment
70
+ *
71
+ * Supports GitHub-flavoured Markdown.
72
+ *
73
+ * @param body any Markdown or GFM content
74
+ * @param options options to configure the parser
75
+ */
76
+ function markdownToBlocks(body, options) {
77
+ if (options === void 0) { options = {}; }
78
+ return __awaiter(this, void 0, void 0, function () {
79
+ var replacements, lexer, tokenizer, origInlineText, tokens, blocks;
80
+ return __generator(this, function (_a) {
81
+ (0, validation_1.validateInput)(body);
82
+ replacements = {
83
+ "&": "&amp;",
84
+ "<": "&lt;",
85
+ ">": "&gt;"
86
+ };
87
+ lexer = new marked_1.marked.Lexer();
88
+ tokenizer = lexer.tokenizer;
89
+ origInlineText = tokenizer.inlineText;
90
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
+ tokenizer.inlineText = function (src) {
92
+ var cap = this.rules.inline.text.exec(src);
93
+ if (!cap) {
94
+ return undefined;
95
+ }
96
+ var raw = cap[0];
97
+ var text = raw.replace(/[&<>]/g, function (char) { return replacements[char]; });
98
+ return { type: "text", raw: raw, text: text };
99
+ };
100
+ tokens = lexer.lex(body);
101
+ // Restore the original inlineText to avoid polluting marked.defaults
102
+ tokenizer.inlineText = origInlineText;
103
+ blocks = (0, internal_1.parseBlocks)(tokens, options);
104
+ (0, validation_1.validateBlockCount)(blocks.length, validation_1.MAX_BLOCKS);
105
+ return [2 /*return*/, blocks];
106
+ });
107
+ });
108
+ }
109
+ exports.markdownToBlocks = markdownToBlocks;
@@ -0,0 +1,5 @@
1
+ import { KnownBlock } from "@slack/types";
2
+ import { marked } from "marked";
3
+ import { RichTextBlock, TableBlock, VideoBlock } from "../slack";
4
+ import { ParsingOptions } from "../types";
5
+ export declare function parseBlocks(tokens: marked.TokensList, options?: ParsingOptions): (KnownBlock | TableBlock | RichTextBlock | VideoBlock)[];