@readme/markdown 15.0.0 → 15.0.2

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.
@@ -20922,72 +20922,319 @@ function push(list, items) {
20922
20922
  return items
20923
20923
  }
20924
20924
 
20925
+ ;// ./node_modules/micromark-util-subtokenize/lib/splice-buffer.js
20926
+ /**
20927
+ * Some of the internal operations of micromark do lots of editing
20928
+ * operations on very large arrays. This runs into problems with two
20929
+ * properties of most circa-2020 JavaScript interpreters:
20930
+ *
20931
+ * - Array-length modifications at the high end of an array (push/pop) are
20932
+ * expected to be common and are implemented in (amortized) time
20933
+ * proportional to the number of elements added or removed, whereas
20934
+ * other operations (shift/unshift and splice) are much less efficient.
20935
+ * - Function arguments are passed on the stack, so adding tens of thousands
20936
+ * of elements to an array with `arr.push(...newElements)` will frequently
20937
+ * cause stack overflows. (see <https://stackoverflow.com/questions/22123769/rangeerror-maximum-call-stack-size-exceeded-why>)
20938
+ *
20939
+ * SpliceBuffers are an implementation of gap buffers, which are a
20940
+ * generalization of the "queue made of two stacks" idea. The splice buffer
20941
+ * maintains a cursor, and moving the cursor has cost proportional to the
20942
+ * distance the cursor moves, but inserting, deleting, or splicing in
20943
+ * new information at the cursor is as efficient as the push/pop operation.
20944
+ * This allows for an efficient sequence of splices (or pushes, pops, shifts,
20945
+ * or unshifts) as long such edits happen at the same part of the array or
20946
+ * generally sweep through the array from the beginning to the end.
20947
+ *
20948
+ * The interface for splice buffers also supports large numbers of inputs by
20949
+ * passing a single array argument rather passing multiple arguments on the
20950
+ * function call stack.
20951
+ *
20952
+ * @template T
20953
+ * Item type.
20954
+ */
20955
+ class SpliceBuffer {
20956
+ /**
20957
+ * @param {ReadonlyArray<T> | null | undefined} [initial]
20958
+ * Initial items (optional).
20959
+ * @returns
20960
+ * Splice buffer.
20961
+ */
20962
+ constructor(initial) {
20963
+ /** @type {Array<T>} */
20964
+ this.left = initial ? [...initial] : [];
20965
+ /** @type {Array<T>} */
20966
+ this.right = [];
20967
+ }
20968
+
20969
+ /**
20970
+ * Array access;
20971
+ * does not move the cursor.
20972
+ *
20973
+ * @param {number} index
20974
+ * Index.
20975
+ * @return {T}
20976
+ * Item.
20977
+ */
20978
+ get(index) {
20979
+ if (index < 0 || index >= this.left.length + this.right.length) {
20980
+ throw new RangeError('Cannot access index `' + index + '` in a splice buffer of size `' + (this.left.length + this.right.length) + '`');
20981
+ }
20982
+ if (index < this.left.length) return this.left[index];
20983
+ return this.right[this.right.length - index + this.left.length - 1];
20984
+ }
20985
+
20986
+ /**
20987
+ * The length of the splice buffer, one greater than the largest index in the
20988
+ * array.
20989
+ */
20990
+ get length() {
20991
+ return this.left.length + this.right.length;
20992
+ }
20993
+
20994
+ /**
20995
+ * Remove and return `list[0]`;
20996
+ * moves the cursor to `0`.
20997
+ *
20998
+ * @returns {T | undefined}
20999
+ * Item, optional.
21000
+ */
21001
+ shift() {
21002
+ this.setCursor(0);
21003
+ return this.right.pop();
21004
+ }
21005
+
21006
+ /**
21007
+ * Slice the buffer to get an array;
21008
+ * does not move the cursor.
21009
+ *
21010
+ * @param {number} start
21011
+ * Start.
21012
+ * @param {number | null | undefined} [end]
21013
+ * End (optional).
21014
+ * @returns {Array<T>}
21015
+ * Array of items.
21016
+ */
21017
+ slice(start, end) {
21018
+ /** @type {number} */
21019
+ const stop = end === null || end === undefined ? Number.POSITIVE_INFINITY : end;
21020
+ if (stop < this.left.length) {
21021
+ return this.left.slice(start, stop);
21022
+ }
21023
+ if (start > this.left.length) {
21024
+ return this.right.slice(this.right.length - stop + this.left.length, this.right.length - start + this.left.length).reverse();
21025
+ }
21026
+ return this.left.slice(start).concat(this.right.slice(this.right.length - stop + this.left.length).reverse());
21027
+ }
21028
+
21029
+ /**
21030
+ * Mimics the behavior of Array.prototype.splice() except for the change of
21031
+ * interface necessary to avoid segfaults when patching in very large arrays.
21032
+ *
21033
+ * This operation moves cursor is moved to `start` and results in the cursor
21034
+ * placed after any inserted items.
21035
+ *
21036
+ * @param {number} start
21037
+ * Start;
21038
+ * zero-based index at which to start changing the array;
21039
+ * negative numbers count backwards from the end of the array and values
21040
+ * that are out-of bounds are clamped to the appropriate end of the array.
21041
+ * @param {number | null | undefined} [deleteCount=0]
21042
+ * Delete count (default: `0`);
21043
+ * maximum number of elements to delete, starting from start.
21044
+ * @param {Array<T> | null | undefined} [items=[]]
21045
+ * Items to include in place of the deleted items (default: `[]`).
21046
+ * @return {Array<T>}
21047
+ * Any removed items.
21048
+ */
21049
+ splice(start, deleteCount, items) {
21050
+ /** @type {number} */
21051
+ const count = deleteCount || 0;
21052
+ this.setCursor(Math.trunc(start));
21053
+ const removed = this.right.splice(this.right.length - count, Number.POSITIVE_INFINITY);
21054
+ if (items) chunkedPush(this.left, items);
21055
+ return removed.reverse();
21056
+ }
21057
+
21058
+ /**
21059
+ * Remove and return the highest-numbered item in the array, so
21060
+ * `list[list.length - 1]`;
21061
+ * Moves the cursor to `length`.
21062
+ *
21063
+ * @returns {T | undefined}
21064
+ * Item, optional.
21065
+ */
21066
+ pop() {
21067
+ this.setCursor(Number.POSITIVE_INFINITY);
21068
+ return this.left.pop();
21069
+ }
21070
+
21071
+ /**
21072
+ * Inserts a single item to the high-numbered side of the array;
21073
+ * moves the cursor to `length`.
21074
+ *
21075
+ * @param {T} item
21076
+ * Item.
21077
+ * @returns {undefined}
21078
+ * Nothing.
21079
+ */
21080
+ push(item) {
21081
+ this.setCursor(Number.POSITIVE_INFINITY);
21082
+ this.left.push(item);
21083
+ }
21084
+
21085
+ /**
21086
+ * Inserts many items to the high-numbered side of the array.
21087
+ * Moves the cursor to `length`.
21088
+ *
21089
+ * @param {Array<T>} items
21090
+ * Items.
21091
+ * @returns {undefined}
21092
+ * Nothing.
21093
+ */
21094
+ pushMany(items) {
21095
+ this.setCursor(Number.POSITIVE_INFINITY);
21096
+ chunkedPush(this.left, items);
21097
+ }
21098
+
21099
+ /**
21100
+ * Inserts a single item to the low-numbered side of the array;
21101
+ * Moves the cursor to `0`.
21102
+ *
21103
+ * @param {T} item
21104
+ * Item.
21105
+ * @returns {undefined}
21106
+ * Nothing.
21107
+ */
21108
+ unshift(item) {
21109
+ this.setCursor(0);
21110
+ this.right.push(item);
21111
+ }
21112
+
21113
+ /**
21114
+ * Inserts many items to the low-numbered side of the array;
21115
+ * moves the cursor to `0`.
21116
+ *
21117
+ * @param {Array<T>} items
21118
+ * Items.
21119
+ * @returns {undefined}
21120
+ * Nothing.
21121
+ */
21122
+ unshiftMany(items) {
21123
+ this.setCursor(0);
21124
+ chunkedPush(this.right, items.reverse());
21125
+ }
21126
+
21127
+ /**
21128
+ * Move the cursor to a specific position in the array. Requires
21129
+ * time proportional to the distance moved.
21130
+ *
21131
+ * If `n < 0`, the cursor will end up at the beginning.
21132
+ * If `n > length`, the cursor will end up at the end.
21133
+ *
21134
+ * @param {number} n
21135
+ * Position.
21136
+ * @return {undefined}
21137
+ * Nothing.
21138
+ */
21139
+ setCursor(n) {
21140
+ if (n === this.left.length || n > this.left.length && this.right.length === 0 || n < 0 && this.left.length === 0) return;
21141
+ if (n < this.left.length) {
21142
+ // Move cursor to the this.left
21143
+ const removed = this.left.splice(n, Number.POSITIVE_INFINITY);
21144
+ chunkedPush(this.right, removed.reverse());
21145
+ } else {
21146
+ // Move cursor to the this.right
21147
+ const removed = this.right.splice(this.left.length + this.right.length - n, Number.POSITIVE_INFINITY);
21148
+ chunkedPush(this.left, removed.reverse());
21149
+ }
21150
+ }
21151
+ }
21152
+
21153
+ /**
21154
+ * Avoid stack overflow by pushing items onto the stack in segments
21155
+ *
21156
+ * @template T
21157
+ * Item type.
21158
+ * @param {Array<T>} list
21159
+ * List to inject into.
21160
+ * @param {ReadonlyArray<T>} right
21161
+ * Items to inject.
21162
+ * @return {undefined}
21163
+ * Nothing.
21164
+ */
21165
+ function chunkedPush(list, right) {
21166
+ /** @type {number} */
21167
+ let chunkStart = 0;
21168
+ if (right.length < 10000) {
21169
+ list.push(...right);
21170
+ } else {
21171
+ while (chunkStart < right.length) {
21172
+ list.push(...right.slice(chunkStart, chunkStart + 10000));
21173
+ chunkStart += 10000;
21174
+ }
21175
+ }
21176
+ }
20925
21177
  ;// ./node_modules/micromark-util-subtokenize/index.js
