quikchat 1.2.6 → 1.2.7
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 +7 -2
- package/dist/build-manifest.json +57 -57
- package/dist/quikchat-md-full.cjs.js +98 -5
- package/dist/quikchat-md-full.cjs.js.map +1 -1
- package/dist/quikchat-md-full.cjs.min.js +3 -3
- package/dist/quikchat-md-full.cjs.min.js.map +1 -1
- package/dist/quikchat-md-full.esm.js +98 -5
- package/dist/quikchat-md-full.esm.js.map +1 -1
- package/dist/quikchat-md-full.esm.min.js +3 -3
- package/dist/quikchat-md-full.esm.min.js.map +1 -1
- package/dist/quikchat-md-full.umd.js +98 -5
- package/dist/quikchat-md-full.umd.js.map +1 -1
- package/dist/quikchat-md-full.umd.min.js +3 -3
- package/dist/quikchat-md-full.umd.min.js.map +1 -1
- package/dist/quikchat-md.cjs.js +98 -5
- package/dist/quikchat-md.cjs.js.map +1 -1
- package/dist/quikchat-md.cjs.min.js +3 -3
- package/dist/quikchat-md.cjs.min.js.map +1 -1
- package/dist/quikchat-md.esm.js +98 -5
- package/dist/quikchat-md.esm.js.map +1 -1
- package/dist/quikchat-md.esm.min.js +3 -3
- package/dist/quikchat-md.esm.min.js.map +1 -1
- package/dist/quikchat-md.umd.js +98 -5
- package/dist/quikchat-md.umd.js.map +1 -1
- package/dist/quikchat-md.umd.min.js +3 -3
- package/dist/quikchat-md.umd.min.js.map +1 -1
- package/dist/quikchat.cjs.js +2 -2
- package/dist/quikchat.cjs.js.map +1 -1
- package/dist/quikchat.cjs.min.js +1 -1
- package/dist/quikchat.cjs.min.js.map +1 -1
- package/dist/quikchat.esm.js +2 -2
- package/dist/quikchat.esm.js.map +1 -1
- package/dist/quikchat.esm.min.js +1 -1
- package/dist/quikchat.esm.min.js.map +1 -1
- package/dist/quikchat.umd.js +2 -2
- package/dist/quikchat.umd.js.map +1 -1
- package/dist/quikchat.umd.min.js +1 -1
- package/dist/quikchat.umd.min.js.map +1 -1
- package/package.json +2 -2
|
@@ -1062,9 +1062,9 @@ var quikchat = /*#__PURE__*/function () {
|
|
|
1062
1062
|
key: "version",
|
|
1063
1063
|
value: function version() {
|
|
1064
1064
|
return {
|
|
1065
|
-
"version": "1.2.
|
|
1065
|
+
"version": "1.2.7",
|
|
1066
1066
|
"license": "BSD-2",
|
|
1067
|
-
"url": "https://github/deftio/quikchat"
|
|
1067
|
+
"url": "https://github.com/deftio/quikchat"
|
|
1068
1068
|
};
|
|
1069
1069
|
}
|
|
1070
1070
|
|
|
@@ -1122,7 +1122,7 @@ var quikchat = /*#__PURE__*/function () {
|
|
|
1122
1122
|
|
|
1123
1123
|
/**
|
|
1124
1124
|
* quikdown_bd - Bidirectional Markdown Parser
|
|
1125
|
-
* @version 1.2.
|
|
1125
|
+
* @version 1.2.12
|
|
1126
1126
|
* @license BSD-2-Clause
|
|
1127
1127
|
* @copyright DeftIO 2025
|
|
1128
1128
|
*/
|
|
@@ -1234,7 +1234,7 @@ function isDashHRLine(trimmed) {
|
|
|
1234
1234
|
// ────────────────────────────────────────────────────────────────────
|
|
1235
1235
|
|
|
1236
1236
|
/** Build-time version stamp (injected by tools/updateVersion) */
|
|
1237
|
-
const quikdownVersion = '1.2.
|
|
1237
|
+
const quikdownVersion = '1.2.12';
|
|
1238
1238
|
|
|
1239
1239
|
/** CSS class prefix used for all generated elements */
|
|
1240
1240
|
const CLASS_PREFIX = 'quikdown-';
|
|
@@ -1242,6 +1242,10 @@ const CLASS_PREFIX = 'quikdown-';
|
|
|
1242
1242
|
/** Placeholder sigils — chosen to be extremely unlikely in real text */
|
|
1243
1243
|
const PLACEHOLDER_CB = '§CB'; // fenced code blocks
|
|
1244
1244
|
const PLACEHOLDER_IC = '§IC'; // inline code spans
|
|
1245
|
+
const PLACEHOLDER_HT = '§HT'; // safe HTML tags (limited mode)
|
|
1246
|
+
|
|
1247
|
+
/** Attributes whose values need URL sanitization */
|
|
1248
|
+
const URL_ATTRIBUTES = { href:1, src:1, action:1, formaction:1 };
|
|
1245
1249
|
|
|
1246
1250
|
/** HTML entity escape map */
|
|
1247
1251
|
const ESC_MAP = {'&':'&','<':'<','>':'>','"':'"',"'":'''};
|
|
@@ -1376,6 +1380,46 @@ function quikdown(markdown, options = {}) {
|
|
|
1376
1380
|
return trimmedUrl;
|
|
1377
1381
|
}
|
|
1378
1382
|
|
|
1383
|
+
/**
|
|
1384
|
+
* Sanitize attributes on an HTML tag string for limited mode.
|
|
1385
|
+
* Strips on* event handlers (case-insensitive) and runs sanitizeUrl()
|
|
1386
|
+
* on href/src/action/formaction values.
|
|
1387
|
+
*/
|
|
1388
|
+
function sanitizeHtmlTagAttrs(tagStr) {
|
|
1389
|
+
// Self-closing or void tag without attributes — pass through
|
|
1390
|
+
if (!/\s/.test(tagStr.replace(/<\/?[a-zA-Z][a-zA-Z0-9]*/, '').replace(/\/?>$/, ''))) {
|
|
1391
|
+
return tagStr;
|
|
1392
|
+
}
|
|
1393
|
+
// Parse: <tagname ...attrs... > or <tagname ...attrs... />
|
|
1394
|
+
const m = tagStr.match(/^(<\/?[a-zA-Z][a-zA-Z0-9]*)([\s\S]*?)(\/?>)$/);
|
|
1395
|
+
/* istanbul ignore next - defensive: Phase 1.5 regex guarantees valid tag shape */
|
|
1396
|
+
if (!m) return tagStr;
|
|
1397
|
+
|
|
1398
|
+
const [, open, attrStr, close] = m;
|
|
1399
|
+
// Match individual attributes: name="value", name='value', name=value, or bare name
|
|
1400
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- linear: no nested quantifiers
|
|
1401
|
+
const attrRe = /([a-zA-Z_][\w\-.:]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
|
|
1402
|
+
const attrs = [];
|
|
1403
|
+
let am;
|
|
1404
|
+
while ((am = attrRe.exec(attrStr)) !== null) {
|
|
1405
|
+
const name = am[1];
|
|
1406
|
+
const value = am[2] !== undefined ? am[2] : am[3] !== undefined ? am[3] : am[4];
|
|
1407
|
+
// Strip event handlers (on*)
|
|
1408
|
+
if (/^on/i.test(name)) continue;
|
|
1409
|
+
if (value === undefined) {
|
|
1410
|
+
// Boolean attribute (e.g. disabled, checked)
|
|
1411
|
+
attrs.push(name);
|
|
1412
|
+
} else {
|
|
1413
|
+
let sanitized = value;
|
|
1414
|
+
if (name.toLowerCase() in URL_ATTRIBUTES) {
|
|
1415
|
+
sanitized = sanitizeUrl(value);
|
|
1416
|
+
}
|
|
1417
|
+
attrs.push(`${name}="${sanitized}"`);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return open + (attrs.length ? ' ' + attrs.join(' ') : '') + close;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1379
1423
|
// ────────────────────────────────────────────────────────────────
|
|
1380
1424
|
// Phase 1 — Code Extraction
|
|
1381
1425
|
// ────────────────────────────────────────────────────────────────
|
|
@@ -1427,17 +1471,57 @@ function quikdown(markdown, options = {}) {
|
|
|
1427
1471
|
return placeholder;
|
|
1428
1472
|
});
|
|
1429
1473
|
|
|
1474
|
+
// ────────────────────────────────────────────────────────────────
|
|
1475
|
+
// Phase 1.5 — Safe HTML Extraction (whitelist mode)
|
|
1476
|
+
// ────────────────────────────────────────────────────────────────
|
|
1477
|
+
// When allow_unsafe_html is an object or array, extract whitelisted
|
|
1478
|
+
// HTML tags, sanitize their attributes, and replace with placeholders.
|
|
1479
|
+
// Non-whitelisted tags stay in text so Phase 2 will escape them.
|
|
1480
|
+
|
|
1481
|
+
const safeTags = [];
|
|
1482
|
+
// Normalize: array → object for O(1) lookup; object used as-is
|
|
1483
|
+
const htmlAllow = Array.isArray(allow_unsafe_html)
|
|
1484
|
+
? Object.fromEntries(allow_unsafe_html.map(t => [t, 1]))
|
|
1485
|
+
: (allow_unsafe_html && typeof allow_unsafe_html === 'object') ? allow_unsafe_html : null;
|
|
1486
|
+
|
|
1487
|
+
if (htmlAllow) {
|
|
1488
|
+
// Pass through HTML comments — browsers render them as nothing
|
|
1489
|
+
html = html.replace(/<!--[\s\S]*?-->/g, (match) => {
|
|
1490
|
+
const idx = safeTags.length;
|
|
1491
|
+
safeTags.push(match);
|
|
1492
|
+
return `${PLACEHOLDER_HT}${idx}§`;
|
|
1493
|
+
});
|
|
1494
|
+
html = html.replace(/<\/?([a-zA-Z][a-zA-Z0-9]*)\b[^>]*\/?>/g, (match, tagName) => {
|
|
1495
|
+
if (tagName.toLowerCase() in htmlAllow) {
|
|
1496
|
+
const sanitized = sanitizeHtmlTagAttrs(match);
|
|
1497
|
+
const idx = safeTags.length;
|
|
1498
|
+
safeTags.push(sanitized);
|
|
1499
|
+
return `${PLACEHOLDER_HT}${idx}§`;
|
|
1500
|
+
}
|
|
1501
|
+
// Not whitelisted — leave in text for Phase 2 to escape
|
|
1502
|
+
return match;
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1430
1506
|
// ────────────────────────────────────────────────────────────────
|
|
1431
1507
|
// Phase 2 — HTML Escaping
|
|
1432
1508
|
// ────────────────────────────────────────────────────────────────
|
|
1433
1509
|
// All remaining text (everything except code placeholders) is escaped
|
|
1434
1510
|
// to prevent XSS. The `allow_unsafe_html` option skips this for
|
|
1435
1511
|
// trusted pipelines that intentionally embed raw HTML.
|
|
1512
|
+
// For whitelist mode, escaping still runs (only `true` bypasses it).
|
|
1436
1513
|
|
|
1437
|
-
if (
|
|
1514
|
+
if (allow_unsafe_html !== true) {
|
|
1438
1515
|
html = escapeHtml(html);
|
|
1439
1516
|
}
|
|
1440
1517
|
|
|
1518
|
+
// Restore safe HTML tag placeholders after escaping
|
|
1519
|
+
if (htmlAllow) {
|
|
1520
|
+
safeTags.forEach((tag, i) => {
|
|
1521
|
+
html = html.replace(`${PLACEHOLDER_HT}${i}§`, tag);
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1441
1525
|
// ────────────────────────────────────────────────────────────────
|
|
1442
1526
|
// Phase 3 — Block Scanning + Inline Formatting + Paragraphs
|
|
1443
1527
|
// ────────────────────────────────────────────────────────────────
|
|
@@ -1679,6 +1763,14 @@ function scanLineBlocks(text, getAttr, dataQd) {
|
|
|
1679
1763
|
while (i < lines.length) {
|
|
1680
1764
|
const line = lines[i];
|
|
1681
1765
|
|
|
1766
|
+
// ── Markdown comment (reference-link hack) ──
|
|
1767
|
+
// [//]: # (comment) or [//]: # "comment" or [//]: #
|
|
1768
|
+
// These produce no output — standard markdown comment convention.
|
|
1769
|
+
if (/^\[\/\/\]: #/.test(line)) {
|
|
1770
|
+
i++;
|
|
1771
|
+
continue;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1682
1774
|
// ── Heading ──
|
|
1683
1775
|
// Count leading '#' characters. Valid heading: 1-6 hashes then a space.
|
|
1684
1776
|
// Example: "## Hello World ##" → <h2>Hello World</h2>
|
|
@@ -2043,6 +2135,7 @@ quikdown.configure = function(options) {
|
|
|
2043
2135
|
/** Semantic version (injected at build time) */
|
|
2044
2136
|
quikdown.version = quikdownVersion;
|
|
2045
2137
|
|
|
2138
|
+
|
|
2046
2139
|
// ════════════════════════════════════════════════════════════════════
|
|
2047
2140
|
// Exports
|
|
2048
2141
|
// ════════════════════════════════════════════════════════════════════
|