slackmark 0.4.0 → 0.6.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 (60) hide show
  1. package/README.md +44 -27
  2. package/dist/adapters/code-fence-adapter.d.ts +1 -1
  3. package/dist/adapters/code-fence-adapter.d.ts.map +1 -1
  4. package/dist/adapters/inline.d.ts.map +1 -1
  5. package/dist/adapters/math-block-adapter.d.ts +1 -1
  6. package/dist/adapters/math-block-adapter.d.ts.map +1 -1
  7. package/dist/adapters/mermaid-fence-adapter.d.ts.map +1 -1
  8. package/dist/adapters/try-render-image.d.ts +9 -0
  9. package/dist/adapters/try-render-image.d.ts.map +1 -0
  10. package/dist/assemble/message-assembler.d.ts +10 -0
  11. package/dist/assemble/message-assembler.d.ts.map +1 -1
  12. package/dist/budget/conversion-context.d.ts +5 -1
  13. package/dist/budget/conversion-context.d.ts.map +1 -1
  14. package/dist/budget/operation-scope.d.ts +9 -0
  15. package/dist/budget/operation-scope.d.ts.map +1 -0
  16. package/dist/budget/recording-receipt-collector.d.ts +13 -0
  17. package/dist/budget/recording-receipt-collector.d.ts.map +1 -0
  18. package/dist/capabilities/capabilities.d.ts +0 -1
  19. package/dist/capabilities/capabilities.d.ts.map +1 -1
  20. package/dist/constants.d.ts +6 -0
  21. package/dist/constants.d.ts.map +1 -1
  22. package/dist/converter/create-converter.d.ts +1 -1
  23. package/dist/converter/create-converter.d.ts.map +1 -1
  24. package/dist/converter/resolve-config.d.ts +4 -2
  25. package/dist/converter/resolve-config.d.ts.map +1 -1
  26. package/dist/converter/slackmark-converter.d.ts +11 -1
  27. package/dist/converter/slackmark-converter.d.ts.map +1 -1
  28. package/dist/errors.d.ts +81 -3
  29. package/dist/errors.d.ts.map +1 -1
  30. package/dist/index.d.ts +6 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +4911 -3556
  33. package/dist/index.js.map +1 -1
  34. package/dist/receipts.d.ts +37 -0
  35. package/dist/receipts.d.ts.map +1 -0
  36. package/dist/renderers/codecogs-renderer.d.ts +5 -5
  37. package/dist/renderers/codecogs-renderer.d.ts.map +1 -1
  38. package/dist/renderers/kroki-renderer.d.ts +5 -5
  39. package/dist/renderers/kroki-renderer.d.ts.map +1 -1
  40. package/dist/renderers/mermaid-ink-renderer.d.ts +5 -5
  41. package/dist/renderers/mermaid-ink-renderer.d.ts.map +1 -1
  42. package/dist/renderers/quickchart-renderer.d.ts +5 -5
  43. package/dist/renderers/quickchart-renderer.d.ts.map +1 -1
  44. package/dist/renderers/renderer-options.d.ts +4 -0
  45. package/dist/renderers/renderer-options.d.ts.map +1 -0
  46. package/dist/renderers/url-utils.d.ts.map +1 -1
  47. package/dist/renderers.js +401 -248
  48. package/dist/renderers.js.map +1 -1
  49. package/dist/slack/fetch-slack-uploader.d.ts +11 -2
  50. package/dist/slack/fetch-slack-uploader.d.ts.map +1 -1
  51. package/dist/slack/slack-upload-error.d.ts +18 -2
  52. package/dist/slack/slack-upload-error.d.ts.map +1 -1
  53. package/dist/slack.d.ts +1 -1
  54. package/dist/slack.d.ts.map +1 -1
  55. package/dist/slack.js +743 -230
  56. package/dist/slack.js.map +1 -1
  57. package/dist/types.d.ts +93 -9
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/validate/validate-blocks.d.ts.map +1 -1
  60. package/package.json +5 -4