20926
21178
  /**
20927
- * @typedef {import('micromark-util-types').Chunk} Chunk
20928
- * @typedef {import('micromark-util-types').Event} Event
20929
- * @typedef {import('micromark-util-types').Token} Token
21179
+ * @import {Chunk, Event, Token} from 'micromark-util-types'
20930
21180
  */
20931
21181
 
20932
21182
 
21183
+
21184
+
21185
+ // Hidden API exposed for testing.
21186
+
21187
+
20933
21188
  /**
20934
21189
  * Tokenize subcontent.
20935
21190
  *
20936
- * @param {Array<Event>} events
21191
+ * @param {Array<Event>} eventsArray
20937
21192
  * List of events.
20938
21193
  * @returns {boolean}
20939
21194
  * Whether subtokens were found.
20940
- */ // eslint-disable-next-line complexity
20941
- function subtokenize(events) {
21195
+ */
21196
+ // eslint-disable-next-line complexity
21197
+ function subtokenize(eventsArray) {
20942
21198
  /** @type {Record<string, number>} */
20943
- const jumps = {}
20944
- let index = -1
21199
+ const jumps = {};
21200
+ let index = -1;
20945
21201
  /** @type {Event} */
20946
- let event
21202
+ let event;
20947
21203
  /** @type {number | undefined} */
20948
- let lineIndex
21204
+ let lineIndex;
20949
21205
  /** @type {number} */
20950
- let otherIndex
21206
+ let otherIndex;
20951
21207
  /** @type {Event} */
20952
- let otherEvent
21208
+ let otherEvent;
20953
21209
  /** @type {Array<Event>} */
20954
- let parameters
21210
+ let parameters;
20955
21211
  /** @type {Array<Event>} */
20956
- let subevents
21212
+ let subevents;
20957
21213
  /** @type {boolean | undefined} */
20958
- let more
21214
+ let more;
21215
+ const events = new SpliceBuffer(eventsArray);
20959
21216
  while (++index < events.length) {
20960
21217
  while (index in jumps) {
20961
- index = jumps[index]
21218
+ index = jumps[index];
20962
21219
  }
20963
- event = events[index]
21220
+ event = events.get(index);
20964
21221
 
20965
21222
  // Add a hook for the GFM tasklist extension, which needs to know if text
20966
21223
  // is in the first content of a list item.
20967
- if (
20968
- index &&
20969
- event[1].type === 'chunkFlow' &&
20970
- events[index - 1][1].type === 'listItemPrefix'
20971
- ) {
20972
- subevents = event[1]._tokenizer.events
20973
- otherIndex = 0
20974
- if (
20975
- otherIndex < subevents.length &&
20976
- subevents[otherIndex][1].type === 'lineEndingBlank'
20977
- ) {
20978
- otherIndex += 2
21224
+ if (index && event[1].type === "chunkFlow" && events.get(index - 1)[1].type === "listItemPrefix") {
21225
+ subevents = event[1]._tokenizer.events;
21226
+ otherIndex = 0;
21227
+ if (otherIndex < subevents.length && subevents[otherIndex][1].type === "lineEndingBlank") {
21228
+ otherIndex += 2;
20979
21229
  }
20980
- if (
20981
- otherIndex < subevents.length &&
20982
- subevents[otherIndex][1].type === 'content'
20983
- ) {
21230
+ if (otherIndex < subevents.length && subevents[otherIndex][1].type === "content") {
20984
21231
  while (++otherIndex < subevents.length) {
20985
- if (subevents[otherIndex][1].type === 'content') {
20986
- break
21232
+ if (subevents[otherIndex][1].type === "content") {
21233
+ break;
20987
21234
  }
20988
- if (subevents[otherIndex][1].type === 'chunkText') {
20989
- subevents[otherIndex][1]._isInFirstContentOfListItem = true
20990
- otherIndex++
21235
+ if (subevents[otherIndex][1].type === "chunkText") {
21236
+ subevents[otherIndex][1]._isInFirstContentOfListItem = true;
21237
+ otherIndex++;
20991
21238
  }
20992
21239
  }
20993
21240
  }
@@ -20996,158 +21243,160 @@ function subtokenize(events) {
20996
21243
  // Enter.
20997
21244
  if (event[0] === 'enter') {
20998
21245
  if (event[1].contentType) {
20999
- Object.assign(jumps, subcontent(events, index))
21000
- index = jumps[index]
21001
- more = true
21246
+ Object.assign(jumps, subcontent(events, index));
21247
+ index = jumps[index];
21248
+ more = true;
21002
21249
  }
21003
21250
  }
21004
21251
  // Exit.
21005
21252
  else if (event[1]._container) {
21006
- otherIndex = index
21007
- lineIndex = undefined
21253
+ otherIndex = index;
21254
+ lineIndex = undefined;
21008
21255
  while (otherIndex--) {
21009
- otherEvent = events[otherIndex]
21010
- if (
21011
- otherEvent[1].type === 'lineEnding' ||
21012
- otherEvent[1].type === 'lineEndingBlank'
21013
- ) {
21256
+ otherEvent = events.get(otherIndex);
21257
+ if (otherEvent[1].type === "lineEnding" || otherEvent[1].type === "lineEndingBlank") {
21014
21258
  if (otherEvent[0] === 'enter') {
21015
21259
  if (lineIndex) {
21016
- events[lineIndex][1].type = 'lineEndingBlank'
21260
+ events.get(lineIndex)[1].type = "lineEndingBlank";
21017
21261
  }
21018
- otherEvent[1].type = 'lineEnding'
21019
- lineIndex = otherIndex
21262
+ otherEvent[1].type = "lineEnding";
21263
+ lineIndex = otherIndex;
21020
21264
  }
21265
+ } else if (otherEvent[1].type === "linePrefix") {
21266
+ // Move past.
21021
21267
  } else {
21022
- break
21268
+ break;
21023
21269
  }
21024
21270
  }
21025
21271
  if (lineIndex) {
21026
21272
  // Fix position.
21027
- event[1].end = Object.assign({}, events[lineIndex][1].start)
21273
+ event[1].end = {
21274
+ ...events.get(lineIndex)[1].start
21275
+ };
21028
21276
 
21029
21277
  // Switch container exit w/ line endings.
21030
- parameters = events.slice(lineIndex, index)
21031
- parameters.unshift(event)
21032
- splice(events, lineIndex, index - lineIndex + 1, parameters)
21278
+ parameters = events.slice(lineIndex, index);
21279
+ parameters.unshift(event);
21280
+ events.splice(lineIndex, index - lineIndex + 1, parameters);
21033
21281
  }
21034
21282
  }
21035
21283
  }
21036
- return !more
21284
+
21285
+ // The changes to the `events` buffer must be copied back into the eventsArray
21286
+ splice(eventsArray, 0, Number.POSITIVE_INFINITY, events.slice(0));
21287
+ return !more;
21037
21288
  }
21038
21289
 
21039
21290
  /**
21040
21291
  * Tokenize embedded tokens.
21041
21292
  *
21042
- * @param {Array<Event>} events
21293
+ * @param {SpliceBuffer<Event>} events
21294
+ * Events.
21043
21295
  * @param {number} eventIndex
21296
+ * Index.
21044
21297
  * @returns {Record<string, number>}
21298
+ * Gaps.
21045
21299
  */
21046
21300
  function subcontent(events, eventIndex) {
21047
- const token = events[eventIndex][1]
21048
- const context = events[eventIndex][2]
21049
- let startPosition = eventIndex - 1
21301
+ const token = events.get(eventIndex)[1];
21302
+ const context = events.get(eventIndex)[2];
21303
+ let startPosition = eventIndex - 1;
21050
21304
  /** @type {Array<number>} */
21051
- const startPositions = []
21052
- const tokenizer =
21053
- token._tokenizer || context.parser[token.contentType](token.start)
21054
- const childEvents = tokenizer.events
21305
+ const startPositions = [];
21306
+ const tokenizer = token._tokenizer || context.parser[token.contentType](token.start);
21307
+ const childEvents = tokenizer.events;
21055
21308
  /** @type {Array<[number, number]>} */
21056
- const jumps = []
21309
+ const jumps = [];
21057
21310
  /** @type {Record<string, number>} */
21058
- const gaps = {}
21311
+ const gaps = {};
21059
21312
  /** @type {Array<Chunk>} */
21060
- let stream
21313
+ let stream;
21061
21314
  /** @type {Token | undefined} */
21062
- let previous
21063
- let index = -1
21315
+ let previous;
21316
+ let index = -1;
21064
21317
  /** @type {Token | undefined} */
21065
- let current = token
21066
- let adjust = 0
21067
- let start = 0
21068
- const breaks = [start]
21318
+ let current = token;
21319
+ let adjust = 0;
21320
+ let start = 0;
21321
+ const breaks = [start];
21069
21322
 
21070
21323
  // Loop forward through the linked tokens to pass them in order to the
21071
21324
  // subtokenizer.
21072
21325
  while (current) {
21073
21326
  // Find the position of the event for this token.
21074
- while (events[++startPosition][1] !== current) {
21327
+ while (events.get(++startPosition)[1] !== current) {
21075
21328
  // Empty.
21076
21329
  }
21077
- startPositions.push(startPosition)
21330
+ startPositions.push(startPosition);
21078
21331
  if (!current._tokenizer) {
21079
- stream = context.sliceStream(current)
21332
+ stream = context.sliceStream(current);
21080
21333
  if (!current.next) {
21081
- stream.push(null)
21334
+ stream.push(null);
21082
21335
  }
21083
21336
  if (previous) {
21084
- tokenizer.defineSkip(current.start)
21337
+ tokenizer.defineSkip(current.start);
21085
21338
  }
21086
21339
  if (current._isInFirstContentOfListItem) {
21087
- tokenizer._gfmTasklistFirstContentOfListItem = true
21340
+ tokenizer._gfmTasklistFirstContentOfListItem = true;
21088
21341
  }
21089
- tokenizer.write(stream)
21342
+ tokenizer.write(stream);
21090
21343
  if (current._isInFirstContentOfListItem) {
21091
- tokenizer._gfmTasklistFirstContentOfListItem = undefined
21344
+ tokenizer._gfmTasklistFirstContentOfListItem = undefined;
21092
21345
  }
21093
21346
  }
21094
21347
 
21095
21348
  // Unravel the next token.
21096
- previous = current
21097
- current = current.next
21349
+ previous = current;
21350
+ current = current.next;
21098
21351
  }
21099
21352
 
21100
21353
  // Now, loop back through all events (and linked tokens), to figure out which
21101
21354
  // parts belong where.
21102
- current = token
21355
+ current = token;
21103
21356
  while (++index < childEvents.length) {
21104
21357
  if (
21105
- // Find a void token that includes a break.
21106
- childEvents[index][0] === 'exit' &&
21107
- childEvents[index - 1][0] === 'enter' &&
21108
- childEvents[index][1].type === childEvents[index - 1][1].type &&
21109
- childEvents[index][1].start.line !== childEvents[index][1].end.line
21110
- ) {
21111
- start = index + 1
21112
- breaks.push(start)
21358
+ // Find a void token that includes a break.
21359
+ childEvents[index][0] === 'exit' && childEvents[index - 1][0] === 'enter' && childEvents[index][1].type === childEvents[index - 1][1].type && childEvents[index][1].start.line !== childEvents[index][1].end.line) {
21360
+ start = index + 1;
21361
+ breaks.push(start);
21113
21362
  // Help GC.
21114
- current._tokenizer = undefined
21115
- current.previous = undefined
21116
- current = current.next
21363
+ current._tokenizer = undefined;
21364
+ current.previous = undefined;
21365
+ current = current.next;
21117
21366
  }
21118
21367
  }
21119
21368
 
21120
21369
  // Help GC.
21121
- tokenizer.events = []
21370
+ tokenizer.events = [];
21122
21371
 
21123
21372
  // If there’s one more token (which is the cases for lines that end in an
21124
21373
  // EOF), that’s perfect: the last point we found starts it.
21125
21374
  // If there isn’t then make sure any remaining content is added to it.
21126
21375
  if (current) {
21127
21376
  // Help GC.
21128
- current._tokenizer = undefined
21129
- current.previous = undefined
21377
+ current._tokenizer = undefined;
21378
+ current.previous = undefined;
21130
21379
  } else {
21131
- breaks.pop()
21380
+ breaks.pop();
21132
21381
  }
21133
21382
 
21134
21383
  // Now splice the events from the subtokenizer into the current events,
21135
21384
  // moving back to front so that splice indices aren’t affected.
21136
- index = breaks.length
21385
+ index = breaks.length;
21137
21386
  while (index--) {
21138
- const slice = childEvents.slice(breaks[index], breaks[index + 1])
21139
- const start = startPositions.pop()
21140
- jumps.unshift([start, start + slice.length - 1])
21141
- splice(events, start, 2, slice)
21387
+ const slice = childEvents.slice(breaks[index], breaks[index + 1]);
21388
+ const start = startPositions.pop();
21389
+ jumps.push([start, start + slice.length - 1]);
21390
+ events.splice(start, 2, slice);
21142
21391
  }
21143
- index = -1
21392
+ jumps.reverse();
21393
+ index = -1;
21144
21394
  while (++index < jumps.length) {
21145
- gaps[adjust + jumps[index][0]] = adjust + jumps[index][1]
21146
- adjust += jumps[index][1] - jumps[index][0] - 1
21395
+ gaps[adjust + jumps[index][0]] = adjust + jumps[index][1];
21396
+ adjust += jumps[index][1] - jumps[index][0] - 1;
21147
21397
  }
21148
- return gaps
21398
+ return gaps;
21149
21399
  }
21150
-
21151
21400
  ;// ./node_modules/micromark/lib/postprocess.js
21152
21401
  /**
21153
21402
  * @typedef {import('micromark-util-types').Event} Event
@@ -68474,12 +68723,104 @@ const mdast_mdast = (text, opts = {}) => {
68474
68723
  */
68475
68724
  const jsxAcornParser = Parser.extend(acorn_jsx_default()());
68476
68725
 
68726
+ ;// ./lib/utils/literal-expression.ts
68727
+
68728
+ const unsupported = (description) => new Error(`not a literal expression: ${description}`);
68729
+ /** Narrows a resolved value for arithmetic, so operands never coerce to `NaN` or `"[object Object]"`. */
68730
+ const asNumber = (value) => {
68731
+ if (typeof value !== 'number')
68732
+ throw unsupported('non-numeric operand');
68733
+ return value;
68734
+ };
68735
+ const asOperand = (value) => {
68736
+ if (typeof value !== 'number' && typeof value !== 'string')
68737
+ throw unsupported('non-primitive operand');
68738
+ return value;
68739
+ };
68740
+ const ARITHMETIC_OPERATORS = {
68741
+ // `+` doubles as string concatenation, so it accepts a string on either side.
68742
+ '+': (left, right) => (typeof left === 'number' && typeof right === 'number' ? left + right : `${left}${right}`),
68743
+ '-': (left, right) => asNumber(left) - asNumber(right),
68744
+ '*': (left, right) => asNumber(left) * asNumber(right),
68745
+ '/': (left, right) => asNumber(left) / asNumber(right),
68746
+ };
68747
+ /** Resolve one estree node, recursing into arrays and objects. Throws for anything not on the allowlist. */
68748
+ const resolveNode = (node) => {
68749
+ switch (node.type) {
68750
+ case 'Literal':
68751
+ // Regex and bigint `Literal`s hold values a tree consumer can't serialise;
68752
+ // a BigInt throws outright in `JSON.stringify`.
68753
+ if ('regex' in node || 'bigint' in node)
68754
+ throw unsupported('regex or bigint literal');
68755
+ return node.value;
68756
+ case 'Identifier':
68757
+ if (node.name !== 'undefined')
68758
+ throw unsupported(`identifier \`${node.name}\``);
68759
+ return undefined;
68760
+ case 'TemplateLiteral':
68761
+ if (node.expressions.length)
68762
+ throw unsupported('template substitution');
68763
+ return node.quasis.map(quasi => quasi.value.cooked).join('');
68764
+ case 'UnaryExpression':
68765
+ if (node.operator !== '-')
68766
+ throw unsupported(`operator \`${node.operator}\``);
68767
+ return -asNumber(resolveNode(node.argument));
68768
+ case 'BinaryExpression': {
68769
+ const applyOperator = ARITHMETIC_OPERATORS[node.operator];
68770
+ if (!applyOperator)
68771
+ throw unsupported(`operator \`${node.operator}\``);
68772
+ return applyOperator(asOperand(resolveNode(node.left)), asOperand(resolveNode(node.right)));
68773
+ }
68774
+ case 'ArrayExpression':
68775
+ return node.elements.map(element => (element === null ? null : resolveNode(element)));
68776
+ case 'ObjectExpression':
68777
+ return node.properties.reduce((memo, property) => {
68778
+ if (property.type !== 'Property' || property.computed)
68779
+ throw unsupported('computed or spread property');
68780
+ const { key } = property;
68781
+ if (key.type !== 'Identifier' && key.type !== 'Literal')
68782
+ throw unsupported('property key');
68783
+ const name = key.type === 'Identifier' ? key.name : String(key.value);
68784
+ // `__proto__` would replace the object's prototype rather than add a property.
68785
+ if (name === '__proto__')
68786
+ throw unsupported('`__proto__` key');
68787
+ memo[name] = resolveNode(property.value);
68788
+ return memo;
68789
+ }, {});
68790
+ default:
68791
+ throw unsupported(node.type);
68792
+ }
68793
+ };
68794
+ /**
68795
+ * Resolve a JSX attribute expression's source to its value without executing it.
68796
+ *
68797
+ * Supports the literal syntax attributes actually use — `true`, `"a"`, `` `a` ``,
68798
+ * `undefined`, `{ textAlign: "left" }`, `["left"]`, `1 + 1`, `'https://' + 'x.com'` —
68799
+ * and refuses everything else, so no identifier, member access, call, assignment or
68800
+ * function body can ever run. The trade-off is that expressions needing a scope or a
68801
+ * method call (`{item.url}`, `{"a".toUpperCase()}`) throw, and callers keep the raw
68802
+ * source instead — the same fallback they already used for an expression that threw.
68803
+ * mdxish rendering is unaffected, since it resolves those later with its scoped
68804
+ * evaluator; only consumers reading attributes straight off the tree see the source.
68805
+ *
68806
+ * @param source expression body, without the surrounding braces
68807
+ * @throws if `source` is unparseable or isn't a supported literal expression
68808
+ */
68809
+ const evaluateLiteralExpression = (source) => {
68810
+ const expression = jsxAcornParser.parseExpressionAt(source, 0, { ecmaVersion: 'latest' });
68811
+ // acorn stops at the first complete expression, so anything trailing means this isn't one.
68812
+ if (expression.end !== source.trimEnd().length)
68813
+ throw unsupported('trailing content');
68814
+ return resolveNode(expression);
68815
+ };
68816
+
68477
68817
  ;// ./processor/utils.ts
68478
68818
 
68479
68819
 
68480
68820
 
68481
68821
 
68482
68822
 
68823
+
68483
68824
  /**
68484
68825
  * Evaluate a JavaScript expression source and return its value.
68485
68826
  *
@@ -68577,8 +68918,10 @@ const utils_getAttrs = (jsx) => jsx.attributes.reduce((memo, attr) => {
68577
68918
  memo[attr.name] = decode_decodeHTMLStrict(attr.value);
68578
68919
  }
68579
68920
  else if (attr.value?.value !== undefined) {
68921
+ // Expression values only survive to here when safeMode is off; `flattenAttributeExpressions`
68922
+ // rewrites them to plain strings at the head of the pipeline otherwise.
68580
68923
  try {
68581
- memo[attr.name] = evaluate(attr.value.value);
68924
+ memo[attr.name] = evaluateLiteralExpression(attr.value.value);
68582
68925
  }
68583
68926
  catch {
68584
68927
  memo[attr.name] = attr.value.value;
@@ -69359,6 +69702,31 @@ const embedTransformer = () => {
69359
69702
  };
69360
69703
  /* harmony default export */ const transform_embeds = (embedTransformer);
69361
69704
 
69705
+ ;// ./processor/transform/flatten-attribute-expressions.ts
69706
+
69707
+
69708
+ /**
69709
+ * Rewrites JSX attribute expressions (`icon={String(1 + 3)}`) into plain string attributes
69710
+ * holding their literal source, so nothing downstream can evaluate them.
69711
+ *
69712
+ * This is the RMDX counterpart to mdxish's `preserveExpressionsAsText` parse option: safeMode's
69713
+ * contract is enforced once, at the head of the pipeline, rather than at every `getAttrs()` call
69714
+ * site. Only registered when safeMode is on.
69715
+ */
69716
+ const flattenAttributeExpressions = () => tree => {
69717
+ lib_visit(tree, utils_isMDXElement, (node) => {
69718
+ node.attributes.forEach(attr => {
69719
+ if (!('name' in attr))
69720
+ return;
69721
+ if (attr.value === null || typeof attr.value === 'string')
69722
+ return;
69723
+ attr.value = attr.value.value;
69724
+ });
69725
+ });
69726
+ return tree;
69727
+ };
69728
+ /* harmony default export */ const flatten_attribute_expressions = (flattenAttributeExpressions);
69729
+
69362
69730
  ;// ./node_modules/gemoji/index.js
69363
69731
  /**
69364
69732
  * @typedef Gemoji
@@ -101344,6 +101712,7 @@ const variables = ({ asMdx } = { asMdx: true }) => tree => {
101344
101712
 
101345
101713
 
101346
101714
 
101715
+
101347
101716
  const defaultTransforms = {
101348
101717
  calloutTransformer: callouts,
101349
101718
  codeTabsTransformer: code_tabs,
@@ -101371,6 +101740,9 @@ const ast_processor_astProcessor = (opts = {}) => {
101371
101740
  const components = opts.components || {};
101372
101741
  let processor = remark_remark()
101373
101742
  .use(lib_remarkMdx)
101743
+ // Must precede every other transformer: it strips evaluable attribute expressions so no
101744
+ // downstream `getAttrs()` call can reach `evaluate()`.
101745
+ .use(opts.safeMode ? flatten_attribute_expressions : undefined)
101374
101746
  .use(remarkPlugins)
101375
101747
  .use(opts.remarkPlugins)
101376
101748
  .use(transform_variables, { asMdx: false })
@@ -113441,7 +113813,9 @@ class MdxSyntaxError extends SyntaxError {
113441
113813
  * as `\r<br>`. Treating both characters as hard breaks doubles the spacing.
113442
113814
  */
113443
113815
  const hardBreaks = () => tree => {
113444
- findAndReplace(tree, [/\r?\n/g, () => ({ type: 'break' })]);
113816
+ // Skip `html-block`: its opaque payload is rendered verbatim, so turning its newlines
113817
+ // into `break` nodes is unnecessary and costly on large blocks.
113818
+ findAndReplace(tree, [/\r?\n/g, () => ({ type: 'break' })], { ignore: ['html-block'] });
113445
113819
  };
113446
113820
  /* harmony default export */ const hard_breaks = (hardBreaks);
113447
113821
 
@@ -115948,12 +116322,59 @@ const lib_h = create_h_createH(node_modules_property_information_html, 'div')
115948
116322
  /** @type {ReturnType<createH>} */
115949
116323
  const lib_s = create_h_createH(node_modules_property_information_svg, 'g', svg_case_sensitive_tag_names_svgCaseSensitiveTagNames)
115950
116324
 
116325
+ ;// ./utils/user.ts
116326
+ /**
116327
+ * Coerce a user variable value to a string for substitution into markdown text.
116328
+ * Non-string values (arrays, objects, numbers) are stringified via JSON or `String()`
116329
+ * so that `<<var>>` syntax doesn't produce `[object Object]` for structured data like
116330
+ * JWT `keys`.
116331
+ */
116332
+ const stringifyVariableValue = (value) => {
116333
+ if (typeof value === 'string')
116334
+ return value;
116335
+ if (value == null)
116336
+ return '';
116337
+ if (typeof value === 'object') {
116338
+ try {
116339
+ return JSON.stringify(value) ?? '';
116340
+ }
116341
+ catch {
116342
+ console.warn('[WARNING] Could not stringify a structured user variable.');
116343
+ return '';
116344
+ }
116345
+ }
116346
+ return String(value);
116347
+ };
116348
+ /**
116349
+ * Flatten `variables.user` into a string-keyed string-valued record by coercing
116350
+ * each value. Used by markdown substitution paths that need a plain
116351
+ * `Record<string, string>` lookup.
116352
+ */
116353
+ const flattenUserVariables = (user) => Object.fromEntries(Object.entries(user).map(([name, value]) => [name, stringifyVariableValue(value)]));
116354
+ const User = (variables) => {
116355
+ const { user = {}, defaults = [] } = variables || {};
116356
+ return new Proxy(user, {
116357
+ get(target, attribute) {
116358
+ if (typeof attribute === 'symbol') {
116359
+ return '';
116360
+ }
116361
+ if (attribute in target) {
116362
+ return target[attribute];
116363
+ }
116364
+ const def = defaults.find((d) => d.name === attribute);
116365
+ return def ? def.default : attribute.toUpperCase();
116366
+ },
116367
+ });
116368
+ };
116369
+ /* harmony default export */ const user = (User);
116370
+
115951
116371
  ;// ./processor/plugin/toc.ts
115952
116372
 
115953
116373
 
115954
116374
 
115955
116375
 
115956
116376
 
116377
+
115957
116378
  const HEADING_TAGS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
115958
116379
  const isHeadingTag = (tag) => (tag ? HEADING_TAGS.includes(tag) : false);
115959
116380
  const isStandardHtmlElement = (node) => STANDARD_HTML_TAGS.has(node.tagName.toLowerCase());
@@ -116065,7 +116486,7 @@ const flattenVariables = (variables) => {
116065
116486
  if (!variables)
116066
116487
  return {};
116067
116488
  return {
116068
- ...variables.user,
116489
+ ...flattenUserVariables(variables.user),
116069
116490
  ...Object.fromEntries((variables.defaults || []).filter(d => !(d.name in variables.user)).map(d => [d.name, d.default])),
116070
116491
  };
116071
116492
  };
@@ -116213,7 +116634,7 @@ const exports_exports = (doc) => {
116213
116634
 
116214
116635
  const hast = (text, opts = {}) => {
116215
116636
  const components = Object.entries(opts.components || {}).reduce((memo, [name, doc]) => {
116216
- memo[name] = mdast(doc);
116637
+ memo[name] = mdast(doc, { safeMode: opts.safeMode });
116217
116638
  return memo;
116218
116639
  }, {});
116219
116640
  const processor = astProcessor(opts)
@@ -122490,6 +122911,10 @@ const NESTED_ATTR_EXPRESSION_RE = /[\w-]+\s*=\s*\{/;
122490
122911
  // Name shape mirrors `componentTagPattern`; the lookbehind skips the inner tag
122491
122912
  // of a legacy `<<VARIABLE>>`.
122492
122913
  const NESTED_COMPONENT_TAG_RE = /(?<!<)<([A-Z][A-Za-z0-9_]*)[\s/>]/g;
122914
+ // Whole blank lines at a component body's edges. `\n` sits inside each group so a
122915
+ // match can only ever span complete lines, never part of a content line.
122916
+ const LEADING_BLANK_LINES_RE = /^(?:[ \t]*\n)+/;
122917
+ const TRAILING_BLANK_LINES_RE = /(?:\n[ \t]*)+$/;
122493
122918
  // Excludes tags with dedicated transformers (`Table`, `HTMLBlock`, inline
122494
122919
  // components), which expect their wrapper to stay raw.
122495
122920
  const hasNestedGenericComponentTag = (content) => [...content.matchAll(NESTED_COMPONENT_TAG_RE)].some(match => !GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(match[1]));
@@ -122523,13 +122948,21 @@ function safeDeindent(text) {
122523
122948
  })
122524
122949
  .join('\n');
122525
122950
  }
122951
+ /**
122952
+ * Drop the blank lines at a component body's edges, keeping the first content line's
122953
+ * indent: `.trim()` stripped that line alone, so a uniformly indented body reparsed
122954
+ * lopsided and every list item after the first nested. The trailing pattern
122955
+ * takes the last content line's newline but leaves its spaces, which are code content
122956
+ * when a fence runs into the end tag.
122957
+ */
122958
+ const trimBlankEdges = (text) => text.replace(LEADING_BLANK_LINES_RE, '').replace(TRAILING_BLANK_LINES_RE, '');
122526
122959
  /**
122527
122960
  * Parse component-body markdown into mdast children. Dedenting shifts columns and
122528
122961
  * stales the top-level `terminateHtmlFlowBlocks` decisions, so that one preprocessor
122529
122962
  * re-runs here; other column-anchored fixups (compact headings, tables) do not.
122530
122963
  */
122531
122964
  const parseMdChildren = (value, safeMode) => {
122532
- const reparseSource = terminateHtmlFlowBlocks(safeDeindent(value).trim());
122965
+ const reparseSource = terminateHtmlFlowBlocks(trimBlankEdges(safeDeindent(value)));
122533
122966
  const parsed = getInlineMdProcessor({ safeMode }).parse(reparseSource);
122534
122967
  // Promote nested wrappers bottom-up so an outer wrapper sees markdown buried in a
122535
122968
  // child claimed whole (e.g. `<li>` in `<ol>`) before its containsMarkdownConstruct check (RM-17560).
@@ -122694,7 +123127,8 @@ function promoteComponentBlocks(tree, safeMode, source) {
122694
123127
  // Case 2: Self-contained block (closing tag in content)
122695
123128
  const closingTagIndex = isPlainLowercaseHtml ? plainClosingTagIndex : contentAfterTag.lastIndexOf(closingTagStr);
122696
123129
  if (closingTagIndex >= 0) {
122697
- // Untrimmed so parseMdChildren can dedent before trimming.
123130
+ // Left as authored: parseMdChildren needs the original columns to dedent by, and
123131
+ // it clears the blank edges itself.
122698
123132
  const componentInnerContent = contentAfterTag.substring(0, closingTagIndex);
122699
123133
  const contentAfterClose = contentAfterTag.substring(closingTagIndex + closingTagStr.length).trim();
122700
123134
  let parsedChildren = [];
@@ -126801,6 +127235,7 @@ function normalizeTableSeparator(content) {
126801
127235
  ;// ./processor/transform/mdxish/variables-code.ts
126802
127236
 
126803
127237
 
127238
+
126804
127239
  // Single combined regex so that resolved values from one pattern are never re-scanned by the other.
126805
127240
  const COMBINED_VARIABLE_REGEX = new RegExp(`${variable_dist.VARIABLE_REGEXP}|${variable_dist.MDX_VARIABLE_REGEXP}`, 'giu');
126806
127241
  // Flatten variables into a single object for easy lookup
@@ -126809,7 +127244,7 @@ function variables_code_flattenVariables(variables) {
126809
127244
  return {};
126810
127245
  return {
126811
127246
  ...Object.fromEntries((variables.defaults || []).map(d => [d.name, d.default])),
126812
- ...variables.user,
127247
+ ...flattenUserVariables(variables.user),
126813
127248
  };
126814
127249
  }
126815
127250
  function resolveCodeVariables(value, resolvedVariables) {
@@ -127297,24 +127732,6 @@ const Contexts = ({ children, terms = [], variables = { user: {}, defaults: [] }
127297
127732
  };
127298
127733
  /* harmony default export */ const contexts = (Contexts);
127299
127734
 
127300
- ;// ./utils/user.ts
127301
- const User = (variables) => {
127302
- const { user = {}, defaults = [] } = variables || {};
127303
- return new Proxy(user, {
127304
- get(target, attribute) {
127305
- if (typeof attribute === 'symbol') {
127306
- return '';
127307
- }
127308
- if (attribute in target) {
127309
- return target[attribute];
127310
- }
127311
- const def = defaults.find((d) => d.name === attribute);
127312
- return def ? def.default : attribute.toUpperCase();
127313
- },
127314
- });
127315
- };
127316
- /* harmony default export */ const user = (User);
127317
-
127318
127735
  ;// ./lib/utils/makeUseMdxComponents.ts
127319
127736
 
127320
127737
 
@@ -127347,6 +127764,7 @@ const makeUseMDXComponents = (more = {}) => {
127347
127764
 
127348
127765
  ;// ./lib/utils/mdxish/mdxish-variables.ts
127349
127766
 
127767
+
127350
127768
  // The `$` guard skips template-literal interpolation: `${user.name}` embeds `{user.name}`, and
127351
127769
  // substituting it would leave a mangled `` `Hi $Name` `` behind. Those belong to an expression,
127352
127770
  // which either evaluated already or is meant to stay literal.
@@ -127370,7 +127788,7 @@ function resolveAttributeVariables(value, user) {
127370
127788
  .replace(MDX_VARIABLE_REGEX, (source, escapePrefix, name, escapeSuffix) => {
127371
127789
  if (escapePrefix || escapeSuffix)
127372
127790
  return source;
127373
- return user[name];
127791
+ return stringifyVariableValue(user[name]);
127374
127792
  });
127375
127793
  }
127376
127794
 
@@ -127646,7 +128064,8 @@ const run_run = (string, _opts = {}) => {
127646
128064
 
127647
128065
  const tags = (doc) => {
127648
128066
  const set = new Set();
127649
- visit(mdast(doc), isMDXElement, (node) => {
128067
+ // Tag names never depend on evaluated attribute values, so always parse in safeMode.
128068
+ visit(mdast(doc, { safeMode: true }), isMDXElement, (node) => {
127650
128069
  if (node.name?.match(/^[A-Z]/)) {
127651
128070
  set.add(node.name);
127652
128071
  }
@@ -127662,13 +128081,14 @@ const tags = (doc) => {
127662
128081
 
127663
128082
 
127664
128083
 
127665
- const { micromarkExtensions, fromMarkdownExtensions } = mdxish_extensions_mdxishExtensions(mdxish_extensions_FEATURES.tags);
128084
+ const { micromarkExtensions, fromMarkdownExtensions } = mdxish_extensions_mdxishExtensions(mdxish_extensions_FEATURES.tags, { safeMode: true });
127666
128085
  const mdxishTags_tags = (doc) => {
127667
128086
  const set = new Set();
127668
128087
  const processor = remark()
127669
128088
  .data('micromarkExtensions', micromarkExtensions)
127670
128089
  .data('fromMarkdownExtensions', fromMarkdownExtensions)
127671
- .use(mdxishMdxComponentBlocks)
128090
+ // Tag names never depend on evaluated attribute values, so always parse in safeMode.
128091
+ .use(mdxishMdxComponentBlocks, { safeMode: true })
127672
128092
  .use(mdxishTables);
127673
128093
  const tree = processor.parse(doc);
127674
128094
  visit(processor.runSync(tree), isMDXElement, (node) => {