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.mjs 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(type, value, kids, span) {
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 normal = node.type.replace(/!\w+/, '*');
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
- sub_from = 0;
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);
@@ -3162,6 +3370,10 @@ var $;
3162
3370
  var $;
3163
3371
  (function ($) {
3164
3372
  $.$mol_compare_deep_cache = new WeakMap();
3373
+ /**
3374
+ * Deeply compares two values. Returns true if equal.
3375
+ * Define `Symbol.toPrimitive` to customize.
3376
+ */
3165
3377
  function $mol_compare_deep(left, right) {
3166
3378
  if (Object.is(left, right))
3167
3379
  return true;
@@ -3301,6 +3513,7 @@ var $;
3301
3513
  "use strict";
3302
3514
  var $;
3303
3515
  (function ($) {
3516
+ /** Log begin of collapsed group only when some logged inside, returns func to close group */
3304
3517
  function $mol_log3_area_lazy(event) {
3305
3518
  const self = this.$;
3306
3519
  const stack = self.$mol_log3_stack;
@@ -3325,6 +3538,7 @@ var $;
3325
3538
  "use strict";
3326
3539
  var $;
3327
3540
  (function ($) {
3541
+ /** Module for working with terminal. Text coloring when output in terminal */
3328
3542
  class $mol_term_color {
3329
3543
  static reset = this.ansi(0, 0);
3330
3544
  static bold = this.ansi(1, 22);
@@ -3396,6 +3610,7 @@ var $;
3396
3610
  "use strict";
3397
3611
  var $;
3398
3612
  (function ($) {
3613
+ /** One-shot fiber */
3399
3614
  class $mol_wire_task extends $mol_wire_fiber {
3400
3615
  static getter(task) {
3401
3616
  return function $mol_wire_task_get(host, args) {
@@ -3421,6 +3636,7 @@ var $;
3421
3636
  }
3422
3637
  const key = (host?.[Symbol.toStringTag] ?? host) + ('.' + task.name + '<#>');
3423
3638
  const next = new $mol_wire_task(key, task, host, args);
3639
+ // Disabled because non-idempotency is required for try-catch
3424
3640
  if (existen?.temp) {
3425
3641
  $$.$mol_log3_warn({
3426
3642
  place: '$mol_wire_task',
@@ -3453,7 +3669,7 @@ var $;
3453
3669
  try {
3454
3670
  next[Symbol.toStringTag] = this[Symbol.toStringTag];
3455
3671
  }
3456
- catch {
3672
+ catch { // Promises throw in strict mode
3457
3673
  Object.defineProperty(next, Symbol.toStringTag, { value: this[Symbol.toStringTag] });
3458
3674
  }
3459
3675
  }
@@ -3478,6 +3694,9 @@ var $;
3478
3694
  "use strict";
3479
3695
  var $;
3480
3696
  (function ($) {
3697
+ /**
3698
+ * Decorates method to fiber to ensure it is executed only once inside other fiber.
3699
+ */
3481
3700
  function $mol_wire_method(host, field, descr) {
3482
3701
  if (!descr)
3483
3702
  descr = Reflect.getOwnPropertyDescriptor(host, field);
@@ -3559,6 +3778,7 @@ var $;
3559
3778
  "use strict";
3560
3779
  var $;
3561
3780
  (function ($) {
3781
+ /** Long-living fiber. */
3562
3782
  class $mol_wire_atom extends $mol_wire_fiber {
3563
3783
  static solo(host, task) {
3564
3784
  const field = task.name + '()';
@@ -3609,7 +3829,11 @@ var $;
3609
3829
  }
3610
3830
  $mol_wire_atom.watching.add(this);
3611
3831
  }
3832
+ /**
3833
+ * Update atom value through another temp fiber.
3834
+ */
3612
3835
  resync(args) {
3836
+ // enforce pulling tasks abort
3613
3837
  for (let cursor = this.pub_from; cursor < this.sub_from; cursor += 2) {
3614
3838
  const pub = this.data[cursor];
3615
3839
  if (pub && pub instanceof $mol_wire_task) {
@@ -3670,7 +3894,7 @@ var $;
3670
3894
  try {
3671
3895
  next[Symbol.toStringTag] = this[Symbol.toStringTag];
3672
3896
  }
3673
- catch {
3897
+ catch { // Promises throw in strict mode
3674
3898
  Object.defineProperty(next, Symbol.toStringTag, { value: this[Symbol.toStringTag] });
3675
3899
  }
3676
3900
  }
@@ -3698,6 +3922,7 @@ var $;
3698
3922
  "use strict";
3699
3923
  var $;
3700
3924
  (function ($) {
3925
+ /** Decorates solo object channel to [mol_wire_atom](../atom/atom.ts). */
3701
3926
  function $mol_wire_solo(host, field, descr) {
3702
3927
  if (!descr)
3703
3928
  descr = Reflect.getOwnPropertyDescriptor(host, field);
@@ -3736,6 +3961,7 @@ var $;
3736
3961
  "use strict";
3737
3962
  var $;
3738
3963
  (function ($) {
3964
+ /** Reactive memoizing multiplexed property decorator. */
3739
3965
  function $mol_wire_plex(host, field, descr) {
3740
3966
  if (!descr)
3741
3967
  descr = Reflect.getOwnPropertyDescriptor(host, field);
@@ -3774,7 +4000,25 @@ var $;
3774
4000
  "use strict";
3775
4001
  var $;
3776
4002
  (function ($) {
4003
+ /**
4004
+ * Reactive memoizing solo property decorator from [mol_wire](../wire/README.md)
4005
+ * @example
4006
+ * '@' $mol_mem
4007
+ * name(next?: string) {
4008
+ * return next ?? 'default'
4009
+ * }
4010
+ * @see https://mol.hyoo.ru/#!section=docs/=qxmh6t_sinbmb
4011
+ */
3777
4012
  $.$mol_mem = $mol_wire_solo;
4013
+ /**
4014
+ * Reactive memoizing multiplexed property decorator [mol_wire](../wire/README.md)
4015
+ * @example
4016
+ * '@' $mol_mem_key
4017
+ * name(id: number, next?: string) {
4018
+ * return next ?? 'default'
4019
+ * }
4020
+ * @see https://mol.hyoo.ru/#!section=docs/=qxmh6t_sinbmb
4021
+ */
3778
4022
  $.$mol_mem_key = $mol_wire_plex;
3779
4023
  })($ || ($ = {}));
3780
4024
 
@@ -3791,7 +4035,7 @@ var $;
3791
4035
  "use strict";
3792
4036
  var $;
3793
4037
  (function ($) {
3794
- const mod = require('module');
4038
+ const mod = require /****/('module');
3795
4039
  const internals = mod.builtinModules;
3796
4040
  function $node_internal_check(name) {
3797
4041
  if (name.startsWith('node:'))
@@ -3805,8 +4049,8 @@ var $;
3805
4049
  "use strict";
3806
4050
  var $;
3807
4051
  (function ($) {
3808
- const path = require('path');
3809
- const mod = require('module');
4052
+ const path = require /****/('path');
4053
+ const mod = require /****/('module');
3810
4054
  const localRequire = mod.createRequire(path.join(process.cwd(), 'package.json'));
3811
4055
  function $node_autoinstall(name) {
3812
4056
  try {
@@ -3881,6 +4125,7 @@ var $;
3881
4125
  ])
3882
4126
  ].map(frame_normalize).join('\n')
3883
4127
  });
4128
+ // в nodejs, что б не дублировалось cause в консоли
3884
4129
  Object.defineProperty(this, 'cause', {
3885
4130
  get: () => cause
3886
4131
  });
@@ -3949,6 +4194,10 @@ var $;
3949
4194
  props[field] = get_val;
3950
4195
  return get_val;
3951
4196
  }
4197
+ /**
4198
+ * Convert asynchronous (promise-based) API to synchronous by wrapping function and method calls in a fiber.
4199
+ * @see https://mol.hyoo.ru/#!section=docs/=1fcpsq_1wh0h2
4200
+ */
3952
4201
  function $mol_wire_sync(obj) {
3953
4202
  return new Proxy(obj, {
3954
4203
  get(obj, field) {
@@ -4110,6 +4359,9 @@ var $;
4110
4359
  "use strict";
4111
4360
  var $;
4112
4361
  (function ($) {
4362
+ /**
4363
+ * Disable reaping of current subscriber
4364
+ */
4113
4365
  function $mol_wire_solid() {
4114
4366
  let current = $mol_wire_auto();
4115
4367
  if (current.temp)
@@ -4135,6 +4387,7 @@ var $;
4135
4387
  "use strict";
4136
4388
  var $;
4137
4389
  (function ($) {
4390
+ /** Run code without state changes */
4138
4391
  function $mol_wire_probe(task, def) {
4139
4392
  const warm = $mol_wire_fiber.warm;
4140
4393
  try {
@@ -4293,6 +4546,11 @@ var $;
4293
4546
  "use strict";
4294
4547
  var $;
4295
4548
  (function ($) {
4549
+ /**
4550
+ * Returns closure that returns constant value.
4551
+ * @example
4552
+ * const rnd = $mol_const( Math.random() )
4553
+ */
4296
4554
  function $mol_const(value) {
4297
4555
  const getter = (() => value);
4298
4556
  getter['()'] = value;
@@ -4307,6 +4565,10 @@ var $;
4307
4565
  "use strict";
4308
4566
  var $;
4309
4567
  (function ($) {
4568
+ /**
4569
+ * Decorates method to fiber to ensure it is executed only once inside other fiber from [mol_wire](../wire/README.md)
4570
+ * @see https://mol.hyoo.ru/#!section=docs/=1fcpsq_1wh0h2
4571
+ */
4310
4572
  $.$mol_action = $mol_wire_method;
4311
4573
  })($ || ($ = {}));
4312
4574
 
@@ -4314,6 +4576,7 @@ var $;
4314
4576
  "use strict";
4315
4577
  var $;
4316
4578
  (function ($) {
4579
+ /** Convert a pseudo-synchronous (Suspense API) API to an explicit asynchronous one (for integrating with external systems). */
4317
4580
  function $mol_wire_async(obj) {
4318
4581
  let fiber;
4319
4582
  const temp = $mol_wire_task.getter(obj);
@@ -4413,7 +4676,8 @@ var $;
4413
4676
  "use strict";
4414
4677
  var $;
4415
4678
  (function ($) {
4416
- let buf = new Uint8Array(2 ** 12);
4679
+ let buf = new Uint8Array(2 ** 12); // 4KB Mem Page
4680
+ /** Temporary buffer. Recursive usage isn't supported. */
4417
4681
  function $mol_charset_buffer(size) {
4418
4682
  if (buf.byteLength < size)
4419
4683
  buf = new Uint8Array(size);
@@ -4435,19 +4699,19 @@ var $;
4435
4699
  let pos = from;
4436
4700
  for (let i = 0; i < str.length; i++) {
4437
4701
  let code = str.charCodeAt(i);
4438
- if (code < 0x80) {
4702
+ if (code < 0x80) { // ASCII - 1 octet
4439
4703
  buf[pos++] = code;
4440
4704
  }
4441
- else if (code < 0x800) {
4705
+ else if (code < 0x800) { // 2 octet
4442
4706
  buf[pos++] = 0xc0 | (code >> 6);
4443
4707
  buf[pos++] = 0x80 | (code & 0x3f);
4444
4708
  }
4445
- else if (code < 0xd800 || code >= 0xe000) {
4709
+ else if (code < 0xd800 || code >= 0xe000) { // 3 octet
4446
4710
  buf[pos++] = 0xe0 | (code >> 12);
4447
4711
  buf[pos++] = 0x80 | ((code >> 6) & 0x3f);
4448
4712
  buf[pos++] = 0x80 | (code & 0x3f);
4449
4713
  }
4450
- else {
4714
+ else { // surrogate pair
4451
4715
  const point = ((code - 0xd800) << 10) + str.charCodeAt(++i) + 0x2400;
4452
4716
  buf[pos++] = 0xf0 | (point >> 18);
4453
4717
  buf[pos++] = 0x80 | ((point >> 12) & 0x3f);
@@ -4511,12 +4775,16 @@ var $;
4511
4775
  (function ($) {
4512
4776
  let file_modes;
4513
4777
  (function (file_modes) {
4778
+ /** create if it doesn't already exist */
4514
4779
  file_modes[file_modes["create"] = $node.fs.constants.O_CREAT] = "create";
4780
+ /** truncate to zero size if it already exists */
4515
4781
  file_modes[file_modes["exists_truncate"] = $node.fs.constants.O_TRUNC] = "exists_truncate";
4782
+ /** throw exception if it already exists */
4516
4783
  file_modes[file_modes["exists_fail"] = $node.fs.constants.O_EXCL] = "exists_fail";
4517
4784
  file_modes[file_modes["read_only"] = $node.fs.constants.O_RDONLY] = "read_only";
4518
4785
  file_modes[file_modes["write_only"] = $node.fs.constants.O_WRONLY] = "write_only";
4519
4786
  file_modes[file_modes["read_write"] = $node.fs.constants.O_RDWR] = "read_write";
4787
+ /** data will be appended to the end */
4520
4788
  file_modes[file_modes["append"] = $node.fs.constants.O_APPEND] = "append";
4521
4789
  })(file_modes || (file_modes = {}));
4522
4790
  function mode_mask(modes) {
@@ -4581,12 +4849,24 @@ var $;
4581
4849
  root() {
4582
4850
  const path = this.path();
4583
4851
  const base = this.constructor.base;
4852
+ // Если путь выше или равен base или если parent такойже как и this - считаем это корнем
4584
4853
  return base.startsWith(path) || this == this.parent();
4585
4854
  }
4586
4855
  stat(next, virt) {
4587
4856
  const path = this.path();
4588
4857
  const parent = this.parent();
4858
+ // Отслеживать проверку наличия родительской папки не стоит до корня диска
4859
+ // Лучше ограничить mam-ом
4589
4860
  if (!this.root()) {
4861
+ /*
4862
+ Если parent папка удалилась, надо ресетнуть все объекты в ней на любой глубине.
4863
+ Например, rm -rf с последующим git pull: parent папка может удалиться, потом создасться,
4864
+ а текущая папка успеет только удалиться до момента выполнения stat.
4865
+ Поэтому parent.exists() не запустит перевычисления, нужна именно parent.version()
4866
+
4867
+ Однако, parent.version() меняется не только при удалении, будет ложное срабатывание
4868
+ С этим придется мириться, красивого решения пока нет.
4869
+ */
4590
4870
  parent.version();
4591
4871
  }
4592
4872
  parent.watcher();
@@ -4600,9 +4880,19 @@ var $;
4600
4880
  if (/([\/\\]\.|___$)/.test(path))
4601
4881
  return;
4602
4882
  const file = this.relative(path.at(-1) === '/' ? path.slice(0, -1) : path);
4883
+ // console.log(type, path)
4884
+ // add (change): добавился файл - у parent надо обновить список sub, если он был заюзан
4885
+ // change, unlink (rename): обновился или удалился файл - ресетим
4886
+ // addDir (change), добавилась папка, у parent обновляем список директорий в sub
4887
+ // дочерние ресетим
4888
+ // unlinkDir (rename), удалилась папка, ресетим ее
4889
+ // stat у всех дочерних обновится сам, т.к. связан с parent.version()
4603
4890
  this.changed.add(file);
4604
4891
  if (!this.watching)
4605
4892
  return;
4893
+ // throttle, пока события поступают не сбрасываем.
4894
+ // аналог awaitWriteFinish из chokidar
4895
+ // интервалы между change-сообщениями модифицируемого файла должны быть меньше watch_debounce
4606
4896
  this.frame?.destructor();
4607
4897
  this.frame = new this.$.$mol_after_timeout(this.watch_debounce(), () => {
4608
4898
  if (!this.watching)
@@ -4611,8 +4901,16 @@ var $;
4611
4901
  $mol_wire_async(this).flush();
4612
4902
  });
4613
4903
  }
4904
+ /**
4905
+ * Должно быть больше, чем время между событиями от вотчера при записи внешним процессом.
4906
+ * Иначе запуск ресетов паралельно с изменением может привести к неконсистентности.
4907
+ */
4614
4908
  static watch_debounce() { return 500; }
4615
4909
  static flush() {
4910
+ // Пока flush работает, вотчер сюда не заходит, но может добавлять новые изменения
4911
+ // на каждом перезапуске они применятся
4912
+ // Пока run выполняется, изменения накапливаются, в конце run вызывается flush
4913
+ // Пока применяются изменения, run должен ожидать конца flush
4616
4914
  for (const file of this.changed) {
4617
4915
  const parent = file.parent();
4618
4916
  try {
@@ -4627,16 +4925,32 @@ var $;
4627
4925
  }
4628
4926
  this.changed.clear();
4629
4927
  this.watching = true;
4928
+ // this.watch_wd?.destructor()
4929
+ // this.watch_wd = null
4630
4930
  }
4631
4931
  static watching = true;
4632
4932
  static lock = new $mol_lock;
4633
4933
  static watch_off(path) {
4634
4934
  this.watching = false;
4935
+ // run должен ожидать конца flush
4635
4936
  this.flush();
4636
4937
  this.watching = false;
4938
+ /*
4939
+ watch запаздывает и событие может прилететь через 3 сек после окончания сайд эффекта
4940
+ поэтому добавляем папку, которую меняет side_effect
4941
+ Когда дойдет до выполнения flush, он ресетнет ее
4942
+
4943
+ Иначе будут лишние срабатывания
4944
+ Например, удалили hyoo/board, watch ресетит и exists начинает отдавать false, срабатывает git clone
4945
+ Сразу после него событие addDir еще не успело прийти,
4946
+ на следующем перезапуске вызывается git pull, т.к.
4947
+ с точки зрения реактивной системы hyoo/board еще не существует.
4948
+ */
4637
4949
  this.changed.add(this.absolute(path));
4638
4950
  }
4951
+ // protected static watch_wd = null as null | $mol_after_timeout
4639
4952
  static unwatched(side_effect, affected_dir) {
4953
+ // ждем, пока выполнится предыдущий unwatched
4640
4954
  const unlock = this.lock.grab();
4641
4955
  this.watch_off(affected_dir);
4642
4956
  try {
@@ -4659,6 +4973,7 @@ var $;
4659
4973
  modified() { return this.stat()?.mtime ?? null; }
4660
4974
  version() {
4661
4975
  const next = this.stat()?.mtime.getTime().toString(36).toUpperCase() ?? '';
4976
+ // console.log('version', next, this.path())
4662
4977
  return next;
4663
4978
  }
4664
4979
  info(path) { return null; }
@@ -4676,15 +4991,19 @@ var $;
4676
4991
  writable(opts) {
4677
4992
  return new WritableStream;
4678
4993
  }
4994
+ // open( ... modes: readonly $mol_file_mode[] ) { return 0 }
4679
4995
  buffer(next) {
4996
+ // Если версия пустая - возвращаем пустой буфер
4680
4997
  let readed = new Uint8Array();
4681
4998
  if (next === undefined) {
4999
+ // Если меняется версия файла, буфер надо перечитать
4682
5000
  if (this.version())
4683
5001
  readed = this.read();
4684
5002
  }
4685
5003
  const prev = $mol_mem_cached(() => this.buffer());
4686
5004
  const changed = prev === undefined || !$mol_compare_array(prev, next ?? readed);
4687
5005
  if (prev !== undefined && changed) {
5006
+ // Логируем, если повторно читаем/пишем и буфер поменялся
4688
5007
  this.$.$mol_log3_rise({
4689
5008
  place: `$mol_file_node.buffer()`,
4690
5009
  message: 'Changed',
@@ -4693,6 +5012,11 @@ var $;
4693
5012
  }
4694
5013
  if (next === undefined)
4695
5014
  return changed ? readed : prev;
5015
+ // Если буфер при записи не поменялся и файл не удаляли перед этим - не записываем новую версию.
5016
+ // Если записывать, это приведет к смене mtime и вотчер снова триггернется, даже если содержимое файла не поменялось.
5017
+ // В этом алгоритме есть изъян.
5018
+ // Если файл записали, потом отключили вотчер, кто-то из вне его поменял, потом включили вотчер, снова записали тот же буфер,
5019
+ // то буфер не запишется на диск, т.к. кэш не консистентен с диском.
4696
5020
  if (!changed && this.exists())
4697
5021
  return prev;
4698
5022
  this.parent().exists(true);
@@ -4728,13 +5052,21 @@ var $;
4728
5052
  }
4729
5053
  return null;
4730
5054
  }
5055
+ // static watch_root = ''
5056
+ // static watcher_warned = false
4731
5057
  watcher() {
5058
+ // const constructor = this.constructor as typeof $mol_file_base
5059
+ // if (! constructor.watcher_warned) {
5060
+ // console.warn(`${constructor}.watcher() not implemented`)
5061
+ // constructor.watcher_warned = true
5062
+ // }
4732
5063
  return {
4733
5064
  destructor() { }
4734
5065
  };
4735
5066
  }
4736
5067
  exists(next) {
4737
5068
  const exists = Boolean(this.stat());
5069
+ // console.log('exists current', exists, 'next', next, this.path())
4738
5070
  if (next === undefined)
4739
5071
  return exists;
4740
5072
  if (next === exists)
@@ -4760,6 +5092,10 @@ var $;
4760
5092
  return match ? match[1].substring(1) : '';
4761
5093
  }
4762
5094
  text(next, virt) {
5095
+ // Если записываем text, и вотчер ресетнул записанный файл,
5096
+ // то надо снова его обновить, вызвать логику, которая делала пуш в text.
5097
+ // Например файл удалили, потом снова создали, версия поменялась - перезаписываем
5098
+ // Если использовать version, то вновь созданный файл, через вотчер запустит свое пересоздание
4763
5099
  if (next !== undefined)
4764
5100
  this.exists();
4765
5101
  return this.text_int(next, virt);
@@ -4784,6 +5120,7 @@ var $;
4784
5120
  if (this.type() !== 'dir')
4785
5121
  return [];
4786
5122
  this.version();
5123
+ // Если дочерний file удалился, список надо обновить
4787
5124
  return this.kids().filter(file => file.exists());
4788
5125
  }
4789
5126
  resolve(path) {
@@ -4928,10 +5265,15 @@ var $;
4928
5265
  watcher(reset) {
4929
5266
  const path = this.path();
4930
5267
  const root = this.root();
5268
+ // Если папки/файла нет, watch упадет с ошибкой
5269
+ // exists обратится к parent.version и parent.watcher
5270
+ // Поэтому у root-папки и выше не надо вызывать exists, иначе поднимется выше base до корня диска
5271
+ // exists вызывать надо, что б пересоздавать вотчер при появлении папки или файла
4931
5272
  if (!root && !this.exists())
4932
5273
  return super.watcher();
4933
5274
  let watcher;
4934
5275
  try {
5276
+ // Между exists и watch файл может удалиться, в любом случае надо обрабатывать ENOENT
4935
5277
  watcher = $node.fs.watch(path);
4936
5278
  }
4937
5279
  catch (error) {
@@ -4941,6 +5283,8 @@ var $;
4941
5283
  if (root || error.code !== 'ENOENT') {
4942
5284
  this.$.$mol_fail_log(error);
4943
5285
  }
5286
+ // Если файла нет - вотчер не создается, создастся потом, когда exists поменяется на true.
5287
+ // Если создание упало с другой ошибкой - не ломаем работу mol_file, деградируем до не реактивной fs.
4944
5288
  return super.watcher();
4945
5289
  }
4946
5290
  watcher.on('change', (type, name) => {
@@ -4952,6 +5296,7 @@ var $;
4952
5296
  watcher.on('error', e => this.$.$mol_fail_log(e));
4953
5297
  let destructed = false;
4954
5298
  watcher.on('close', () => {
5299
+ // Если в процессе работы вотчер сам закрылся, надо его переоткрыть
4955
5300
  if (!destructed)
4956
5301
  setTimeout(() => $mol_wire_async(this).watcher(null), 500);
4957
5302
  });
@@ -5133,6 +5478,10 @@ var $;
5133
5478
  "use strict";
5134
5479
  var $;
5135
5480
  (function ($) {
5481
+ /**
5482
+ * Localisation in $mol framework
5483
+ * @see https://mol.hyoo.ru/#!section=docs/=s5aqnb_odub8l
5484
+ */
5136
5485
  class $mol_locale extends $mol_object {
5137
5486
  static lang_default() {
5138
5487
  return 'en';
@@ -5282,12 +5631,14 @@ var $;
5282
5631
  '=>': bind => [],
5283
5632
  '^': (ref, belt, context) => [
5284
5633
  ref.struct('...', [
5634
+ // prop ^ foo
5285
5635
  ref.kids[0]?.type
5286
5636
  ? ref.struct('()', [
5287
5637
  ref.struct('this'),
5288
5638
  ref.struct('[]', [ref.data(name_of.call(this, ref.kids[0]))]),
5289
5639
  args_of.call(this, ref.kids[0])
5290
5640
  ])
5641
+ // Having $having foo / ^
5291
5642
  : context.chain
5292
5643
  ? ref.struct('()', [
5293
5644
  ref.struct('this'),
@@ -5299,6 +5650,7 @@ var $;
5299
5650
  ref.struct('(,)', [ref.struct('obj')]),
5300
5651
  ...context.chain.slice(1).map(field => ref.struct('[]', [ref.data(field)]))
5301
5652
  ])
5653
+ // prop ^
5302
5654
  : ref.struct('()', [
5303
5655
  ref.struct('super'),
5304
5656
  ref.struct('[]', [ref.data(name)]),