stream-markdown-parser 0.0.73 → 0.0.74

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/dist/index.js CHANGED
@@ -601,7 +601,7 @@ var require_markdown_it_task_checkbox = /* @__PURE__ */ __commonJS({ "../../node
601
601
  }) });
602
602
 
603
603
  //#endregion
604
- //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.7/node_modules/markdown-it-ts/dist/chunk-Bp6m_JJh.js
604
+ //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.9/node_modules/markdown-it-ts/dist/chunk-Bp6m_JJh.js
605
605
  var import_markdown_it_task_checkbox = /* @__PURE__ */ __toESM(require_markdown_it_task_checkbox(), 1);
606
606
  var __defProp = Object.defineProperty;
607
607
  var __export = (all) => {
@@ -2187,7 +2187,7 @@ var require_punycode = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/pu
2187
2187
  }) });
2188
2188
 
2189
2189
  //#endregion
2190
- //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.7/node_modules/markdown-it-ts/dist/index.js
2190
+ //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.9/node_modules/markdown-it-ts/dist/index.js
2191
2191
  var import_punycode = /* @__PURE__ */ __toESM(require_punycode(), 1);
2192
2192
  var utils_exports = /* @__PURE__ */ __export({
2193
2193
  arrayReplaceAt: () => arrayReplaceAt,
@@ -2445,10 +2445,23 @@ function parseLinkDestination(str, start, max) {
2445
2445
  return result;
2446
2446
  }
2447
2447
  var parse_link_destination_default = parseLinkDestination;
2448
- /**
2449
- * Parse link label: returns the end position of label or -1 if not found
2450
- * Assumes first character ([) already matches
2451
- */
2448
+ const FALLBACK_TO_INLINE_SCAN = -2;
2449
+ function scanPlainLinkLabel(src, start, max) {
2450
+ let pos = start + 1;
2451
+ while (pos < max) {
2452
+ const marker = src.charCodeAt(pos);
2453
+ if (marker === 93) return pos;
2454
+ if (marker === 92) {
2455
+ pos += 2;
2456
+ continue;
2457
+ }
2458
+ if (marker === 96 || marker === 60) return FALLBACK_TO_INLINE_SCAN;
2459
+ if (marker === 33 && pos + 1 < max && src.charCodeAt(pos + 1) === 91) return FALLBACK_TO_INLINE_SCAN;
2460
+ if (marker === 91) return FALLBACK_TO_INLINE_SCAN;
2461
+ pos++;
2462
+ }
2463
+ return -1;
2464
+ }
2452
2465
  function parseLinkLabel(state, start, disableNested) {
2453
2466
  let level = 1;
2454
2467
  let found = false;
@@ -2457,7 +2470,15 @@ function parseLinkLabel(state, start, disableNested) {
2457
2470
  const src = state.src;
2458
2471
  const max = state.posMax;
2459
2472
  const oldPos = state.pos;
2460
- const inline$1 = state.md.inline;
2473
+ const noCloseFrom = state.__mdtsLinkLabelNoCloseFrom;
2474
+ if (typeof noCloseFrom === "number" && start + 1 >= noCloseFrom) return -1;
2475
+ const nextClose = src.indexOf("]", start + 1);
2476
+ if (nextClose < 0 || nextClose >= max) {
2477
+ state.__mdtsLinkLabelNoCloseFrom = start + 1;
2478
+ return -1;
2479
+ }
2480
+ const fastLabelEnd = scanPlainLinkLabel(src, start, max);
2481
+ if (fastLabelEnd !== FALLBACK_TO_INLINE_SCAN) return fastLabelEnd;
2461
2482
  state.pos = start + 1;
2462
2483
  while (state.pos < max) {
2463
2484
  marker = src.charCodeAt(state.pos);
@@ -2469,7 +2490,7 @@ function parseLinkLabel(state, start, disableNested) {
2469
2490
  }
2470
2491
  }
2471
2492
  prevPos = state.pos;
2472
- inline$1.skipToken(state);
2493
+ state.md.inline.skipToken(state);
2473
2494
  if (marker === 91) {
2474
2495
  if (prevPos === state.pos - 1) level++;
2475
2496
  else if (disableNested) {
@@ -2600,6 +2621,12 @@ function normalizeLinkText(url) {
2600
2621
  }
2601
2622
  return decode_default(format(parsed), `${decode_default.defaultChars}%`);
2602
2623
  }
2624
+ function setStrategyDiagnostics(env, info) {
2625
+ if (!env) return;
2626
+ try {
2627
+ env.__mdtsStrategyInfo = info;
2628
+ } catch {}
2629
+ }
2603
2630
  /**
2604
2631
  * class Token
2605
2632
  *
@@ -2787,42 +2814,22 @@ function block(state) {
2787
2814
  /**
2788
2815
  * Process autolinks '<protocol:...>'
2789
2816
  */
2790
- const EMAIL_RE = /^[\w.!#$%&'*+/=?^`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i;
2791
- function isAsciiLetter$2(ch) {
2792
- const lc = ch | 32;
2793
- return lc >= 97 && lc <= 122;
2794
- }
2795
- function isAutolinkScheme(src, start, end) {
2796
- const len = end - start;
2797
- if (len < 2 || len > 32 || !isAsciiLetter$2(src.charCodeAt(start))) return false;
2798
- for (let pos = start + 1; pos < end; pos++) {
2799
- const ch = src.charCodeAt(pos);
2800
- if (ch >= 48 && ch <= 57 || ch >= 65 && ch <= 90 || ch >= 97 && ch <= 122 || ch === 43 || ch === 45 || ch === 46) continue;
2801
- return false;
2802
- }
2803
- return true;
2804
- }
2817
+ const EMAIL_RE = /^([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/;
2818
+ const AUTOLINK_RE = /^([a-zA-Z][a-zA-Z0-9+.-]{1,31}):([^<>\x00-\x20]*)$/;
2805
2819
  function autolink(state, silent) {
2806
2820
  let pos = state.pos;
2807
2821
  const src = state.src;
2808
2822
  if (src.charCodeAt(pos) !== 60) return false;
2809
2823
  const start = pos;
2810
2824
  const max = state.posMax;
2811
- let colonPos = -1;
2812
- let hasAt = false;
2813
2825
  for (;;) {
2814
2826
  if (++pos >= max) return false;
2815
2827
  const ch = src.charCodeAt(pos);
2816
2828
  if (ch === 60) return false;
2817
2829
  if (ch === 62) break;
2818
- if (ch <= 32) return false;
2819
- if (ch === 58) {
2820
- if (colonPos < 0) colonPos = pos;
2821
- } else if (ch === 64) hasAt = true;
2822
- }
2823
- if (colonPos < 0 && !hasAt) return false;
2824
- if (colonPos > start + 2 && isAutolinkScheme(src, start + 1, colonPos)) {
2825
- const url = src.slice(start + 1, pos);
2830
+ }
2831
+ const url = src.slice(start + 1, pos);
2832
+ if (AUTOLINK_RE.test(url)) {
2826
2833
  const fullUrl = state.md.normalizeLink(url);
2827
2834
  if (!state.md.validateLink(fullUrl)) return false;
2828
2835
  if (!silent) {
@@ -2830,18 +2837,16 @@ function autolink(state, silent) {
2830
2837
  token_o.attrs = [["href", fullUrl]];
2831
2838
  token_o.markup = "autolink";
2832
2839
  token_o.info = "auto";
2833
- const token_t = state.pushSimple("text", "");
2840
+ const token_t = state.push("text", "", 0);
2834
2841
  token_t.content = state.md.normalizeLinkText(url);
2835
2842
  const token_c = state.push("link_close", "a", -1);
2836
2843
  token_c.markup = "autolink";
2837
2844
  token_c.info = "auto";
2838
2845
  }
2839
- state.pos = pos + 1;
2846
+ state.pos += url.length + 2;
2840
2847
  return true;
2841
2848
  }
2842
- if (hasAt) {
2843
- const url = src.slice(start + 1, pos);
2844
- if (!EMAIL_RE.test(url)) return false;
2849
+ if (EMAIL_RE.test(url)) {
2845
2850
  const fullUrl = state.md.normalizeLink(`mailto:${url}`);
2846
2851
  if (!state.md.validateLink(fullUrl)) return false;
2847
2852
  if (!silent) {
@@ -2849,13 +2854,13 @@ function autolink(state, silent) {
2849
2854
  token_o.attrs = [["href", fullUrl]];
2850
2855
  token_o.markup = "autolink";
2851
2856
  token_o.info = "auto";
2852
- const token_t = state.pushSimple("text", "");
2857
+ const token_t = state.push("text", "", 0);
2853
2858
  token_t.content = state.md.normalizeLinkText(url);
2854
2859
  const token_c = state.push("link_close", "a", -1);
2855
2860
  token_c.markup = "autolink";
2856
2861
  token_c.info = "auto";
2857
2862
  }
2858
- state.pos = pos + 1;
2863
+ state.pos += url.length + 2;
2859
2864
  return true;
2860
2865
  }
2861
2866
  return false;
@@ -3641,9 +3646,11 @@ var text_default = text;
3641
3646
  var InlineRuler = class {
3642
3647
  rules = [];
3643
3648
  cache = null;
3649
+ namedCache = null;
3644
3650
  version = 0;
3645
3651
  invalidateCache() {
3646
3652
  this.cache = null;
3653
+ this.namedCache = null;
3647
3654
  this.version++;
3648
3655
  }
3649
3656
  /**
@@ -3743,6 +3750,11 @@ var InlineRuler = class {
3743
3750
  if (!this.cache) this.compileCache();
3744
3751
  return this.cache.get(chain) ?? [];
3745
3752
  }
3753
+ getNamedRules(chainName) {
3754
+ const chain = chainName || "";
3755
+ if (!this.namedCache) this.compileCache();
3756
+ return this.namedCache.get(chain) ?? [];
3757
+ }
3746
3758
  compileCache() {
3747
3759
  const chains = new Set([""]);
3748
3760
  for (const rule of this.rules) {
@@ -3750,16 +3762,24 @@ var InlineRuler = class {
3750
3762
  if (rule.alt) for (const alt of rule.alt) chains.add(alt);
3751
3763
  }
3752
3764
  const cache = /* @__PURE__ */ new Map();
3765
+ const namedCache = /* @__PURE__ */ new Map();
3753
3766
  for (const chain of chains) {
3754
3767
  const bucket = [];
3768
+ const namedBucket = [];
3755
3769
  for (const rule of this.rules) {
3756
3770
  if (!rule.enabled) continue;
3757
3771
  if (chain !== "" && !rule.alt?.includes(chain)) continue;
3758
3772
  bucket.push(rule.fn);
3773
+ namedBucket.push({
3774
+ name: rule.name,
3775
+ fn: rule.fn
3776
+ });
3759
3777
  }
3760
3778
  cache.set(chain, bucket);
3779
+ namedCache.set(chain, namedBucket);
3761
3780
  }
3762
3781
  this.cache = cache;
3782
+ this.namedCache = namedCache;
3763
3783
  }
3764
3784
  };
3765
3785
  /**
@@ -3871,6 +3891,78 @@ var StateInline = class {
3871
3891
  }
3872
3892
  };
3873
3893
  StateInline.prototype.Token = Token;
3894
+ function now() {
3895
+ if (typeof performance !== "undefined" && typeof performance.now === "function") return performance.now();
3896
+ return Date.now();
3897
+ }
3898
+ function median(values) {
3899
+ if (values.length === 0) return 0;
3900
+ const sorted = values.slice().sort((a, b) => a - b);
3901
+ const mid = Math.floor(sorted.length / 2);
3902
+ return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
3903
+ }
3904
+ function createRecord(chain, name) {
3905
+ return {
3906
+ chain,
3907
+ name,
3908
+ calls: 0,
3909
+ hits: 0,
3910
+ inclusiveMs: 0,
3911
+ medianMs: 0,
3912
+ maxMs: 0,
3913
+ normalCalls: 0,
3914
+ normalHits: 0,
3915
+ silentCalls: 0,
3916
+ silentHits: 0,
3917
+ samples: []
3918
+ };
3919
+ }
3920
+ function getRuleProfile(env) {
3921
+ const carrier = env;
3922
+ if (!carrier) return null;
3923
+ if (carrier.__mdtsRuleProfile) return carrier.__mdtsRuleProfile;
3924
+ if (!carrier.__mdtsProfileRules) return null;
3925
+ const meta = carrier.__mdtsProfileRules === true ? {} : carrier.__mdtsProfileRules;
3926
+ const session = {
3927
+ enabled: true,
3928
+ fixture: meta.fixture,
3929
+ mode: meta.mode,
3930
+ startedAt: now(),
3931
+ records: Object.create(null)
3932
+ };
3933
+ carrier.__mdtsRuleProfile = session;
3934
+ return session;
3935
+ }
3936
+ function recordRuleInvocation(env, chain, name, durationMs, hit, silent) {
3937
+ const session = getRuleProfile(env);
3938
+ if (!session) return;
3939
+ const key = `${chain}:${name}`;
3940
+ const record = session.records[key] ?? (session.records[key] = createRecord(chain, name));
3941
+ record.calls++;
3942
+ record.inclusiveMs += durationMs;
3943
+ if (durationMs > record.maxMs) record.maxMs = durationMs;
3944
+ record.samples.push(durationMs);
3945
+ if (silent) {
3946
+ record.silentCalls++;
3947
+ if (hit) record.silentHits++;
3948
+ } else {
3949
+ record.normalCalls++;
3950
+ if (hit) record.normalHits++;
3951
+ }
3952
+ if (hit) record.hits++;
3953
+ session.completedAt = now();
3954
+ }
3955
+ function finalizeRuleProfile(env) {
3956
+ const session = getRuleProfile(env);
3957
+ if (!session) return null;
3958
+ const records = Object.keys(session.records);
3959
+ for (let i = 0; i < records.length; i++) {
3960
+ const record = session.records[records[i]];
3961
+ record.medianMs = median(record.samples);
3962
+ }
3963
+ session.completedAt = now();
3964
+ return session;
3965
+ }
3874
3966
  /**
3875
3967
  * ParserInline - inline parser with Ruler-based rule management
3876
3968
  */
@@ -3939,9 +4031,11 @@ var ParserInline = class {
3939
4031
  skipToken(state) {
3940
4032
  const pos = state.pos;
3941
4033
  const rules = this.getRules();
4034
+ const namedRules = this.ruler.getNamedRules("");
3942
4035
  const len = rules.length;
3943
4036
  const cache = state.cache;
3944
4037
  const cached = cache[pos];
4038
+ const shouldProfile = !!state.env && (Object.prototype.hasOwnProperty.call(state.env, "__mdtsRuleProfile") || Object.prototype.hasOwnProperty.call(state.env, "__mdtsProfileRules"));
3945
4039
  if (cached !== void 0) {
3946
4040
  state.pos = cached;
3947
4041
  return;
@@ -3949,7 +4043,13 @@ var ParserInline = class {
3949
4043
  let ok = false;
3950
4044
  if (state.level < state.maxNesting) for (let i = 0; i < len; i++) {
3951
4045
  state.level++;
3952
- ok = rules[i](state, true);
4046
+ if (!shouldProfile) ok = rules[i](state, true);
4047
+ else {
4048
+ const startedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
4049
+ ok = namedRules[i].fn(state, true);
4050
+ const endedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
4051
+ recordRuleInvocation(state.env, "inline", namedRules[i].name, endedAt - startedAt, !!ok, true);
4052
+ }
3953
4053
  state.level--;
3954
4054
  if (ok) {
3955
4055
  if (pos >= state.pos) throw new Error("inline rule didn't increment state.pos");
@@ -3965,13 +4065,21 @@ var ParserInline = class {
3965
4065
  */
3966
4066
  tokenize(state) {
3967
4067
  const rules = this.getRules();
4068
+ const namedRules = this.ruler.getNamedRules("");
3968
4069
  const len = rules.length;
3969
4070
  const end = state.posMax;
4071
+ const shouldProfile = !!state.env && (Object.prototype.hasOwnProperty.call(state.env, "__mdtsRuleProfile") || Object.prototype.hasOwnProperty.call(state.env, "__mdtsProfileRules"));
3970
4072
  while (state.pos < end) {
3971
4073
  const prevPos = state.pos;
3972
4074
  let ok = false;
3973
4075
  if (state.level < state.maxNesting) for (let i = 0; i < len; i++) {
3974
- ok = rules[i](state, false);
4076
+ if (!shouldProfile) ok = rules[i](state, false);
4077
+ else {
4078
+ const startedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
4079
+ ok = namedRules[i].fn(state, false);
4080
+ const endedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
4081
+ recordRuleInvocation(state.env, "inline", namedRules[i].name, endedAt - startedAt, !!ok, false);
4082
+ }
3975
4083
  if (ok) {
3976
4084
  if (prevPos >= state.pos) throw new Error("inline rule didn't increment state.pos");
3977
4085
  break;
@@ -4001,8 +4109,16 @@ var ParserInline = class {
4001
4109
  const state = new StateInline(src, md, env, outTokens);
4002
4110
  this.tokenize(state);
4003
4111
  const rules2 = this.getRules2();
4112
+ const namedRules2 = this.ruler2.getNamedRules("");
4004
4113
  const len = rules2.length;
4005
- for (let i = 0; i < len; i++) rules2[i](state, false);
4114
+ const shouldProfile = !!state.env && (Object.prototype.hasOwnProperty.call(state.env, "__mdtsRuleProfile") || Object.prototype.hasOwnProperty.call(state.env, "__mdtsProfileRules"));
4115
+ for (let i = 0; i < len; i++) if (!shouldProfile) rules2[i](state, false);
4116
+ else {
4117
+ const startedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
4118
+ namedRules2[i].fn(state, false);
4119
+ const endedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
4120
+ recordRuleInvocation(state.env, "inline2", namedRules2[i].name, endedAt - startedAt, true, false);
4121
+ }
4006
4122
  }
4007
4123
  parse(str, md, env, outTokens) {
4008
4124
  this.parseSource(str, md, env, outTokens);
@@ -4199,8 +4315,10 @@ function replacements(state) {
4199
4315
  var CoreRuler = class {
4200
4316
  rules = [];
4201
4317
  cache = null;
4318
+ namedCache = null;
4202
4319
  invalidateCache() {
4203
4320
  this.cache = null;
4321
+ this.namedCache = null;
4204
4322
  }
4205
4323
  push(name, fn) {
4206
4324
  const idx = this.rules.findIndex((r) => r.name === name);
@@ -4290,11 +4408,19 @@ var CoreRuler = class {
4290
4408
  }
4291
4409
  compileCache() {
4292
4410
  this.cache = this.rules.filter((r) => r.enabled).map((r) => r.fn);
4411
+ this.namedCache = this.rules.filter((r) => r.enabled).map((r) => ({
4412
+ name: r.name,
4413
+ fn: r.fn
4414
+ }));
4293
4415
  }
4294
4416
  getRules(_chainName = "") {
4295
4417
  if (!this.cache) this.compileCache();
4296
4418
  return this.cache;
4297
4419
  }
4420
+ getNamedRules(_chainName = "") {
4421
+ if (!this.namedCache) this.compileCache();
4422
+ return this.namedCache;
4423
+ }
4298
4424
  };
4299
4425
  const QUOTE_TEST_RE = /['"]/;
4300
4426
  const QUOTE_RE = /['"]/g;
@@ -4449,7 +4575,7 @@ function blockquote(state, startLine, endLine, silent) {
4449
4575
  const oldBSCount = [];
4450
4576
  const oldSCount = [];
4451
4577
  const oldTShift = [];
4452
- const terminatorRules = state.md.block.ruler.getRules("blockquote");
4578
+ const terminatorRules = state.md.block.ruler.getRulesForState(state, "blockquote");
4453
4579
  const oldParentType = state.parentType;
4454
4580
  state.parentType = "blockquote";
4455
4581
  let lastLineEmpty = false;
@@ -4839,19 +4965,46 @@ function html_block(state, startLine, endLine, silent) {
4839
4965
  token.content = state.getLines(startLine, nextLine, state.blkIndent, true);
4840
4966
  return true;
4841
4967
  }
4968
+ function hasPipeOnLine(src, start, max) {
4969
+ for (let pos = start; pos < max; pos++) if (src.charCodeAt(pos) === 124) return true;
4970
+ return false;
4971
+ }
4972
+ function canUseParagraphTerminatorFastPath(state) {
4973
+ const ruler = state?.md?.block?.ruler;
4974
+ if (!ruler) return false;
4975
+ return ruler.version === ruler.__mdtsDefaultVersion;
4976
+ }
4977
+ function couldTerminateParagraph(src, start, max) {
4978
+ if (start >= max) return false;
4979
+ const marker = src.charCodeAt(start);
4980
+ switch (marker) {
4981
+ case 35:
4982
+ case 42:
4983
+ case 43:
4984
+ case 45:
4985
+ case 60:
4986
+ case 62:
4987
+ case 95:
4988
+ case 96:
4989
+ case 126: return true;
4990
+ }
4991
+ if (marker >= 48 && marker <= 57) return true;
4992
+ return hasPipeOnLine(src, start, max);
4993
+ }
4842
4994
  const HEADING_TAGS = [
4843
4995
  "",
4844
4996
  "h1",
4845
4997
  "h2"
4846
4998
  ];
4847
4999
  function lheading(state, startLine, endLine) {
4848
- const terminatorRules = state.md.block.ruler.getRules("paragraph");
5000
+ const terminatorRules = state.md.block.ruler.getRulesForState(state, "paragraph");
4849
5001
  const src = state.src;
4850
5002
  const bMarks = state.bMarks;
4851
5003
  const tShift = state.tShift;
4852
5004
  const eMarks = state.eMarks;
4853
5005
  const sCount = state.sCount;
4854
5006
  const blkIndent = state.blkIndent;
5007
+ const canUseFastTerminatorHint = canUseParagraphTerminatorFastPath(state);
4855
5008
  if (sCount[startLine] - blkIndent >= 4) return false;
4856
5009
  const oldParentType = state.parentType;
4857
5010
  state.parentType = "paragraph";
@@ -4883,6 +5036,7 @@ function lheading(state, startLine, endLine) {
4883
5036
  }
4884
5037
  }
4885
5038
  if (sCount[nextLine] < 0) continue;
5039
+ if (canUseFastTerminatorHint && !couldTerminateParagraph(src, lineStart, max)) continue;
4886
5040
  let terminate = false;
4887
5041
  for (let i = 0, l = terminatorRules.length; i < l; i++) if (terminatorRules[i](state, nextLine, endLine, true)) {
4888
5042
  terminate = true;
@@ -5054,7 +5208,7 @@ function list(state, startLine, endLine, silent) {
5054
5208
  state.tokens[state.tokens.length - 1].markup = markerMarkup;
5055
5209
  let prevEmptyEnd = false;
5056
5210
  const listTokIdx = state.tokens.length - 1;
5057
- const terminatorRules = state.md.block.ruler.getRules("list");
5211
+ const terminatorRules = state.md.block.ruler.getRulesForState(state, "list");
5058
5212
  const oldParentType = state.parentType;
5059
5213
  state.parentType = "list";
5060
5214
  while (nextLine < endLine) {
@@ -5160,7 +5314,7 @@ function isSpace$3(code$1) {
5160
5314
  return code$1 === 9 || code$1 === 32;
5161
5315
  }
5162
5316
  function paragraph(state, startLine, endLine) {
5163
- const terminatorRules = state.md.block.ruler.getRules("paragraph");
5317
+ const terminatorRules = state.md.block.ruler.getRulesForState(state, "paragraph");
5164
5318
  const oldParentType = state.parentType;
5165
5319
  const src = state.src;
5166
5320
  const bMarks = state.bMarks;
@@ -5168,34 +5322,35 @@ function paragraph(state, startLine, endLine) {
5168
5322
  const eMarks = state.eMarks;
5169
5323
  const sCount = state.sCount;
5170
5324
  const blkIndent = state.blkIndent;
5325
+ const canUseFastTerminatorHint = canUseParagraphTerminatorFastPath(state);
5171
5326
  let nextLine = startLine + 1;
5172
5327
  state.parentType = "paragraph";
5173
5328
  for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
5174
5329
  if (sCount[nextLine] - blkIndent > 3) continue;
5175
5330
  if (sCount[nextLine] < 0) continue;
5176
5331
  if (oldParentType === "list" && sCount[nextLine] >= blkIndent) {
5177
- const start = bMarks[nextLine] + tShift[nextLine];
5178
- const max = eMarks[nextLine];
5179
- if (start < max) {
5180
- const marker = src.charCodeAt(start);
5332
+ const start$1 = bMarks[nextLine] + tShift[nextLine];
5333
+ const max$1 = eMarks[nextLine];
5334
+ if (start$1 < max$1) {
5335
+ const marker = src.charCodeAt(start$1);
5181
5336
  if (marker === 42 || marker === 45 || marker === 43) {
5182
- if (start + 1 >= max || isSpace$3(src.charCodeAt(start + 1))) break;
5183
- } else if (marker >= 48 && marker <= 57 && start + 1 < max) {
5184
- let pos = start + 1;
5337
+ if (start$1 + 1 >= max$1 || isSpace$3(src.charCodeAt(start$1 + 1))) break;
5338
+ } else if (marker >= 48 && marker <= 57 && start$1 + 1 < max$1) {
5339
+ let pos = start$1 + 1;
5185
5340
  for (;;) {
5186
- if (pos >= max) {
5341
+ if (pos >= max$1) {
5187
5342
  pos = -1;
5188
5343
  break;
5189
5344
  }
5190
5345
  const ch = src.charCodeAt(pos++);
5191
5346
  if (ch >= 48 && ch <= 57) {
5192
- if (pos - start >= 10) {
5347
+ if (pos - start$1 >= 10) {
5193
5348
  pos = -1;
5194
5349
  break;
5195
5350
  }
5196
5351
  continue;
5197
5352
  }
5198
- if ((ch === 41 || ch === 46) && (pos >= max || isSpace$3(src.charCodeAt(pos)))) break;
5353
+ if ((ch === 41 || ch === 46) && (pos >= max$1 || isSpace$3(src.charCodeAt(pos)))) break;
5199
5354
  pos = -1;
5200
5355
  break;
5201
5356
  }
@@ -5203,6 +5358,9 @@ function paragraph(state, startLine, endLine) {
5203
5358
  }
5204
5359
  }
5205
5360
  }
5361
+ const start = bMarks[nextLine] + tShift[nextLine];
5362
+ const max = eMarks[nextLine];
5363
+ if (canUseFastTerminatorHint && !couldTerminateParagraph(src, start, max)) continue;
5206
5364
  let terminate = false;
5207
5365
  for (let i = 0, l = terminatorRules.length; i < l; i++) if (terminatorRules[i](state, nextLine, endLine, true)) {
5208
5366
  terminate = true;
@@ -5233,7 +5391,7 @@ function reference(state, startLine, _endLine, silent) {
5233
5391
  let pos = state.bMarks[startLine] + state.tShift[startLine];
5234
5392
  let max = state.eMarks[startLine];
5235
5393
  let nextLine = startLine + 1;
5236
- const terminatorRules = state.md.block.ruler.getRules("reference");
5394
+ const terminatorRules = state.md.block.ruler.getRulesForState(state, "reference");
5237
5395
  if (state.sCount[startLine] - state.blkIndent >= 4) return false;
5238
5396
  if (state.src.charCodeAt(pos) !== 91) return false;
5239
5397
  function getNextLine(nextLine$1) {
@@ -5444,7 +5602,7 @@ function table(state, startLine, endLine, silent) {
5444
5602
  if (silent) return true;
5445
5603
  const oldParentType = state.parentType;
5446
5604
  state.parentType = "table";
5447
- const terminatorRules = state.md.block.ruler.getRules("blockquote");
5605
+ const terminatorRules = state.md.block.ruler.getRulesForState(state, "blockquote");
5448
5606
  const token_to = state.push("table_open", "table", 1);
5449
5607
  const tableLines = [startLine, 0];
5450
5608
  token_to.map = tableLines;
@@ -5506,15 +5664,14 @@ function table(state, startLine, endLine, silent) {
5506
5664
  state.line = nextLine;
5507
5665
  return true;
5508
5666
  }
5509
- /**
5510
- * Block-level rule management with Ruler pattern
5511
- */
5512
5667
  var BlockRuler = class {
5513
5668
  _rules = [];
5514
5669
  cache = null;
5670
+ namedCache = null;
5515
5671
  version = 0;
5516
5672
  invalidateCache() {
5517
5673
  this.cache = null;
5674
+ this.namedCache = null;
5518
5675
  this.version++;
5519
5676
  }
5520
5677
  push(name, fn, options) {
@@ -5557,6 +5714,24 @@ var BlockRuler = class {
5557
5714
  if (!this.cache) this.compileCache();
5558
5715
  return this.cache[chain] ?? [];
5559
5716
  }
5717
+ getNamedRules(chainName) {
5718
+ const chain = chainName || "";
5719
+ if (!this.namedCache) this.compileCache();
5720
+ return this.namedCache[chain] ?? [];
5721
+ }
5722
+ getRulesForState(state, chainName) {
5723
+ const env = state?.env;
5724
+ if (!(!!env && (Object.prototype.hasOwnProperty.call(env, "__mdtsRuleProfile") || Object.prototype.hasOwnProperty.call(env, "__mdtsProfileRules")))) return this.getRules(chainName);
5725
+ return this.getNamedRules(chainName).map(({ name, fn }) => {
5726
+ return (currentState, startLine, endLine, silent) => {
5727
+ const startedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
5728
+ const ok = fn(currentState, startLine, endLine, silent);
5729
+ const endedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
5730
+ recordRuleInvocation(currentState?.env, "block", name, endedAt - startedAt, ok, !!silent);
5731
+ return ok;
5732
+ };
5733
+ });
5734
+ }
5560
5735
  at(name, fn, options) {
5561
5736
  const index = this._rules.findIndex((r) => r.name === name);
5562
5737
  if (index === -1) throw new Error(`Parser rule not found: ${name}`);
@@ -5617,16 +5792,24 @@ var BlockRuler = class {
5617
5792
  for (const alt of rule.alt) chains.add(alt);
5618
5793
  }
5619
5794
  const cache = Object.create(null);
5795
+ const namedCache = Object.create(null);
5620
5796
  for (const chain of chains) {
5621
5797
  const bucket = [];
5798
+ const namedBucket = [];
5622
5799
  for (const rule of this._rules) {
5623
5800
  if (!rule.enabled) continue;
5624
5801
  if (chain !== "" && !rule.alt.includes(chain)) continue;
5625
5802
  bucket.push(rule.fn);
5803
+ namedBucket.push({
5804
+ name: rule.name,
5805
+ fn: rule.fn
5806
+ });
5626
5807
  }
5627
5808
  cache[chain] = bucket;
5809
+ namedCache[chain] = namedBucket;
5628
5810
  }
5629
5811
  this.cache = cache;
5812
+ this.namedCache = namedCache;
5630
5813
  }
5631
5814
  };
5632
5815
  function isSpace(code$1) {
@@ -5871,12 +6054,14 @@ var ParserBlock = class {
5871
6054
  constructor() {
5872
6055
  this.ruler = new BlockRuler();
5873
6056
  for (let i = 0; i < _rules.length; i++) this.ruler.push(_rules[i][0], _rules[i][1], { alt: (_rules[i][2] || []).slice() });
6057
+ this.ruler.__mdtsDefaultVersion = this.ruler.version;
5874
6058
  }
5875
6059
  /**
5876
6060
  * Generate tokens for input range
5877
6061
  */
5878
6062
  tokenize(state, startLine, endLine) {
5879
6063
  const rules = this.getRules();
6064
+ const namedRules = this.ruler.getNamedRules("");
5880
6065
  const len = rules.length;
5881
6066
  const maxNesting = state.md.options.maxNesting;
5882
6067
  const bMarks = state.bMarks;
@@ -5885,6 +6070,7 @@ var ParserBlock = class {
5885
6070
  const sCount = state.sCount;
5886
6071
  let line = startLine;
5887
6072
  let hasEmptyLines = false;
6073
+ const shouldProfile = !!state.env && (Object.prototype.hasOwnProperty.call(state.env, "__mdtsRuleProfile") || Object.prototype.hasOwnProperty.call(state.env, "__mdtsProfileRules"));
5888
6074
  while (line < endLine) {
5889
6075
  while (line < endLine && bMarks[line] + tShift[line] >= eMarks[line]) line++;
5890
6076
  state.line = line;
@@ -5897,7 +6083,13 @@ var ParserBlock = class {
5897
6083
  const prevLine = state.line;
5898
6084
  let ok = false;
5899
6085
  for (let i = 0; i < len; i++) {
5900
- ok = rules[i](state, line, endLine, false);
6086
+ if (!shouldProfile) ok = rules[i](state, line, endLine, false);
6087
+ else {
6088
+ const startedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
6089
+ ok = namedRules[i].fn(state, line, endLine, false);
6090
+ const endedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
6091
+ recordRuleInvocation(state.env, "block", namedRules[i].name, endedAt - startedAt, ok, false);
6092
+ }
5901
6093
  if (ok) {
5902
6094
  if (prevLine >= state.line) throw new Error("block rule didn't increment state.line");
5903
6095
  break;
@@ -6019,7 +6211,19 @@ var ParserCore = class {
6019
6211
  }
6020
6212
  process(state) {
6021
6213
  const rules = this.cachedCoreRules ?? (this.cachedCoreRules = this.ruler.getRules(""));
6022
- for (let i = 0; i < rules.length; i++) rules[i](state);
6214
+ const namedRules = this.ruler.getNamedRules("");
6215
+ const shouldProfile = !!state.env && (Object.prototype.hasOwnProperty.call(state.env, "__mdtsRuleProfile") || Object.prototype.hasOwnProperty.call(state.env, "__mdtsProfileRules"));
6216
+ for (let i = 0; i < rules.length; i++) {
6217
+ if (!shouldProfile) {
6218
+ rules[i](state);
6219
+ continue;
6220
+ }
6221
+ const startedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
6222
+ namedRules[i].fn(state);
6223
+ const endedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
6224
+ recordRuleInvocation(state.env, "core", namedRules[i].name, endedAt - startedAt, true, false);
6225
+ }
6226
+ finalizeRuleProfile(state.env);
6023
6227
  }
6024
6228
  parseSource(src, env = {}, md) {
6025
6229
  if (typeof src !== "string" && hasNormalizationChars(src)) return this.parse(sourceToString(src), env, md);
@@ -7065,8 +7269,8 @@ const FULL_DISCRETE_RECOMMENDATIONS = [
7065
7269
  {
7066
7270
  max: 5e5,
7067
7271
  strategy: "discrete",
7068
- maxChunkChars: 32e3,
7069
- maxChunkLines: 350,
7272
+ maxChunkChars: 64e3,
7273
+ maxChunkLines: 700,
7070
7274
  maxChunks: 16,
7071
7275
  notes: "<=500k"
7072
7276
  },
@@ -7091,26 +7295,34 @@ const STREAM_DISCRETE_RECOMMENDATIONS = [
7091
7295
  {
7092
7296
  max: 2e4,
7093
7297
  strategy: "discrete",
7094
- maxChunkChars: 16e3,
7298
+ maxChunkChars: 2e4,
7095
7299
  maxChunkLines: 200,
7096
- maxChunks: 12,
7300
+ maxChunks: 24,
7097
7301
  notes: "<=20k"
7098
7302
  },
7099
7303
  {
7100
- max: 5e4,
7304
+ max: 1e5,
7101
7305
  strategy: "discrete",
7102
- maxChunkChars: 16e3,
7103
- maxChunkLines: 250,
7104
- maxChunks: 12,
7105
- notes: "<=50k"
7306
+ maxChunkChars: 2e4,
7307
+ maxChunkLines: 200,
7308
+ maxChunks: 24,
7309
+ notes: "<=100k"
7106
7310
  },
7107
7311
  {
7108
7312
  max: 5e5,
7109
7313
  strategy: "discrete",
7110
- maxChunkChars: 32e3,
7111
- maxChunkLines: 350,
7112
- maxChunks: 16,
7314
+ maxChunkChars: 64e3,
7315
+ maxChunkLines: 700,
7316
+ maxChunks: 32,
7113
7317
  notes: "<=500k"
7318
+ },
7319
+ {
7320
+ max: 5e6,
7321
+ strategy: "discrete",
7322
+ maxChunkChars: 64e3,
7323
+ maxChunkLines: 700,
7324
+ maxChunks: 32,
7325
+ notes: "<=5M"
7114
7326
  }
7115
7327
  ];
7116
7328
  function toRecommendation(fenceAware, discrete) {
@@ -7166,10 +7378,10 @@ function recommendStreamChunkStrategy(sizeChars, sizeLines = Math.max(0, sizeCha
7166
7378
  break;
7167
7379
  }
7168
7380
  }
7169
- if (sizeChars > 5e5) return {
7381
+ if (sizeChars > 5e6) return {
7170
7382
  strategy: "plain",
7171
7383
  fenceAware,
7172
- notes: ">500k plain"
7384
+ notes: ">5M plain"
7173
7385
  };
7174
7386
  if (adaptive) return {
7175
7387
  strategy: "adaptive",
@@ -7584,6 +7796,7 @@ function makeEmptyStats() {
7584
7796
  total: 0,
7585
7797
  cacheHits: 0,
7586
7798
  appendHits: 0,
7799
+ unboundedAppendHits: 0,
7587
7800
  tailHits: 0,
7588
7801
  fullParses: 0,
7589
7802
  resets: 0,
@@ -7596,12 +7809,16 @@ var StreamParser = class {
7596
7809
  cache = null;
7597
7810
  stats = makeEmptyStats();
7598
7811
  MIN_SIZE_FOR_OPTIMIZATION = 1e3;
7599
- DEFAULT_SKIP_CACHE_CHARS = 6e5;
7600
- DEFAULT_SKIP_CACHE_LINES = 1e4;
7812
+ DEFAULT_SKIP_CACHE_CHARS = 1e6;
7813
+ DEFAULT_SKIP_CACHE_LINES = 1e5;
7814
+ IMPLICIT_STREAM_CHUNK_MIN_CHARS = 16e4;
7601
7815
  MIN_LIST_LINES_FOR_MERGE = 80;
7602
7816
  MIN_LIST_CHARS_FOR_MERGE = 800;
7603
7817
  MIN_TABLE_LINES_FOR_MERGE = 48;
7604
7818
  MIN_TABLE_CHARS_FOR_MERGE = 1200;
7819
+ MIN_UNBOUNDED_APPEND_TOTAL_CHARS = 5e5;
7820
+ MIN_UNBOUNDED_APPEND_CHARS = 64e3;
7821
+ MIN_UNBOUNDED_APPEND_LINES = 700;
7605
7822
  constructor(core) {
7606
7823
  this.core = core;
7607
7824
  }
@@ -7620,7 +7837,10 @@ var StreamParser = class {
7620
7837
  const cached = this.cache;
7621
7838
  if (!cached || envProvided && envProvided !== cached.env) {
7622
7839
  const workingEnv = envProvided ?? {};
7623
- const chunkedEnabled$1 = !!md.options?.streamChunkedFallback;
7840
+ const explicitChunkFallbackSetting$1 = !!md.__explicitStreamChunkFallbackSetting;
7841
+ const wantsChunking$1 = !!md.options?.streamChunkedFallback;
7842
+ const allowImplicitChunk$1 = !explicitChunkFallbackSetting$1;
7843
+ const chunkedEnabled$1 = wantsChunking$1 || allowImplicitChunk$1;
7624
7844
  const chunkAdaptive$1 = md.options?.streamChunkAdaptive !== false;
7625
7845
  const targetChunks$1 = md.options?.streamChunkTargetChunks ?? 8;
7626
7846
  const chunkSizeCharsCfg$1 = md.options?.streamChunkSizeChars;
@@ -7642,6 +7862,12 @@ var StreamParser = class {
7642
7862
  this.stats.total += 1;
7643
7863
  this.stats.fullParses += 1;
7644
7864
  this.stats.lastMode = "full";
7865
+ setStrategyDiagnostics(workingEnv, {
7866
+ area: "stream",
7867
+ path: "stream-full",
7868
+ reason: "skip-cache-large-one-shot",
7869
+ unbounded: !!workingEnv.__mdtsUnboundedInfo
7870
+ });
7645
7871
  return parsed$2.tokens;
7646
7872
  } else if (chunkedEnabled$1) {
7647
7873
  const clamp$1 = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
@@ -7651,7 +7877,8 @@ var StreamParser = class {
7651
7877
  const useLines = recommendation?.maxChunkLines ?? (chunkAdaptive$1 ? clamp$1(Math.ceil(srcLineCount / targetChunks$1), 150, 700) : chunkSizeLinesCfg$1 ?? 200);
7652
7878
  const useMaxChunks = recommendation?.maxChunks ?? (chunkAdaptive$1 ? clamp$1(Math.ceil(src.length / 64e3), targetChunks$1, 32) : chunkMaxChunksCfg$1);
7653
7879
  const hasTrailingNewline = src.length > 0 && src.charCodeAt(src.length - 1) === 10;
7654
- if (recommendation?.strategy !== "plain" && (src.length >= useChars * 2 || srcLineCount >= useLines * 2) && hasTrailingNewline) {
7880
+ const shouldAutoChunk = allowImplicitChunk$1 && src.length >= this.IMPLICIT_STREAM_CHUNK_MIN_CHARS && recommendation?.strategy !== "plain";
7881
+ if ((wantsChunking$1 || shouldAutoChunk) && (src.length >= useChars * 2 || srcLineCount >= useLines * 2) && hasTrailingNewline) {
7655
7882
  const tokens = chunkedParse(md, src, workingEnv, {
7656
7883
  maxChunkChars: useChars,
7657
7884
  maxChunkLines: useLines,
@@ -7669,6 +7896,12 @@ var StreamParser = class {
7669
7896
  this.stats.total += 1;
7670
7897
  this.stats.chunkedParses = (this.stats.chunkedParses || 0) + 1;
7671
7898
  this.stats.lastMode = "chunked";
7899
+ setStrategyDiagnostics(workingEnv, {
7900
+ area: "stream",
7901
+ path: "stream-chunked",
7902
+ chunked: true,
7903
+ reason: wantsChunking$1 ? "explicit-initial-large-doc" : "default-initial-large-doc"
7904
+ });
7672
7905
  return tokens;
7673
7906
  }
7674
7907
  }
@@ -7685,12 +7918,23 @@ var StreamParser = class {
7685
7918
  this.stats.total += 1;
7686
7919
  this.stats.fullParses += 1;
7687
7920
  this.stats.lastMode = "full";
7921
+ setStrategyDiagnostics(workingEnv, {
7922
+ area: "stream",
7923
+ path: "stream-full",
7924
+ reason: "initial-parse",
7925
+ unbounded: !!workingEnv.__mdtsUnboundedInfo
7926
+ });
7688
7927
  return parsed$1.tokens;
7689
7928
  }
7690
7929
  if (src === cached.src) {
7691
7930
  this.stats.total += 1;
7692
7931
  this.stats.cacheHits += 1;
7693
7932
  this.stats.lastMode = "cache";
7933
+ setStrategyDiagnostics(cached.env, {
7934
+ area: "stream",
7935
+ path: "stream-cache",
7936
+ reason: "same-source"
7937
+ });
7694
7938
  return cached.tokens;
7695
7939
  }
7696
7940
  const threshold = md.options?.streamOptimizationMinSize ?? this.MIN_SIZE_FOR_OPTIMIZATION;
@@ -7710,6 +7954,12 @@ var StreamParser = class {
7710
7954
  this.stats.total += 1;
7711
7955
  this.stats.fullParses += 1;
7712
7956
  this.stats.lastMode = "full";
7957
+ setStrategyDiagnostics(fallbackEnv$1, {
7958
+ area: "stream",
7959
+ path: "stream-full",
7960
+ reason: "small-non-append",
7961
+ unbounded: !!fallbackEnv$1.__mdtsUnboundedInfo
7962
+ });
7713
7963
  return nextTokens$1;
7714
7964
  }
7715
7965
  const appended = this.getAppendedSegment(cached.src, src);
@@ -7819,6 +8069,7 @@ var StreamParser = class {
7819
8069
  return appendedLineCount;
7820
8070
  };
7821
8071
  const canDirectParseAppend = this.canDirectlyParseAppend(cached);
8072
+ const useUnboundedAppend = canDirectParseAppend && this.shouldUseUnboundedAppend(src, cached, appended);
7822
8073
  let shouldAttemptContext = false;
7823
8074
  if (!canDirectParseAppend) switch (ctxStrategy) {
7824
8075
  case "lines":
@@ -7854,10 +8105,15 @@ var StreamParser = class {
7854
8105
  }
7855
8106
  } else appendedState = null;
7856
8107
  if (!appendedState) {
7857
- const simpleState = this.core.parse(appended, cached.env, md);
7858
8108
  const lineOffset = cachedLineCount;
7859
- if (lineOffset > 0) this.shiftTokenLines(simpleState.tokens, lineOffset);
7860
- appendedState = simpleState;
8109
+ if (useUnboundedAppend) {
8110
+ appendedState = { tokens: parseStringUnbounded(md, appended, cached.env, { mode: "stream" }) };
8111
+ if (lineOffset > 0) this.shiftTokenLines(appendedState.tokens, lineOffset);
8112
+ } else {
8113
+ const simpleState = this.core.parse(appended, cached.env, md);
8114
+ if (lineOffset > 0) this.shiftTokenLines(simpleState.tokens, lineOffset);
8115
+ appendedState = simpleState;
8116
+ }
7861
8117
  }
7862
8118
  if (cached.tokens.length > 0 && appendedState.tokens.length > 0) {
7863
8119
  const lastCached = cached.tokens[cached.tokens.length - 1];
@@ -7866,7 +8122,7 @@ var StreamParser = class {
7866
8122
  if (lastCached.type === "inline" && firstApp.type === "inline") {
7867
8123
  if (firstApp.children && firstApp.children.length > 0) {
7868
8124
  if (!lastCached.children) lastCached.children = [];
7869
- lastCached.children.push(...firstApp.children);
8125
+ this.appendTokens(lastCached.children, firstApp.children);
7870
8126
  }
7871
8127
  lastCached.content = (lastCached.content || "") + (firstApp.content || "");
7872
8128
  appendedState.tokens.shift();
@@ -7901,7 +8157,7 @@ var StreamParser = class {
7901
8157
  }
7902
8158
  }
7903
8159
  if (dup > 0) a.splice(0, dup);
7904
- if (a.length > 0) cached.tokens.push(...a);
8160
+ if (a.length > 0) this.appendTokens(cached.tokens, a);
7905
8161
  }
7906
8162
  cached.src = src;
7907
8163
  cached.lineCount = cachedLineCount + (appendedLineCount ?? countLines(appended));
@@ -7918,7 +8174,14 @@ var StreamParser = class {
7918
8174
  } else cached.lastSegment = void 0;
7919
8175
  this.stats.total += 1;
7920
8176
  this.stats.appendHits += 1;
8177
+ if (useUnboundedAppend) this.stats.unboundedAppendHits = (this.stats.unboundedAppendHits || 0) + 1;
7921
8178
  this.stats.lastMode = "append";
8179
+ setStrategyDiagnostics(cached.env, {
8180
+ area: "stream",
8181
+ path: useUnboundedAppend ? "stream-unbounded-append" : "stream-append",
8182
+ reason: useUnboundedAppend ? "large-delta" : "safe-append",
8183
+ unbounded: useUnboundedAppend
8184
+ });
7922
8185
  return cached.tokens;
7923
8186
  }
7924
8187
  const fallbackEnv = envProvided ?? cached.env;
@@ -7927,9 +8190,17 @@ var StreamParser = class {
7927
8190
  this.stats.total += 1;
7928
8191
  this.stats.tailHits += 1;
7929
8192
  this.stats.lastMode = "tail";
8193
+ setStrategyDiagnostics(fallbackEnv, {
8194
+ area: "stream",
8195
+ path: "stream-tail",
8196
+ reason: "tail-reparse"
8197
+ });
7930
8198
  return tailReparsed;
7931
8199
  }
7932
- const chunkedEnabled = !!md.options?.streamChunkedFallback;
8200
+ const explicitChunkFallbackSetting = !!md.__explicitStreamChunkFallbackSetting;
8201
+ const wantsChunking = !!md.options?.streamChunkedFallback;
8202
+ const allowImplicitChunk = !explicitChunkFallbackSetting && !appended;
8203
+ const chunkedEnabled = wantsChunking || allowImplicitChunk;
7933
8204
  const chunkAdaptive = md.options?.streamChunkAdaptive !== false;
7934
8205
  const targetChunks = md.options?.streamChunkTargetChunks ?? 8;
7935
8206
  const chunkSizeCharsCfg = md.options?.streamChunkSizeChars;
@@ -7947,7 +8218,8 @@ var StreamParser = class {
7947
8218
  const useLines = recommendation?.maxChunkLines ?? (chunkAdaptive ? clamp$1(Math.ceil(srcLineCount2 / targetChunks), 150, 700) : chunkSizeLinesCfg ?? 200);
7948
8219
  const useMaxChunks = recommendation?.maxChunks ?? (chunkAdaptive ? clamp$1(Math.ceil(src.length / 64e3), targetChunks, 32) : chunkMaxChunksCfg);
7949
8220
  const hasTrailingNewline2 = src.length > 0 && src.charCodeAt(src.length - 1) === 10;
7950
- if (recommendation?.strategy !== "plain" && (src.length >= useChars * 2 || srcLineCount2 >= useLines * 2) && hasTrailingNewline2) {
8221
+ const shouldAutoChunk = allowImplicitChunk && src.length >= this.IMPLICIT_STREAM_CHUNK_MIN_CHARS && recommendation?.strategy !== "plain";
8222
+ if ((wantsChunking || shouldAutoChunk) && (src.length >= useChars * 2 || srcLineCount2 >= useLines * 2) && hasTrailingNewline2) {
7951
8223
  const tokens = chunkedParse(md, src, fallbackEnv, {
7952
8224
  maxChunkChars: useChars,
7953
8225
  maxChunkLines: useLines,
@@ -7965,6 +8237,12 @@ var StreamParser = class {
7965
8237
  this.stats.total += 1;
7966
8238
  this.stats.chunkedParses = (this.stats.chunkedParses || 0) + 1;
7967
8239
  this.stats.lastMode = "chunked";
8240
+ setStrategyDiagnostics(fallbackEnv, {
8241
+ area: "stream",
8242
+ path: "stream-chunked",
8243
+ chunked: true,
8244
+ reason: wantsChunking ? "explicit-fallback-large-doc" : "default-fallback-large-doc"
8245
+ });
7968
8246
  return tokens;
7969
8247
  }
7970
8248
  }
@@ -7982,21 +8260,43 @@ var StreamParser = class {
7982
8260
  this.stats.total += 1;
7983
8261
  this.stats.fullParses += 1;
7984
8262
  this.stats.lastMode = "full";
8263
+ setStrategyDiagnostics(fallbackEnv, {
8264
+ area: "stream",
8265
+ path: "stream-full",
8266
+ reason: "fallback-full",
8267
+ unbounded: !!fallbackEnv.__mdtsUnboundedInfo
8268
+ });
7985
8269
  return nextTokens;
7986
8270
  }
7987
8271
  parseFullDocument(src, env, md, knownLineCount, needLineCount = true) {
7988
8272
  const autoUnboundedDecision = getAutoUnboundedDecision(md, src.length, knownLineCount);
7989
- if (autoUnboundedDecision === "yes") return {
7990
- tokens: parseStringUnbounded(md, src, env),
7991
- lineCount: knownLineCount ?? (needLineCount ? countLines(src) : 0)
7992
- };
8273
+ if (autoUnboundedDecision === "yes") {
8274
+ setStrategyDiagnostics(env, {
8275
+ area: "stream",
8276
+ path: "stream-full",
8277
+ reason: "auto-unbounded-char-threshold",
8278
+ unbounded: true
8279
+ });
8280
+ return {
8281
+ tokens: parseStringUnbounded(md, src, env),
8282
+ lineCount: knownLineCount ?? (needLineCount ? countLines(src) : 0)
8283
+ };
8284
+ }
7993
8285
  let lineCount = knownLineCount;
7994
8286
  if (autoUnboundedDecision === "need-lines") {
7995
8287
  lineCount = countLines(src);
7996
- if (shouldAutoUseUnbounded(md, src.length, lineCount)) return {
7997
- tokens: parseStringUnbounded(md, src, env),
7998
- lineCount
7999
- };
8288
+ if (shouldAutoUseUnbounded(md, src.length, lineCount)) {
8289
+ setStrategyDiagnostics(env, {
8290
+ area: "stream",
8291
+ path: "stream-full",
8292
+ reason: "auto-unbounded-line-threshold",
8293
+ unbounded: true
8294
+ });
8295
+ return {
8296
+ tokens: parseStringUnbounded(md, src, env),
8297
+ lineCount
8298
+ };
8299
+ }
8000
8300
  }
8001
8301
  if (lineCount === void 0) lineCount = needLineCount ? countLines(src) : 0;
8002
8302
  return {
@@ -8004,6 +8304,12 @@ var StreamParser = class {
8004
8304
  lineCount
8005
8305
  };
8006
8306
  }
8307
+ shouldUseUnboundedAppend(src, _cached, appended) {
8308
+ if (!appended) return false;
8309
+ if (src.length < this.MIN_UNBOUNDED_APPEND_TOTAL_CHARS && appended.length < this.MIN_UNBOUNDED_APPEND_CHARS) return false;
8310
+ if (appended.length >= this.MIN_UNBOUNDED_APPEND_CHARS) return true;
8311
+ return countLines(appended) >= this.MIN_UNBOUNDED_APPEND_LINES;
8312
+ }
8007
8313
  getAppendedSegment(prev, next) {
8008
8314
  if (!next.startsWith(prev)) return null;
8009
8315
  if (!prev.endsWith("\n")) return null;
@@ -8052,7 +8358,7 @@ var StreamParser = class {
8052
8358
  cached.src = src;
8053
8359
  cached.env = env;
8054
8360
  cached.tokens.length = lastSegment.tokenStart;
8055
- cached.tokens.push(...tailState.tokens);
8361
+ this.appendTokens(cached.tokens, tailState.tokens);
8056
8362
  cached.lineCount = countLines(src);
8057
8363
  if (localLastSegment) cached.lastSegment = {
8058
8364
  tokenStart: lastSegment.tokenStart + localLastSegment.tokenStart,
@@ -8118,6 +8424,9 @@ var StreamParser = class {
8118
8424
  getStats() {
8119
8425
  return { ...this.stats };
8120
8426
  }
8427
+ appendTokens(target, source) {
8428
+ for (let i = 0; i < source.length; i++) target.push(source[i]);
8429
+ }
8121
8430
  updateCacheLineCount(cache, lineCount) {
8122
8431
  cache.lineCount = lineCount ?? countLines(cache.src);
8123
8432
  cache.lastSegment = void 0;
@@ -8431,6 +8740,9 @@ function hasExplicitChunkOverride(presetOptions, userOptions, keys) {
8431
8740
  }
8432
8741
  return false;
8433
8742
  }
8743
+ function hasExplicitOption(presetOptions, userOptions, key) {
8744
+ return !!userOptions && Object.prototype.hasOwnProperty.call(userOptions, key) || !!presetOptions && Object.prototype.hasOwnProperty.call(presetOptions, key);
8745
+ }
8434
8746
  function markdownIt(presetName, options) {
8435
8747
  let opts = {
8436
8748
  html: false,
@@ -8451,8 +8763,8 @@ function markdownIt(presetName, options) {
8451
8763
  streamChunkAdaptive: true,
8452
8764
  streamChunkTargetChunks: 8,
8453
8765
  streamChunkMaxChunks: void 0,
8454
- streamSkipCacheAboveChars: 6e5,
8455
- streamSkipCacheAboveLines: 1e4,
8766
+ streamSkipCacheAboveChars: 1e6,
8767
+ streamSkipCacheAboveLines: 1e5,
8456
8768
  fullChunkedFallback: false,
8457
8769
  fullChunkThresholdChars: 2e4,
8458
8770
  fullChunkThresholdLines: 400,
@@ -8511,6 +8823,8 @@ function markdownIt(presetName, options) {
8511
8823
  "streamChunkSizeLines",
8512
8824
  "streamChunkMaxChunks"
8513
8825
  ]);
8826
+ let explicitFullChunkFallbackSetting = hasExplicitOption(preset?.options, userOptions, "fullChunkedFallback");
8827
+ let explicitStreamChunkFallbackSetting = hasExplicitOption(preset?.options, userOptions, "streamChunkedFallback");
8514
8828
  const core = new ParserCore();
8515
8829
  let renderer = null;
8516
8830
  const getRenderer = () => {
@@ -8552,6 +8866,8 @@ function markdownIt(presetName, options) {
8552
8866
  options: opts,
8553
8867
  __explicitFullChunkConfig: explicitFullChunkConfig,
8554
8868
  __explicitStreamChunkConfig: explicitStreamChunkConfig,
8869
+ __explicitFullChunkFallbackSetting: explicitFullChunkFallbackSetting,
8870
+ __explicitStreamChunkFallbackSetting: explicitStreamChunkFallbackSetting,
8555
8871
  set(newOpts) {
8556
8872
  this.options = {
8557
8873
  ...this.options,
@@ -8565,6 +8881,14 @@ function markdownIt(presetName, options) {
8565
8881
  explicitStreamChunkConfig = true;
8566
8882
  this.__explicitStreamChunkConfig = true;
8567
8883
  }
8884
+ if (Object.prototype.hasOwnProperty.call(newOpts, "fullChunkedFallback")) {
8885
+ explicitFullChunkFallbackSetting = true;
8886
+ this.__explicitFullChunkFallbackSetting = true;
8887
+ }
8888
+ if (Object.prototype.hasOwnProperty.call(newOpts, "streamChunkedFallback")) {
8889
+ explicitStreamChunkFallbackSetting = true;
8890
+ this.__explicitStreamChunkFallbackSetting = true;
8891
+ }
8568
8892
  if (renderer) renderer.set(newOpts);
8569
8893
  if (typeof newOpts.stream === "boolean") {
8570
8894
  this.stream.enabled = newOpts.stream;
@@ -8657,20 +8981,38 @@ function markdownIt(presetName, options) {
8657
8981
  helpers: { ...helpers_exports },
8658
8982
  parse(src, env = {}) {
8659
8983
  if (typeof src !== "string") throw new TypeError("Input data should be a String");
8984
+ let countedLines;
8660
8985
  if (!this.stream.enabled && !this.options.fullChunkedFallback) {
8661
8986
  const autoUnboundedDecision = getAutoUnboundedDecision(this, src.length);
8662
- if (autoUnboundedDecision === "yes") return parseStringUnbounded(this, src, env);
8663
- if (autoUnboundedDecision === "need-lines" && shouldAutoUseUnbounded(this, src.length, countLines(src))) return parseStringUnbounded(this, src, env);
8664
- return core.parse(src, env, this).tokens;
8987
+ if (autoUnboundedDecision === "yes") {
8988
+ setStrategyDiagnostics(env, {
8989
+ area: "parse",
8990
+ path: "auto-unbounded",
8991
+ unbounded: true,
8992
+ reason: "char-threshold"
8993
+ });
8994
+ return parseStringUnbounded(this, src, env);
8995
+ }
8996
+ if (autoUnboundedDecision === "need-lines") countedLines = countLines(src);
8665
8997
  }
8666
8998
  if (!this.stream.enabled) {
8667
8999
  const chars = src.length;
8668
- if (this.options.fullChunkedFallback) {
8669
- const lines = countLines(src);
8670
- const autoRecommendation = this.options.autoTuneChunks !== false && !explicitFullChunkConfig ? recommendFullChunkStrategy(chars, lines, this.options) : null;
8671
- if (chars >= (this.options.fullChunkThresholdChars ?? 2e4) || lines >= (this.options.fullChunkThresholdLines ?? 400)) {
8672
- if (autoRecommendation) {
8673
- if (autoRecommendation.strategy === "plain") return core.parse(src, env, this).tokens;
9000
+ const lines = countedLines ?? countLines(src);
9001
+ const auto = this.options.autoTuneChunks !== false;
9002
+ const userForcedChunk = explicitFullChunkConfig;
9003
+ const allowImplicitChunk = !explicitFullChunkFallbackSetting;
9004
+ const wantsChunking = !!this.options.fullChunkedFallback;
9005
+ const shouldAutoChunk = allowImplicitChunk && chars >= 2e5;
9006
+ const autoRecommendation = auto && !userForcedChunk ? recommendFullChunkStrategy(chars, lines, this.options) : null;
9007
+ if (wantsChunking || shouldAutoChunk) {
9008
+ if (wantsChunking ? chars >= (this.options.fullChunkThresholdChars ?? 2e4) || lines >= (this.options.fullChunkThresholdLines ?? 400) : shouldAutoChunk) {
9009
+ if (autoRecommendation && autoRecommendation.strategy !== "plain") {
9010
+ setStrategyDiagnostics(env, {
9011
+ area: "parse",
9012
+ path: "full-chunk",
9013
+ chunked: true,
9014
+ reason: wantsChunking ? "explicit-full-chunk" : "default-large-string"
9015
+ });
8674
9016
  return chunkedParse(this, src, env, {
8675
9017
  maxChunkChars: autoRecommendation.maxChunkChars,
8676
9018
  maxChunkLines: autoRecommendation.maxChunkLines,
@@ -8678,23 +9020,45 @@ function markdownIt(presetName, options) {
8678
9020
  maxChunks: autoRecommendation.maxChunks
8679
9021
  });
8680
9022
  }
8681
- const clamp$1 = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
8682
- const adaptive = this.options.fullChunkAdaptive !== false;
8683
- const target = this.options.fullChunkTargetChunks ?? 8;
8684
- const dynMaxChunkChars = clamp$1(Math.ceil(chars / target), 8e3, 64e3);
8685
- const dynMaxChunkLines = clamp$1(Math.ceil(lines / target), 150, 700);
8686
- const maxChunkChars = adaptive ? dynMaxChunkChars : this.options.fullChunkSizeChars ?? 1e4;
8687
- const maxChunkLines = adaptive ? dynMaxChunkLines : this.options.fullChunkSizeLines ?? 200;
8688
- const maxChunks = adaptive ? clamp$1(Math.ceil(chars / 64e3), target, 32) : this.options.fullChunkMaxChunks;
8689
- return chunkedParse(this, src, env, {
8690
- maxChunkChars,
8691
- maxChunkLines,
8692
- fenceAware: this.options.fullChunkFenceAware ?? true,
8693
- maxChunks
8694
- });
9023
+ if (wantsChunking) {
9024
+ const clamp$1 = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
9025
+ const adaptive = this.options.fullChunkAdaptive !== false;
9026
+ const target = this.options.fullChunkTargetChunks ?? 8;
9027
+ const dynMaxChunkChars = clamp$1(Math.ceil(chars / target), 8e3, 64e3);
9028
+ const dynMaxChunkLines = clamp$1(Math.ceil(lines / target), 150, 700);
9029
+ const maxChunkChars = adaptive ? dynMaxChunkChars : this.options.fullChunkSizeChars ?? 1e4;
9030
+ const maxChunkLines = adaptive ? dynMaxChunkLines : this.options.fullChunkSizeLines ?? 200;
9031
+ const maxChunks = adaptive ? clamp$1(Math.ceil(chars / 64e3), target, 32) : this.options.fullChunkMaxChunks;
9032
+ setStrategyDiagnostics(env, {
9033
+ area: "parse",
9034
+ path: "full-chunk",
9035
+ chunked: true,
9036
+ reason: "explicit-full-chunk"
9037
+ });
9038
+ return chunkedParse(this, src, env, {
9039
+ maxChunkChars,
9040
+ maxChunkLines,
9041
+ fenceAware: this.options.fullChunkFenceAware ?? true,
9042
+ maxChunks
9043
+ });
9044
+ }
8695
9045
  }
8696
9046
  }
9047
+ if (countedLines !== void 0 && shouldAutoUseUnbounded(this, chars, lines)) {
9048
+ setStrategyDiagnostics(env, {
9049
+ area: "parse",
9050
+ path: "auto-unbounded",
9051
+ unbounded: true,
9052
+ reason: "line-threshold"
9053
+ });
9054
+ return parseStringUnbounded(this, src, env);
9055
+ }
8697
9056
  }
9057
+ setStrategyDiagnostics(env, {
9058
+ area: "parse",
9059
+ path: "plain",
9060
+ reason: "default-plain"
9061
+ });
8698
9062
  return core.parse(src, env, this).tokens;
8699
9063
  },
8700
9064
  parseIterable(chunks, env = {}) {
@@ -8734,6 +9098,7 @@ function markdownIt(presetName, options) {
8734
9098
  total: 0,
8735
9099
  cacheHits: 0,
8736
9100
  appendHits: 0,
9101
+ unboundedAppendHits: 0,
8737
9102
  tailHits: 0,
8738
9103
  fullParses: 0,
8739
9104
  resets: 0,
@@ -10128,7 +10493,7 @@ function fixLinkToken(tokens) {
10128
10493
  tokens[i - 1].content = "";
10129
10494
  }
10130
10495
  }
10131
- if (curToken.type === "link_close" && curToken.nesting === -1 && tokens[i + 1]?.type === "text" && tokens[i - 1]?.type === "text") {
10496
+ if (curToken.type === "link_close" && curToken.nesting === -1 && tokens[i - 2]?.type === "link_open" && tokens[i + 1]?.type === "text" && tokens[i - 1]?.type === "text") {
10132
10497
  const text$1 = tokens[i - 1].content || "";
10133
10498
  const attrs = tokens[i - 2].attrs || [];
10134
10499
  const href = attrs.find((a) => a[0] === "href")?.[1] || "";