package/dist/renderers.js CHANGED
@@ -1,269 +1,422 @@
1
- // src/constants.ts
2
- var IMAGE_URL_MAX_CHARS = 3e3;
3
-
4
- // src/renderers/encoding.ts
5
- var BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1
+ //#endregion
2
+ //#region src/renderers/encoding.ts
3
+ const BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
4
+ /**
5
+ * Kroki and mermaid.ink both accept zlib-deflated, unpadded base64url payloads.
6
+ */
6
7
  async function deflateBase64Url(value) {
7
- const stream = new CompressionStream("deflate");
8
- const compressed = readAll(stream.readable);
9
- const writer = stream.writable.getWriter();
10
- await writer.write(new TextEncoder().encode(value));
11
- await writer.close();
12
- writer.releaseLock();
13
- return encodeBase64Url(await compressed);
8
+ const stream = new CompressionStream("deflate");
9
+ const compressed = readAll(stream.readable);
10
+ const writer = stream.writable.getWriter();
11
+ await writer.write(new TextEncoder().encode(value));
12
+ await writer.close();
13
+ writer.releaseLock();
14
+ return encodeBase64Url(await compressed);
14
15
  }
15
16
  async function readAll(stream) {
16
- const reader = stream.getReader();
17
- const chunks = [];
18
- let length = 0;
19
- while (true) {
20
- const result = await reader.read();
21
- if (result.done) {
22
- break;
23
- }
24
- if (result.value !== void 0) {
25
- chunks.push(result.value);
26
- length += result.value.byteLength;
27
- }
28
- }
29
- reader.releaseLock();
30
- const output = new Uint8Array(length);
31
- let offset = 0;
32
- for (const chunk of chunks) {
33
- output.set(chunk, offset);
34
- offset += chunk.byteLength;
35
- }
36
- return output;
17
+ const reader = stream.getReader();
18
+ const chunks = [];
19
+ let length = 0;
20
+ while (true) {
21
+ const result = await reader.read();
22
+ if (result.done) break;
23
+ if (result.value !== void 0) {
24
+ chunks.push(result.value);
25
+ length += result.value.byteLength;
26
+ }
27
+ }
28
+ reader.releaseLock();
29
+ const output = new Uint8Array(length);
30
+ let offset = 0;
31
+ for (const chunk of chunks) {
32
+ output.set(chunk, offset);
33
+ offset += chunk.byteLength;
34
+ }
35
+ return output;
37
36
  }
38
37
  function encodeBase64Url(bytes) {
39
- const output = [];
40
- for (let index = 0; index < bytes.length; index += 3) {
41
- const first = bytes[index] ?? 0;
42
- const second = bytes[index + 1];
43
- const third = bytes[index + 2];
44
- output.push(
45
- BASE64URL_ALPHABET[first >> 2] ?? "",
46
- BASE64URL_ALPHABET[(first & 3) << 4 | (second ?? 0) >> 4] ?? ""
47
- );
48
- if (second !== void 0) {
49
- output.push(BASE64URL_ALPHABET[(second & 15) << 2 | (third ?? 0) >> 6] ?? "");
50
- }
51
- if (third !== void 0) {
52
- output.push(BASE64URL_ALPHABET[third & 63] ?? "");
53
- }
54
- }
55
- return output.join("");
38
+ const output = [];
39
+ for (let index = 0; index < bytes.length; index += 3) {
40
+ const first = bytes[index] ?? 0;
41
+ const second = bytes[index + 1];
42
+ const third = bytes[index + 2];
43
+ output.push(BASE64URL_ALPHABET[first >> 2] ?? "", BASE64URL_ALPHABET[(first & 3) << 4 | (second ?? 0) >> 4] ?? "");
44
+ if (second !== void 0) output.push(BASE64URL_ALPHABET[(second & 15) << 2 | (third ?? 0) >> 6] ?? "");
45
+ if (third !== void 0) output.push(BASE64URL_ALPHABET[third & 63] ?? "");
46
+ }
47
+ return output.join("");
56
48
  }
