lib0 1.0.0-rc.26 → 1.0.0-rc.28

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.
@@ -39,7 +39,7 @@ export const $attribution: s.Schema<Attribution>;
39
39
  * @typedef {{ [key: string]: any }} Formats
40
40
  */
41
41
  /**
42
- * @typedef {{ id: string, key: number|string, assoc: 1|-1, attrs?: object }} MarkJSON
42
+ * @typedef {{ id: string, key: number|string, assoc: 1|-1, attrs?: object, transient?: boolean }} MarkJSON
43
43
  */
44
44
  /**
45
45
  * @typedef {{
@@ -423,7 +423,7 @@ export class SetAttrOp<V extends unknown = any, K extends string | number = any>
423
423
  get fingerprint(): string;
424
424
  toJSON(): {
425
425
  type: "insert";
426
- value: V | DeltaJSON;
426
+ value: DeltaJSON | V;
427
427
  } & ({
428
428
  attribution: Attribution;
429
429
  } | {
@@ -549,13 +549,13 @@ export const $anyOp: s.Schema<DeleteOp<any> | TextOp | InsertOp<any> | ModifyOp<
549
549
  export const $setAttrOp: s.Schema<SetAttrOp<any>>;
550
550
  export const $modifyAttrOp: s.Schema<ModifyAttrOp<any>>;
551
551
  export const $deleteAttrOp: s.Schema<DeleteAttrOp<any>>;
552
- export const $anyAttrOp: s.Schema<ModifyAttrOp<any, string> | SetAttrOp<any, any> | DeleteAttrOp<any, string | number>>;
552
+ export const $anyAttrOp: s.Schema<SetAttrOp<any, any> | DeleteAttrOp<any, string | number> | ModifyAttrOp<any, string>>;
553
553
  export function $setAttrOpWith<Content extends fingerprintTrait.Fingerprintable>($content: s.Schema<Content>): s.Schema<SetAttrOp<Content>>;
554
554
  export function $insertOpWith<Content extends fingerprintTrait.Fingerprintable>($content: s.Schema<Content>): s.Schema<InsertOp<Content>>;
555
555
  export function $modifyOpWith<Modify extends DeltaAny>($content: s.Schema<Modify>): s.Schema<ModifyOp<Modify>>;
556
556
  export function $modifyAttrOpWith<Modify extends DeltaAny>($content: s.Schema<Modify>): s.Schema<ModifyAttrOp<Modify>>;
557
557
  export const $mark: s.Schema<Mark>;
558
- export function createMark(key: number | string, id?: string, assoc?: 1 | -1, attrs?: object | null): Mark;
558
+ export function createMark(key: number | string, id?: string, assoc?: 1 | -1, attrs?: object | null, transient?: boolean): Mark;
559
559
  /**
560
560
  * @template {DeltaConf} [Conf={}]
561
561
  * @extends {DeltaData<
@@ -745,9 +745,11 @@ export class DeltaBuilder<Conf extends DeltaConf = {}, FixedConf extends boolean
745
745
  *
746
746
  * @param {import('./position.js').Pos} pos
747
747
  * @param {string} [id]
748
+ * @param {boolean} [transient] flag the mark as never-to-be-stored (a one-shot mapping probe, see
749
+ * {@link Mark#transient} and {@link import('./position.js').mapPositionsA})
748
750
  * @return {this}
749
751
  */
750
- addMark(pos: import("./position.js").Pos, id?: string): this;
752
+ addMark(pos: import("./position.js").Pos, id?: string, transient?: boolean): this;
751
753
  /**
752
754
  * Remove the mark with `id`, located via `pos` (the position it was added at). On a settled document
753
755
  * that holds the mark this removes it in place; on a fresh/markless builder it records a transmittable
@@ -1125,6 +1127,7 @@ export type MarkJSON = {
1125
1127
  key: number | string;
1126
1128
  assoc: 1 | -1;
1127
1129
  attrs?: object;
1130
+ transient?: boolean;
1128
1131
  };
1129
1132
  export type DeltaJSON = {
1130
1133
  type: "delta";
@@ -1307,6 +1310,7 @@ import * as fingerprintTrait from '../trait/fingerprint.js';
1307
1310
  * - `attrs` — optional user-supplied data carried with the mark **by reference**: the same
1308
1311
  * object is shared across the mark, its `copy`/`clone`s, `toJSON`, and every `MarkPos` from
1309
1312
  * `marksToPositions`, so the caller must treat it as immutable (do not mutate it after attaching).
1313
+ * - `transient` — the mark only rides through a mapping and must never be stored (see the field doc).
1310
1314
  *
1311
1315
  * A `Mark` is **immutable** — never mutate one in place; to "move" a mark, replace it with a fresh
1312
1316
  * `Mark` via {@link Mark#copy} (the {@link Marks} set keys by id, so re-adding the same id replaces
@@ -1319,8 +1323,9 @@ declare class Mark {
1319
1323
  * @param {string} [id] unique id; defaults to a fresh GUID
1320
1324
  * @param {1|-1} [assoc] gravity at a boundary; defaults to right (`1`)
1321
1325
  * @param {object?} [attrs] optional user data, stored by reference; treat as immutable
1326
+ * @param {boolean} [transient] the mark must never be stored; see the field doc
1322
1327
  */
