squint-cljs 0.14.197 → 0.14.198

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.
@@ -20,6 +20,28 @@ function findKey(iter, tar, key) {
20
20
  }
21
21
  }
22
22
 
23
+ function isSortedMap(m) {
24
+ return m != null && m[SORTED_TAG] === true && m[TYPE_TAG] === MAP_TYPE;
25
+ }
26
+ function isMapLike(m) {
27
+ return (
28
+ m != null &&
29
+ typeof m === 'object' &&
30
+ (m.constructor === Object || m instanceof Map || m[TYPE_TAG] === MAP_TYPE)
31
+ );
32
+ }
33
+ function mapHas(m, k) {
34
+ return m instanceof Map || m[TYPE_TAG] === MAP_TYPE ? m.has(k) : has.call(m, k);
35
+ }
36
+ function mapGet(m, k) {
37
+ return m instanceof Map || m[TYPE_TAG] === MAP_TYPE ? m.get(k) : m[k];
38
+ }
39
+ function mapCount(m) {
40
+ return m instanceof Map || m[TYPE_TAG] === MAP_TYPE
41
+ ? m.size
42
+ : Object.keys(m).length;
43
+ }
44
+
23
45
  function dequal(foo, bar) {
24
46
  // supports primitives, Array, Set, Map and plain objects
25
47
  // like CLJS: does not support NaN
@@ -28,7 +50,21 @@ function dequal(foo, bar) {
28
50
  if (foo == null) return bar == null;
29
51
  var ctor, len, tmp;
30
52
 
53
+ // A sorted map compares by entries against any map type (object, Map, sorted).
54
+ const fooSorted = isSortedMap(foo);
55
+ if (fooSorted || isSortedMap(bar)) {
56
+ const sm = fooSorted ? foo : bar;
57
+ const other = sm === foo ? bar : foo;
58
+ if (!isMapLike(other) || mapCount(sm) !== mapCount(other)) return false;
59
+ for (const k of sm.keys()) {
60
+ if (!mapHas(other, k) || !dequal(sm.get(k), mapGet(other, k))) return false;
61
+ }
62
+ return true;
63
+ }
64
+
31
65
  if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
66
+ if (ctor === Date) return foo.getTime() === bar.getTime();
67
+
32
68
  if (ctor === Array) {
33
69
  if ((len = foo.length) === bar.length) {
34
70
  while (len-- && dequal(foo[len], bar[len]));
@@ -219,7 +255,8 @@ function copyMeta(from, to) {
219
255
  function copy(o) {
220
256
  switch (typeConst(o)) {
221
257
  case MAP_TYPE:
222
- return copyMeta(o, new Map(o));
258
+ // new o.constructor(o) preserves a SortedMap; for a plain Map it is new Map(o)
259
+ return copyMeta(o, new o.constructor(o));
223
260
  case SET_TYPE:
224
261
  return copyMeta(o, new o.constructor(o));
225
262
  case ARRAY_TYPE:
@@ -242,6 +279,14 @@ export function assoc(o, k, v, ...kvs) {
242
279
  return ret;
243
280
  }
244
281
 
282
+ // squint has no distinct hash-map or array-map type; both build a plain object.
283
+ export function hash_map(...kvs) {
284
+ if (kvs.length === 0) return {};
285
+ return assoc({}, ...kvs);
286
+ }
287
+
288
+ export const array_map = hash_map;
289
+
245
290
  const MAP_TYPE = 1;
246
291
  const ARRAY_TYPE = 2;
247
292
  const OBJECT_TYPE = 3;
@@ -457,6 +502,8 @@ export function conj(...xs) {
457
502
  }
458
503
 
459
504
  const [_o, ...rest] = xs;
505
+ // (conj coll) with nothing to add returns coll unchanged, including nil.
506
+ if (rest.length === 0) return _o;
460
507
 
461
508
  let o = _o;
462
509
  if (o === null || o === undefined) {
@@ -518,11 +565,15 @@ export function disj_BANG_(s, ...xs) {
518
565
 
519
566
  export function disj(s, ...xs) {
520
567
  if (s == null) return s;
521
- const s1 = new s.constructor([...s]);
568
+ // pass s itself (not a spread) so a SortedSet keeps its comparator
569
+ const s1 = new s.constructor(s);
522
570
  return disj_BANG_(s1, ...xs);
523
571
  }
524
572
 
525
573
  export function contains_QMARK_(coll, v) {
574
+ if (typeof coll === 'string') {
575
+ return int_QMARK_(v) && v >= 0 && v < coll.length;
576
+ }
526
577
  switch (typeConst(coll)) {
527
578
  case SET_TYPE:
528
579
  case MAP_TYPE:
@@ -658,7 +709,11 @@ export function seq_QMARK_(x) {
658
709
  return x != null && !!x[Symbol.iterator];
659
710
  }
660
711
 
661
- export const sequential_QMARK_ = seq_QMARK_;
712
+ export function sequential_QMARK_(x) {
713
+ // vectors and lists are arrays; lazy seqs and cons carry the lazy brand.
714
+ // Sets, maps and strings are iterable but not sequential.
715
+ return Array.isArray(x) || x?.[TYPE_TAG] === LAZY_ITERABLE_TYPE;
716
+ }
662
717
 
663
718
  export function seqable_QMARK_(x) {
664
719
  return (
@@ -715,11 +770,17 @@ export const es6_iterator = _iterator;
715
770
 
716
771
  export function seq(x) {
717
772
  if (x == null) return x;
773
+ // a string seqs into its characters, like CLJS.
774
+ if (typeof x === 'string') return x.length ? [...x] : null;
718
775
  const iter = iterable(x);
719
776
  // return nil for terminal checking
720
777
  if (iter.length === 0 || iter.size === 0) {
721
778
  return null;
722
779
  }
780
+ // a set is iterable but not sequential; materialize its elements.
781
+ if (iter instanceof Set || iter[TYPE_TAG] === SET_TYPE) {
782
+ return [...iter];
783
+ }
723
784
  const _i = iter[Symbol.iterator]();
724
785
  if (_i.next().done) return null;
725
786
  return iter;
@@ -1034,6 +1095,7 @@ function mapChunks(coll, xf) {
1034
1095
  export const Cons = defclass(
1035
1096
  class Cons {
1036
1097
  constructor(x, coll) {
1098
+ this[TYPE_TAG] = LAZY_ITERABLE_TYPE;
1037
1099
  this.x = x;
1038
1100
  this.coll = coll;
1039
1101
  }
@@ -1366,6 +1428,19 @@ export function re_pattern(s) {
1366
1428
  }
1367
1429
 
1368
1430
  export function subvec(arr, start, end) {
1431
+ if (!isVectorArray(arr)) {
1432
+ throw new Error('subvec: argument must be a vector');
1433
+ }
1434
+ if (end === undefined) end = arr.length;
1435
+ if (start == null || end == null) {
1436
+ throw new Error('subvec: start and end must not be nil');
1437
+ }
1438
+ // CLJS coerces the indices with (int x) before bounds-checking.
1439
+ start = start | 0;
1440
+ end = end | 0;
1441
+ if (start < 0 || end < start || end > arr.length) {
1442
+ throw new Error('subvec: index out of bounds');
1443
+ }
1369
1444
  return arr.slice(start, end);
1370
1445
  }
1371
1446
 
@@ -1471,6 +1546,9 @@ export function apply(f, ...args) {
1471
1546
  }
1472
1547
 
1473
1548
  export function even_QMARK_(x) {
1549
+ if (!Number.isInteger(x)) {
1550
+ throw new Error(`Argument must be an integer: ${x}`);
1551
+ }
1474
1552
  return x % 2 == 0;
1475
1553
  }
1476
1554
 
@@ -1496,7 +1574,7 @@ class List extends Array {
1496
1574
  }
1497
1575
 
1498
1576
  export function list_QMARK_(x) {
1499
- return typeConst(x) === LIST_TYPE;
1577
+ return x?.[TYPE_TAG] === LIST_TYPE;
1500
1578
  }
1501
1579
 
1502
1580
  export function list(...args) {
@@ -1682,8 +1760,10 @@ export function partition_all(n, ...args) {
1682
1760
  }
1683
1761
 
1684
1762
  export function partition(n, ...args) {
1763
+ // pad stays undefined for arity 1 and 2: only arity 4 provides a pad, which
1764
+ // makes the final partial partition be emitted (padded when pad has elements).
1685
1765
  let step = n,
1686
- pad = [],
1766
+ pad,
1687
1767
  coll = args[0];
1688
1768
 
1689
1769
  if (args.length === 2) {
@@ -1718,8 +1798,10 @@ function partitionInternal(n, step, pad, coll, all) {
1718
1798
  if (p.length > 0) {
1719
1799
  if (p.length === n || all) {
1720
1800
  yield p;
1721
- } else if (pad.length) {
1722
- p.push(...pad.slice(0, n - p.length));
1801
+ } else if (pad !== undefined) {
1802
+ if (pad != null) {
1803
+ p.push(...[...iterable(pad)].slice(0, n - p.length));
1804
+ }
1723
1805
  yield p;
1724
1806
  }
1725
1807
  }
@@ -1814,6 +1896,8 @@ export function empty(coll) {
1814
1896
  }
1815
1897
 
1816
1898
  export function merge(...args) {
1899
+ // with no truthy maps, return nil like CLJS `(when (some identity maps) ...)`.
1900
+ if (!args.some(truth_)) return null;
1817
1901
  // if the first arg is nil we coerce it into a map.
1818
1902
  const firstArg = args[0];
1819
1903
  let obj;
@@ -1905,20 +1989,16 @@ export function repeat(...args) {
1905
1989
  if (args.length == 0 || args.length > 2) {
1906
1990
  throw new Error(`Invalid arity: ${args.length}`);
1907
1991
  }
1908
-
1909
- return {
1910
- [IIterable]: true,
1911
- [IIterable__iterator]:
1912
- args.length == 1
1913
- ? function* () {
1914
- const x = args[0];
1915
- while (true) yield x;
1916
- }
1917
- : function* () {
1918
- const [n, x] = args;
1919
- for (var i = 0; i < n; i++) yield x;
1920
- },
1921
- };
1992
+ if (args.length == 1) {
1993
+ const x = args[0];
1994
+ return lazy(function* () {
1995
+ while (true) yield x;
1996
+ });
1997
+ }
1998
+ const [n, x] = args;
1999
+ return lazy(function* () {
2000
+ for (var i = 0; i < n; i++) yield x;
2001
+ });
1922
2002
  }
1923
2003
 
1924
2004
  export function ensure_reduced(x) {
@@ -1970,6 +2050,8 @@ export function take_last(n, coll) {
1970
2050
  lastN[i % n] = x;
1971
2051
  i++;
1972
2052
  }
2053
+ // an empty or nil coll has no last n elements; return nil like CLJS.
2054
+ if (i === 0) return null;
1973
2055
  if (i % n !== 0 && i >= n) {
1974
2056
  return lastN.slice(i % n).concat(lastN.slice(0, i % n));
1975
2057
  } else {
@@ -2147,14 +2229,14 @@ export function update_in(coll, path, f, ...args) {
2147
2229
  return assoc_in(coll, path, f(get_in(coll, path), ...args));
2148
2230
  }
2149
2231
 
2150
- export function fnil(f, x, ...xs) {
2232
+ export function fnil(f, ...defaults) {
2151
2233
  f = __toFn(f);
2152
- return function (a, ...args) {
2153
- if (!a) {
2154
- return f(x, ...xs, ...args);
2155
- } else {
2156
- return f(a, ...xs, ...args);
2234
+ const n = defaults.length;
2235
+ return function (...args) {
2236
+ for (let i = 0; i < n; i++) {
2237
+ if (args[i] == null) args[i] = defaults[i];
2157
2238
  }
2239
+ return f(...args);
2158
2240
  };
2159
2241
  }
2160
2242
 
@@ -2328,7 +2410,10 @@ export function repeatedly(n, f) {
2328
2410
  n = undefined;
2329
2411
  }
2330
2412
  const res = _repeatedly(f);
2331
- if (n) {
2413
+ if (n !== undefined) {
2414
+ if (typeof n !== 'number') {
2415
+ throw new Error('repeatedly: count must be a number, got: ' + str(n));
2416
+ }
2332
2417
  return take(n, res);
2333
2418
  } else {
2334
2419
  return res;
@@ -2363,6 +2448,11 @@ export function frequencies(coll) {
2363
2448
  // The lazy-seq macro emits `new LazySeq(() => body)`: a LazyIterable whose step
2364
2449
  // evaluates the body thunk on first force and reads it unchunked.
2365
2450
  export class LazySeq extends LazyIterable {
2451
+ // Core fns build the base LazyIterable, not this subclass, so widen
2452
+ // instanceof to any lazy cell for `(instance? LazySeq x)` parity with CLJS.
2453
+ static [Symbol.hasInstance](x) {
2454
+ return x instanceof LazyIterable;
2455
+ }
2366
2456
  constructor(f) {
2367
2457
  super(() => unchunkedSteps(es6_iterator(iterable(f())))());
2368
2458
  }
@@ -2414,7 +2504,7 @@ export function some_QMARK_(x) {
2414
2504
  }
2415
2505
 
2416
2506
  export function boolean$(x) {
2417
- return !!x;
2507
+ return truth_(x);
2418
2508
  }
2419
2509
 
2420
2510
  export function zero_QMARK_(x) {
@@ -2488,11 +2578,13 @@ export function aclone(arr) {
2488
2578
  }
2489
2579
 
2490
2580
  export function add_watch(ref, key, fn) {
2491
- return ref._add_watch(key, fn);
2581
+ ref._add_watch(key, fn);
2582
+ return ref;
2492
2583
  }
2493
2584
 
2494
2585
  export function remove_watch(ref, key) {
2495
- return ref._remove_watch(key);
2586
+ ref._remove_watch(key);
2587
+ return ref;
2496
2588
  }
2497
2589
 
2498
2590
  export function reduce_kv(f, init, m) {
@@ -2524,6 +2616,7 @@ export function map_QMARK_(coll) {
2524
2616
  if (coll == null) return false;
2525
2617
  if (isObj(coll)) return true;
2526
2618
  if (coll instanceof Map) return true;
2619
+ if (coll[TYPE_TAG] === MAP_TYPE) return true;
2527
2620
  return false;
2528
2621
  }
2529
2622
 
@@ -2706,25 +2799,33 @@ export function keys(obj) {
2706
2799
  if (obj == null) return;
2707
2800
  const t = typeConst(obj);
2708
2801
  switch (t) {
2709
- case OBJECT_TYPE:
2710
- return Object.keys(obj);
2802
+ case OBJECT_TYPE: {
2803
+ const ks = Object.keys(obj);
2804
+ if (ks.length) return ks;
2805
+ return;
2806
+ }
2711
2807
  case MAP_TYPE:
2712
- return Array.from(obj.keys());
2808
+ if (obj.size) return Array.from(obj.keys());
2809
+ return;
2713
2810
  }
2714
2811
  }
2715
2812
 
2716
2813
  export function js_keys(obj) {
2717
- return keys(obj);
2814
+ return keys(obj) ?? [];
2718
2815
  }
2719
2816
 
2720
2817
  export function vals(obj) {
2721
2818
  if (obj == null) return;
2722
2819
  const t = typeConst(obj);
2723
2820
  switch (t) {
2724
- case OBJECT_TYPE:
2725
- return Object.values(obj);
2821
+ case OBJECT_TYPE: {
2822
+ const vs = Object.values(obj);
2823
+ if (vs.length) return vs;
2824
+ return;
2825
+ }
2726
2826
  case MAP_TYPE:
2727
- return Array.from(obj.values());
2827
+ if (obj.size) return Array.from(obj.values());
2828
+ return;
2728
2829
  }
2729
2830
  }
2730
2831
 
@@ -2867,6 +2968,13 @@ export function with_meta(x, m) {
2867
2968
  wrapped[_metaSym] = m;
2868
2969
  return wrapped;
2869
2970
  }
2971
+ // A lazy seq or cons is not copied element-wise: clone the head so the new
2972
+ // value carries its own metadata without forcing realization.
2973
+ if (x?.[TYPE_TAG] === LAZY_ITERABLE_TYPE) {
2974
+ const ret = Object.assign(Object.create(Object.getPrototypeOf(x)), x);
2975
+ ret[_metaSym] = m;
2976
+ return ret;
2977
+ }
2870
2978
  const ret = copy(x);
2871
2979
  ret[_metaSym] = m;
2872
2980
  return ret;
@@ -3082,12 +3190,13 @@ export function persistent_BANG_(x) {
3082
3190
 
3083
3191
  const SortedSet = defclass(
3084
3192
  class SortedSet {
3085
- constructor(xs) {
3193
+ constructor(xs, cmp) {
3086
3194
  this[TYPE_TAG] = SET_TYPE;
3087
3195
  this[SORTED_TAG] = true;
3088
- const isSorted = xs instanceof SortedSet;
3196
+ this._cmp = cmp ?? xs?._cmp;
3197
+ const isSorted = xs instanceof SortedSet && xs._cmp === this._cmp;
3089
3198
  if (!isSorted) {
3090
- xs = sort(xs);
3199
+ xs = this._cmp ? sort(this._cmp, xs) : sort(xs);
3091
3200
  }
3092
3201
  const s = new Set(xs);
3093
3202
  // we don't re-use xs since xs can contain duplicates
@@ -3097,9 +3206,10 @@ class SortedSet {
3097
3206
  add(x) {
3098
3207
  if (this._set.has(x)) return this;
3099
3208
  const xs = this._elts;
3209
+ const cmp = this._cmp;
3100
3210
  let added = false;
3101
3211
  for (let i = 0; i < xs.length; i++) {
3102
- if (compare(x, xs[i]) <= 0) {
3212
+ if ((cmp ? cmp(x, xs[i]) : compare(x, xs[i])) <= 0) {
3103
3213
  xs.splice(i, 0, x);
3104
3214
  added = true;
3105
3215
  break;
@@ -3153,6 +3263,85 @@ export function sorted_set(...xs) {
3153
3263
  return new SortedSet(xs);
3154
3264
  }
3155
3265
 
3266
+ export function sorted_set_by(cmp, ...xs) {
3267
+ return new SortedSet(xs, fnToComparator(__toFn(cmp)));
3268
+ }
3269
+
3270
+ // A map that keeps its keys in sorted order. Backed by a sorted key array plus
3271
+ // a plain Map for lookup, and branded MAP_TYPE so map ops dispatch normally.
3272
+ const SortedMap = defclass(
3273
+ class SortedMap {
3274
+ constructor(entries, cmp) {
3275
+ this[TYPE_TAG] = MAP_TYPE;
3276
+ this[SORTED_TAG] = true;
3277
+ this._cmp = cmp ?? entries?._cmp;
3278
+ this._map = new Map();
3279
+ this._keys = [];
3280
+ this.size = 0;
3281
+ if (entries) {
3282
+ for (const [k, v] of entries) this.set(k, v);
3283
+ }
3284
+ }
3285
+ set(k, v) {
3286
+ if (!this._map.has(k)) {
3287
+ const ks = this._keys;
3288
+ const cmp = this._cmp;
3289
+ let i = 0;
3290
+ while (i < ks.length && (cmp ? cmp(k, ks[i]) : compare(k, ks[i])) > 0) i++;
3291
+ ks.splice(i, 0, k);
3292
+ }
3293
+ this._map.set(k, v);
3294
+ this.size = this._map.size;
3295
+ return this;
3296
+ }
3297
+ get(k) {
3298
+ return this._map.get(k);
3299
+ }
3300
+ has(k) {
3301
+ return this._map.has(k);
3302
+ }
3303
+ delete(k) {
3304
+ if (this._map.delete(k)) {
3305
+ this._keys.splice(this._keys.indexOf(k), 1);
3306
+ this.size = this._map.size;
3307
+ }
3308
+ return this;
3309
+ }
3310
+ *keys() {
3311
+ yield* this._keys;
3312
+ }
3313
+ *values() {
3314
+ for (const k of this._keys) yield this._map.get(k);
3315
+ }
3316
+ *entries() {
3317
+ for (const k of this._keys) yield [k, this._map.get(k)];
3318
+ }
3319
+ forEach(f) {
3320
+ for (const k of this._keys) f(this._map.get(k), k, this);
3321
+ }
3322
+ clear() {
3323
+ this._map.clear();
3324
+ this._keys = [];
3325
+ this.size = 0;
3326
+ }
3327
+ [Symbol.iterator]() {
3328
+ return this.entries();
3329
+ }
3330
+ }
3331
+ );
3332
+
3333
+ export function sorted_map(...kvs) {
3334
+ const m = new SortedMap();
3335
+ for (let i = 0; i < kvs.length; i += 2) m.set(kvs[i], kvs[i + 1]);
3336
+ return m;
3337
+ }
3338
+
3339
+ export function sorted_map_by(cmp, ...kvs) {
3340
+ const m = new SortedMap(null, fnToComparator(__toFn(cmp)));
3341
+ for (let i = 0; i < kvs.length; i += 2) m.set(kvs[i], kvs[i + 1]);
3342
+ return m;
3343
+ }
3344
+
3156
3345
  function mkBoundFn(_sc, test, key) {
3157
3346
  return (e) => {
3158
3347
  return test(compare(e, key), 0);
@@ -3218,6 +3407,14 @@ export function long$(x) {
3218
3407
  return fix(x);
3219
3408
  }
3220
3409
 
3410
+ export function float$(x) {
3411
+ return x;
3412
+ }
3413
+
3414
+ export function double$(x) {
3415
+ return x;
3416
+ }
3417
+
3221
3418
  export function type(x) {
3222
3419
  return x != null && x.constructor;
3223
3420
  }
@@ -3232,7 +3429,7 @@ function preserving_reduced(rf) {
3232
3429
  }
3233
3430
 
3234
3431
  export function cat(rf) {
3235
- rf = preserving_reduced(rf);
3432
+ const rrf = preserving_reduced(rf);
3236
3433
  return (...args) => {
3237
3434
  switch (args.length) {
3238
3435
  case 0:
@@ -3240,7 +3437,7 @@ export function cat(rf) {
3240
3437
  case 1:
3241
3438
  return rf(args[0]);
3242
3439
  case 2:
3243
- return reduce(rf, args[0], args[1]);
3440
+ return reduce(rrf, args[0], args[1]);
3244
3441
  }
3245
3442
  };
3246
3443
  }
@@ -3264,7 +3461,11 @@ export function memoize(f) {
3264
3461
  }
3265
3462
 
3266
3463
  export function peek(vec) {
3267
- if (array_QMARK_(vec)) {
3464
+ // A list peeks at its front; squint lists are array-backed, so check list
3465
+ // before array to avoid returning the last element.
3466
+ if (list_QMARK_(vec)) {
3467
+ return first(vec);
3468
+ } else if (array_QMARK_(vec)) {
3268
3469
  return vec[vec.length - 1];
3269
3470
  } else {
3270
3471
  return first(vec);
@@ -3272,7 +3473,12 @@ export function peek(vec) {
3272
3473
  }
3273
3474
 
3274
3475
  export function pop(vec) {
3275
- if (array_QMARK_(vec)) {
3476
+ if (vec == null) return null;
3477
+ if (list_QMARK_(vec)) {
3478
+ if (vec.length === 0) throw new Error("Can't pop empty list");
3479
+ return rest(vec);
3480
+ } else if (array_QMARK_(vec)) {
3481
+ if (vec.length === 0) throw new Error("Can't pop empty vector");
3276
3482
  const ret = [...vec];
3277
3483
  ret.pop();
3278
3484
  return ret;
@@ -3311,6 +3517,27 @@ export function random_uuid() {
3311
3517
  return crypto.randomUUID();
3312
3518
  }
3313
3519
 
3520
+ export class UUID {
3521
+ constructor(uuid) {
3522
+ this.uuid = uuid;
3523
+ }
3524
+ toString() {
3525
+ return this.uuid;
3526
+ }
3527
+ }
3528
+
3529
+ export function uuid(s) {
3530
+ return new UUID(s);
3531
+ }
3532
+
3533
+ export function uuid_QMARK_(x) {
3534
+ return x instanceof UUID;
3535
+ }
3536
+
3537
+ export function inst_QMARK_(x) {
3538
+ return x instanceof Date;
3539
+ }
3540
+
3314
3541
  export class Delay {
3315
3542
  constructor(f) {
3316
3543
  this.f = f;
@@ -3326,6 +3553,13 @@ export class Delay {
3326
3553
  }
3327
3554
  }
3328
3555
 
3556
+ export function realized_QMARK_(x) {
3557
+ if (x instanceof Delay || x instanceof LazyIterable) {
3558
+ return x.realized === true;
3559
+ }
3560
+ throw new Error('realized? not supported on: ' + str(x));
3561
+ }
3562
+
3329
3563
  function clj__GT_js_(x, seen) {
3330
3564
  // we need to protect against circular objects
3331
3565
  if (seen.has(x)) return x;
@@ -3385,6 +3619,7 @@ function toEDN(value, seen = new WeakSet(), readably = true) {
3385
3619
  if (typeof value === 'bigint') return `${value}N`;
3386
3620
 
3387
3621
  if (typeof value === 'object') {
3622
+ if (value instanceof UUID) return readably ? `#uuid "${value.uuid}"` : value.uuid;
3388
3623
  // seen tracks the current ancestor path only. A shared reference that
3389
3624
  // appears under sibling branches is a DAG, not a cycle, so delete on exit.
3390
3625
  if (seen.has(value)) return '#object[circular]';