mol_view_tree2_lib 1.0.196 → 1.0.197
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/node.d.ts +278 -1
- package/node.d.ts.map +1 -1
- package/node.deps.json +1 -1
- package/node.js +371 -19
- package/node.js.map +1 -1
- package/node.mjs +371 -19
- package/node.test.js +675 -36
- package/node.test.js.map +1 -1
- package/package.json +1 -1
- package/web.d.ts +282 -1
- package/web.d.ts.map +1 -1
- package/web.deps.json +1 -1
- package/web.js +364 -16
- package/web.js.map +1 -1
- package/web.mjs +364 -16
- package/web.test.js +304 -17
- package/web.test.js.map +1 -1
package/web.js
CHANGED
|
@@ -44,6 +44,11 @@ var $;
|
|
|
44
44
|
var $;
|
|
45
45
|
(function ($) {
|
|
46
46
|
const instances = new WeakSet();
|
|
47
|
+
/**
|
|
48
|
+
* Proxy that delegates all to lazy returned target.
|
|
49
|
+
*
|
|
50
|
+
* $mol_delegate( Array.prototype , ()=> fetch_array() )
|
|
51
|
+
*/
|
|
47
52
|
function $mol_delegate(proto, target) {
|
|
48
53
|
const proxy = new Proxy(proto, {
|
|
49
54
|
get: (_, field) => {
|
|
@@ -147,7 +152,7 @@ var $;
|
|
|
147
152
|
var $;
|
|
148
153
|
(function ($) {
|
|
149
154
|
function $mol_fail_hidden(error) {
|
|
150
|
-
throw error;
|
|
155
|
+
throw error; /// Use 'Never Pause Here' breakpoint in DevTools or simply blackbox this script
|
|
151
156
|
}
|
|
152
157
|
$.$mol_fail_hidden = $mol_fail_hidden;
|
|
153
158
|
})($ || ($ = {}));
|
|
@@ -239,6 +244,9 @@ var $;
|
|
|
239
244
|
[Symbol.dispose]() {
|
|
240
245
|
this.destructor();
|
|
241
246
|
}
|
|
247
|
+
//[ Symbol.toPrimitive ]( hint: string ) {
|
|
248
|
+
// return hint === 'number' ? this.valueOf() : this.toString()
|
|
249
|
+
//}
|
|
242
250
|
toString() {
|
|
243
251
|
return this[Symbol.toStringTag] || this.constructor.name + '<>';
|
|
244
252
|
}
|
|
@@ -250,6 +258,7 @@ var $;
|
|
|
250
258
|
"use strict";
|
|
251
259
|
var $;
|
|
252
260
|
(function ($) {
|
|
261
|
+
/** Position in any resource. */
|
|
253
262
|
class $mol_span extends $mol_object2 {
|
|
254
263
|
uri;
|
|
255
264
|
source;
|
|
@@ -265,13 +274,17 @@ var $;
|
|
|
265
274
|
this.length = length;
|
|
266
275
|
this[Symbol.toStringTag] = this.uri + ('#' + this.row + ':' + this.col + '/' + this.length);
|
|
267
276
|
}
|
|
277
|
+
/** Span for begin of unknown resource */
|
|
268
278
|
static unknown = $mol_span.begin('?');
|
|
279
|
+
/** Makes new span for begin of resource. */
|
|
269
280
|
static begin(uri, source = '') {
|
|
270
281
|
return new $mol_span(uri, source, 1, 1, 0);
|
|
271
282
|
}
|
|
283
|
+
/** Makes new span for end of resource. */
|
|
272
284
|
static end(uri, source) {
|
|
273
285
|
return new $mol_span(uri, source, 1, source.length + 1, 0);
|
|
274
286
|
}
|
|
287
|
+
/** Makes new span for entire resource. */
|
|
275
288
|
static entire(uri, source) {
|
|
276
289
|
return new $mol_span(uri, source, 1, 1, source.length);
|
|
277
290
|
}
|
|
@@ -286,15 +299,19 @@ var $;
|
|
|
286
299
|
length: this.length
|
|
287
300
|
};
|
|
288
301
|
}
|
|
302
|
+
/** Makes new error for this span. */
|
|
289
303
|
error(message, Class = Error) {
|
|
290
304
|
return new Class(`${message} (${this})`);
|
|
291
305
|
}
|
|
306
|
+
/** Makes new span for same uri. */
|
|
292
307
|
span(row, col, length) {
|
|
293
308
|
return new $mol_span(this.uri, this.source, row, col, length);
|
|
294
309
|
}
|
|
310
|
+
/** Makes new span after end of this. */
|
|
295
311
|
after(length = 0) {
|
|
296
312
|
return new $mol_span(this.uri, this.source, this.row, this.col + this.length, length);
|
|
297
313
|
}
|
|
314
|
+
/** Makes new span between begin and end. */
|
|
298
315
|
slice(begin, end = -1) {
|
|
299
316
|
let len = this.length;
|
|
300
317
|
if (begin < 0)
|
|
@@ -317,6 +334,7 @@ var $;
|
|
|
317
334
|
"use strict";
|
|
318
335
|
var $;
|
|
319
336
|
(function ($) {
|
|
337
|
+
/** Syntax error with cordinates and source line snippet. */
|
|
320
338
|
class $mol_error_syntax extends SyntaxError {
|
|
321
339
|
reason;
|
|
322
340
|
line;
|
|
@@ -335,6 +353,7 @@ var $;
|
|
|
335
353
|
"use strict";
|
|
336
354
|
var $;
|
|
337
355
|
(function ($) {
|
|
356
|
+
/** Parses tree format from string. */
|
|
338
357
|
function $mol_tree2_from_string(str, uri = '?') {
|
|
339
358
|
const span = $mol_span.entire(uri, str);
|
|
340
359
|
var root = $mol_tree2.list([], span);
|
|
@@ -344,6 +363,7 @@ var $;
|
|
|
344
363
|
var indent = 0;
|
|
345
364
|
var line_start = pos;
|
|
346
365
|
row++;
|
|
366
|
+
// read indent
|
|
347
367
|
while (str.length > pos && str[pos] == '\t') {
|
|
348
368
|
indent++;
|
|
349
369
|
pos++;
|
|
@@ -352,8 +372,10 @@ var $;
|
|
|
352
372
|
min_indent = indent;
|
|
353
373
|
}
|
|
354
374
|
indent -= min_indent;
|
|
375
|
+
// invalid tab size
|
|
355
376
|
if (indent < 0 || indent >= stack.length) {
|
|
356
377
|
const sp = span.span(row, 1, pos - line_start);
|
|
378
|
+
// skip error line
|
|
357
379
|
while (str.length > pos && str[pos] != '\n') {
|
|
358
380
|
pos++;
|
|
359
381
|
}
|
|
@@ -368,7 +390,9 @@ var $;
|
|
|
368
390
|
}
|
|
369
391
|
stack.length = indent + 1;
|
|
370
392
|
var parent = stack[indent];
|
|
393
|
+
// parse types
|
|
371
394
|
while (str.length > pos && str[pos] != '\\' && str[pos] != '\n') {
|
|
395
|
+
// type can not contain space and tab
|
|
372
396
|
var error_start = pos;
|
|
373
397
|
while (str.length > pos && (str[pos] == ' ' || str[pos] == '\t')) {
|
|
374
398
|
pos++;
|
|
@@ -380,6 +404,7 @@ var $;
|
|
|
380
404
|
const sp = span.span(row, error_start - line_start + 1, pos - error_start);
|
|
381
405
|
this.$mol_fail(new this.$mol_error_syntax(`Wrong nodes separator`, str.substring(line_start, line_end), sp));
|
|
382
406
|
}
|
|
407
|
+
// read type
|
|
383
408
|
var type_start = pos;
|
|
384
409
|
while (str.length > pos &&
|
|
385
410
|
str[pos] != '\\' &&
|
|
@@ -394,10 +419,12 @@ var $;
|
|
|
394
419
|
parent_kids.push(next);
|
|
395
420
|
parent = next;
|
|
396
421
|
}
|
|
422
|
+
// read one space if exists
|
|
397
423
|
if (str.length > pos && str[pos] == ' ') {
|
|
398
424
|
pos++;
|
|
399
425
|
}
|
|
400
426
|
}
|
|
427
|
+
// read data
|
|
401
428
|
if (str.length > pos && str[pos] == '\\') {
|
|
402
429
|
var data_start = pos;
|
|
403
430
|
while (str.length > pos && str[pos] != '\n') {
|
|
@@ -408,6 +435,7 @@ var $;
|
|
|
408
435
|
parent_kids.push(next);
|
|
409
436
|
parent = next;
|
|
410
437
|
}
|
|
438
|
+
// now must be end of text
|
|
411
439
|
if (str.length === pos && stack.length > 0) {
|
|
412
440
|
const sp = span.span(row, pos - line_start + 1, 1);
|
|
413
441
|
this.$mol_fail(new this.$mol_error_syntax(`Unexpected EOF, LF required`, str.substring(line_start, str.length), sp));
|
|
@@ -424,6 +452,7 @@ var $;
|
|
|
424
452
|
"use strict";
|
|
425
453
|
var $;
|
|
426
454
|
(function ($) {
|
|
455
|
+
/** Serializes tree to string in tree format. */
|
|
427
456
|
function $mol_tree2_to_string(tree) {
|
|
428
457
|
let output = [];
|
|
429
458
|
function dump(tree, prefix = '') {
|
|
@@ -467,12 +496,25 @@ var $;
|
|
|
467
496
|
"use strict";
|
|
468
497
|
var $;
|
|
469
498
|
(function ($) {
|
|
499
|
+
/**
|
|
500
|
+
* Abstract Syntax Tree with human readable serialization.
|
|
501
|
+
* Avoid direct instantiation. Use static factories instead.
|
|
502
|
+
* @see https://github.com/nin-jin/tree.d
|
|
503
|
+
*/
|
|
470
504
|
class $mol_tree2 extends Object {
|
|
471
505
|
type;
|
|
472
506
|
value;
|
|
473
507
|
kids;
|
|
474
508
|
span;
|
|
475
|
-
constructor(
|
|
509
|
+
constructor(
|
|
510
|
+
/** Type of structural node, `value` should be empty */
|
|
511
|
+
type,
|
|
512
|
+
/** Content of data node, `type` should be empty */
|
|
513
|
+
value,
|
|
514
|
+
/** Child nodes */
|
|
515
|
+
kids,
|
|
516
|
+
/** Position in most far source resource */
|
|
517
|
+
span) {
|
|
476
518
|
super();
|
|
477
519
|
this.type = type;
|
|
478
520
|
this.value = value;
|
|
@@ -480,12 +522,15 @@ var $;
|
|
|
480
522
|
this.span = span;
|
|
481
523
|
this[Symbol.toStringTag] = type || '\\' + value;
|
|
482
524
|
}
|
|
525
|
+
/** Makes collection node. */
|
|
483
526
|
static list(kids, span = $mol_span.unknown) {
|
|
484
527
|
return new $mol_tree2('', '', kids, span);
|
|
485
528
|
}
|
|
529
|
+
/** Makes new derived collection node. */
|
|
486
530
|
list(kids) {
|
|
487
531
|
return $mol_tree2.list(kids, this.span);
|
|
488
532
|
}
|
|
533
|
+
/** Makes data node for any string. */
|
|
489
534
|
static data(value, kids = [], span = $mol_span.unknown) {
|
|
490
535
|
const chunks = value.split('\n');
|
|
491
536
|
if (chunks.length > 1) {
|
|
@@ -499,21 +544,26 @@ var $;
|
|
|
499
544
|
}
|
|
500
545
|
return new $mol_tree2('', value, kids, span);
|
|
501
546
|
}
|
|
547
|
+
/** Makes new derived data node. */
|
|
502
548
|
data(value, kids = []) {
|
|
503
549
|
return $mol_tree2.data(value, kids, this.span);
|
|
504
550
|
}
|
|
551
|
+
/** Makes struct node. */
|
|
505
552
|
static struct(type, kids = [], span = $mol_span.unknown) {
|
|
506
553
|
if (/[ \n\t\\]/.test(type)) {
|
|
507
554
|
$$.$mol_fail(span.error(`Wrong type ${JSON.stringify(type)}`));
|
|
508
555
|
}
|
|
509
556
|
return new $mol_tree2(type, '', kids, span);
|
|
510
557
|
}
|
|
558
|
+
/** Makes new derived structural node. */
|
|
511
559
|
struct(type, kids = []) {
|
|
512
560
|
return $mol_tree2.struct(type, kids, this.span);
|
|
513
561
|
}
|
|
562
|
+
/** Makes new derived node with different kids id defined. */
|
|
514
563
|
clone(kids, span = this.span) {
|
|
515
564
|
return new $mol_tree2(this.type, this.value, kids, span);
|
|
516
565
|
}
|
|
566
|
+
/** Returns multiline text content. */
|
|
517
567
|
text() {
|
|
518
568
|
var values = [];
|
|
519
569
|
for (var kid of this.kids) {
|
|
@@ -523,15 +573,20 @@ var $;
|
|
|
523
573
|
}
|
|
524
574
|
return this.value + values.join('\n');
|
|
525
575
|
}
|
|
576
|
+
/** Parses tree format. */
|
|
577
|
+
/** @deprecated Use $mol_tree2_from_string */
|
|
526
578
|
static fromString(str, uri = 'unknown') {
|
|
527
579
|
return $$.$mol_tree2_from_string(str, uri);
|
|
528
580
|
}
|
|
581
|
+
/** Serializes to tree format. */
|
|
529
582
|
toString() {
|
|
530
583
|
return $$.$mol_tree2_to_string(this);
|
|
531
584
|
}
|
|
585
|
+
/** Makes new tree with node overrided by path. */
|
|
532
586
|
insert(value, ...path) {
|
|
533
587
|
return this.update($mol_maybe(value), ...path)[0];
|
|
534
588
|
}
|
|
589
|
+
/** Makes new tree with node overrided by path. */
|
|
535
590
|
update(value, ...path) {
|
|
536
591
|
if (path.length === 0)
|
|
537
592
|
return value;
|
|
@@ -564,6 +619,7 @@ var $;
|
|
|
564
619
|
return [this.clone(kids)];
|
|
565
620
|
}
|
|
566
621
|
}
|
|
622
|
+
/** Query nodes by path. */
|
|
567
623
|
select(...path) {
|
|
568
624
|
let next = [this];
|
|
569
625
|
for (const type of path) {
|
|
@@ -590,6 +646,7 @@ var $;
|
|
|
590
646
|
}
|
|
591
647
|
return this.list(next);
|
|
592
648
|
}
|
|
649
|
+
/** Filter kids by path or value. */
|
|
593
650
|
filter(path, value) {
|
|
594
651
|
const sub = this.kids.filter(item => {
|
|
595
652
|
var found = item.select(...path);
|
|
@@ -617,9 +674,11 @@ var $;
|
|
|
617
674
|
$mol_fail_hidden(error);
|
|
618
675
|
}
|
|
619
676
|
}
|
|
677
|
+
/** Transform tree through context with transformers */
|
|
620
678
|
hack(belt, context = {}) {
|
|
621
679
|
return [].concat(...this.kids.map(child => child.hack_self(belt, context)));
|
|
622
680
|
}
|
|
681
|
+
/** Makes Error with node coordinates. */
|
|
623
682
|
error(message, Class = Error) {
|
|
624
683
|
return this.span.error(`${message}\n${this.clone([])}`, Class);
|
|
625
684
|
}
|
|
@@ -1016,14 +1075,18 @@ var $;
|
|
|
1016
1075
|
];
|
|
1017
1076
|
},
|
|
1018
1077
|
'': (input, belt) => {
|
|
1078
|
+
// string
|
|
1019
1079
|
if (!input.type)
|
|
1020
1080
|
return [
|
|
1021
1081
|
input.data(JSON.stringify(input.text())),
|
|
1022
1082
|
];
|
|
1083
|
+
// variable
|
|
1023
1084
|
if (/^[\w$#][\w0-9$]*$/i.test(input.type))
|
|
1024
1085
|
return [
|
|
1025
1086
|
input.data(input.type),
|
|
1087
|
+
// ... input.hack( context ),
|
|
1026
1088
|
];
|
|
1089
|
+
// number
|
|
1027
1090
|
if ($mol_tree2_js_is_number(input.type))
|
|
1028
1091
|
return [
|
|
1029
1092
|
input.data(input.type)
|
|
@@ -1348,6 +1411,7 @@ var $;
|
|
|
1348
1411
|
"use strict";
|
|
1349
1412
|
var $;
|
|
1350
1413
|
(function ($) {
|
|
1414
|
+
/** Makes JSON from json.tree. */
|
|
1351
1415
|
function $mol_tree2_to_json(tree) {
|
|
1352
1416
|
if (!tree.type) {
|
|
1353
1417
|
if (tree.kids.every(kid => !kid.type))
|
|
@@ -1613,8 +1677,10 @@ var $;
|
|
|
1613
1677
|
var $;
|
|
1614
1678
|
(function ($) {
|
|
1615
1679
|
let x = /x/[Symbol.matchAll];
|
|
1680
|
+
/** Type safe reguar expression builder */
|
|
1616
1681
|
class $mol_regexp extends RegExp {
|
|
1617
1682
|
groups;
|
|
1683
|
+
/** Prefer to use $mol_regexp.from */
|
|
1618
1684
|
constructor(source, flags = 'gsu', groups = []) {
|
|
1619
1685
|
super(source, flags);
|
|
1620
1686
|
this.groups = groups;
|
|
@@ -1634,12 +1700,14 @@ var $;
|
|
|
1634
1700
|
this.lastIndex = index;
|
|
1635
1701
|
}
|
|
1636
1702
|
}
|
|
1703
|
+
/** Parses input and returns found capture groups or null */
|
|
1637
1704
|
[Symbol.match](str) {
|
|
1638
1705
|
const res = [...this[Symbol.matchAll](str)].filter(r => r.groups).map(r => r[0]);
|
|
1639
1706
|
if (!res.length)
|
|
1640
1707
|
return null;
|
|
1641
1708
|
return res;
|
|
1642
1709
|
}
|
|
1710
|
+
/** Splits string by regexp edges */
|
|
1643
1711
|
[Symbol.split](str) {
|
|
1644
1712
|
const res = [];
|
|
1645
1713
|
let token_last = null;
|
|
@@ -1694,12 +1762,14 @@ var $;
|
|
|
1694
1762
|
get native() {
|
|
1695
1763
|
return new RegExp(this.source, this.flags);
|
|
1696
1764
|
}
|
|
1765
|
+
/** Makes regexp that greedy repeats this pattern with delimiter */
|
|
1697
1766
|
static separated(chunk, sep) {
|
|
1698
1767
|
return $mol_regexp.from([
|
|
1699
1768
|
$mol_regexp.repeat_greedy([[chunk], sep], 0),
|
|
1700
1769
|
chunk,
|
|
1701
1770
|
]);
|
|
1702
1771
|
}
|
|
1772
|
+
/** Makes regexp that non-greedy repeats this pattern from min to max count */
|
|
1703
1773
|
static repeat(source, min = 0, max = Number.POSITIVE_INFINITY) {
|
|
1704
1774
|
const regexp = $mol_regexp.from(source);
|
|
1705
1775
|
const upper = Number.isFinite(max) ? max : '';
|
|
@@ -1715,6 +1785,7 @@ var $;
|
|
|
1715
1785
|
};
|
|
1716
1786
|
return regexp2;
|
|
1717
1787
|
}
|
|
1788
|
+
/** Makes regexp that greedy repeats this pattern from min to max count */
|
|
1718
1789
|
static repeat_greedy(source, min = 0, max = Number.POSITIVE_INFINITY) {
|
|
1719
1790
|
const regexp = $mol_regexp.from(source);
|
|
1720
1791
|
const upper = Number.isFinite(max) ? max : '';
|
|
@@ -1730,6 +1801,7 @@ var $;
|
|
|
1730
1801
|
};
|
|
1731
1802
|
return regexp2;
|
|
1732
1803
|
}
|
|
1804
|
+
/** Makes regexp that match any of options */
|
|
1733
1805
|
static vary(sources, flags = 'gsu') {
|
|
1734
1806
|
const groups = [];
|
|
1735
1807
|
const chunks = sources.map(source => {
|
|
@@ -1739,17 +1811,21 @@ var $;
|
|
|
1739
1811
|
});
|
|
1740
1812
|
return new $mol_regexp(`(?:${chunks.join('|')})`, flags, groups);
|
|
1741
1813
|
}
|
|
1814
|
+
/** Makes regexp that allow absent of this pattern */
|
|
1742
1815
|
static optional(source) {
|
|
1743
1816
|
return $mol_regexp.repeat_greedy(source, 0, 1);
|
|
1744
1817
|
}
|
|
1818
|
+
/** Makes regexp that look ahead for pattern */
|
|
1745
1819
|
static force_after(source) {
|
|
1746
1820
|
const regexp = $mol_regexp.from(source);
|
|
1747
1821
|
return new $mol_regexp(`(?=${regexp.source})`, regexp.flags, regexp.groups);
|
|
1748
1822
|
}
|
|
1823
|
+
/** Makes regexp that look ahead for pattern */
|
|
1749
1824
|
static forbid_after(source) {
|
|
1750
1825
|
const regexp = $mol_regexp.from(source);
|
|
1751
1826
|
return new $mol_regexp(`(?!${regexp.source})`, regexp.flags, regexp.groups);
|
|
1752
1827
|
}
|
|
1828
|
+
/** Converts some js values to regexp */
|
|
1753
1829
|
static from(source, { ignoreCase, multiline } = {
|
|
1754
1830
|
ignoreCase: false,
|
|
1755
1831
|
multiline: false,
|
|
@@ -1850,9 +1926,11 @@ var $;
|
|
|
1850
1926
|
return regexp;
|
|
1851
1927
|
}
|
|
1852
1928
|
}
|
|
1929
|
+
/** Makes regexp which includes only unicode category */
|
|
1853
1930
|
static unicode_only(...category) {
|
|
1854
1931
|
return new $mol_regexp(`\\p{${category.join('=')}}`);
|
|
1855
1932
|
}
|
|
1933
|
+
/** Makes regexp which excludes unicode category */
|
|
1856
1934
|
static unicode_except(...category) {
|
|
1857
1935
|
return new $mol_regexp(`\\P{${category.join('=')}}`);
|
|
1858
1936
|
}
|
|
@@ -1976,13 +2054,23 @@ var $;
|
|
|
1976
2054
|
var $;
|
|
1977
2055
|
(function ($) {
|
|
1978
2056
|
const err = $mol_view_tree2_error_str;
|
|
2057
|
+
const is_writable = (input) => input.type.includes('?');
|
|
1979
2058
|
function $mol_view_tree2_class_props(klass) {
|
|
1980
2059
|
let props = this.$mol_view_tree2_class_super(klass);
|
|
2060
|
+
// ! syntax to * and ?val syntax to ?
|
|
1981
2061
|
props = props.clone(props.hack({
|
|
1982
2062
|
'': (node, belt) => {
|
|
1983
|
-
const
|
|
2063
|
+
const next = node.type.indexOf('?');
|
|
2064
|
+
const id = node.type.indexOf('!');
|
|
2065
|
+
let normal = node.type;
|
|
2066
|
+
const ch = node.type[id + 1];
|
|
2067
|
+
if (id !== -1 && ch?.toUpperCase() !== ch?.toLowerCase())
|
|
2068
|
+
normal = `${normal.substring(0, id)}*${next === -1 ? '' : '?'}`;
|
|
2069
|
+
else if (next !== -1)
|
|
2070
|
+
normal = normal.substring(0, next + 1);
|
|
1984
2071
|
if (node.type === normal)
|
|
1985
2072
|
return [node.clone(node.hack(belt))];
|
|
2073
|
+
console.warn(`Syntax ${node.type} is deprecated. Use ${normal} instead`);
|
|
1986
2074
|
return [node.struct(normal, node.hack(belt))];
|
|
1987
2075
|
}
|
|
1988
2076
|
}));
|
|
@@ -2019,12 +2107,26 @@ var $;
|
|
|
2019
2107
|
this.$mol_fail(err `Need a child ${operator.span}`);
|
|
2020
2108
|
if (!context.factory)
|
|
2021
2109
|
this.$mol_fail(err `Need a parent ${left.span}`);
|
|
2110
|
+
if (is_writable(left) !== is_writable(right))
|
|
2111
|
+
this.$mol_fail(err `Left and right operands are not compatible at ${operator.span}`);
|
|
2022
2112
|
add_inner(right.clone([
|
|
2023
2113
|
right.struct('=', [
|
|
2024
2114
|
context.factory.struct(context.factory.type.replace(/\*.*/, '*'), [left.clone([])]),
|
|
2025
2115
|
]),
|
|
2026
2116
|
]));
|
|
2027
2117
|
}
|
|
2118
|
+
else if (operator?.type === "<=>") {
|
|
2119
|
+
const right = operator.kids[0];
|
|
2120
|
+
if (!right)
|
|
2121
|
+
this.$mol_fail(err `Need a child ${operator.span}`);
|
|
2122
|
+
if (!is_writable(left))
|
|
2123
|
+
this.$mol_fail(err `Expected writable at ${left.span}`);
|
|
2124
|
+
if (!is_writable(right))
|
|
2125
|
+
this.$mol_fail(err `Expected writable at ${right.span}`);
|
|
2126
|
+
}
|
|
2127
|
+
else if (operator?.type === "<=" && is_writable(left)) {
|
|
2128
|
+
this.$mol_fail(err `Expected readonly at ${left.span}`);
|
|
2129
|
+
}
|
|
2028
2130
|
if (right)
|
|
2029
2131
|
context = { factory: right.clone([]) };
|
|
2030
2132
|
else if (operator && !context.factory && $mol_view_tree2_class_match(operator)) {
|
|
@@ -2164,6 +2266,7 @@ var $;
|
|
|
2164
2266
|
const left_parts = this.$mol_view_tree2_prop_parts(left);
|
|
2165
2267
|
const right_parts = this.$mol_view_tree2_prop_parts(right);
|
|
2166
2268
|
let conflict;
|
|
2269
|
+
// if (left_parts.next && right_parts.next) conflict = 'next'
|
|
2167
2270
|
if (left_parts.key && right_parts.key)
|
|
2168
2271
|
conflict = 'key';
|
|
2169
2272
|
if (conflict) {
|
|
@@ -2288,7 +2391,7 @@ var $;
|
|
|
2288
2391
|
}, context);
|
|
2289
2392
|
return prop.struct('indent', [
|
|
2290
2393
|
prop.struct('line', [
|
|
2291
|
-
channel_signature.call(this, prop, ...val),
|
|
2394
|
+
channel_signature.call(this, prop, ...val), // Parameter, not Return
|
|
2292
2395
|
prop.data(': '),
|
|
2293
2396
|
...val,
|
|
2294
2397
|
])
|
|
@@ -2329,6 +2432,7 @@ var $;
|
|
|
2329
2432
|
"use strict";
|
|
2330
2433
|
var $;
|
|
2331
2434
|
(function ($) {
|
|
2435
|
+
/** Generates unique identifier. */
|
|
2332
2436
|
function $mol_guid(length = 8, exists = () => false) {
|
|
2333
2437
|
for (;;) {
|
|
2334
2438
|
let id = Math.random().toString(36).substring(2, length + 2).toUpperCase();
|
|
@@ -2344,11 +2448,16 @@ var $;
|
|
|
2344
2448
|
"use strict";
|
|
2345
2449
|
var $;
|
|
2346
2450
|
(function ($) {
|
|
2451
|
+
/** Special status statuses. */
|
|
2347
2452
|
let $mol_wire_cursor;
|
|
2348
2453
|
(function ($mol_wire_cursor) {
|
|
2454
|
+
/** Update required. */
|
|
2349
2455
|
$mol_wire_cursor[$mol_wire_cursor["stale"] = -1] = "stale";
|
|
2456
|
+
/** Some of (transitive) pub update required. */
|
|
2350
2457
|
$mol_wire_cursor[$mol_wire_cursor["doubt"] = -2] = "doubt";
|
|
2458
|
+
/** Actual state but may be dropped. */
|
|
2351
2459
|
$mol_wire_cursor[$mol_wire_cursor["fresh"] = -3] = "fresh";
|
|
2460
|
+
/** State will never be changed. */
|
|
2352
2461
|
$mol_wire_cursor[$mol_wire_cursor["final"] = -4] = "final";
|
|
2353
2462
|
})($mol_wire_cursor = $.$mol_wire_cursor || ($.$mol_wire_cursor = {}));
|
|
2354
2463
|
})($ || ($ = {}));
|
|
@@ -2357,6 +2466,9 @@ var $;
|
|
|
2357
2466
|
"use strict";
|
|
2358
2467
|
var $;
|
|
2359
2468
|
(function ($) {
|
|
2469
|
+
/**
|
|
2470
|
+
* Collects subscribers in compact array. 28B
|
|
2471
|
+
*/
|
|
2360
2472
|
class $mol_wire_pub extends Object {
|
|
2361
2473
|
constructor(id = `$mol_wire_pub:${$mol_guid()}`) {
|
|
2362
2474
|
super();
|
|
@@ -2364,10 +2476,17 @@ var $;
|
|
|
2364
2476
|
}
|
|
2365
2477
|
[Symbol.toStringTag];
|
|
2366
2478
|
data = [];
|
|
2479
|
+
// Derived objects should be Arrays.
|
|
2367
2480
|
static get [Symbol.species]() {
|
|
2368
2481
|
return Array;
|
|
2369
2482
|
}
|
|
2370
|
-
|
|
2483
|
+
/**
|
|
2484
|
+
* Index of first subscriber.
|
|
2485
|
+
*/
|
|
2486
|
+
sub_from = 0; // 4B
|
|
2487
|
+
/**
|
|
2488
|
+
* All current subscribers.
|
|
2489
|
+
*/
|
|
2371
2490
|
get sub_list() {
|
|
2372
2491
|
const res = [];
|
|
2373
2492
|
for (let i = this.sub_from; i < this.data.length; i += 2) {
|
|
@@ -2375,14 +2494,23 @@ var $;
|
|
|
2375
2494
|
}
|
|
2376
2495
|
return res;
|
|
2377
2496
|
}
|
|
2497
|
+
/**
|
|
2498
|
+
* Has any subscribers or not.
|
|
2499
|
+
*/
|
|
2378
2500
|
get sub_empty() {
|
|
2379
2501
|
return this.sub_from === this.data.length;
|
|
2380
2502
|
}
|
|
2503
|
+
/**
|
|
2504
|
+
* Subscribe subscriber to this publisher events and return position of subscriber that required to unsubscribe.
|
|
2505
|
+
*/
|
|
2381
2506
|
sub_on(sub, pub_pos) {
|
|
2382
2507
|
const pos = this.data.length;
|
|
2383
2508
|
this.data.push(sub, pub_pos);
|
|
2384
2509
|
return pos;
|
|
2385
2510
|
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Unsubscribe subscriber from this publisher events by subscriber position provided by `on(pub)`.
|
|
2513
|
+
*/
|
|
2386
2514
|
sub_off(sub_pos) {
|
|
2387
2515
|
if (!(sub_pos < this.data.length)) {
|
|
2388
2516
|
$mol_fail(new Error(`Wrong pos ${sub_pos}`));
|
|
@@ -2395,21 +2523,39 @@ var $;
|
|
|
2395
2523
|
if (end === this.sub_from)
|
|
2396
2524
|
this.reap();
|
|
2397
2525
|
}
|
|
2526
|
+
/**
|
|
2527
|
+
* Called when last sub was unsubscribed.
|
|
2528
|
+
**/
|
|
2398
2529
|
reap() { }
|
|
2530
|
+
/**
|
|
2531
|
+
* Autowire this publisher with current subscriber.
|
|
2532
|
+
**/
|
|
2399
2533
|
promote() {
|
|
2400
2534
|
$mol_wire_auto()?.track_next(this);
|
|
2401
2535
|
}
|
|
2536
|
+
/**
|
|
2537
|
+
* Enforce actualization. Should not throw errors.
|
|
2538
|
+
*/
|
|
2402
2539
|
fresh() { }
|
|
2540
|
+
/**
|
|
2541
|
+
* Allow to put data to caches in the subtree.
|
|
2542
|
+
*/
|
|
2403
2543
|
complete() { }
|
|
2404
2544
|
get incompleted() {
|
|
2405
2545
|
return false;
|
|
2406
2546
|
}
|
|
2547
|
+
/**
|
|
2548
|
+
* Notify subscribers about self changes.
|
|
2549
|
+
*/
|
|
2407
2550
|
emit(quant = $mol_wire_cursor.stale) {
|
|
2408
2551
|
for (let i = this.sub_from; i < this.data.length; i += 2) {
|
|
2409
2552
|
;
|
|
2410
2553
|
this.data[i].absorb(quant, this.data[i + 1]);
|
|
2411
2554
|
}
|
|
2412
2555
|
}
|
|
2556
|
+
/**
|
|
2557
|
+
* Moves peer from one position to another. Doesn't clear data at old position!
|
|
2558
|
+
*/
|
|
2413
2559
|
peer_move(from_pos, to_pos) {
|
|
2414
2560
|
const peer = this.data[from_pos];
|
|
2415
2561
|
const self_pos = this.data[from_pos + 1];
|
|
@@ -2417,6 +2563,9 @@ var $;
|
|
|
2417
2563
|
this.data[to_pos + 1] = self_pos;
|
|
2418
2564
|
peer.peer_repos(self_pos, to_pos);
|
|
2419
2565
|
}
|
|
2566
|
+
/**
|
|
2567
|
+
* Updates self position in the peer.
|
|
2568
|
+
*/
|
|
2420
2569
|
peer_repos(peer_pos, self_pos) {
|
|
2421
2570
|
this.data[peer_pos + 1] = self_pos;
|
|
2422
2571
|
}
|
|
@@ -2432,10 +2581,16 @@ var $;
|
|
|
2432
2581
|
var $;
|
|
2433
2582
|
(function ($) {
|
|
2434
2583
|
$.$mol_wire_auto_sub = null;
|
|
2584
|
+
/**
|
|
2585
|
+
* When fulfilled, all publishers are promoted to this subscriber on access to its.
|
|
2586
|
+
*/
|
|
2435
2587
|
function $mol_wire_auto(next = $.$mol_wire_auto_sub) {
|
|
2436
2588
|
return $.$mol_wire_auto_sub = next;
|
|
2437
2589
|
}
|
|
2438
2590
|
$.$mol_wire_auto = $mol_wire_auto;
|
|
2591
|
+
/**
|
|
2592
|
+
* Affection queue. Used to prevent accidental stack overflow on emit.
|
|
2593
|
+
*/
|
|
2439
2594
|
$.$mol_wire_affected = [];
|
|
2440
2595
|
})($ || ($ = {}));
|
|
2441
2596
|
|
|
@@ -2443,6 +2598,7 @@ var $;
|
|
|
2443
2598
|
"use strict";
|
|
2444
2599
|
var $;
|
|
2445
2600
|
(function ($) {
|
|
2601
|
+
// https://docs.google.com/document/d/1FTascZXT9cxfetuPRT2eXPQKXui4nWFivUnS_335T3U/preview#
|
|
2446
2602
|
$['devtoolsFormatters'] ||= [];
|
|
2447
2603
|
function $mol_dev_format_register(config) {
|
|
2448
2604
|
$['devtoolsFormatters'].push(config);
|
|
@@ -2494,6 +2650,7 @@ var $;
|
|
|
2494
2650
|
return false;
|
|
2495
2651
|
if (!val)
|
|
2496
2652
|
return false;
|
|
2653
|
+
// if( Error.isError( val ) ) true
|
|
2497
2654
|
if (val[$.$mol_dev_format_body])
|
|
2498
2655
|
return true;
|
|
2499
2656
|
return false;
|
|
@@ -2511,12 +2668,16 @@ var $;
|
|
|
2511
2668
|
return $.$mol_dev_format_accent($mol_dev_format_native(val), '💨', $mol_dev_format_native(error), '');
|
|
2512
2669
|
}
|
|
2513
2670
|
}
|
|
2671
|
+
// if( Error.isError( val ) ) {
|
|
2672
|
+
// return $mol_dev_format_native( val )
|
|
2673
|
+
// }
|
|
2514
2674
|
return null;
|
|
2515
2675
|
},
|
|
2516
2676
|
});
|
|
2517
2677
|
function $mol_dev_format_native(obj) {
|
|
2518
2678
|
if (typeof obj === 'undefined')
|
|
2519
2679
|
return $.$mol_dev_format_shade('undefined');
|
|
2680
|
+
// if( ![ 'object', 'function', 'symbol' ].includes( typeof obj ) ) return obj
|
|
2520
2681
|
return [
|
|
2521
2682
|
'object',
|
|
2522
2683
|
{
|
|
@@ -2574,6 +2735,9 @@ var $;
|
|
|
2574
2735
|
'margin-left': '13px'
|
|
2575
2736
|
});
|
|
2576
2737
|
class Stack extends Array {
|
|
2738
|
+
// [ Symbol.toPrimitive ]() {
|
|
2739
|
+
// return this.toString()
|
|
2740
|
+
// }
|
|
2577
2741
|
toString() {
|
|
2578
2742
|
return this.join('\n');
|
|
2579
2743
|
}
|
|
@@ -2596,6 +2760,7 @@ var $;
|
|
|
2596
2760
|
this.method = call.getMethodName() ?? '';
|
|
2597
2761
|
if (this.method === this.function)
|
|
2598
2762
|
this.method = '';
|
|
2763
|
+
// const func = c.getFunction()
|
|
2599
2764
|
this.pos = [call.getEnclosingLineNumber() ?? 0, call.getEnclosingColumnNumber() ?? 0];
|
|
2600
2765
|
this.eval = call.getEvalOrigin() ?? '';
|
|
2601
2766
|
this.source = call.getScriptNameOrSourceURL() ?? '';
|
|
@@ -2642,9 +2807,16 @@ var $;
|
|
|
2642
2807
|
"use strict";
|
|
2643
2808
|
var $;
|
|
2644
2809
|
(function ($) {
|
|
2810
|
+
/**
|
|
2811
|
+
* Publisher that can auto collect other publishers. 32B
|
|
2812
|
+
*
|
|
2813
|
+
* P1 P2 P3 P4 S1 S2 S3
|
|
2814
|
+
* ^ ^
|
|
2815
|
+
* pubs_from subs_from
|
|
2816
|
+
*/
|
|
2645
2817
|
class $mol_wire_pub_sub extends $mol_wire_pub {
|
|
2646
|
-
pub_from = 0;
|
|
2647
|
-
cursor = $mol_wire_cursor.stale;
|
|
2818
|
+
pub_from = 0; // 4B
|
|
2819
|
+
cursor = $mol_wire_cursor.stale; // 4B
|
|
2648
2820
|
get temp() {
|
|
2649
2821
|
return false;
|
|
2650
2822
|
}
|
|
@@ -2762,10 +2934,27 @@ var $;
|
|
|
2762
2934
|
return;
|
|
2763
2935
|
this.cursor = quant;
|
|
2764
2936
|
this.emit($mol_wire_cursor.doubt);
|
|
2937
|
+
// if( pos >= 0 && pos < this.sub_from - 2 ) {
|
|
2938
|
+
// const pub = this.data[ pos ] as $mol_wire_pub
|
|
2939
|
+
// if( pub instanceof $mol_wire_task ) return
|
|
2940
|
+
// for(
|
|
2941
|
+
// let cursor = this.pub_from;
|
|
2942
|
+
// cursor < this.sub_from;
|
|
2943
|
+
// cursor += 2
|
|
2944
|
+
// ) {
|
|
2945
|
+
// const pub = this.data[ cursor ] as $mol_wire_pub
|
|
2946
|
+
// if( pub instanceof $mol_wire_task ) {
|
|
2947
|
+
// pub.destructor()
|
|
2948
|
+
// }
|
|
2949
|
+
// }
|
|
2950
|
+
// }
|
|
2765
2951
|
}
|
|
2766
2952
|
[$mol_dev_format_head]() {
|
|
2767
2953
|
return $mol_dev_format_native(this);
|
|
2768
2954
|
}
|
|
2955
|
+
/**
|
|
2956
|
+
* Is subscribed to any publisher or not.
|
|
2957
|
+
*/
|
|
2769
2958
|
get pub_empty() {
|
|
2770
2959
|
return this.sub_from === this.pub_from;
|
|
2771
2960
|
}
|
|
@@ -2821,6 +3010,13 @@ var $;
|
|
|
2821
3010
|
var $;
|
|
2822
3011
|
(function ($) {
|
|
2823
3012
|
const wrappers = new WeakMap();
|
|
3013
|
+
/**
|
|
3014
|
+
* Suspendable task with support both sync/async api.
|
|
3015
|
+
*
|
|
3016
|
+
* A1 A2 A3 A4 P1 P2 P3 P4 S1 S2 S3
|
|
3017
|
+
* ^ ^ ^
|
|
3018
|
+
* args_from pubs_from subs_from
|
|
3019
|
+
**/
|
|
2824
3020
|
class $mol_wire_fiber extends $mol_wire_pub_sub {
|
|
2825
3021
|
task;
|
|
2826
3022
|
host;
|
|
@@ -2841,6 +3037,7 @@ var $;
|
|
|
2841
3037
|
});
|
|
2842
3038
|
}
|
|
2843
3039
|
static sync() {
|
|
3040
|
+
// Sync whole fiber graph
|
|
2844
3041
|
while (this.planning.size) {
|
|
2845
3042
|
for (const fiber of this.planning) {
|
|
2846
3043
|
this.planning.delete(fiber);
|
|
@@ -2851,6 +3048,7 @@ var $;
|
|
|
2851
3048
|
fiber.fresh();
|
|
2852
3049
|
}
|
|
2853
3050
|
}
|
|
3051
|
+
// Collect garbage
|
|
2854
3052
|
while (this.reaping.size) {
|
|
2855
3053
|
const fibers = this.reaping;
|
|
2856
3054
|
this.reaping = new Set;
|
|
@@ -3002,6 +3200,10 @@ var $;
|
|
|
3002
3200
|
this.cursor = $mol_wire_cursor.stale;
|
|
3003
3201
|
this.fresh();
|
|
3004
3202
|
}
|
|
3203
|
+
/**
|
|
3204
|
+
* Synchronous execution. Throws Promise when waits async task (SuspenseAPI provider).
|
|
3205
|
+
* Should be called inside SuspenseAPI consumer (ie fiber).
|
|
3206
|
+
*/
|
|
3005
3207
|
sync() {
|
|
3006
3208
|
if (!$mol_wire_fiber.warm) {
|
|
3007
3209
|
return this.result();
|
|
@@ -3016,6 +3218,10 @@ var $;
|
|
|
3016
3218
|
}
|
|
3017
3219
|
return this.cache;
|
|
3018
3220
|
}
|
|
3221
|
+
/**
|
|
3222
|
+
* Asynchronous execution.
|
|
3223
|
+
* It's SuspenseAPI consumer. So SuspenseAPI providers can be called inside.
|
|
3224
|
+
*/
|
|
3019
3225
|
async async_raw() {
|
|
3020
3226
|
while (true) {
|
|
3021
3227
|
this.fresh();
|
|
@@ -3028,6 +3234,7 @@ var $;
|
|
|
3028
3234
|
if (!$mol_promise_like(this.cache))
|
|
3029
3235
|
return this.cache;
|
|
3030
3236
|
if (this.cursor === $mol_wire_cursor.final) {
|
|
3237
|
+
// never ends on destructed fiber
|
|
3031
3238
|
await new Promise(() => { });
|
|
3032
3239
|
}
|
|
3033
3240
|
}
|
|
@@ -3075,6 +3282,7 @@ var $;
|
|
|
3075
3282
|
var $;
|
|
3076
3283
|
(function ($) {
|
|
3077
3284
|
const TypedArray = Object.getPrototypeOf(Uint8Array);
|
|
3285
|
+
/** Returns string key for any value. */
|
|
3078
3286
|
function $mol_key(value) {
|
|
3079
3287
|
primitives: {
|
|
3080
3288
|
if (typeof value === 'bigint')
|
|
@@ -3082,9 +3290,9 @@ var $;
|
|
|
3082
3290
|
if (typeof value === 'symbol')
|
|
3083
3291
|
return `Symbol(${value.description})`;
|
|
3084
3292
|
if (!value)
|
|
3085
|
-
return JSON.stringify(value);
|
|
3293
|
+
return JSON.stringify(value); // 0, null, ""
|
|
3086
3294
|
if (typeof value !== 'object' && typeof value !== 'function')
|
|
3087
|
-
return JSON.stringify(value);
|
|
3295
|
+
return JSON.stringify(value); // boolean, number, string
|
|
3088
3296
|
}
|
|
3089
3297
|
caching: {
|
|
3090
3298
|
let key = $mol_key_store.get(value);
|
|
@@ -3168,6 +3376,10 @@ var $;
|
|
|
3168
3376
|
var $;
|
|
3169
3377
|
(function ($) {
|
|
3170
3378
|
$.$mol_compare_deep_cache = new WeakMap();
|
|
3379
|
+
/**
|
|
3380
|
+
* Deeply compares two values. Returns true if equal.
|
|
3381
|
+
* Define `Symbol.toPrimitive` to customize.
|
|
3382
|
+
*/
|
|
3171
3383
|
function $mol_compare_deep(left, right) {
|
|
3172
3384
|
if (Object.is(left, right))
|
|
3173
3385
|
return true;
|
|
@@ -3307,6 +3519,7 @@ var $;
|
|
|
3307
3519
|
"use strict";
|
|
3308
3520
|
var $;
|
|
3309
3521
|
(function ($) {
|
|
3522
|
+
/** Log begin of collapsed group only when some logged inside, returns func to close group */
|
|
3310
3523
|
function $mol_log3_area_lazy(event) {
|
|
3311
3524
|
const self = this.$;
|
|
3312
3525
|
const stack = self.$mol_log3_stack;
|
|
@@ -3363,6 +3576,7 @@ var $;
|
|
|
3363
3576
|
"use strict";
|
|
3364
3577
|
var $;
|
|
3365
3578
|
(function ($) {
|
|
3579
|
+
/** One-shot fiber */
|
|
3366
3580
|
class $mol_wire_task extends $mol_wire_fiber {
|
|
3367
3581
|
static getter(task) {
|
|
3368
3582
|
return function $mol_wire_task_get(host, args) {
|
|
@@ -3388,6 +3602,7 @@ var $;
|
|
|
3388
3602
|
}
|
|
3389
3603
|
const key = (host?.[Symbol.toStringTag] ?? host) + ('.' + task.name + '<#>');
|
|
3390
3604
|
const next = new $mol_wire_task(key, task, host, args);
|
|
3605
|
+
// Disabled because non-idempotency is required for try-catch
|
|
3391
3606
|
if (existen?.temp) {
|
|
3392
3607
|
$$.$mol_log3_warn({
|
|
3393
3608
|
place: '$mol_wire_task',
|
|
@@ -3420,7 +3635,7 @@ var $;
|
|
|
3420
3635
|
try {
|
|
3421
3636
|
next[Symbol.toStringTag] = this[Symbol.toStringTag];
|
|
3422
3637
|
}
|
|
3423
|
-
catch {
|
|
3638
|
+
catch { // Promises throw in strict mode
|
|
3424
3639
|
Object.defineProperty(next, Symbol.toStringTag, { value: this[Symbol.toStringTag] });
|
|
3425
3640
|
}
|
|
3426
3641
|
}
|
|
@@ -3445,6 +3660,9 @@ var $;
|
|
|
3445
3660
|
"use strict";
|
|
3446
3661
|
var $;
|
|
3447
3662
|
(function ($) {
|
|
3663
|
+
/**
|
|
3664
|
+
* Decorates method to fiber to ensure it is executed only once inside other fiber.
|
|
3665
|
+
*/
|
|
3448
3666
|
function $mol_wire_method(host, field, descr) {
|
|
3449
3667
|
if (!descr)
|
|
3450
3668
|
descr = Reflect.getOwnPropertyDescriptor(host, field);
|
|
@@ -3514,6 +3732,7 @@ var $;
|
|
|
3514
3732
|
let error;
|
|
3515
3733
|
let result;
|
|
3516
3734
|
let handler;
|
|
3735
|
+
/// Debugger will stop at exceptions but exception will be returned normally
|
|
3517
3736
|
function $mol_try_web(handler2) {
|
|
3518
3737
|
handler = handler2;
|
|
3519
3738
|
error = undefined;
|
|
@@ -3554,6 +3773,7 @@ var $;
|
|
|
3554
3773
|
"use strict";
|
|
3555
3774
|
var $;
|
|
3556
3775
|
(function ($) {
|
|
3776
|
+
/** Long-living fiber. */
|
|
3557
3777
|
class $mol_wire_atom extends $mol_wire_fiber {
|
|
3558
3778
|
static solo(host, task) {
|
|
3559
3779
|
const field = task.name + '()';
|
|
@@ -3604,7 +3824,11 @@ var $;
|
|
|
3604
3824
|
}
|
|
3605
3825
|
$mol_wire_atom.watching.add(this);
|
|
3606
3826
|
}
|
|
3827
|
+
/**
|
|
3828
|
+
* Update atom value through another temp fiber.
|
|
3829
|
+
*/
|
|
3607
3830
|
resync(args) {
|
|
3831
|
+
// enforce pulling tasks abort
|
|
3608
3832
|
for (let cursor = this.pub_from; cursor < this.sub_from; cursor += 2) {
|
|
3609
3833
|
const pub = this.data[cursor];
|
|
3610
3834
|
if (pub && pub instanceof $mol_wire_task) {
|
|
@@ -3665,7 +3889,7 @@ var $;
|
|
|
3665
3889
|
try {
|
|
3666
3890
|
next[Symbol.toStringTag] = this[Symbol.toStringTag];
|
|
3667
3891
|
}
|
|
3668
|
-
catch {
|
|
3892
|
+
catch { // Promises throw in strict mode
|
|
3669
3893
|
Object.defineProperty(next, Symbol.toStringTag, { value: this[Symbol.toStringTag] });
|
|
3670
3894
|
}
|
|
3671
3895
|
}
|
|
@@ -3693,6 +3917,7 @@ var $;
|
|
|
3693
3917
|
"use strict";
|
|
3694
3918
|
var $;
|
|
3695
3919
|
(function ($) {
|
|
3920
|
+
/** Decorates solo object channel to [mol_wire_atom](../atom/atom.ts). */
|
|
3696
3921
|
function $mol_wire_solo(host, field, descr) {
|
|
3697
3922
|
if (!descr)
|
|
3698
3923
|
descr = Reflect.getOwnPropertyDescriptor(host, field);
|
|
@@ -3731,6 +3956,7 @@ var $;
|
|
|
3731
3956
|
"use strict";
|
|
3732
3957
|
var $;
|
|
3733
3958
|
(function ($) {
|
|
3959
|
+
/** Reactive memoizing multiplexed property decorator. */
|
|
3734
3960
|
function $mol_wire_plex(host, field, descr) {
|
|
3735
3961
|
if (!descr)
|
|
3736
3962
|
descr = Reflect.getOwnPropertyDescriptor(host, field);
|
|
@@ -3769,7 +3995,25 @@ var $;
|
|
|
3769
3995
|
"use strict";
|
|
3770
3996
|
var $;
|
|
3771
3997
|
(function ($) {
|
|
3998
|
+
/**
|
|
3999
|
+
* Reactive memoizing solo property decorator from [mol_wire](../wire/README.md)
|
|
4000
|
+
* @example
|
|
4001
|
+
* '@' $mol_mem
|
|
4002
|
+
* name(next?: string) {
|
|
4003
|
+
* return next ?? 'default'
|
|
4004
|
+
* }
|
|
4005
|
+
* @see https://mol.hyoo.ru/#!section=docs/=qxmh6t_sinbmb
|
|
4006
|
+
*/
|
|
3772
4007
|
$.$mol_mem = $mol_wire_solo;
|
|
4008
|
+
/**
|
|
4009
|
+
* Reactive memoizing multiplexed property decorator [mol_wire](../wire/README.md)
|
|
4010
|
+
* @example
|
|
4011
|
+
* '@' $mol_mem_key
|
|
4012
|
+
* name(id: number, next?: string) {
|
|
4013
|
+
* return next ?? 'default'
|
|
4014
|
+
* }
|
|
4015
|
+
* @see https://mol.hyoo.ru/#!section=docs/=qxmh6t_sinbmb
|
|
4016
|
+
*/
|
|
3773
4017
|
$.$mol_mem_key = $mol_wire_plex;
|
|
3774
4018
|
})($ || ($ = {}));
|
|
3775
4019
|
|
|
@@ -3790,6 +4034,9 @@ var $;
|
|
|
3790
4034
|
"use strict";
|
|
3791
4035
|
var $;
|
|
3792
4036
|
(function ($) {
|
|
4037
|
+
/**
|
|
4038
|
+
* Disable reaping of current subscriber
|
|
4039
|
+
*/
|
|
3793
4040
|
function $mol_wire_solid() {
|
|
3794
4041
|
let current = $mol_wire_auto();
|
|
3795
4042
|
if (current.temp)
|
|
@@ -3815,6 +4062,7 @@ var $;
|
|
|
3815
4062
|
"use strict";
|
|
3816
4063
|
var $;
|
|
3817
4064
|
(function ($) {
|
|
4065
|
+
/** Run code without state changes */
|
|
3818
4066
|
function $mol_wire_probe(task, def) {
|
|
3819
4067
|
const warm = $mol_wire_fiber.warm;
|
|
3820
4068
|
try {
|
|
@@ -3877,6 +4125,10 @@ var $;
|
|
|
3877
4125
|
props[field] = get_val;
|
|
3878
4126
|
return get_val;
|
|
3879
4127
|
}
|
|
4128
|
+
/**
|
|
4129
|
+
* Convert asynchronous (promise-based) API to synchronous by wrapping function and method calls in a fiber.
|
|
4130
|
+
* @see https://mol.hyoo.ru/#!section=docs/=1fcpsq_1wh0h2
|
|
4131
|
+
*/
|
|
3880
4132
|
function $mol_wire_sync(obj) {
|
|
3881
4133
|
return new Proxy(obj, {
|
|
3882
4134
|
get(obj, field) {
|
|
@@ -4042,6 +4294,11 @@ var $;
|
|
|
4042
4294
|
"use strict";
|
|
4043
4295
|
var $;
|
|
4044
4296
|
(function ($) {
|
|
4297
|
+
/**
|
|
4298
|
+
* Returns closure that returns constant value.
|
|
4299
|
+
* @example
|
|
4300
|
+
* const rnd = $mol_const( Math.random() )
|
|
4301
|
+
*/
|
|
4045
4302
|
function $mol_const(value) {
|
|
4046
4303
|
const getter = (() => value);
|
|
4047
4304
|
getter['()'] = value;
|
|
@@ -4056,6 +4313,10 @@ var $;
|
|
|
4056
4313
|
"use strict";
|
|
4057
4314
|
var $;
|
|
4058
4315
|
(function ($) {
|
|
4316
|
+
/**
|
|
4317
|
+
* Decorates method to fiber to ensure it is executed only once inside other fiber from [mol_wire](../wire/README.md)
|
|
4318
|
+
* @see https://mol.hyoo.ru/#!section=docs/=1fcpsq_1wh0h2
|
|
4319
|
+
*/
|
|
4059
4320
|
$.$mol_action = $mol_wire_method;
|
|
4060
4321
|
})($ || ($ = {}));
|
|
4061
4322
|
|
|
@@ -4084,6 +4345,7 @@ var $;
|
|
|
4084
4345
|
"use strict";
|
|
4085
4346
|
var $;
|
|
4086
4347
|
(function ($) {
|
|
4348
|
+
/** Convert a pseudo-synchronous (Suspense API) API to an explicit asynchronous one (for integrating with external systems). */
|
|
4087
4349
|
function $mol_wire_async(obj) {
|
|
4088
4350
|
let fiber;
|
|
4089
4351
|
const temp = $mol_wire_task.getter(obj);
|
|
@@ -4183,7 +4445,8 @@ var $;
|
|
|
4183
4445
|
"use strict";
|
|
4184
4446
|
var $;
|
|
4185
4447
|
(function ($) {
|
|
4186
|
-
let buf = new Uint8Array(2 ** 12);
|
|
4448
|
+
let buf = new Uint8Array(2 ** 12); // 4KB Mem Page
|
|
4449
|
+
/** Temporary buffer. Recursive usage isn't supported. */
|
|
4187
4450
|
function $mol_charset_buffer(size) {
|
|
4188
4451
|
if (buf.byteLength < size)
|
|
4189
4452
|
buf = new Uint8Array(size);
|
|
@@ -4205,19 +4468,19 @@ var $;
|
|
|
4205
4468
|
let pos = from;
|
|
4206
4469
|
for (let i = 0; i < str.length; i++) {
|
|
4207
4470
|
let code = str.charCodeAt(i);
|
|
4208
|
-
if (code < 0x80) {
|
|
4471
|
+
if (code < 0x80) { // ASCII - 1 octet
|
|
4209
4472
|
buf[pos++] = code;
|
|
4210
4473
|
}
|
|
4211
|
-
else if (code < 0x800) {
|
|
4474
|
+
else if (code < 0x800) { // 2 octet
|
|
4212
4475
|
buf[pos++] = 0xc0 | (code >> 6);
|
|
4213
4476
|
buf[pos++] = 0x80 | (code & 0x3f);
|
|
4214
4477
|
}
|
|
4215
|
-
else if (code < 0xd800 || code >= 0xe000) {
|
|
4478
|
+
else if (code < 0xd800 || code >= 0xe000) { // 3 octet
|
|
4216
4479
|
buf[pos++] = 0xe0 | (code >> 12);
|
|
4217
4480
|
buf[pos++] = 0x80 | ((code >> 6) & 0x3f);
|
|
4218
4481
|
buf[pos++] = 0x80 | (code & 0x3f);
|
|
4219
4482
|
}
|
|
4220
|
-
else {
|
|
4483
|
+
else { // surrogate pair
|
|
4221
4484
|
const point = ((code - 0xd800) << 10) + str.charCodeAt(++i) + 0x2400;
|
|
4222
4485
|
buf[pos++] = 0xf0 | (point >> 18);
|
|
4223
4486
|
buf[pos++] = 0x80 | ((point >> 12) & 0x3f);
|
|
@@ -4299,12 +4562,24 @@ var $;
|
|
|
4299
4562
|
root() {
|
|
4300
4563
|
const path = this.path();
|
|
4301
4564
|
const base = this.constructor.base;
|
|
4565
|
+
// Если путь выше или равен base или если parent такойже как и this - считаем это корнем
|
|
4302
4566
|
return base.startsWith(path) || this == this.parent();
|
|
4303
4567
|
}
|
|
4304
4568
|
stat(next, virt) {
|
|
4305
4569
|
const path = this.path();
|
|
4306
4570
|
const parent = this.parent();
|
|
4571
|
+
// Отслеживать проверку наличия родительской папки не стоит до корня диска
|
|
4572
|
+
// Лучше ограничить mam-ом
|
|
4307
4573
|
if (!this.root()) {
|
|
4574
|
+
/*
|
|
4575
|
+
Если parent папка удалилась, надо ресетнуть все объекты в ней на любой глубине.
|
|
4576
|
+
Например, rm -rf с последующим git pull: parent папка может удалиться, потом создасться,
|
|
4577
|
+
а текущая папка успеет только удалиться до момента выполнения stat.
|
|
4578
|
+
Поэтому parent.exists() не запустит перевычисления, нужна именно parent.version()
|
|
4579
|
+
|
|
4580
|
+
Однако, parent.version() меняется не только при удалении, будет ложное срабатывание
|
|
4581
|
+
С этим придется мириться, красивого решения пока нет.
|
|
4582
|
+
*/
|
|
4308
4583
|
parent.version();
|
|
4309
4584
|
}
|
|
4310
4585
|
parent.watcher();
|
|
@@ -4318,9 +4593,19 @@ var $;
|
|
|
4318
4593
|
if (/([\/\\]\.|___$)/.test(path))
|
|
4319
4594
|
return;
|
|
4320
4595
|
const file = this.relative(path.at(-1) === '/' ? path.slice(0, -1) : path);
|
|
4596
|
+
// console.log(type, path)
|
|
4597
|
+
// add (change): добавился файл - у parent надо обновить список sub, если он был заюзан
|
|
4598
|
+
// change, unlink (rename): обновился или удалился файл - ресетим
|
|
4599
|
+
// addDir (change), добавилась папка, у parent обновляем список директорий в sub
|
|
4600
|
+
// дочерние ресетим
|
|
4601
|
+
// unlinkDir (rename), удалилась папка, ресетим ее
|
|
4602
|
+
// stat у всех дочерних обновится сам, т.к. связан с parent.version()
|
|
4321
4603
|
this.changed.add(file);
|
|
4322
4604
|
if (!this.watching)
|
|
4323
4605
|
return;
|
|
4606
|
+
// throttle, пока события поступают не сбрасываем.
|
|
4607
|
+
// аналог awaitWriteFinish из chokidar
|
|
4608
|
+
// интервалы между change-сообщениями модифицируемого файла должны быть меньше watch_debounce
|
|
4324
4609
|
this.frame?.destructor();
|
|
4325
4610
|
this.frame = new this.$.$mol_after_timeout(this.watch_debounce(), () => {
|
|
4326
4611
|
if (!this.watching)
|
|
@@ -4329,8 +4614,16 @@ var $;
|
|
|
4329
4614
|
$mol_wire_async(this).flush();
|
|
4330
4615
|
});
|
|
4331
4616
|
}
|
|
4617
|
+
/**
|
|
4618
|
+
* Должно быть больше, чем время между событиями от вотчера при записи внешним процессом.
|
|
4619
|
+
* Иначе запуск ресетов паралельно с изменением может привести к неконсистентности.
|
|
4620
|
+
*/
|
|
4332
4621
|
static watch_debounce() { return 500; }
|
|
4333
4622
|
static flush() {
|
|
4623
|
+
// Пока flush работает, вотчер сюда не заходит, но может добавлять новые изменения
|
|
4624
|
+
// на каждом перезапуске они применятся
|
|
4625
|
+
// Пока run выполняется, изменения накапливаются, в конце run вызывается flush
|
|
4626
|
+
// Пока применяются изменения, run должен ожидать конца flush
|
|
4334
4627
|
for (const file of this.changed) {
|
|
4335
4628
|
const parent = file.parent();
|
|
4336
4629
|
try {
|
|
@@ -4345,16 +4638,32 @@ var $;
|
|
|
4345
4638
|
}
|
|
4346
4639
|
this.changed.clear();
|
|
4347
4640
|
this.watching = true;
|
|
4641
|
+
// this.watch_wd?.destructor()
|
|
4642
|
+
// this.watch_wd = null
|
|
4348
4643
|
}
|
|
4349
4644
|
static watching = true;
|
|
4350
4645
|
static lock = new $mol_lock;
|
|
4351
4646
|
static watch_off(path) {
|
|
4352
4647
|
this.watching = false;
|
|
4648
|
+
// run должен ожидать конца flush
|
|
4353
4649
|
this.flush();
|
|
4354
4650
|
this.watching = false;
|
|
4651
|
+
/*
|
|
4652
|
+
watch запаздывает и событие может прилететь через 3 сек после окончания сайд эффекта
|
|
4653
|
+
поэтому добавляем папку, которую меняет side_effect
|
|
4654
|
+
Когда дойдет до выполнения flush, он ресетнет ее
|
|
4655
|
+
|
|
4656
|
+
Иначе будут лишние срабатывания
|
|
4657
|
+
Например, удалили hyoo/board, watch ресетит и exists начинает отдавать false, срабатывает git clone
|
|
4658
|
+
Сразу после него событие addDir еще не успело прийти,
|
|
4659
|
+
на следующем перезапуске вызывается git pull, т.к.
|
|
4660
|
+
с точки зрения реактивной системы hyoo/board еще не существует.
|
|
4661
|
+
*/
|
|
4355
4662
|
this.changed.add(this.absolute(path));
|
|
4356
4663
|
}
|
|
4664
|
+
// protected static watch_wd = null as null | $mol_after_timeout
|
|
4357
4665
|
static unwatched(side_effect, affected_dir) {
|
|
4666
|
+
// ждем, пока выполнится предыдущий unwatched
|
|
4358
4667
|
const unlock = this.lock.grab();
|
|
4359
4668
|
this.watch_off(affected_dir);
|
|
4360
4669
|
try {
|
|
@@ -4377,6 +4686,7 @@ var $;
|
|
|
4377
4686
|
modified() { return this.stat()?.mtime ?? null; }
|
|
4378
4687
|
version() {
|
|
4379
4688
|
const next = this.stat()?.mtime.getTime().toString(36).toUpperCase() ?? '';
|
|
4689
|
+
// console.log('version', next, this.path())
|
|
4380
4690
|
return next;
|
|
4381
4691
|
}
|
|
4382
4692
|
info(path) { return null; }
|
|
@@ -4394,15 +4704,19 @@ var $;
|
|
|
4394
4704
|
writable(opts) {
|
|
4395
4705
|
return new WritableStream;
|
|
4396
4706
|
}
|
|
4707
|
+
// open( ... modes: readonly $mol_file_mode[] ) { return 0 }
|
|
4397
4708
|
buffer(next) {
|
|
4709
|
+
// Если версия пустая - возвращаем пустой буфер
|
|
4398
4710
|
let readed = new Uint8Array();
|
|
4399
4711
|
if (next === undefined) {
|
|
4712
|
+
// Если меняется версия файла, буфер надо перечитать
|
|
4400
4713
|
if (this.version())
|
|
4401
4714
|
readed = this.read();
|
|
4402
4715
|
}
|
|
4403
4716
|
const prev = $mol_mem_cached(() => this.buffer());
|
|
4404
4717
|
const changed = prev === undefined || !$mol_compare_array(prev, next ?? readed);
|
|
4405
4718
|
if (prev !== undefined && changed) {
|
|
4719
|
+
// Логируем, если повторно читаем/пишем и буфер поменялся
|
|
4406
4720
|
this.$.$mol_log3_rise({
|
|
4407
4721
|
place: `$mol_file_node.buffer()`,
|
|
4408
4722
|
message: 'Changed',
|
|
@@ -4411,6 +4725,11 @@ var $;
|
|
|
4411
4725
|
}
|
|
4412
4726
|
if (next === undefined)
|
|
4413
4727
|
return changed ? readed : prev;
|
|
4728
|
+
// Если буфер при записи не поменялся и файл не удаляли перед этим - не записываем новую версию.
|
|
4729
|
+
// Если записывать, это приведет к смене mtime и вотчер снова триггернется, даже если содержимое файла не поменялось.
|
|
4730
|
+
// В этом алгоритме есть изъян.
|
|
4731
|
+
// Если файл записали, потом отключили вотчер, кто-то из вне его поменял, потом включили вотчер, снова записали тот же буфер,
|
|
4732
|
+
// то буфер не запишется на диск, т.к. кэш не консистентен с диском.
|
|
4414
4733
|
if (!changed && this.exists())
|
|
4415
4734
|
return prev;
|
|
4416
4735
|
this.parent().exists(true);
|
|
@@ -4446,13 +4765,21 @@ var $;
|
|
|
4446
4765
|
}
|
|
4447
4766
|
return null;
|
|
4448
4767
|
}
|
|
4768
|
+
// static watch_root = ''
|
|
4769
|
+
// static watcher_warned = false
|
|
4449
4770
|
watcher() {
|
|
4771
|
+
// const constructor = this.constructor as typeof $mol_file_base
|
|
4772
|
+
// if (! constructor.watcher_warned) {
|
|
4773
|
+
// console.warn(`${constructor}.watcher() not implemented`)
|
|
4774
|
+
// constructor.watcher_warned = true
|
|
4775
|
+
// }
|
|
4450
4776
|
return {
|
|
4451
4777
|
destructor() { }
|
|
4452
4778
|
};
|
|
4453
4779
|
}
|
|
4454
4780
|
exists(next) {
|
|
4455
4781
|
const exists = Boolean(this.stat());
|
|
4782
|
+
// console.log('exists current', exists, 'next', next, this.path())
|
|
4456
4783
|
if (next === undefined)
|
|
4457
4784
|
return exists;
|
|
4458
4785
|
if (next === exists)
|
|
@@ -4478,6 +4805,10 @@ var $;
|
|
|
4478
4805
|
return match ? match[1].substring(1) : '';
|
|
4479
4806
|
}
|
|
4480
4807
|
text(next, virt) {
|
|
4808
|
+
// Если записываем text, и вотчер ресетнул записанный файл,
|
|
4809
|
+
// то надо снова его обновить, вызвать логику, которая делала пуш в text.
|
|
4810
|
+
// Например файл удалили, потом снова создали, версия поменялась - перезаписываем
|
|
4811
|
+
// Если использовать version, то вновь созданный файл, через вотчер запустит свое пересоздание
|
|
4481
4812
|
if (next !== undefined)
|
|
4482
4813
|
this.exists();
|
|
4483
4814
|
return this.text_int(next, virt);
|
|
@@ -4502,6 +4833,7 @@ var $;
|
|
|
4502
4833
|
if (this.type() !== 'dir')
|
|
4503
4834
|
return [];
|
|
4504
4835
|
this.version();
|
|
4836
|
+
// Если дочерний file удалился, список надо обновить
|
|
4505
4837
|
return this.kids().filter(file => file.exists());
|
|
4506
4838
|
}
|
|
4507
4839
|
resolve(path) {
|
|
@@ -4711,6 +5043,7 @@ var $;
|
|
|
4711
5043
|
])
|
|
4712
5044
|
].map(frame_normalize).join('\n')
|
|
4713
5045
|
});
|
|
5046
|
+
// в nodejs, что б не дублировалось cause в консоли
|
|
4714
5047
|
Object.defineProperty(this, 'cause', {
|
|
4715
5048
|
get: () => cause
|
|
4716
5049
|
});
|
|
@@ -4863,6 +5196,7 @@ var $;
|
|
|
4863
5196
|
});
|
|
4864
5197
|
return Object.assign(promise, {
|
|
4865
5198
|
destructor: () => {
|
|
5199
|
+
// Abort of done request breaks response parsing
|
|
4866
5200
|
if (!done && !controller.signal.aborted)
|
|
4867
5201
|
controller.abort();
|
|
4868
5202
|
},
|
|
@@ -4940,10 +5274,12 @@ var $;
|
|
|
4940
5274
|
let res = this.path() + '/' + path;
|
|
4941
5275
|
while (true) {
|
|
4942
5276
|
let prev = res;
|
|
5277
|
+
// foo/../ -> /
|
|
4943
5278
|
res = res.replace(/\/[^\/.]+\/\.\.\//, '/');
|
|
4944
5279
|
if (prev === res)
|
|
4945
5280
|
break;
|
|
4946
5281
|
}
|
|
5282
|
+
// http://localhost/.. -> http://localhost
|
|
4947
5283
|
res = res.replace(/\/\.\.\/?$/, '');
|
|
4948
5284
|
if (res === this.path())
|
|
4949
5285
|
return this;
|
|
@@ -5059,8 +5395,13 @@ var $;
|
|
|
5059
5395
|
(function ($) {
|
|
5060
5396
|
class $mol_file_web extends $mol_file_webdav {
|
|
5061
5397
|
static base = new URL('.', $mol_dom_context.document?.currentScript?.['src'] ?? globalThis.location.href).toString();
|
|
5398
|
+
// Вотчер выключен, версия всегда будет одна
|
|
5399
|
+
// Если пустая строка - будет считаться, что файла нет
|
|
5062
5400
|
version() { return '1'; }
|
|
5401
|
+
// Ворнинги подавляем, иначе в каждом приложении, загружающим локали, будет ворнинг
|
|
5402
|
+
// override watcher() { return { destructor() {} }}
|
|
5063
5403
|
info() {
|
|
5404
|
+
// Директории не поддерживаются
|
|
5064
5405
|
try {
|
|
5065
5406
|
const response = this.fetch({ method: 'HEAD' });
|
|
5066
5407
|
const headers = response.headers();
|
|
@@ -5094,6 +5435,10 @@ var $;
|
|
|
5094
5435
|
"use strict";
|
|
5095
5436
|
var $;
|
|
5096
5437
|
(function ($) {
|
|
5438
|
+
/**
|
|
5439
|
+
* Localisation in $mol framework
|
|
5440
|
+
* @see https://mol.hyoo.ru/#!section=docs/=s5aqnb_odub8l
|
|
5441
|
+
*/
|
|
5097
5442
|
class $mol_locale extends $mol_object {
|
|
5098
5443
|
static lang_default() {
|
|
5099
5444
|
return 'en';
|
|
@@ -5243,12 +5588,14 @@ var $;
|
|
|
5243
5588
|
'=>': bind => [],
|
|
5244
5589
|
'^': (ref, belt, context) => [
|
|
5245
5590
|
ref.struct('...', [
|
|
5591
|
+
// prop ^ foo
|
|
5246
5592
|
ref.kids[0]?.type
|
|
5247
5593
|
? ref.struct('()', [
|
|
5248
5594
|
ref.struct('this'),
|
|
5249
5595
|
ref.struct('[]', [ref.data(name_of.call(this, ref.kids[0]))]),
|
|
5250
5596
|
args_of.call(this, ref.kids[0])
|
|
5251
5597
|
])
|
|
5598
|
+
// Having $having foo / ^
|
|
5252
5599
|
: context.chain
|
|
5253
5600
|
? ref.struct('()', [
|
|
5254
5601
|
ref.struct('this'),
|
|
@@ -5260,6 +5607,7 @@ var $;
|
|
|
5260
5607
|
ref.struct('(,)', [ref.struct('obj')]),
|
|
5261
5608
|
...context.chain.slice(1).map(field => ref.struct('[]', [ref.data(field)]))
|
|
5262
5609
|
])
|
|
5610
|
+
// prop ^
|
|
5263
5611
|
: ref.struct('()', [
|
|
5264
5612
|
ref.struct('super'),
|
|
5265
5613
|
ref.struct('[]', [ref.data(name)]),
|