1323
- constructor(key: number | string, id?: string, assoc?: 1 | -1, attrs?: object | null);
1328
+ constructor(key: number | string, id?: string, assoc?: 1 | -1, attrs?: object | null, transient?: boolean);
1324
1329
  /**
1325
1330
  * @readonly
1326
1331
  * @type {number|string}
@@ -1341,6 +1346,18 @@ declare class Mark {
1341
1346
  * @type {object?}
1342
1347
  */
1343
1348
  readonly attrs: object | null;
1349
+ /**
1350
+ * A transient mark exists only to ride through a mapping (see
1351
+ * {@link import('./position.js').mapPositionsA}) and must never be stored: a consumer that
1352
+ * maintains persistent state (a document, a transformer's internal model of one) either filters
1353
+ * transient marks before applying a change to that state, or removes them when found on
1354
+ * final/settled state. How that is achieved is left to the implementation — the core does not
1355
+ * enforce it (today nothing in lib0 stores marks outside a document, so nothing needs to).
1356
+ *
1357
+ * @readonly
1358
+ * @type {boolean}
1359
+ */
1360
+ readonly transient: boolean;
1344
1361
  /**
1345
1362
  * A copy of this mark, optionally at a different `key` (used to "move" an otherwise-immutable mark).
1346
1363
  *
@@ -35,6 +35,8 @@ export function equals(a: Pos | MarkPos, b: Pos | MarkPos): boolean;
35
35
  */
36
36
  export const $markPos: s.Schema<MarkPos>;
37
37
  export function marksToPositions(d: delta.DeltaAny): Array<MarkPos>;
38
+ export function mapPositionsA(tr: import("./transformer/core.js").Transformer<any, any>, positions: Array<Pos>): Array<Pos | null>;
39
+ export function mapPositionsB(tr: import("./transformer/core.js").Transformer<any, any>, positions: Array<Pos>): Array<Pos | null>;
38
40
  /**
39
41
  * A single step of a {@link Pos} path: a `string` attribute key, or a `number` content index.
40
42
  */
package/dist/random.d.ts CHANGED
@@ -2,5 +2,9 @@ export const rand: () => number;
2
2
  export function uint32(): number;
3
3
  export function uint53(): number;
4
4
  export function oneOf<T>(arr: Array<T>): T;
5
- export function uuidv4(): string;
5
+ export function _uuidv4Polyfill(): string;
6
+ /**
7
+ * @type {() => string}
8
+ */
9
+ export const uuidv4: () => string;
6
10
  //# sourceMappingURL=random.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lib0",
