recursive-set 3.0.0 → 5.0.0

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/cjs/index.js CHANGED
@@ -1,289 +1,478 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
2
+ /**
3
+ * @module recursive-set
4
+ * High-Performance Recursive Set with "Freeze-on-Hash" semantics.
5
+ */
5
6
  Object.defineProperty(exports, "__esModule", { value: true });
6
7
  exports.RecursiveSet = exports.Tuple = void 0;
7
8
  exports.emptySet = emptySet;
8
9
  exports.singleton = singleton;
9
10
  exports.fromIterable = fromIterable;
10
- const functional_red_black_tree_1 = __importDefault(require("functional-red-black-tree"));
11
- /**
12
- * @module recursive-set
13
- * A mutable recursive set implementation.
14
- * Powered by functional Red-Black Trees for O(log n) operations and O(1) cloning.
15
- */
16
- /**
17
- * A lightweight wrapper for Tuples to enable Value-Equality in RecursiveSet.
18
- * Immutable by design.
19
- */
20
- class Tuple {
21
- values;
22
- constructor(...values) {
23
- this.values = values;
11
+ // === HASHING ENGINE (FNV-1a) ===
12
+ const FNV_PRIME = 16777619;
13
+ const FNV_OFFSET = 2166136261;
14
+ function hashString(str) {
15
+ let hash = FNV_OFFSET;
16
+ const len = str.length;
17
+ for (let i = 0; i < len; i++) {
18
+ hash ^= str.charCodeAt(i);
19
+ hash = Math.imul(hash, FNV_PRIME);
24
20
  }
25
- get length() {
26
- return this.values.length;
21
+ return hash >>> 0;
22
+ }
23
+ function hashValue(val) {
24
+ if (typeof val === 'string')
25
+ return hashString(val);
26
+ if (typeof val === 'number') {
27
+ let hash = FNV_OFFSET;
28
+ hash ^= (val | 0);
29
+ hash = Math.imul(hash, FNV_PRIME);
30
+ return hash >>> 0;
27
31
  }
28
- get(index) {
29
- return this.values[index];
32
+ // Fast Path: Objects with cached hash
33
+ if (val && typeof val === 'object' && 'hashCode' in val) {
34
+ return val.hashCode;
30
35
  }
31
- *[Symbol.iterator]() {
32
- for (const val of this.values) {
33
- yield val;
36
+ if (Array.isArray(val)) {
37
+ let h = FNV_OFFSET;
38
+ const len = val.length;
39
+ for (let i = 0; i < len; i++) {
40
+ h ^= hashValue(val[i]);
41
+ h = Math.imul(h, FNV_PRIME);
34
42
  }
43
+ return h >>> 0;
35
44
  }
36
- toString() {
37
- return `(${this.values.map(v => String(v)).join(', ')})`;
45
+ return 0;
46
+ }
47
+ // === COMPARATOR ===
48
+ function compare(a, b) {
49
+ if (a === b)
50
+ return 0;
51
+ // 1. Hash Short-Circuit (Optimization)
52
+ const aH = a?.hashCode;
53
+ const bH = b?.hashCode;
54
+ const ha = (aH !== undefined) ? aH : hashValue(a);
55
+ const hb = (bH !== undefined) ? bH : hashValue(b);
56
+ if (ha !== hb)
57
+ return ha < hb ? -1 : 1;
58
+ // 2. Structural Type Check
59
+ const typeA = typeof a;
60
+ const typeB = typeof b;
61
+ if (typeA === 'string' && typeB === 'string')
62
+ return a < b ? -1 : 1;
63
+ if (typeA === 'number' && typeB === 'number')
64
+ return a < b ? -1 : 1;
65
+ const isSetA = a instanceof RecursiveSet;
66
+ const isSetB = b instanceof RecursiveSet;
67
+ if (isSetA && isSetB) {
68
+ return a.compare(b);
38
69
  }
39
- [Symbol.for('nodejs.util.inspect.custom')]() {
40
- return this.toString();
70
+ const isSeqA = Array.isArray(a) || a instanceof Tuple;
71
+ const isSeqB = Array.isArray(b) || b instanceof Tuple;
72
+ // Sort Order: Primitives (0) < Sequences (1) < Sets (2)
73
+ if (isSetA !== isSetB || isSeqA !== isSeqB) {
74
+ const scoreA = isSetA ? 2 : isSeqA ? 1 : 0;
75
+ const scoreB = isSetB ? 2 : isSeqB ? 1 : 0;
76
+ return scoreA - scoreB;
41
77
  }
78
+ // 3. Sequence Comparison
79
+ if (isSeqA && isSeqB) {
80
+ const valA = (a instanceof Tuple) ? a.values : a;
81
+ const valB = (b instanceof Tuple) ? b.values : b;
82
+ const len = valA.length;
83
+ if (len !== valB.length)
84
+ return len - valB.length;
85
+ for (let i = 0; i < len; i++) {
86
+ const diff = compare(valA[i], valB[i]);
87
+ if (diff !== 0)
88
+ return diff;
89
+ }
90
+ return 0;
91
+ }
92
+ return a < b ? -1 : 1;
93
+ }
94
+ // === CLASSES ===
95
+ class Tuple {
96
+ values;
97
+ hashCode;
98
+ constructor(...values) {
99
+ this.values = values;
100
+ this.hashCode = hashValue(values);
101
+ }
102
+ get length() { return this.values.length; }
103
+ get(i) { return this.values[i]; }
104
+ *[Symbol.iterator]() { yield* this.values; }
105
+ toString() { return `(${this.values.join(', ')})`; }
106
+ [Symbol.for('nodejs.util.inspect.custom')]() { return this.toString(); }
42
107
  }
43
108
  exports.Tuple = Tuple;
44
109
  class RecursiveSet {
45
- _tree;
110
+ _elements;
111
+ _hashCode = null;
112
+ _isFrozen = false;
113
+ static compare(a, b) { return compare(a, b); }
114
+ constructor(...elements) {
115
+ if (elements.length === 0) {
116
+ this._elements = [];
117
+ this._hashCode = 0;
118
+ }
119
+ else {
120
+ this._elements = elements;
121
+ this._elements.sort(compare);
122
+ this._unique();
123
+ }
124
+ }
125
+ _checkFrozen(op) {
126
+ if (this._isFrozen) {
127
+ throw new Error(`InvalidOperation: Cannot ${op} a frozen RecursiveSet.\n` +
128
+ `This Set has been hashed or used in a collection (Value Semantics).\n` +
129
+ `Use .mutableCopy() to create a modifiable copy.`);
130
+ }
131
+ }
132
+ _unique() {
133
+ const arr = this._elements;
134
+ const len = arr.length;
135
+ if (len < 2)
136
+ return;
137
+ let write = 1;
138
+ for (let read = 1; read < len; read++) {
139
+ if (compare(arr[read], arr[read - 1]) !== 0) {
140
+ arr[write++] = arr[read];
141
+ }
142
+ }
143
+ arr.length = write;
144
+ }
46
145
  /**
47
- * Static comparator for Red-Black Tree ordering.
48
- * Supports Primitives, RecursiveSets and Tuples.
49
- * REJECTS plain JS Objects and Arrays to enforce strict semantics.
146
+ * Calculates/Caches hash code and FREEZES the set.
50
147
  */
51
- static compare(a, b) {
52
- if (a === b)
53
- return 0;
54
- const isSetA = a instanceof RecursiveSet;
55
- const isSetB = b instanceof RecursiveSet;
56
- const isTupA = a instanceof Tuple;
57
- const isTupB = b instanceof Tuple;
58
- // Sort Order: Primitives (0) < Tuples (1) < Sets (2)
59
- const getTypeScore = (isSet, isTup) => {
60
- if (isSet)
61
- return 2;
62
- if (isTup)
63
- return 1;
64
- return 0;
65
- };
66
- const scoreA = getTypeScore(isSetA, isTupA);
67
- const scoreB = getTypeScore(isSetB, isTupB);
68
- if (scoreA !== scoreB)
69
- return scoreA < scoreB ? -1 : 1;
70
- // 1. Sets
71
- if (isSetA && isSetB) {
72
- const setA = a;
73
- const setB = b;
74
- if (setA.size !== setB.size)
75
- return setA.size < setB.size ? -1 : 1;
76
- let iterA = setA._tree.begin;
77
- let iterB = setB._tree.begin;
78
- while (iterA.valid && iterB.valid) {
79
- const cmp = RecursiveSet.compare(iterA.key, iterB.key);
80
- if (cmp !== 0)
81
- return cmp;
82
- iterA.next();
83
- iterB.next();
84
- }
85
- return 0;
148
+ get hashCode() {
149
+ if (this._hashCode !== null)
150
+ return this._hashCode;
151
+ let h = 0;
152
+ const arr = this._elements;
153
+ const len = arr.length;
154
+ for (let i = 0; i < len; i++) {
155
+ h = Math.imul(31, h) + hashValue(arr[i]);
86
156
  }
87
- // 2. Tuples
88
- if (isTupA && isTupB) {
89
- const tupA = a;
90
- const tupB = b;
91
- if (tupA.length !== tupB.length)
92
- return tupA.length < tupB.length ? -1 : 1;
93
- for (let i = 0; i < tupA.length; i++) {
94
- const cmp = RecursiveSet.compare(tupA.get(i), tupB.get(i));
95
- if (cmp !== 0)
96
- return cmp;
97
- }
157
+ this._hashCode = h | 0;
158
+ this._isFrozen = true;
159
+ return this._hashCode;
160
+ }
161
+ get isFrozen() { return this._isFrozen; }
162
+ compare(other) {
163
+ if (this === other)
98
164
  return 0;
165
+ const h1 = this.hashCode;
166
+ const h2 = other.hashCode;
167
+ if (h1 !== h2)
168
+ return h1 < h2 ? -1 : 1;
169
+ const arrA = this._elements;
170
+ const arrB = other._elements;
171
+ const len = arrA.length;
172
+ if (len !== arrB.length)
173
+ return len - arrB.length;
174
+ for (let i = 0; i < len; i++) {
175
+ const cmp = compare(arrA[i], arrB[i]);
176
+ if (cmp !== 0)
177
+ return cmp;
99
178
  }
100
- // 3. Primitives (guaranteed by add() validation)
101
- const tA = typeof a;
102
- const tB = typeof b;
103
- if (tA !== tB)
104
- return tA > tB ? 1 : -1;
105
- // @ts-ignore
106
- if (a < b)
107
- return -1;
108
- // @ts-ignore
109
- if (a > b)
110
- return 1;
111
179
  return 0;
112
180
  }
113
- constructor(...elements) {
114
- this._tree = (0, functional_red_black_tree_1.default)(RecursiveSet.compare);
115
- for (const el of elements) {
116
- this.add(el);
181
+ get size() { return this._elements.length; }
182
+ isEmpty() { return this._elements.length === 0; }
183
+ has(element) {
184
+ const arr = this._elements;
185
+ const len = arr.length;
186
+ // Linear Scan (Prefetch-friendly for small sets)
187
+ if (len < 16) {
188
+ for (let i = 0; i < len; i++) {
189
+ if (compare(arr[i], element) === 0)
190
+ return true;
191
+ }
192
+ return false;
117
193
  }
194
+ // Binary Search
195
+ let low = 0, high = len - 1;
196
+ while (low <= high) {
197
+ const mid = (low + high) >>> 1;
198
+ const cmp = compare(arr[mid], element);
199
+ if (cmp === 0)
200
+ return true;
201
+ if (cmp < 0)
202
+ low = mid + 1;
203
+ else
204
+ high = mid - 1;
205
+ }
206
+ return false;
118
207
  }
119
- // === Copy-on-Write Support ===
120
- /**
121
- * Creates a shallow copy of the set in O(1) time.
122
- */
123
- clone() {
124
- const clone = new RecursiveSet();
125
- clone._tree = this._tree;
126
- return clone;
127
- }
128
- // === Mutable Operations ===
129
208
  add(element) {
130
- // Validation
131
- if (typeof element === "number" && Number.isNaN(element)) {
209
+ this._checkFrozen('add() to');
210
+ // Validation (Inlined for Performance)
211
+ if (typeof element === 'object' && element !== null) {
212
+ if (!(element instanceof RecursiveSet || element instanceof Tuple || Array.isArray(element))) {
213
+ throw new Error("Unsupported Type: Use Tuple, Array or RecursiveSet.");
214
+ }
215
+ }
216
+ else if (Number.isNaN(element)) {
132
217
  throw new Error("NaN is not supported");
133
218
  }
134
- const isSet = element instanceof RecursiveSet;
135
- const isTup = element instanceof Tuple;
136
- const isObject = element !== null && typeof element === 'object' && !isSet && !isTup;
137
- if (isObject) {
138
- throw new Error("Plain Objects and Arrays are not supported. " +
139
- "Use Tuple for sequences or RecursiveSet for nested structures.");
219
+ const arr = this._elements;
220
+ const len = arr.length;
221
+ // Optimization: Append to end (common in construction)
222
+ if (len > 0) {
223
+ const lastCmp = compare(arr[len - 1], element);
224
+ if (lastCmp < 0) {
225
+ arr.push(element);
226
+ this._hashCode = null;
227
+ return this;
228
+ }
229
+ if (lastCmp === 0)
230
+ return this;
231
+ }
232
+ else {
233
+ arr.push(element);
234
+ this._hashCode = null;
235
+ return this;
140
236
  }
141
- // Idempotency
142
- if (this.has(element))
237
+ // Small Array Strategy
238
+ if (len < 16) {
239
+ for (let i = 0; i < len; i++) {
240
+ const cmp = compare(arr[i], element);
241
+ if (cmp === 0)
242
+ return this;
243
+ if (cmp > 0) {
244
+ arr.splice(i, 0, element);
245
+ this._hashCode = null;
246
+ return this;
247
+ }
248
+ }
249
+ arr.push(element); // Should be unreachable given append check, but safe fallback
143
250
  return this;
144
- this._tree = this._tree.insert(element, true);
251
+ }
252
+ // Large Array Strategy
253
+ let low = 0, high = len - 1, idx = 0;
254
+ while (low <= high) {
255
+ const mid = (low + high) >>> 1;
256
+ const cmp = compare(arr[mid], element);
257
+ if (cmp === 0)
258
+ return this;
259
+ if (cmp < 0) {
260
+ idx = mid + 1;
261
+ low = mid + 1;
262
+ }
263
+ else {
264
+ idx = mid;
265
+ high = mid - 1;
266
+ }
267
+ }
268
+ arr.splice(idx, 0, element);
269
+ this._hashCode = null;
145
270
  return this;
146
271
  }
147
272
  remove(element) {
148
- this._tree = this._tree.remove(element);
273
+ this._checkFrozen('remove() from');
274
+ const arr = this._elements;
275
+ const len = arr.length;
276
+ if (len < 16) {
277
+ for (let i = 0; i < len; i++) {
278
+ if (compare(arr[i], element) === 0) {
279
+ arr.splice(i, 1);
280
+ this._hashCode = null;
281
+ return this;
282
+ }
283
+ }
284
+ return this;
285
+ }
286
+ let low = 0, high = len - 1;
287
+ while (low <= high) {
288
+ const mid = (low + high) >>> 1;
289
+ const cmp = compare(arr[mid], element);
290
+ if (cmp === 0) {
291
+ arr.splice(mid, 1);
292
+ this._hashCode = null;
293
+ return this;
294
+ }
295
+ if (cmp < 0)
296
+ low = mid + 1;
297
+ else
298
+ high = mid - 1;
299
+ }
149
300
  return this;
150
301
  }
151
302
  clear() {
152
- this._tree = (0, functional_red_black_tree_1.default)(RecursiveSet.compare);
303
+ this._checkFrozen('clear()');
304
+ this._elements = [];
305
+ this._hashCode = 0;
153
306
  return this;
154
307
  }
155
- // === Set Operations ===
308
+ mutableCopy() {
309
+ const s = new RecursiveSet();
310
+ s._elements = this._elements.slice();
311
+ return s;
312
+ }
313
+ clone() { return this.mutableCopy(); }
156
314
  union(other) {
157
- const result = this.clone();
158
- for (const el of other)
159
- result.add(el);
160
- return result;
315
+ const s = new RecursiveSet();
316
+ const arrA = this._elements;
317
+ const arrB = other._elements;
318
+ if (arrA.length === 0)
319
+ return other.clone();
320
+ if (arrB.length === 0)
321
+ return this.clone();
322
+ const res = [];
323
+ let i = 0, j = 0;
324
+ const lenA = arrA.length, lenB = arrB.length;
325
+ while (i < lenA && j < lenB) {
326
+ const cmp = compare(arrA[i], arrB[j]);
327
+ if (cmp < 0)
328
+ res.push(arrA[i++]);
329
+ else if (cmp > 0)
330
+ res.push(arrB[j++]);
331
+ else {
332
+ res.push(arrA[i++]);
333
+ j++;
334
+ }
335
+ }
336
+ while (i < lenA)
337
+ res.push(arrA[i++]);
338
+ while (j < lenB)
339
+ res.push(arrB[j++]);
340
+ s._elements = res;
341
+ return s;
161
342
  }
162
343
  intersection(other) {
163
- const result = new RecursiveSet();
164
- const [smaller, larger] = this.size < other.size ? [this, other] : [other, this];
165
- for (const el of smaller) {
166
- if (larger.has(el)) {
167
- result.add(el);
344
+ const s = new RecursiveSet();
345
+ const arrA = this._elements;
346
+ const arrB = other._elements;
347
+ const res = [];
348
+ let i = 0, j = 0;
349
+ const lenA = arrA.length, lenB = arrB.length;
350
+ while (i < lenA && j < lenB) {
351
+ const cmp = compare(arrA[i], arrB[j]);
352
+ if (cmp < 0)
353
+ i++;
354
+ else if (cmp > 0)
355
+ j++;
356
+ else {
357
+ res.push(arrA[i++]);
358
+ j++;
168
359
  }
169
360
  }
170
- return result;
361
+ s._elements = res;
362
+ return s;
171
363
  }
172
364
  difference(other) {
173
- const result = new RecursiveSet();
174
- for (const el of this) {
175
- if (!other.has(el)) {
176
- result.add(el);
365
+ const s = new RecursiveSet();
366
+ const arrA = this._elements;
367
+ const arrB = other._elements;
368
+ const res = [];
369
+ let i = 0, j = 0;
370
+ const lenA = arrA.length, lenB = arrB.length;
371
+ while (i < lenA && j < lenB) {
372
+ const cmp = compare(arrA[i], arrB[j]);
373
+ if (cmp < 0)
374
+ res.push(arrA[i++]);
375
+ else if (cmp > 0)
376
+ j++;
377
+ else {
378
+ i++;
379
+ j++;
177
380
  }
178
381
  }
179
- return result;
382
+ while (i < lenA)
383
+ res.push(arrA[i++]);
384
+ s._elements = res;
385
+ return s;
180
386
  }
181
387
  symmetricDifference(other) {
182
- return this.difference(other).union(other.difference(this));
388
+ const s = new RecursiveSet();
389
+ const arrA = this._elements;
390
+ const arrB = other._elements;
391
+ const res = [];
392
+ let i = 0, j = 0;
393
+ const lenA = arrA.length, lenB = arrB.length;
394
+ while (i < lenA && j < lenB) {
395
+ const cmp = compare(arrA[i], arrB[j]);
396
+ if (cmp < 0)
397
+ res.push(arrA[i++]);
398
+ else if (cmp > 0)
399
+ res.push(arrB[j++]);
400
+ else {
401
+ i++;
402
+ j++;
403
+ }
404
+ }
405
+ while (i < lenA)
406
+ res.push(arrA[i++]);
407
+ while (j < lenB)
408
+ res.push(arrB[j++]);
409
+ s._elements = res;
410
+ return s;
183
411
  }
184
412
  powerset() {
185
413
  const n = this.size;
186
- if (n > 30)
187
- throw new Error("Powerset size exceeds 32-bit integer limit");
188
- const elements = [];
189
- this._tree.forEach((key) => { elements.push(key); });
414
+ if (n > 20)
415
+ throw new Error("Powerset too large");
190
416
  const subsets = [];
191
- for (let i = 0; i < (1 << n); i++) {
417
+ const max = 1 << n;
418
+ for (let i = 0; i < max; i++) {
192
419
  const subset = new RecursiveSet();
193
420
  for (let j = 0; j < n; j++) {
194
- if (i & (1 << j)) {
195
- subset.add(elements[j]);
196
- }
421
+ if (i & (1 << j))
422
+ subset._elements.push(this._elements[j]);
197
423
  }
198
424
  subsets.push(subset);
199
425
  }
200
426
  return new RecursiveSet(...subsets);
201
427
  }
202
- /**
203
- * Returns the Cartesian product as a set of Tuples.
204
- * Uses the Tuple class to ensure structural equality.
205
- */
206
428
  cartesianProduct(other) {
207
429
  const result = new RecursiveSet();
208
- for (const x of this) {
209
- for (const y of other) {
210
- result.add(new Tuple(x, y));
430
+ const arrA = this._elements;
431
+ const arrB = other._elements;
432
+ for (const x of arrA) {
433
+ for (const y of arrB) {
434
+ result._elements.push(new Tuple(x, y));
211
435
  }
212
436
  }
213
437
  return result;
214
438
  }
215
- // === Predicates ===
216
- has(element) {
217
- return this._tree.get(element) !== undefined;
218
- }
219
439
  isSubset(other) {
220
440
  if (this.size > other.size)
221
441
  return false;
222
- for (const el of this) {
223
- if (!other.has(el))
442
+ let i = 0, j = 0;
443
+ const arrA = this._elements, arrB = other._elements;
444
+ while (i < arrA.length && j < arrB.length) {
445
+ const cmp = compare(arrA[i], arrB[j]);
446
+ if (cmp < 0)
224
447
  return false;
448
+ if (cmp > 0)
449
+ j++;
450
+ else {
451
+ i++;
452
+ j++;
453
+ }
225
454
  }
226
- return true;
227
- }
228
- isSuperset(other) {
229
- return other.isSubset(this);
230
- }
231
- isProperSubset(other) {
232
- return this.isSubset(other) && !this.equals(other);
233
- }
234
- isEmpty() {
235
- return this.size === 0;
236
- }
237
- equals(other) {
238
- return RecursiveSet.compare(this, other) === 0;
239
- }
240
- // === Utility ===
241
- get size() {
242
- return this._tree.length;
243
- }
244
- toSet() {
245
- const result = new Set();
246
- this._tree.forEach((key) => { result.add(key); });
247
- return result;
248
- }
249
- // Lazy Iterator
250
- *[Symbol.iterator]() {
251
- let iter = this._tree.begin;
252
- while (iter.valid) {
253
- yield iter.key;
254
- iter.next();
255
- }
455
+ return i === arrA.length;
256
456
  }
457
+ isSuperset(other) { return other.isSubset(this); }
458
+ isProperSubset(other) { return this.isSubset(other) && this.size < other.size; }
459
+ equals(other) { return this.compare(other) === 0; }
460
+ toSet() { return new Set(this._elements); }
461
+ *[Symbol.iterator]() { yield* this._elements; }
257
462
  toString() {
258
463
  if (this.isEmpty())
259
464
  return "∅";
260
- const elements = [];
261
- this._tree.forEach((key) => {
262
- if (key instanceof RecursiveSet) {
263
- elements.push(key.toString());
264
- }
265
- else if (key instanceof Tuple) {
266
- elements.push(key.toString());
267
- }
268
- else {
269
- elements.push(String(key));
270
- }
465
+ const elementsStr = this._elements.map(el => {
466
+ if (Array.isArray(el))
467
+ return `[${el.join(', ')}]`;
468
+ return String(el);
271
469
  });
272
- return `{${elements.join(", ")}}`;
273
- }
274
- [Symbol.for('nodejs.util.inspect.custom')]() {
275
- return this.toString();
470
+ return `{${elementsStr.join(', ')}}`;
276
471
  }
472
+ [Symbol.for('nodejs.util.inspect.custom')]() { return this.toString(); }
277
473
  }
278
474
  exports.RecursiveSet = RecursiveSet;
279
- // === Helpers ===
280
- function emptySet() {
281
- return new RecursiveSet();
282
- }
283
- function singleton(element) {
284
- return new RecursiveSet(element);
285
- }
286
- function fromIterable(iterable) {
287
- return new RecursiveSet(...iterable);
288
- }
475
+ function emptySet() { return new RecursiveSet(); }
476
+ function singleton(element) { return new RecursiveSet(element); }
477
+ function fromIterable(iterable) { return new RecursiveSet(...iterable); }
289
478
  //# sourceMappingURL=index.js.map