57
-
58
- // src/renderers/url-utils.ts
49
+ //#endregion
50
+ //#region src/errors.ts
51
+ const CONFIGURATION_ERROR_BRAND = Symbol.for("slackmark.error.configuration");
52
+ const OPERATION_ERROR_BRAND = Symbol.for("slackmark.error.operation");
53
+ /**
54
+ * Marks a class prototype so instances from any bundled copy answer to one identity.
55
+ * Subclasses inherit the brand, and instances carry no extra own property.
56
+ */
57
+ function brandErrorPrototype(prototype, brand) {
58
+ Object.defineProperty(prototype, brand, {
59
+ configurable: false,
60
+ enumerable: false,
61
+ value: true,
62
+ writable: false
63
+ });
64
+ }
65
+ /**
66
+ * Reads a brand without trusting the value: a hostile getter must not escape a guard.
67
+ */
68
+ function hasErrorBrand(value, brand) {
69
+ if (typeof value !== "object" || value === null) return false;
70
+ try {
71
+ return Reflect.get(value, brand) === true;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+ var ConfigurationError = class extends Error {
77
+ static {
78
+ brandErrorPrototype(this.prototype, CONFIGURATION_ERROR_BRAND);
79
+ }
80
+ name;
81
+ code;
82
+ stage;
83
+ retryable;
84
+ receipts;
85
+ settledReceipts;
86
+ constructor(message, options = {}) {
87
+ super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
88
+ this.name = "ConfigurationError";
89
+ this.code = options.code ?? "invalid_configuration";
90
+ this.stage = options.stage;
91
+ this.retryable = false;
92
+ this.receipts = freezeReceipts(options.receipts ?? []);
93
+ this.settledReceipts = options.settledReceipts ?? Promise.resolve(this.receipts);
94
+ }
95
+ };
96
+ (class extends Error {
97
+ static {
98
+ brandErrorPrototype(this.prototype, OPERATION_ERROR_BRAND);
99
+ }
100
+ name;
101
+ code;
102
+ stage;
103
+ retryable;
104
+ receipts;
105
+ settledReceipts;
106
+ constructor(message, code, stage, retryable, options = {}) {
107
+ super(message, options.cause !== void 0 ? { cause: options.cause } : void 0);
108
+ this.name = "OperationError";
109
+ this.code = code;
110
+ this.stage = stage;
111
+ this.retryable = retryable;
112
+ this.receipts = freezeReceipts(options.receipts ?? []);
113
+ this.settledReceipts = options.settledReceipts ?? Promise.resolve(this.receipts);
114
+ }
115
+ });
116
+ /**
117
+ * Identifies a {@link ConfigurationError} from any entry of this package.
118
+ *
119
+ * Use this instead of `instanceof` when the error may have been thrown by a different
120
+ * entry — for example a `ConfigurationError` from `slackmark/renderers` caught by code
121
+ * that imported the class from `slackmark`.
122
+ */
123
+ function isConfigurationError(value) {
124
+ return hasErrorBrand(value, CONFIGURATION_ERROR_BRAND);
125
+ }
126
+ function freezeReceipts(receipts) {
127
+ return Object.freeze(receipts.map((receipt) => Object.freeze({ ...receipt })));
128
+ }
129
+ //#endregion
130
+ //#region src/renderers/renderer-options.ts
131
+ function snapshotRendererOptions(options, allowed, label) {
132
+ if (typeof options !== "object" || options === null || Array.isArray(options)) throw invalidRendererOptions(`${label} options must be a plain object`);
133
+ let prototype;
134
+ let keys;
135
+ try {
136
+ prototype = Object.getPrototypeOf(options);
137
+ keys = Object.keys(options);
138
+ } catch (cause) {
139
+ throw invalidRendererOptions(`${label} options could not be inspected`, cause);
140
+ }
141
+ if (prototype !== Object.prototype && prototype !== null) throw invalidRendererOptions(`${label} options must be a plain object`);
142
+ const allowedKeys = new Set(allowed);
143
+ const snapshot = {};
144
+ for (const key of keys) {
145
+ if (!allowedKeys.has(key)) throw invalidRendererOptions(`Unknown ${label} option: ${key}`);
146
+ try {
147
+ snapshot[key] = Reflect.get(options, key);
148
+ } catch (cause) {
149
+ throw invalidRendererOptions(`${label}.${key} could not be read`, cause);
150
+ }
151
+ }
152
+ return Object.freeze(snapshot);
153
+ }
154
+ function invalidRendererOptions(message, cause) {
155
+ return new ConfigurationError(message, {
156
+ code: "invalid_renderer_options",
157
+ stage: "configuration",
158
+ cause
159
+ });
160
+ }
161
+ //#endregion
162
+ //#region src/renderers/url-utils.ts
59
163
  function normalizeBaseUrl(baseUrl) {
60
- const normalized = baseUrl.replace(/\/+$/u, "");
61
- if (normalized.length === 0) {
62
- throw new TypeError("baseUrl must not be empty");
63
- }
64
- return normalized;
164
+ if (typeof baseUrl !== "string" || baseUrl.length === 0 || baseUrl !== baseUrl.trim()) throw invalidRendererOptions("baseUrl must be a nonempty trimmed string");
165
+ const normalized = baseUrl.replace(/\/+$/u, "");
166
+ try {
167
+ const parsed = new URL(normalized);
168
+ if (parsed.protocol !== "https:" || parsed.username.length > 0 || parsed.password.length > 0) throw invalidRendererOptions("baseUrl must be credential-free absolute HTTPS");
169
+ } catch (cause) {
170
+ if (isConfigurationError(cause)) throw cause;
171
+ throw invalidRendererOptions("baseUrl must be credential-free absolute HTTPS", cause);
172
+ }
173
+ return normalized;
65
174
  }
66
175
  function normalizeFenceLanguage(lang) {
67
- return lang.trim().toLowerCase();
176
+ return lang.trim().toLowerCase();
68
177
  }
69
-
70
- // src/renderers/kroki-renderer.ts
71
- var DEFAULT_KROKI_BASE_URL = "https://kroki.io";
72
- var KROKI_DIAGRAM_TYPES = Object.freeze({
73
- blockdiag: "blockdiag",
74
- bpmn: "bpmn",
75
- bytefield: "bytefield",
76
- c4: "c4plantuml",
77
- c4plantuml: "c4plantuml",
78
- d2: "d2",
79
- dbml: "dbml",
80
- ditaa: "ditaa",
81
- dot: "graphviz",
82
- erd: "erd",
83
- excalidraw: "excalidraw",
84
- graphviz: "graphviz",
85
- mermaid: "mermaid",
86
- nomnoml: "nomnoml",
87
- pikchr: "pikchr",
88
- plantuml: "plantuml",
89
- puml: "plantuml",
90
- structurizr: "structurizr",
91
- svgbob: "svgbob",
92
- symbolator: "symbolator",
93
- tikz: "tikz",
94
- umlet: "umlet",
95
- vega: "vega",
96
- "vega-lite": "vegalite",
97
- vegalite: "vegalite",
98
- wavedrom: "wavedrom",
99
- wireviz: "wireviz"
178
+ //#endregion
179
+ //#region src/renderers/kroki-renderer.ts
180
+ const DEFAULT_KROKI_BASE_URL = "https://kroki.io";
181
+ /**
182
+ * Fence-language aliases for Kroki's supported diagram types.
183
+ *
184
+ * @see https://docs.kroki.io/kroki/setup/usage/
185
+ */
186
+ const KROKI_DIAGRAM_TYPES = Object.freeze({
187
+ blockdiag: "blockdiag",
188
+ bpmn: "bpmn",
189
+ bytefield: "bytefield",
190
+ c4: "c4plantuml",
191
+ c4plantuml: "c4plantuml",
192
+ d2: "d2",
193
+ dbml: "dbml",
194
+ ditaa: "ditaa",
195
+ dot: "graphviz",
196
+ erd: "erd",
197
+ excalidraw: "excalidraw",
198
+ graphviz: "graphviz",
199
+ mermaid: "mermaid",
200
+ nomnoml: "nomnoml",
201
+ pikchr: "pikchr",
202
+ plantuml: "plantuml",
203
+ puml: "plantuml",
204
+ structurizr: "structurizr",
205
+ svgbob: "svgbob",
206
+ symbolator: "symbolator",
207
+ tikz: "tikz",
208
+ umlet: "umlet",
209
+ vega: "vega",
210
+ "vega-lite": "vegalite",
211
+ vegalite: "vegalite",
212
+ wavedrom: "wavedrom",
213
+ wireviz: "wireviz"
100
214
  });
215
+ /**
216
+ * Produces durable Kroki GET URLs without performing network I/O.
217
+ */
101
218
  var KrokiRenderer = class {
102
- baseUrl;
103
- diagramTypes;
104
- constructor(options = {}) {
105
- this.baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_KROKI_BASE_URL);
106
- this.diagramTypes = Object.freeze({
107
- ...KROKI_DIAGRAM_TYPES,
108
- ...options.diagramTypes
109
- });
110
- }
111
- canRender(req) {
112
- return this.diagramTypes[normalizeFenceLanguage(req.lang)] !== void 0;
113
- }
114
- async render(req) {
115
- const diagramType = this.diagramTypes[normalizeFenceLanguage(req.lang)];
116
- if (diagramType === void 0) {
117
- return null;
118
- }
119
- const encoded = await deflateBase64Url(req.source);
120
- const imageUrl = `${this.baseUrl}/${encodeURIComponent(diagramType)}/png/${encoded}`;
121
- if (imageUrl.length > IMAGE_URL_MAX_CHARS) {
122
- return null;
123
- }
124
- return {
125
- kind: "url",
126
- imageUrl,
127
- altText: req.altText
128
- };
129
- }
219
+ id;
220
+ output;
221
+ baseUrl;
222
+ diagramTypes;
223
+ constructor(options = {}) {
224
+ const validated = snapshotRendererOptions(options, ["baseUrl", "diagramTypes"], "KrokiRenderer");
225
+ this.id = "kroki";
226
+ this.output = "url";
227
+ this.baseUrl = normalizeBaseUrl(validated.baseUrl === void 0 ? DEFAULT_KROKI_BASE_URL : validated.baseUrl);
228
+ this.diagramTypes = Object.freeze({
229
+ ...KROKI_DIAGRAM_TYPES,
230
+ ...validateDiagramTypes(validated.diagramTypes)
231
+ });
232
+ }
233
+ canRender(req) {
234
+ return this.diagramTypes[normalizeFenceLanguage(req.lang)] !== void 0;
235
+ }
236
+ async render(req, _context) {
237
+ const diagramType = this.diagramTypes[normalizeFenceLanguage(req.lang)];
238
+ if (diagramType === void 0) return null;
239
+ const encoded = await deflateBase64Url(req.source);
240
+ const imageUrl = `${this.baseUrl}/${encodeURIComponent(diagramType)}/png/${encoded}`;
241
+ if (imageUrl.length > 3e3) return null;
242
+ return {
243
+ kind: "url",
244
+ imageUrl,
245
+ altText: req.altText
246
+ };
247
+ }
130
248
  };
131
-
132
- // src/renderers/mermaid-ink-renderer.ts
133
- var DEFAULT_MERMAID_INK_BASE_URL = "https://mermaid.ink";
134
- var DEFAULT_MERMAID_INK_BACKGROUND = "!white";
249
+ function validateDiagramTypes(value) {
250
+ if (value === void 0) return {};
251
+ let keys;
252
+ try {
253
+ keys = Object.keys(value);
254
+ } catch (cause) {
255
+ throw invalidRendererOptions("KrokiRenderer.diagramTypes could not be inspected", cause);
256
+ }
257
+ const snapshot = snapshotRendererOptions(value, keys, "KrokiRenderer.diagramTypes");
258
+ for (const [language, diagramType] of Object.entries(snapshot)) if (language.length === 0 || typeof diagramType !== "string" || diagramType.length === 0) throw invalidRendererOptions("KrokiRenderer.diagramTypes values must be nonempty strings");
259
+ return snapshot;
260
+ }
261
+ //#endregion
262
+ //#region src/renderers/mermaid-ink-renderer.ts
263
+ const DEFAULT_MERMAID_INK_BASE_URL = "https://mermaid.ink";
264
+ const DEFAULT_MERMAID_INK_BACKGROUND = "!white";
265
+ /**
266
+ * Produces mermaid.ink pako-state PNG URLs without performing network I/O.
267
+ */
135
268
  var MermaidInkRenderer = class {
136
- baseUrl;
137
- theme;
138
- bgColor;
139
- constructor(options = {}) {
140
- this.baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_MERMAID_INK_BASE_URL);
141
- this.theme = options.theme ?? "default";
142
- this.bgColor = options.bgColor ?? DEFAULT_MERMAID_INK_BACKGROUND;
143
- }
144
- canRender(req) {
145
- return normalizeFenceLanguage(req.lang) === "mermaid";
146
- }
147
- async render(req) {
148
- if (!this.canRender(req)) {
149
- return null;
150
- }
151
- const state = {
152
- code: req.source,
153
- mermaid: { theme: this.theme }
154
- };
155
- const encoded = await deflateBase64Url(JSON.stringify(state));
156
- const imageUrl = `${this.baseUrl}/img/pako:${encoded}?type=png&bgColor=${encodeURIComponent(this.bgColor)}`;
157
- if (imageUrl.length > IMAGE_URL_MAX_CHARS) {
158
- return null;
159
- }
160
- return {
161
- kind: "url",
162
- imageUrl,
163
- altText: req.altText
164
- };
165
- }
269
+ id;
270
+ output;
271
+ baseUrl;
272
+ theme;
273
+ bgColor;
274
+ constructor(options = {}) {
275
+ const validated = snapshotRendererOptions(options, [
276
+ "baseUrl",
277
+ "theme",
278
+ "bgColor"
279
+ ], "MermaidInkRenderer");
280
+ this.id = "mermaid-ink";
281
+ this.output = "url";
282
+ this.baseUrl = normalizeBaseUrl(validated.baseUrl === void 0 ? DEFAULT_MERMAID_INK_BASE_URL : validated.baseUrl);
283
+ this.theme = validated.theme === void 0 ? "default" : validated.theme;
284
+ this.bgColor = validated.bgColor === void 0 ? DEFAULT_MERMAID_INK_BACKGROUND : validated.bgColor;
285
+ if (![
286
+ "default",
287
+ "neutral",
288
+ "dark",
289
+ "forest"
290
+ ].includes(this.theme) || typeof this.bgColor !== "string" || this.bgColor.length === 0) throw invalidRendererOptions("MermaidInkRenderer theme or bgColor is invalid");
291
+ }
292
+ canRender(req) {
293
+ return normalizeFenceLanguage(req.lang) === "mermaid";
294
+ }
295
+ async render(req, _context) {
296
+ if (!this.canRender(req)) return null;
297
+ const state = {
298
+ code: req.source,
299
+ mermaid: { theme: this.theme }
300
+ };
301
+ const encoded = await deflateBase64Url(JSON.stringify(state));
302
+ const imageUrl = `${this.baseUrl}/img/pako:${encoded}?type=png&bgColor=${encodeURIComponent(this.bgColor)}`;
303
+ if (imageUrl.length > 3e3) return null;
304
+ return {
305
+ kind: "url",
306
+ imageUrl,
307
+ altText: req.altText
308
+ };
309
+ }
166
310
  };
167
-
168
- // src/renderers/codecogs-renderer.ts
169
- var DEFAULT_CODECOGS_BASE_URL = "https://latex.codecogs.com";
170
- var DEFAULT_CODECOGS_DPI = 200;
171
- var MATH_FENCE_LANGUAGES = /* @__PURE__ */ new Set(["katex", "latex", "math", "tex"]);
311
+ //#endregion
312
+ //#region src/renderers/codecogs-renderer.ts
313
+ const DEFAULT_CODECOGS_BASE_URL = "https://latex.codecogs.com";
314
+ const DEFAULT_CODECOGS_DPI = 200;
315
+ const MATH_FENCE_LANGUAGES = /* @__PURE__ */ new Set([
316
+ "katex",
317
+ "latex",
318
+ "math",
319
+ "tex"
320
+ ]);
321
+ /**
322
+ * Produces deterministic CodeCogs `png.image` URLs for display math.
323
+ */
172
324
  var CodeCogsRenderer = class {
173
- baseUrl;
174
- dpi;
175
- constructor(options = {}) {
176
- this.baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_CODECOGS_BASE_URL);
177
- this.dpi = options.dpi ?? DEFAULT_CODECOGS_DPI;
178
- if (!Number.isInteger(this.dpi) || this.dpi <= 0) {
179
- throw new RangeError("dpi must be a positive integer");
180
- }
181
- }
182
- canRender(req) {
183
- return MATH_FENCE_LANGUAGES.has(normalizeFenceLanguage(req.lang));
184
- }
185
- async render(req) {
186
- if (!this.canRender(req)) {
187
- return null;
188
- }
189
- const formula = `\\dpi{${String(this.dpi)}} ${req.source}`;
190
- const imageUrl = `${this.baseUrl}/png.image?${encodeURIComponent(formula)}`;
191
- if (imageUrl.length > IMAGE_URL_MAX_CHARS) {
192
- return null;
193
- }
194
- return {
195
- kind: "url",
196
- imageUrl,
197
- altText: req.altText
198
- };
199
- }
325
+ id;
326
+ output;
327
+ baseUrl;
328
+ dpi;
329
+ constructor(options = {}) {
330
+ const validated = snapshotRendererOptions(options, ["baseUrl", "dpi"], "CodeCogsRenderer");
331
+ this.id = "codecogs";
332
+ this.output = "url";
333
+ this.baseUrl = normalizeBaseUrl(validated.baseUrl === void 0 ? DEFAULT_CODECOGS_BASE_URL : validated.baseUrl);
334
+ this.dpi = validated.dpi === void 0 ? DEFAULT_CODECOGS_DPI : validated.dpi;
335
+ if (!Number.isInteger(this.dpi) || this.dpi <= 0) throw invalidRendererOptions("dpi must be a positive integer");
336
+ }
337
+ canRender(req) {
338
+ return MATH_FENCE_LANGUAGES.has(normalizeFenceLanguage(req.lang));
339
+ }
340
+ async render(req, _context) {
341
+ if (!this.canRender(req)) return null;
342
+ const formula = `\\dpi{${String(this.dpi)}} ${req.source}`;
343
+ const imageUrl = `${this.baseUrl}/png.image?${encodeURIComponent(formula)}`;
344
+ if (imageUrl.length > 3e3) return null;
345
+ return {
346
+ kind: "url",
347
+ imageUrl,
348
+ altText: req.altText
349
+ };
350
+ }
200
351
  };
201
-
202
- // src/renderers/quickchart-renderer.ts
203
- var DEFAULT_QUICKCHART_BASE_URL = "https://quickchart.io";
204
- var DEFAULT_QUICKCHART_WIDTH = 500;
205
- var DEFAULT_QUICKCHART_HEIGHT = 300;
206
- var DEFAULT_QUICKCHART_BACKGROUND = "white";
207
- var DEFAULT_QUICKCHART_DEVICE_PIXEL_RATIO = 2;
208
- var CHART_FENCE_LANGUAGES = /* @__PURE__ */ new Set([
209
- "chart",
210
- "chart.js",
211
- "chartjs",
212
- "quickchart"
352
+ //#endregion
353
+ //#region src/renderers/quickchart-renderer.ts
354
+ const DEFAULT_QUICKCHART_BASE_URL = "https://quickchart.io";
355
+ const DEFAULT_QUICKCHART_WIDTH = 500;
356
+ const DEFAULT_QUICKCHART_HEIGHT = 300;
357
+ const DEFAULT_QUICKCHART_BACKGROUND = "white";
358
+ const DEFAULT_QUICKCHART_DEVICE_PIXEL_RATIO = 2;
359
+ const CHART_FENCE_LANGUAGES = /* @__PURE__ */ new Set([
360
+ "chart",
361
+ "chart.js",
362
+ "chartjs",
363
+ "quickchart"
213
364
  ]);
365
+ /**
366
+ * Produces full, durable QuickChart GET URLs. It never uses the expiring
367
+ * `/chart/create` short-URL API.
368
+ */
214
369
  var QuickChartRenderer = class {
215
- baseUrl;
216
- width;
217
- height;
218
- backgroundColor;
219
- devicePixelRatio;
220
- constructor(options = {}) {
221
- this.baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_QUICKCHART_BASE_URL);
222
- this.width = options.width ?? DEFAULT_QUICKCHART_WIDTH;
223
- this.height = options.height ?? DEFAULT_QUICKCHART_HEIGHT;
224
- this.backgroundColor = options.backgroundColor ?? DEFAULT_QUICKCHART_BACKGROUND;
225
- this.devicePixelRatio = options.devicePixelRatio ?? DEFAULT_QUICKCHART_DEVICE_PIXEL_RATIO;
226
- if (!Number.isInteger(this.width) || this.width <= 0) {
227
- throw new RangeError("width must be a positive integer");
228
- }
229
- if (!Number.isInteger(this.height) || this.height <= 0) {
230
- throw new RangeError("height must be a positive integer");
231
- }
232
- if (!Number.isFinite(this.devicePixelRatio) || this.devicePixelRatio <= 0) {
233
- throw new RangeError("devicePixelRatio must be positive");
234
- }
235
- }
236
- canRender(req) {
237
- return CHART_FENCE_LANGUAGES.has(normalizeFenceLanguage(req.lang));
238
- }
239
- async render(req) {
240
- if (!this.canRender(req)) {
241
- return null;
242
- }
243
- const query = [
244
- `c=${encodeURIComponent(req.source)}`,
245
- `width=${String(this.width)}`,
246
- `height=${String(this.height)}`,
247
- `backgroundColor=${encodeURIComponent(this.backgroundColor)}`,
248
- `devicePixelRatio=${String(this.devicePixelRatio)}`,
249
- "format=png"
250
- ].join("&");
251
- const imageUrl = `${this.baseUrl}/chart?${query}`;
252
- if (imageUrl.length > IMAGE_URL_MAX_CHARS) {
253
- return null;
254
- }
255
- return {
256
- kind: "url",
257
- imageUrl,
258
- altText: req.altText
259
- };
260
- }
261
- };
262
- export {
263
- CodeCogsRenderer,
264
- KROKI_DIAGRAM_TYPES,
265
- KrokiRenderer,
266
- MermaidInkRenderer,
267
- QuickChartRenderer
370
+ id;
371
+ output;
372
+ baseUrl;
373
+ width;
374
+ height;
375
+ backgroundColor;
376
+ devicePixelRatio;
377
+ constructor(options = {}) {
378
+ const validated = snapshotRendererOptions(options, [
379
+ "baseUrl",
380
+ "width",
381
+ "height",
382
+ "backgroundColor",
383
+ "devicePixelRatio"
384
+ ], "QuickChartRenderer");
385
+ this.id = "quickchart";
386
+ this.output = "url";
387
+ this.baseUrl = normalizeBaseUrl(validated.baseUrl === void 0 ? DEFAULT_QUICKCHART_BASE_URL : validated.baseUrl);
388
+ this.width = validated.width === void 0 ? DEFAULT_QUICKCHART_WIDTH : validated.width;
389
+ this.height = validated.height === void 0 ? DEFAULT_QUICKCHART_HEIGHT : validated.height;
390
+ this.backgroundColor = validated.backgroundColor === void 0 ? DEFAULT_QUICKCHART_BACKGROUND : validated.backgroundColor;
391
+ this.devicePixelRatio = validated.devicePixelRatio === void 0 ? DEFAULT_QUICKCHART_DEVICE_PIXEL_RATIO : validated.devicePixelRatio;
392
+ if (!Number.isInteger(this.width) || this.width <= 0) throw invalidRendererOptions("width must be a positive integer");
393
+ if (!Number.isInteger(this.height) || this.height <= 0) throw invalidRendererOptions("height must be a positive integer");
394
+ if (!Number.isFinite(this.devicePixelRatio) || this.devicePixelRatio <= 0) throw invalidRendererOptions("devicePixelRatio must be positive");
395
+ if (typeof this.backgroundColor !== "string" || this.backgroundColor.length === 0) throw invalidRendererOptions("backgroundColor must be a nonempty string");
396
+ }
397
+ canRender(req) {
398
+ return CHART_FENCE_LANGUAGES.has(normalizeFenceLanguage(req.lang));
399
+ }
400
+ async render(req, _context) {
401
+ if (!this.canRender(req)) return null;
402
+ const query = [
403
+ `c=${encodeURIComponent(req.source)}`,
404
+ `width=${String(this.width)}`,
405
+ `height=${String(this.height)}`,
406
+ `backgroundColor=${encodeURIComponent(this.backgroundColor)}`,
407
+ `devicePixelRatio=${String(this.devicePixelRatio)}`,
408
+ "format=png"
409
+ ].join("&");
410
+ const imageUrl = `${this.baseUrl}/chart?${query}`;
411
+ if (imageUrl.length > 3e3) return null;
412
+ return {
413
+ kind: "url",
414
+ imageUrl,
415
+ altText: req.altText
416
+ };
417
+ }
268
418
  };
419
+ //#endregion
420
+ export { CodeCogsRenderer, KROKI_DIAGRAM_TYPES, KrokiRenderer, MermaidInkRenderer, QuickChartRenderer };
421
+
269
422
  //# sourceMappingURL=renderers.js.map