3
- "version": "1.0.0-rc.26",
3
+ "version": "1.0.0-rc.28",
4
4
  "description": "isomorphic utility functions",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -90,7 +90,7 @@ export const $attribution = /* @__PURE__ */(() => s.$object({
90
90
  */
91
91
 
92
92
  /**
93
- * @typedef {{ id: string, key: number|string, assoc: 1|-1, attrs?: object }} MarkJSON
93
+ * @typedef {{ id: string, key: number|string, assoc: 1|-1, attrs?: object, transient?: boolean }} MarkJSON
94
94
  */
95
95
 
96
96
  /**
@@ -1136,6 +1136,7 @@ export const $modifyAttrOpWith = $content => s.$custom(o => $modifyAttrOp.check(
1136
1136
  * - `attrs` — optional user-supplied data carried with the mark **by reference**: the same
1137
1137
  * object is shared across the mark, its `copy`/`clone`s, `toJSON`, and every `MarkPos` from
1138
1138
  * `marksToPositions`, so the caller must treat it as immutable (do not mutate it after attaching).
1139
+ * - `transient` — the mark only rides through a mapping and must never be stored (see the field doc).
1139
1140
  *
1140
1141
  * A `Mark` is **immutable** — never mutate one in place; to "move" a mark, replace it with a fresh
1141
1142
  * `Mark` via {@link Mark#copy} (the {@link Marks} set keys by id, so re-adding the same id replaces
@@ -1148,8 +1149,9 @@ class Mark {
1148
1149
  * @param {string} [id] unique id; defaults to a fresh GUID
1149
1150
  * @param {1|-1} [assoc] gravity at a boundary; defaults to right (`1`)
1150
1151
  * @param {object?} [attrs] optional user data, stored by reference; treat as immutable
1152
+ * @param {boolean} [transient] the mark must never be stored; see the field doc
1151
1153
  */
1152
- constructor (key, id = rand.uuidv4(), assoc = 1, attrs = null) {
1154
+ constructor (key, id = rand.uuidv4(), assoc = 1, attrs = null, transient = false) {
1153
1155
  /**
1154
1156
  * @readonly
1155
1157
  * @type {number|string}
@@ -1170,6 +1172,18 @@ class Mark {
1170
1172
  * @type {object?}
1171
1173
  */
1172
1174
  this.attrs = attrs
1175
+ /**
1176
+ * A transient mark exists only to ride through a mapping (see
1177
+ * {@link import('./position.js').mapPositionsA}) and must never be stored: a consumer that
1178
+ * maintains persistent state (a document, a transformer's internal model of one) either filters
1179
+ * transient marks before applying a change to that state, or removes them when found on
1180
+ * final/settled state. How that is achieved is left to the implementation — the core does not
1181
+ * enforce it (today nothing in lib0 stores marks outside a document, so nothing needs to).
1182
+ *
1183
+ * @readonly
1184
+ * @type {boolean}
1185
+ */
1186
+ this.transient = transient
1173
1187
  }
1174
1188
 
1175
1189
  /**
@@ -1179,23 +1193,26 @@ class Mark {
1179
1193
  * @return {Mark}
1180
1194
  */
1181
1195
  copy (key = this.key) {
1182
- return new Mark(key, this.id, this.assoc, this.attrs)
1196
+ return new Mark(key, this.id, this.assoc, this.attrs, this.transient)
1183
1197
  }
1184
1198
 
1185
1199
  /**
1186
1200
  * @return {MarkJSON}
1187
1201
  */
1188
1202
  toJSON () {
1189
- return this.attrs === null
1203
+ /** @type {MarkJSON} */
1204
+ const json = this.attrs === null
1190
1205
  ? { id: this.id, key: this.key, assoc: this.assoc }
1191
1206
  : { id: this.id, key: this.key, assoc: this.assoc, attrs: this.attrs }
1207
+ if (this.transient) json.transient = true
1208
+ return json
1192
1209
  }
1193
1210
 
1194
1211
  /**
1195
1212
  * @param {Mark} other
1196
1213
  */
1197
1214
  [equalityTrait.EqualityTraitSymbol] (other) {
1198
- return $mark.check(other) && this.id === other.id && this.key === other.key && this.assoc === other.assoc && fun.equalityDeep(this.attrs, other.attrs)
1215
+ return $mark.check(other) && this.id === other.id && this.key === other.key && this.assoc === other.assoc && this.transient === other.transient && fun.equalityDeep(this.attrs, other.attrs)
1199
1216
  }
1200
1217
  }
1201
1218
 
@@ -1203,16 +1220,17 @@ export const $mark = /** @type {s.Schema<Mark>} */ (Mark.prototype.$type = s.$ty
1203
1220
 
1204
1221
  /**
1205
1222
  * Create a {@link Mark} (use this instead of `new Mark(...)`). `id` defaults to a fresh GUID, `assoc`
1206
- * to right gravity (`1`), `attrs` to `null`. A `Mark` is stored on a delta node's own
1207
- * {@link Marks} set (see {@link DeltaBuilder#addMark}).
1223
+ * to right gravity (`1`), `attrs` to `null`, `transient` to `false`. A `Mark` is stored on a delta
1224
+ * node's own {@link Marks} set (see {@link DeltaBuilder#addMark}).
1208
1225
  *
1209
1226
  * @param {number|string} key
1210
1227
  * @param {string} [id]
1211
1228
  * @param {1|-1} [assoc]
1212
1229
  * @param {object?} [attrs]
1230
+ * @param {boolean} [transient] never to be stored (see {@link Mark#transient})
1213
1231
  * @return {Mark}
1214
1232
  */
1215
- export const createMark = (key, id, assoc, attrs) => new Mark(key, id, assoc, attrs)
1233
+ export const createMark = (key, id, assoc, attrs, transient) => new Mark(key, id, assoc, attrs, transient)
1216
1234
 
1217
1235
  /**
1218
1236
  * @typedef {Delta<any>} DeltaAny
@@ -2135,12 +2153,14 @@ export class DeltaBuilder extends Delta {
2135
2153
  *
2136
2154
  * @param {import('./position.js').Pos} pos
2137
2155
  * @param {string} [id]
2156
+ * @param {boolean} [transient] flag the mark as never-to-be-stored (a one-shot mapping probe, see
2157
+ * {@link Mark#transient} and {@link import('./position.js').mapPositionsA})
2138
2158
  * @return {this}
2139
2159
  */
2140
- addMark (pos, id = rand.uuidv4()) {
2160
+ addMark (pos, id = rand.uuidv4(), transient = false) {
2141
2161
  // apply with the default `final = this.isFinal` (do NOT force `{ final: true }`): a fresh change
2142
2162
  // builder is non-final so a sibling `removeMark` stays transmittable; a final doc collects nothing
2143
- this.apply(/** @type {Delta<Conf>} */ (markChange(pos, id, false)))
2163
+ this.apply(/** @type {Delta<Conf>} */ (markChange(pos, id, false, transient)))
2144
2164
  return this
2145
2165
  }
2146
2166
 
@@ -3363,14 +3383,15 @@ const rebaseRootMarks = (node, other, priority) => {
3363
3383
  * @param {import('./position.js').Pos} pos
3364
3384
  * @param {string} id
3365
3385
  * @param {boolean} isDelete
3386
+ * @param {boolean} [transient] never to be stored (see {@link Mark#transient})
3366
3387
  * @return {DeltaBuilderAny}
3367
3388
  */
3368
- const markChange = (pos, id, isDelete) => {
3389
+ const markChange = (pos, id, isDelete, transient = false) => {
3369
3390
  const path = pos.path
3370
3391
  // a mark anchors at the terminal step of its path (a content offset or attribute key); the root
3371
3392
  // position `[]` has no terminal and cannot carry a mark - reject it instead of recursing forever
3372
3393
  if (path.length === 0) throw error.create('cannot place a mark at the root position (empty path): a mark needs a terminal content-offset or attribute-key step')
3373
- const mark = isDelete ? null : createMark(path[path.length - 1], id, pos.assoc, pos.attrs ?? null)
3394
+ const mark = isDelete ? null : createMark(path[path.length - 1], id, pos.assoc, pos.attrs ?? null, transient)
3374
3395
  /**
3375
3396
  * @param {number} i
3376
3397
  * @return {DeltaBuilderAny}
@@ -104,9 +104,12 @@ export const $markPos = /* @__PURE__ */ s.$object({
104
104
  })
105
105
 
106
106
  /**
107
- * Reconstruct every {@link MarkPos} stored as a {@link import('./delta.js').Mark mark} inside `d` (a
108
- * settled delta). A mark's `path` is the content indices / attribute keys walked to reach it, plus the
109
- * mark's own `key`. Add marks with {@link import('./delta.js').DeltaBuilder#addMark}.
107
+ * Reconstruct every {@link MarkPos} stored as a {@link import('./delta.js').Mark mark} inside `d` a
108
+ * settled delta or a change delta (e.g. a raw transformer output, or the change `addMark` builds). A
109
+ * mark's `path` is the content indices / attribute keys walked to reach it, plus the mark's own `key`;
110
+ * the walk descends insert embeds, `modify` values, and `setAttr`/`modifyAttr` values. In a change
111
+ * delta the indices are post-change coordinates (delete ops span no slot). Add marks with
112
+ * {@link import('./delta.js').DeltaBuilder#addMark}.
110
113
  *
111
114
  * Subtrees are pruned via each node's conservative `maybeHasMarks` flag (`false` ⇒ guaranteed empty,
112
115
  * skipped). This is also the **sole resetter** of that flag: a descended subtree that turns out to hold
@@ -137,7 +140,8 @@ export const marksToPositions = d => {
137
140
  : { id: m.id, path: [...path, m.key], assoc: m.assoc, attrs: m.attrs })
138
141
  }
139
142
  }
140
- // a settled delta has only text/insert children; descend the delta-valued embeds
143
+ // descend the delta-valued insert embeds and the modify values (a change delta's mark rides on a
144
+ // modify's nested root marks - see markChange; `apply` may also leave a modify on a settled node)
141
145
  let i = 0
142
146
  for (const op of node.children) {
143
147
  if (delta.$insertOp.check(op)) {
@@ -145,7 +149,12 @@ export const marksToPositions = d => {
145
149
  if (delta.$deltaAny.check(el) && el.maybeHasMarks && walk(el, [...path, i])) found = true
146
150
  i++
147
151
  }
148
- } else {
152
+ } else if (delta.$modifyOp.check(op)) {
153
+ if (op.value.maybeHasMarks && walk(op.value, [...path, i])) found = true
154
+ i++
155
+ } else if (!delta.$deleteOp.check(op)) {
156
+ // text / retain advance the content index; a delete occupies only the pre-change coordinate -
157
+ // mark paths (like mark keys, see delta.js's shiftMarkKey) are post-change content indices
149
158
  i += op.length
150
159
  }
151
160
  }
@@ -163,3 +172,66 @@ export const marksToPositions = d => {
163
172
  walk(d, [])
164
173
  return out
165
174
  }
175
+
176
+ /**
177
+ * Shared body of {@link mapPositionsA}/{@link mapPositionsB}: encode `positions` as marks on one
178
+ * synthetic change (ids are the input indices), run it through `transform` once, and read the images
179
+ * back off the raw output. Nothing is ever applied to a document, so no mark persists anywhere.
180
+ *
181
+ * @param {Array<Pos>} positions
182
+ * @param {(change: delta.DeltaBuilderAny) => delta.DeltaAny?} transform
183
+ * @return {Array<Pos?>}
184
+ */
185
+ const mapPositionsThrough = (positions, transform) => {
186
+ if (positions.length === 0) return []
187
+ const change = /** @type {delta.DeltaBuilderAny} */ (delta.create())
188
+ positions.forEach((pos, i) => change.addMark(pos, '' + i, true)) // transient probes - never to be stored
189
+ const mapped = transform(change)
190
+ /**
191
+ * @type {Map<string, MarkPos>}
192
+ */
193
+ const byId = new Map()
194
+ if (mapped != null) {
195
+ // the raw output may alias transformer-internal state; marksToPositions' `maybeHasMarks`
196
+ // self-correction is safe there (`false` is only written where verifiably no marks exist)
197
+ for (const m of marksToPositions(mapped)) {
198
+ if (!byId.has(m.id)) byId.set(m.id, m) // fan-out (e.g. one datum in several project holes): first image in doc order wins
199
+ }
200
+ }
201
+ return positions.map((_, i) => {
202
+ const m = byId.get('' + i)
203
+ return m === undefined ? null : create(m.path, m.assoc, m.attrs ?? null)
204
+ })
205
+ }
206
+
207
+ /**
208
+ * One-shot map of `positions` living on a transformer's **A side** to the B side (the mirror of
209
+ * {@link mapPositionsB}) — for when you want an answer *now* without maintaining a position-set as
210
+ * stored marks. One transform pass maps the whole batch; the result is in input order, each entry the
211
+ * B-side image or `null` when the position has none (a transformer maps marks best-effort — see the
212
+ * readme). `assoc` and `attrs` ride through on the probe mark.
213
+ *
214
+ * The probes are `transient` marks ({@link import('./delta.js').createMark Mark}`#transient`): a
215
+ * transformer that keeps marks in internal state must not store them (none does today). Beyond that
216
+ * the call is state-neutral by construction — a mark-only change shifts no content and is never
217
+ * applied to any document, so neither the documents nor the transformer are (semantically) modified.
218
+ *
219
+ * The transformer must already have been fed the document state (a live
220
+ * {@link import('./rdt.js').Binding} always has): with no document behind it there is nothing for a
221
+ * position to point into — and e.g. a fresh `project` transformer would consume its initial render.
222
+ *
223
+ * @param {import('./transformer/core.js').Transformer<any, any>} tr
224
+ * @param {Array<Pos>} positions positions on the A side
225
+ * @return {Array<Pos?>} their images on the B side (input order; `null` = no image)
226
+ */
227
+ export const mapPositionsA = (tr, positions) => mapPositionsThrough(positions, c => tr.applyA(c).b)
228
+
229
+ /**
230
+ * One-shot map of `positions` living on a transformer's **B side** to the A side — see
231
+ * {@link mapPositionsA} for the contract (this is the same mapping, fed through `applyB`).
232
+ *
233
+ * @param {import('./transformer/core.js').Transformer<any, any>} tr
234
+ * @param {Array<Pos>} positions positions on the B side
235
+ * @return {Array<Pos?>} their images on the A side (input order; `null` = no image)
236
+ */
237
+ export const mapPositionsB = (tr, positions) => mapPositionsThrough(positions, c => tr.applyB(c).a)
package/src/function.js CHANGED
@@ -61,8 +61,6 @@ export const equalityStrict = (a, b) => a === b
61
61
  */
62
62
  export const equalityFlat = (a, b) => a === b || (a != null && b != null && a.constructor === b.constructor && ((array.isArray(a) && array.equalFlat(a, /** @type {Array<T>} */ (b))) || (typeof a === 'object' && object.equalFlat(a, b))))
63
63
 
64
- /* c8 ignore start */
65
-
66
64
  /**
67
65
  * @param {any} a
68
66
  * @param {any} b
@@ -72,13 +70,20 @@ export const equalityDeep = (a, b) => {
72
70
  if (a === b) {
73
71
  return true
74
72
  }
75
- if (a == null || b == null || (a.constructor !== b.constructor && (a.constructor || Object) !== (b.constructor || Object))) {
73
+ if (a == null || b == null) {
74
+ return false
75
+ }
76
+ // read the constructor off the prototype, not the object — a plain object may declare an own
77
+ // property named "constructor" (e.g. `{ constructor: 4 }`)
78
+ const aC = Object.getPrototypeOf(a)?.constructor
79
+ const bC = Object.getPrototypeOf(b)?.constructor
80
+ if (aC !== bC && (aC || Object) !== (bC || Object)) {
76
81
  return false
77
82
  }
78
83
  if (a[equalityTrait.EqualityTraitSymbol] != null) {
79
84
  return a[equalityTrait.EqualityTraitSymbol](b)
80
85
  }
81
- switch (a.constructor) {
86
+ switch (aC) {
82
87
  case ArrayBuffer:
83
88
  a = new Uint8Array(a)
84
89
  b = new Uint8Array(b)
@@ -152,7 +157,6 @@ export const equalityDeep = (a, b) => {
152
157
  */
153
158
  // @ts-ignore
154
159
  export const isOneOf = (value, options) => options.includes(value)
155
- /* c8 ignore stop */
156
160
 
157
161
  export const isArray = array.isArray
158
162
 
package/src/random.js CHANGED
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * Isomorphic module for true random numbers / buffers / uuids.
3
3
  *
4
- * Attention: falls back to Math.random if the browser does not support crypto.
4
+ * uuidv4 uses the native `crypto.randomUUID` when available and falls back to a
5
+ * `getRandomValues`-based implementation otherwise (insecure browser contexts,
6
+ * react-native).
5
7
  *
6
8
  * @module random
7
9
  */
@@ -30,8 +32,20 @@ export const oneOf = arr => arr[math.floor(rand() * arr.length)]
30
32
  const uuidv4Template = [1e7] + -1e3 + -4e3 + -8e3 + -1e11
31
33
 
32
34
  /**
35
+ * Fallback for environments without `crypto.randomUUID` (insecure browser
36
+ * contexts, react-native).
37
+ *
33
38
  * @return {string}
34
39
  */
35
- export const uuidv4 = () => uuidv4Template.replace(/[018]/g, /** @param {number} c */ c =>
40
+ export const _uuidv4Polyfill = () => uuidv4Template.replace(/[018]/g, /** @param {number} c */ c =>
36
41
  (c ^ uint32() & 15 >> c / 4).toString(16)
37
42
  )
43
+
44
+ /**
45
+ * @type {() => string}
46
+ */
47
+ export const uuidv4 = /* @__PURE__ */ (() =>
48
+ typeof crypto !== 'undefined' && crypto.randomUUID != null
49
+ ? crypto.randomUUID.bind(crypto)
50
+ : _uuidv4Polyfill
51
+ )()